-
Notifications
You must be signed in to change notification settings - Fork 0
/
spawner.lua
197 lines (143 loc) · 5.14 KB
/
spawner.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
local S = minetest.get_translator("mobs")
local max_per_block = tonumber(minetest.settings:get("max_objects_per_block") or 99)
-- helper functions
local function is_player(player)
if player and type(player) == "userdata" and minetest.is_player(player) then
return true
end
end
local square = math.sqrt
local get_distance = function(a, b)
if not a or not b then return 50 end -- nil check and default distance
local x, y, z = a.x - b.x, a.y - b.y, a.z - b.z
return square(x * x + y * y + z * z)
end
-- mob spawner
local spawner_default = "mobs_animal:pumba 10 15 0 0 0"
minetest.register_node("mobs:spawner", {
tiles = {"mob_spawner.png"},
drawtype = "glasslike",
paramtype = "light",
walkable = true,
description = S("Mob Spawner"),
groups = {cracky = 1, pickaxey = 3},
is_ground_content = false,
_mcl_hardness = 1,
_mcl_blast_resistance = 5,
sounds = mobs.node_sound_stone_defaults(),
on_construct = function(pos)
local meta = minetest.get_meta(pos)
-- setup formspec
local head = S("(mob name) (min light) (max light) (amount)"
.. " (player distance) (Y offset)")
-- text entry formspec
meta:set_string("formspec", "size[10,3.5]"
.. "label[0.15,0.5;" .. minetest.formspec_escape(head) .. "]"
.. "field[1,2.5;8.5,0.8;text;" .. S("Command:")
.. ";${command}]")
meta:set_string("infotext", S("Spawner Not Active (enter settings)"))
meta:set_string("command", spawner_default)
end,
on_right_click = function(pos, placer)
if minetest.is_protected(pos, placer:get_player_name()) then return end
end,
on_receive_fields = function(pos, formname, fields, sender)
if not fields.text or fields.text == "" then return end
local meta = minetest.get_meta(pos)
local comm = fields.text:split(" ")
local name = sender:get_player_name()
if minetest.is_protected(pos, name) then
minetest.record_protection_violation(pos, name)
return
end
local mob = comm[1] or "" -- mob to spawn
local mlig = tonumber(comm[2]) -- min light
local xlig = tonumber(comm[3]) -- max light
local num = tonumber(comm[4]) -- total mobs in area
local pla = tonumber(comm[5]) -- player distance (0 to disable)
local yof = tonumber(comm[6]) or 0 -- Y offset to spawn mob
if mob ~= "" and mobs.spawning_mobs[mob] and num and num >= 0 and num <= 10
and mlig and mlig >= 0 and mlig <= 15 and xlig and xlig >= 0 and xlig <= 15
and pla and pla >= 0 and pla <= 20 and yof and yof > -10 and yof < 10 then
meta:set_string("command", fields.text)
meta:set_string("infotext", S("Spawner Active (@1)", mob))
else
minetest.chat_send_player(name, S("Mob Spawner settings failed!"))
minetest.chat_send_player(name,
S("Syntax: “name min_light[0-14] max_light[0-14] "
.. "max_mobs_in_area[0 to disable] player_distance[1-20] "
.. "y_offset[-10 to 10]”"))
end
end
})
-- spawner abm
minetest.register_abm({
label = "Mob spawner node",
nodenames = {"mobs:spawner"},
interval = 10,
chance = 4,
catch_up = false,
action = function(pos, node, active_object_count, active_object_count_wider)
-- return if too many entities already
if active_object_count_wider >= max_per_block then return end
-- get meta and command
local meta = minetest.get_meta(pos)
local comm = meta:get_string("command"):split(" ")
-- get settings from command
local mob = comm[1]
local mlig = tonumber(comm[2])
local xlig = tonumber(comm[3])
local num = tonumber(comm[4])
local pla = tonumber(comm[5]) or 0
local yof = tonumber(comm[6]) or 0
-- if amount is 0 then do nothing
if num == 0 then return end
-- are we spawning a registered mob?
if not mobs.spawning_mobs[mob] then
--print ("--- mob doesn't exist", mob)
return
end
-- check objects inside 9x9 area around spawner
local objs = minetest.get_objects_inside_radius(pos, 9)
local count = 0
local ent
-- count mob objects of same type in area
for _, obj in ipairs(objs) do
ent = obj:get_luaentity()
if ent and ent.name and ent.name == mob then count = count + 1 end
end
-- is there too many of same type?
if count >= num then return end
-- when player distance above 0, spawn mob if player detected and in range
if pla > 0 then
local in_range, player
local players = minetest.get_connected_players()
for i = 1, #players do
player = players[i]
if get_distance(player:get_pos(), pos) <= pla then
in_range = true
break
end
end
-- player not found
if not in_range then return end
end
-- set medium mob usually spawns in (defaults to air)
local reg = minetest.registered_entities[mob].fly_in
if not reg or type(reg) == "string" then reg = {(reg or "air")} end
-- find air blocks within 5 nodes of spawner
local air = minetest.find_nodes_in_area(
{x = pos.x - 5, y = pos.y + yof, z = pos.z - 5},
{x = pos.x + 5, y = pos.y + yof, z = pos.z + 5}, reg)
-- spawn in random air block
if air and #air > 0 then
local pos2 = air[math.random(#air)]
local lig = minetest.get_node_light(pos2) or 0
pos2.y = pos2.y + 0.5
-- only if light levels are within range
if lig >= mlig and lig <= xlig and minetest.registered_entities[mob] then
minetest.add_entity(pos2, mob)
end
end
end
})