From b6aafedf25a2b8dd3bc598cc8a9b49de8fb3697f Mon Sep 17 00:00:00 2001 From: teknomunk Date: Mon, 17 Jun 2024 07:15:04 -0500 Subject: [PATCH 01/13] Fix space check function has_room() in mcl_mobs/spawning.lua so it allows spiderproofing --- mods/ENTITIES/mcl_mobs/spawning.lua | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/mods/ENTITIES/mcl_mobs/spawning.lua b/mods/ENTITIES/mcl_mobs/spawning.lua index d47b2212f..1fb07247c 100644 --- a/mods/ENTITIES/mcl_mobs/spawning.lua +++ b/mods/ENTITIES/mcl_mobs/spawning.lua @@ -542,13 +542,20 @@ local function has_room(self,pos) end end table.insert(nodes,"air") - local x = cb[4] - cb[1] - local y = cb[5] - cb[2] - local z = cb[6] - cb[3] - local r = math.ceil(x * y * z) - local p1 = vector.offset(pos,cb[1],cb[2],cb[3]) - local p2 = vector.offset(pos,cb[4],cb[5],cb[6]) - local n = #minetest.find_nodes_in_area(p1,p2,nodes) or 0 + + local p1 = vector.offset(pos,cb[1],cb[2] + 1,cb[3]) + p1.x = math.floor(p1.x) + p1.y = math.floor(p1.y) + p1.z = math.floor(p1.z) + + local p2 = vector.offset(p1,cb[4] - cb[1], cb[5] - cb[2], cb[6] - cb[3]) + p2.x = math.floor(p2.x) + p2.y = math.floor(p2.y) + p2.z = math.floor(p2.z) + + local r = (p2.x - p1.x + 1) * (p2.y - p1.y + 1) * (p2.z - p1.z + 1) + local found_nodes = minetest.find_nodes_in_area(p1,p2,nodes) + local n = #found_nodes or 0 if r > n then minetest.log("warning","[mcl_mobs] No room for mob "..self.name.." at "..minetest.pos_to_string(vector.round(pos))) return false From d8d39ffd52a1c36315109f98cac53ee85a482b26 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Mon, 17 Jun 2024 20:27:55 -0500 Subject: [PATCH 02/13] Add spawnbox parameter that overrides collision box for spawn volume checks --- mods/ENTITIES/mcl_mobs/spawning.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mods/ENTITIES/mcl_mobs/spawning.lua b/mods/ENTITIES/mcl_mobs/spawning.lua index 1fb07247c..aed87631c 100644 --- a/mods/ENTITIES/mcl_mobs/spawning.lua +++ b/mods/ENTITIES/mcl_mobs/spawning.lua @@ -531,7 +531,7 @@ local function get_water_spawn(p) end local function has_room(self,pos) - local cb = self.collisionbox + local cb = self.spawnbox or self.collisionbox local nodes = {} if self.fly_in then local t = type(self.fly_in) From fa09b650108ed8d79e22f9e9ba4235aa3b0a2a5d Mon Sep 17 00:00:00 2001 From: teknomunk Date: Tue, 18 Jun 2024 06:07:30 -0500 Subject: [PATCH 03/13] Add most of the code for sub-node accurate spawning volume check (needs a function to calculate bounding box height of nodes) --- mods/ENTITIES/mcl_mobs/spawning.lua | 44 +++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/mods/ENTITIES/mcl_mobs/spawning.lua b/mods/ENTITIES/mcl_mobs/spawning.lua index aed87631c..45516c754 100644 --- a/mods/ENTITIES/mcl_mobs/spawning.lua +++ b/mods/ENTITIES/mcl_mobs/spawning.lua @@ -548,19 +548,47 @@ local function has_room(self,pos) p1.y = math.floor(p1.y) p1.z = math.floor(p1.z) - local p2 = vector.offset(p1,cb[4] - cb[1], cb[5] - cb[2], cb[6] - cb[3]) + local cb_height = cb[5] - cb[2] + local p2 = vector.offset(p1,cb[4] - cb[1], cb_height, cb[6] - cb[3]) p2.x = math.floor(p2.x) p2.y = math.floor(p2.y) p2.z = math.floor(p2.z) - local r = (p2.x - p1.x + 1) * (p2.y - p1.y + 1) * (p2.z - p1.z + 1) - local found_nodes = minetest.find_nodes_in_area(p1,p2,nodes) - local n = #found_nodes or 0 - if r > n then - minetest.log("warning","[mcl_mobs] No room for mob "..self.name.." at "..minetest.pos_to_string(vector.round(pos))) - return false + -- Check if the entire spawn volume is free + local dx = p2.x - p1.x + 1 + local dy = p2.y - p1.y + 1 + local dz = p2.z - p1.z + 1 + local n = #minetest.find_nodes_in_area(p1,p2,nodes) or 0 + if n == ( dx * dz * dz ) then return true end + + -- Make sure the entire volume except for the top level is free before checking the top layer + if dy > 1 then + n = #minetest.find_nodes_in_area(p1, vector.offset(p2, 0, -1, 0), nodes) + if n < (dx * dz * ( dy - 1)) then return false end end - return true + + -- Check the top layer to see if we have enough space to spawn in + local top_layer_height = 1 + local processed = {} + for x = p1.x,p2.x do + for z = p1.z,p2.z do + local node = minetest.get_node(vector.new(x,p2.y,z)) or { name = "ignore" } + if not processed[node.name] then + local nodedef = minetest.registered_nodes + + -- TODO: calculate node bounding box and select the lowest y value + local lowest_y = 0 + top_layer_height = math.min(lowest_y, top_layer_height) + + processed[node.name] = true + end + end + end + if top_layer_height + dy - 1 >= cb_height then return true end + + -- We don't have room + mcl_log("No room for mob "..self.name.." at "..minetest.pos_to_string(vector.round(pos))) + return false end mcl_mobs.custom_biomecheck = nil From 4d58f63485945709e8026e32a377f6bbe30fc265 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Mon, 24 Jun 2024 08:53:20 -0500 Subject: [PATCH 04/13] Implement partial node spawning check --- mods/ENTITIES/mcl_mobs/spawning.lua | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/mods/ENTITIES/mcl_mobs/spawning.lua b/mods/ENTITIES/mcl_mobs/spawning.lua index 45516c754..43a4e5dfa 100644 --- a/mods/ENTITIES/mcl_mobs/spawning.lua +++ b/mods/ENTITIES/mcl_mobs/spawning.lua @@ -572,15 +572,20 @@ local function has_room(self,pos) local processed = {} for x = p1.x,p2.x do for z = p1.z,p2.z do - local node = minetest.get_node(vector.new(x,p2.y,z)) or { name = "ignore" } - if not processed[node.name] then - local nodedef = minetest.registered_nodes + local test_pos = vector.new(x,p2.y,z) + local node = minetest.get_node(test_pos) or { name = "ignore" } + local cache_name = string.format("%s-%d", node.name, node.param2) + if not processed[cache_name] then + -- Calculate node bounding box and select the lowest y value + local boxes = minetest.get_node_boxes("collision_box", test_pos, node) + for i = 1,#boxes do + local box = boxes[i] + local y_test = box[2] + 0.5 + if y_test < top_layer_height then top_layer_height = y_test end - -- TODO: calculate node bounding box and select the lowest y value - local lowest_y = 0 - top_layer_height = math.min(lowest_y, top_layer_height) - - processed[node.name] = true + local y_test = box[5] + 0.5 + if y_test < top_layer_height then top_layer_height = y_test end + end end end end From c41ce8ba59d4770199a771788abdeae181ab138b Mon Sep 17 00:00:00 2001 From: teknomunk Date: Mon, 24 Jun 2024 09:36:22 -0500 Subject: [PATCH 05/13] Make spiders require 3x1x3 space to spawn --- mods/ENTITIES/mobs_mc/spider.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/mods/ENTITIES/mobs_mc/spider.lua b/mods/ENTITIES/mobs_mc/spider.lua index d01c7afbe..7fd8909ba 100644 --- a/mods/ENTITIES/mobs_mc/spider.lua +++ b/mods/ENTITIES/mobs_mc/spider.lua @@ -67,6 +67,7 @@ local spider = { curiosity = 10, head_yaw="z", collisionbox = {-0.7, -0.01, -0.7, 0.7, 0.89, 0.7}, + spawnbox = {-1.2, -0.01, -1.2, 1.2, 0.89, 1.2}, visual = "mesh", mesh = "mobs_mc_spider.b3d", textures = { From fa3df0d8c6920bdf411f9aed07afd28a557ef7bf Mon Sep 17 00:00:00 2001 From: teknomunk Date: Wed, 26 Jun 2024 15:56:21 -0500 Subject: [PATCH 06/13] Add check for presence of minetest.get_node_boxes before attempting sub-node space checks --- mods/ENTITIES/mcl_mobs/spawning.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mods/ENTITIES/mcl_mobs/spawning.lua b/mods/ENTITIES/mcl_mobs/spawning.lua index 43a4e5dfa..987918e99 100644 --- a/mods/ENTITIES/mcl_mobs/spawning.lua +++ b/mods/ENTITIES/mcl_mobs/spawning.lua @@ -561,6 +561,9 @@ local function has_room(self,pos) local n = #minetest.find_nodes_in_area(p1,p2,nodes) or 0 if n == ( dx * dz * dz ) then return true end + -- If we don't have an implementation of get_node_boxes, we can't check for sub-node space + if not minetest.get_node_boxes then return false end + -- Make sure the entire volume except for the top level is free before checking the top layer if dy > 1 then n = #minetest.find_nodes_in_area(p1, vector.offset(p2, 0, -1, 0), nodes) From 15efd00a29b415221e4da17f91ab858648004829 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Wed, 26 Jun 2024 19:18:53 -0500 Subject: [PATCH 07/13] Replace second call to minetest.find_nodes_in_area with checking top layer for matching nodes, change p2 calculation to use ceil(value) - 1, fix dx*dy*dz calculation --- mods/ENTITIES/mcl_mobs/spawning.lua | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/mods/ENTITIES/mcl_mobs/spawning.lua b/mods/ENTITIES/mcl_mobs/spawning.lua index 987918e99..515b96e66 100644 --- a/mods/ENTITIES/mcl_mobs/spawning.lua +++ b/mods/ENTITIES/mcl_mobs/spawning.lua @@ -550,23 +550,31 @@ local function has_room(self,pos) local cb_height = cb[5] - cb[2] local p2 = vector.offset(p1,cb[4] - cb[1], cb_height, cb[6] - cb[3]) - p2.x = math.floor(p2.x) - p2.y = math.floor(p2.y) - p2.z = math.floor(p2.z) + p2.x = math.ceil(p2.x) - 1 + p2.y = math.ceil(p2.y) - 1 + p2.z = math.ceil(p2.z) - 1 -- Check if the entire spawn volume is free local dx = p2.x - p1.x + 1 local dy = p2.y - p1.y + 1 local dz = p2.z - p1.z + 1 - local n = #minetest.find_nodes_in_area(p1,p2,nodes) or 0 - if n == ( dx * dz * dz ) then return true end + local found_nodes = minetest.find_nodes_in_area(p1,p2,nodes) or 0 + local n = #found_nodes + if n == ( dx * dy * dz ) then return true end -- If we don't have an implementation of get_node_boxes, we can't check for sub-node space if not minetest.get_node_boxes then return false end -- Make sure the entire volume except for the top level is free before checking the top layer if dy > 1 then - n = #minetest.find_nodes_in_area(p1, vector.offset(p2, 0, -1, 0), nodes) + -- Remove nodes in the top layer from the count + for i = 1,#found_nodes do + if found_nodes[i].y == p2.y then + n = n - 1 + end + end + + -- If the entire volume except the top layer isn't air (or nodes) then we can't spawn this mob here if n < (dx * dz * ( dy - 1)) then return false end end From 8ef08128b16a92013fef80810a72f437e411fc70 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Wed, 26 Jun 2024 19:24:55 -0500 Subject: [PATCH 08/13] Add short circuit if sub-node space check isn't possible: --- mods/ENTITIES/mcl_mobs/spawning.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mods/ENTITIES/mcl_mobs/spawning.lua b/mods/ENTITIES/mcl_mobs/spawning.lua index 515b96e66..86c942be0 100644 --- a/mods/ENTITIES/mcl_mobs/spawning.lua +++ b/mods/ENTITIES/mcl_mobs/spawning.lua @@ -565,6 +565,10 @@ local function has_room(self,pos) -- If we don't have an implementation of get_node_boxes, we can't check for sub-node space if not minetest.get_node_boxes then return false end + -- Check if it's possible for a sub-node space check to succeed + local needed_in_bottom_section = (dx * dz * ( dy - 1)) + if n < needed_in_bottom_section then return false end + -- Make sure the entire volume except for the top level is free before checking the top layer if dy > 1 then -- Remove nodes in the top layer from the count @@ -575,7 +579,7 @@ local function has_room(self,pos) end -- If the entire volume except the top layer isn't air (or nodes) then we can't spawn this mob here - if n < (dx * dz * ( dy - 1)) then return false end + if n < needed_in_bottom_section then return false end end -- Check the top layer to see if we have enough space to spawn in From 6c50e0a82b97c118eedcc370df2bd2ade8ae50fa Mon Sep 17 00:00:00 2001 From: teknomunk Date: Fri, 13 Sep 2024 17:10:05 -0500 Subject: [PATCH 09/13] Fix volume used for room check during spawn, make mcl_mobs.spawn check for room before adding entity, change iron golems and mob spawners to use mcl_mobs.spawn --- mods/CORE/mcl_util/init.lua | 12 ++++++++ mods/ENTITIES/mcl_mobs/spawning.lua | 40 +++++++++++++++++++-------- mods/ENTITIES/mobs_mc/villager.lua | 18 +++++++----- mods/ITEMS/mcl_mobspawners/init.lua | 43 ++++++++++------------------- 4 files changed, 66 insertions(+), 47 deletions(-) diff --git a/mods/CORE/mcl_util/init.lua b/mods/CORE/mcl_util/init.lua index 1f53feef7..36cbf2e63 100644 --- a/mods/CORE/mcl_util/init.lua +++ b/mods/CORE/mcl_util/init.lua @@ -48,6 +48,18 @@ function table.pairs_by_keys(t, f) return iter end +local function table.pull_random_items(table) + local count = #table + return function() + local idx = math.random(count) + local res = table[idx] + table[idx] = table[count] + table[count] = nil + count = count - 1 + return res + end +end + local LOGGING_ON = minetest.settings:get_bool("mcl_logging_default", false) local LOG_MODULE = "[MCL2]" function mcl_util.mcl_log(message, module, bypass_default_logger) diff --git a/mods/ENTITIES/mcl_mobs/spawning.lua b/mods/ENTITIES/mcl_mobs/spawning.lua index 86c942be0..8a2e099cf 100644 --- a/mods/ENTITIES/mcl_mobs/spawning.lua +++ b/mods/ENTITIES/mcl_mobs/spawning.lua @@ -530,7 +530,7 @@ local function get_water_spawn(p) end end -local function has_room(self,pos) +local function has_room(self, pos) local cb = self.spawnbox or self.collisionbox local nodes = {} if self.fly_in then @@ -543,16 +543,16 @@ local function has_room(self,pos) end table.insert(nodes,"air") - local p1 = vector.offset(pos,cb[1],cb[2] + 1,cb[3]) - p1.x = math.floor(p1.x) - p1.y = math.floor(p1.y) - p1.z = math.floor(p1.z) - + -- Calculate area to check for room local cb_height = cb[5] - cb[2] - local p2 = vector.offset(p1,cb[4] - cb[1], cb_height, cb[6] - cb[3]) - p2.x = math.ceil(p2.x) - 1 - p2.y = math.ceil(p2.y) - 1 - p2.z = math.ceil(p2.z) - 1 + local p1 = vector.new( + math.round(pos.x + cb[1]), + pos.y, + math.round(pos.z + cb[3])) + local p2 = vector.new( + math.round(pos.x + cb[4]), + math.ceil(p1.y + cb_height) - 1, + math.round(pos.z + cb[6])) -- Check if the entire spawn volume is free local dx = p2.x - p1.x + 1 @@ -560,7 +560,23 @@ local function has_room(self,pos) local dz = p2.z - p1.z + 1 local found_nodes = minetest.find_nodes_in_area(p1,p2,nodes) or 0 local n = #found_nodes - if n == ( dx * dy * dz ) then return true end + --[[ + minetest.log(dump({ + cb = cb, + pos = pos, + n = n, + dx = dx, + dy = dy, + dz = dz, + p1 = p1, + p2 = p2, + found_nodes = found_nodes, + nodes = nodes, + })) + ]] + if n == ( dx * dy * dz ) then + return true + end -- If we don't have an implementation of get_node_boxes, we can't check for sub-node space if not minetest.get_node_boxes then return false end @@ -699,10 +715,10 @@ function mcl_mobs.spawn(pos,id) if not pos or not id then return false end local def = minetest.registered_entities[id] or minetest.registered_entities["mobs_mc:"..id] or minetest.registered_entities["extra_mobs:"..id] if not def or not def.is_mob or (def.can_spawn and not def.can_spawn(pos)) then return false end + if not has_room(def, pos) then return false end return minetest.add_entity(pos, def.name) end - local function spawn_group(p,mob,spawn_on,amount_to_spawn) local nn= minetest.find_nodes_in_area_under_air(vector.offset(p,-5,-3,-5),vector.offset(p,5,3,5),spawn_on) local o diff --git a/mods/ENTITIES/mobs_mc/villager.lua b/mods/ENTITIES/mobs_mc/villager.lua index 47b4cb470..76cf2715d 100644 --- a/mods/ENTITIES/mobs_mc/villager.lua +++ b/mods/ENTITIES/mobs_mc/villager.lua @@ -1049,14 +1049,18 @@ local function has_summon_participants(self) end local function summon_golem(self) - vector.offset(self.object:get_pos(),-10,-10,-10) - local nn = minetest.find_nodes_in_area_under_air(vector.offset(self.object:get_pos(),-10,-10,-10),vector.offset(self.object:get_pos(),10,10,10),{"group:solid","group:water"}) - table.shuffle(nn) - for _,n in pairs(nn) do - local up = minetest.find_nodes_in_area(vector.offset(n,0,1,0),vector.offset(n,0,3,0),{"air"}) - if up and #up >= 3 then + local pos = self.object:get_pos() + local p1 = vector.offset(pos, -10, -10, -10) + local p2 = vector.offset(pos, 10, 10, 10) + local nn = minetest.find_nodes_in_area_under_air(p1, p2,{"group:solid","group:water"}) + for n in table.pull_random_items(nn) do + n.y = n.y + 1 + minetest.log("trying to summon golem at "..vector.to_string(n)) + + local summon = mcl_mobs.spawn(n, "mobs_mc:iron_golem") + if summon then minetest.sound_play("mcl_portals_open_end_portal", {pos=n, gain=0.5, max_hear_distance = 16}, true) - return minetest.add_entity(vector.offset(n,0,1,0),"mobs_mc:iron_golem") + return summon end end end diff --git a/mods/ITEMS/mcl_mobspawners/init.lua b/mods/ITEMS/mcl_mobspawners/init.lua index f2ee44ab1..13fc1010e 100644 --- a/mods/ITEMS/mcl_mobspawners/init.lua +++ b/mods/ITEMS/mcl_mobspawners/init.lua @@ -147,20 +147,14 @@ local function spawn_mobs(pos, elapsed) -- get meta local meta = minetest.get_meta(pos) - -- get settings - local mob = meta:get_string("Mob") - local mlig = meta:get_int("MinLight") - local xlig = meta:get_int("MaxLight") - local num = meta:get_int("MaxMobsInArea") - local pla = meta:get_int("PlayerDistance") - local yof = meta:get_int("YOffset") - -- if amount is 0 then do nothing + local num = meta:get_int("MaxMobsInArea") if num == 0 then return end -- are we spawning a registered mob? + local mob = meta:get_string("Mob") if not mcl_mobs.spawning_mobs[mob] then minetest.log("error", "[mcl_mobspawners] Mob Spawner: Mob doesn't exist: "..mob) return @@ -174,17 +168,14 @@ local function spawn_mobs(pos, elapsed) local timer = minetest.get_node_timer(pos) -- spawn mob if player detected and in range + local pla = meta:get_int("PlayerDistance") if pla > 0 then - local in_range = 0 local objs = minetest.get_objects_inside_radius(pos, pla) for _,oir in pairs(objs) do - if oir:is_player() then - in_range = 1 - break end end @@ -213,7 +204,6 @@ local function spawn_mobs(pos, elapsed) -- count mob objects of same type in area for k, obj in ipairs(objs) do - ent = obj:get_luaentity() if ent and ent.name and ent.name == mob then @@ -228,31 +218,28 @@ local function spawn_mobs(pos, elapsed) end -- find air blocks within 8×3×8 nodes of spawner + local yof = meta:get_int("YOffset") local air = minetest.find_nodes_in_area( {x = pos.x - 4, y = pos.y - 1 + yof, z = pos.z - 4}, {x = pos.x + 4, y = pos.y + 1 + yof, z = pos.z + 4}, {"air"}) - -- spawn up to 4 mobs in random air blocks + -- spawn mobs in random air blocks. Default max of 4 if air then - local max = 4 - if spawn_count_overrides[mob] then - max = spawn_count_overrides[mob] - end - for a=1, max do - if #air <= 0 then - -- We're out of space! Stop spawning - break - end - local air_index = math.random(#air) - local pos2 = air[air_index] - local lig = minetest.get_node_light(pos2) or 0 + local num_to_spawn = spawn_count_overrides[mob] or 4 + local mlig = meta:get_int("MinLight") + local xlig = meta:get_int("MaxLight") - pos2.y = pos2.y + 0.5 + for pos2 = table.pull_random_items(air) do + local pos2 = air[air_index] -- only if light levels are within range + local lig = minetest.get_node_light(pos2) or 0 if lig >= mlig and lig <= xlig then - minetest.add_entity(pos2, mob) + if mcl_mobs.spawn(pos2, mob) then + num_to_spawn = num_to_spawn - 1 + if num_to_spawn == 0 then break end + end end table.remove(air, air_index) end From e65370b8455cc4a68df6369927aaf9e1260e631f Mon Sep 17 00:00:00 2001 From: teknomunk Date: Fri, 13 Sep 2024 17:36:19 -0500 Subject: [PATCH 10/13] Fixes --- mods/CORE/mcl_util/init.lua | 2 +- mods/ENTITIES/mobs_mc/villager.lua | 1 - mods/ITEMS/mcl_mobspawners/init.lua | 5 +---- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/mods/CORE/mcl_util/init.lua b/mods/CORE/mcl_util/init.lua index 36cbf2e63..15f935a41 100644 --- a/mods/CORE/mcl_util/init.lua +++ b/mods/CORE/mcl_util/init.lua @@ -48,7 +48,7 @@ function table.pairs_by_keys(t, f) return iter end -local function table.pull_random_items(table) +function table.pull_random_items(table) local count = #table return function() local idx = math.random(count) diff --git a/mods/ENTITIES/mobs_mc/villager.lua b/mods/ENTITIES/mobs_mc/villager.lua index 76cf2715d..e80518e48 100644 --- a/mods/ENTITIES/mobs_mc/villager.lua +++ b/mods/ENTITIES/mobs_mc/villager.lua @@ -1055,7 +1055,6 @@ local function summon_golem(self) local nn = minetest.find_nodes_in_area_under_air(p1, p2,{"group:solid","group:water"}) for n in table.pull_random_items(nn) do n.y = n.y + 1 - minetest.log("trying to summon golem at "..vector.to_string(n)) local summon = mcl_mobs.spawn(n, "mobs_mc:iron_golem") if summon then diff --git a/mods/ITEMS/mcl_mobspawners/init.lua b/mods/ITEMS/mcl_mobspawners/init.lua index 13fc1010e..80f57faa8 100644 --- a/mods/ITEMS/mcl_mobspawners/init.lua +++ b/mods/ITEMS/mcl_mobspawners/init.lua @@ -230,9 +230,7 @@ local function spawn_mobs(pos, elapsed) local mlig = meta:get_int("MinLight") local xlig = meta:get_int("MaxLight") - for pos2 = table.pull_random_items(air) do - local pos2 = air[air_index] - + for pos2 in table.pull_random_items(air) do -- only if light levels are within range local lig = minetest.get_node_light(pos2) or 0 if lig >= mlig and lig <= xlig then @@ -241,7 +239,6 @@ local function spawn_mobs(pos, elapsed) if num_to_spawn == 0 then break end end end - table.remove(air, air_index) end end From 31a3788ce157eb5f8ff6a4cb79db25cbad1dc94c Mon Sep 17 00:00:00 2001 From: teknomunk Date: Fri, 13 Sep 2024 20:24:34 -0500 Subject: [PATCH 11/13] Address review comments --- mods/CORE/mcl_util/init.lua | 20 +++++++++++--------- mods/ENTITIES/mcl_mobs/spawning.lua | 4 ++-- mods/ENTITIES/mobs_mc/villager.lua | 3 ++- mods/ITEMS/mcl_mobspawners/init.lua | 3 ++- 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/mods/CORE/mcl_util/init.lua b/mods/CORE/mcl_util/init.lua index 15f935a41..6143bfb41 100644 --- a/mods/CORE/mcl_util/init.lua +++ b/mods/CORE/mcl_util/init.lua @@ -48,16 +48,18 @@ function table.pairs_by_keys(t, f) return iter end -function table.pull_random_items(table) +-- Removes one element randomly selected from the array section of the table and +-- returns it, or nil if there are no elements in the array section of the table +function table.remove_random_element(table) local count = #table - return function() - local idx = math.random(count) - local res = table[idx] - table[idx] = table[count] - table[count] = nil - count = count - 1 - return res - end + if count == 0 then return nil end + + local idx = math.random(count) + local res = table[idx] + table[idx] = table[count] + table[count] = nil + count = count - 1 + return res end local LOGGING_ON = minetest.settings:get_bool("mcl_logging_default", false) diff --git a/mods/ENTITIES/mcl_mobs/spawning.lua b/mods/ENTITIES/mcl_mobs/spawning.lua index 8a2e099cf..d5ebe8788 100644 --- a/mods/ENTITIES/mcl_mobs/spawning.lua +++ b/mods/ENTITIES/mcl_mobs/spawning.lua @@ -574,7 +574,7 @@ local function has_room(self, pos) nodes = nodes, })) ]] - if n == ( dx * dy * dz ) then + if n == dx * dy * dz then return true end @@ -582,7 +582,7 @@ local function has_room(self, pos) if not minetest.get_node_boxes then return false end -- Check if it's possible for a sub-node space check to succeed - local needed_in_bottom_section = (dx * dz * ( dy - 1)) + local needed_in_bottom_section = dx * ( dy - 1) * dz if n < needed_in_bottom_section then return false end -- Make sure the entire volume except for the top level is free before checking the top layer diff --git a/mods/ENTITIES/mobs_mc/villager.lua b/mods/ENTITIES/mobs_mc/villager.lua index e80518e48..5ea20c707 100644 --- a/mods/ENTITIES/mobs_mc/villager.lua +++ b/mods/ENTITIES/mobs_mc/villager.lua @@ -1053,7 +1053,8 @@ local function summon_golem(self) local p1 = vector.offset(pos, -10, -10, -10) local p2 = vector.offset(pos, 10, 10, 10) local nn = minetest.find_nodes_in_area_under_air(p1, p2,{"group:solid","group:water"}) - for n in table.pull_random_items(nn) do + while #nn > 0 do + local n = table.remove_random_element(nn) n.y = n.y + 1 local summon = mcl_mobs.spawn(n, "mobs_mc:iron_golem") diff --git a/mods/ITEMS/mcl_mobspawners/init.lua b/mods/ITEMS/mcl_mobspawners/init.lua index 80f57faa8..5c934a819 100644 --- a/mods/ITEMS/mcl_mobspawners/init.lua +++ b/mods/ITEMS/mcl_mobspawners/init.lua @@ -230,7 +230,8 @@ local function spawn_mobs(pos, elapsed) local mlig = meta:get_int("MinLight") local xlig = meta:get_int("MaxLight") - for pos2 in table.pull_random_items(air) do + while #air > 0 do + local pos2 = table.remove_random_element(air) -- only if light levels are within range local lig = minetest.get_node_light(pos2) or 0 if lig >= mlig and lig <= xlig then From 626bdd13d8538e13dd7093cc61460a8062b6b9ef Mon Sep 17 00:00:00 2001 From: teknomunk Date: Fri, 13 Sep 2024 21:31:04 -0500 Subject: [PATCH 12/13] Change several places where mobs are created to use mcl_mobs.spawn() instead of minetest.add_entity() --- mods/ENTITIES/mcl_mobs/breeding.lua | 3 +- mods/ENTITIES/mcl_mobs/init.lua | 25 +++++++++-------- mods/ENTITIES/mcl_mobs/spawning.lua | 32 +++++++++++----------- mods/ENTITIES/mobs_mc/slime+magma_cube.lua | 12 ++++---- mods/ENTITIES/mobs_mc/villager_evoker.lua | 14 ++++++---- mods/ENVIRONMENT/lightning/init.lua | 7 ++--- 6 files changed, 50 insertions(+), 43 deletions(-) diff --git a/mods/ENTITIES/mcl_mobs/breeding.lua b/mods/ENTITIES/mcl_mobs/breeding.lua index 90e625db8..559195681 100644 --- a/mods/ENTITIES/mcl_mobs/breeding.lua +++ b/mods/ENTITIES/mcl_mobs/breeding.lua @@ -105,7 +105,7 @@ end -- Spawn a child function mcl_mobs.spawn_child(pos, mob_type) - local child = minetest.add_entity(pos, mob_type) + local child = mcl_mobs.spawn(pos, mob_type) if not child then return end @@ -285,6 +285,7 @@ function mob_class:check_breeding() end local child = mcl_mobs.spawn_child(pos, parent1.name) + if not child then return end local ent_c = child:get_luaentity() diff --git a/mods/ENTITIES/mcl_mobs/init.lua b/mods/ENTITIES/mcl_mobs/init.lua index 81b5fdfe7..74e6edbec 100644 --- a/mods/ENTITIES/mcl_mobs/init.lua +++ b/mods/ENTITIES/mcl_mobs/init.lua @@ -528,7 +528,7 @@ end -- Note: This also introduces the “spawn_egg” group: -- * spawn_egg=1: Spawn egg (generic mob, no metadata) -- * spawn_egg=2: Spawn egg (captured/tamed mob, metadata) -function mcl_mobs.register_egg(mob, desc, background_color, overlay_color, addegg, no_creative) +function mcl_mobs.register_egg(mob_id, desc, background_color, overlay_color, addegg, no_creative) local grp = {spawn_egg = 1} @@ -539,7 +539,7 @@ function mcl_mobs.register_egg(mob, desc, background_color, overlay_color, addeg local invimg = "(spawn_egg.png^[multiply:" .. background_color ..")^(spawn_egg_overlay.png^[multiply:" .. overlay_color .. ")" if old_spawn_icons then - local mobname = mob:gsub("mobs_mc:","") + local mobname = mob_id:gsub("mobs_mc:","") local fn = "mobs_mc_spawn_icon_"..mobname..".png" if mcl_util.file_exists(minetest.get_modpath("mobs_mc").."/textures/"..fn) then invimg = fn @@ -551,7 +551,7 @@ function mcl_mobs.register_egg(mob, desc, background_color, overlay_color, addeg end -- register old stackable mob egg - minetest.register_craftitem(mob, { + minetest.register_craftitem(mob_id, { description = desc, inventory_image = invimg, @@ -561,7 +561,6 @@ function mcl_mobs.register_egg(mob, desc, background_color, overlay_color, addeg _doc_items_usagehelp = S("Just place it where you want the mob to appear. Animals will spawn tamed, unless you hold down the sneak key while placing. If you place this on a mob spawner, you change the mob it spawns."), on_place = function(itemstack, placer, pointed_thing) - local pos = pointed_thing.above -- am I clicking on something with existing on_rightclick function? @@ -571,11 +570,12 @@ function mcl_mobs.register_egg(mob, desc, background_color, overlay_color, addeg return def.on_rightclick(pointed_thing.under, under, placer, itemstack) end + local mob_name = itemstack:get_name() + if pos and within_limits(pos, 0) and not minetest.is_protected(pos, placer:get_player_name()) then local name = placer:get_player_name() local privs = minetest.get_player_privs(name) - if under.name == "mcl_mobspawners:spawner" then if minetest.is_protected(pointed_thing.under, name) then minetest.record_protection_violation(pointed_thing.under, name) @@ -593,7 +593,6 @@ function mcl_mobs.register_egg(mob, desc, background_color, overlay_color, addeg --minetest.log("max light: " .. mob_light_lvl[2]) -- Handle egg conversion - local mob_name = itemstack:get_name() local convert_to = (minetest.registered_entities[mob_name] or {})._convert_to if convert_to then mob_name = convert_to end @@ -604,19 +603,24 @@ function mcl_mobs.register_egg(mob, desc, background_color, overlay_color, addeg return itemstack end - if not minetest.registered_entities[mob] then + if not minetest.registered_entities[mob_name] then return itemstack end if minetest.settings:get_bool("only_peaceful_mobs", false) - and minetest.registered_entities[mob].type == "monster" then + and minetest.registered_entities[mob_name].type == "monster" then minetest.chat_send_player(name, S("Only peaceful mobs allowed!")) return itemstack end - pos.y = pos.y - 0.5 + pos.y = pos.y - 1 + local mob = mcl_mobs.spawn(pos, mob_name) + if not object then + pos.y = pos.y + 1 + mob = mcl_mobs.spawn(pos, mob_name) + if not mob then return end + end - local mob = minetest.add_entity(pos, mob) local entityname = itemstack:get_name() minetest.log("action", "Player " ..name.." spawned "..entityname.." at "..minetest.pos_to_string(pos)) local ent = mob:get_luaentity() @@ -647,5 +651,4 @@ function mcl_mobs.register_egg(mob, desc, background_color, overlay_color, addeg return itemstack end, }) - end diff --git a/mods/ENTITIES/mcl_mobs/spawning.lua b/mods/ENTITIES/mcl_mobs/spawning.lua index d5ebe8788..332603392 100644 --- a/mods/ENTITIES/mcl_mobs/spawning.lua +++ b/mods/ENTITIES/mcl_mobs/spawning.lua @@ -1,4 +1,5 @@ --lua locals +local DEBUG = false local math, vector, minetest, mcl_mobs = math, vector, minetest, mcl_mobs local mob_class = mcl_mobs.mob_class @@ -547,7 +548,7 @@ local function has_room(self, pos) local cb_height = cb[5] - cb[2] local p1 = vector.new( math.round(pos.x + cb[1]), - pos.y, + math.floor(pos.y), math.round(pos.z + cb[3])) local p2 = vector.new( math.round(pos.x + cb[4]), @@ -560,20 +561,20 @@ local function has_room(self, pos) local dz = p2.z - p1.z + 1 local found_nodes = minetest.find_nodes_in_area(p1,p2,nodes) or 0 local n = #found_nodes - --[[ - minetest.log(dump({ - cb = cb, - pos = pos, - n = n, - dx = dx, - dy = dy, - dz = dz, - p1 = p1, - p2 = p2, - found_nodes = found_nodes, - nodes = nodes, - })) - ]] + if DEBUG then + minetest.log(dump({ + cb = cb, + pos = pos, + n = n, + dx = dx, + dy = dy, + dz = dz, + p1 = p1, + p2 = p2, + found_nodes = found_nodes, + nodes = nodes, + })) + end if n == dx * dy * dz then return true end @@ -623,7 +624,6 @@ local function has_room(self, pos) if top_layer_height + dy - 1 >= cb_height then return true end -- We don't have room - mcl_log("No room for mob "..self.name.." at "..minetest.pos_to_string(vector.round(pos))) return false end diff --git a/mods/ENTITIES/mobs_mc/slime+magma_cube.lua b/mods/ENTITIES/mobs_mc/slime+magma_cube.lua index fa9d24d09..57eb5a859 100644 --- a/mods/ENTITIES/mobs_mc/slime+magma_cube.lua +++ b/mods/ENTITIES/mobs_mc/slime+magma_cube.lua @@ -53,12 +53,14 @@ local spawn_children_on_die = function(child_mob, spawn_distance, eject_speed) eject_speed = eject_speed * 0.5 end end - local mob = minetest.add_entity(newpos, child_mob) - if not mother_stuck then - mob:set_velocity(dir * eject_speed) + local mob = mcl_mobs.spawn(newpos, child_mob) + if mob then + if not mother_stuck then + mob:set_velocity(dir * eject_speed) + end + mob:set_yaw(angle - math.pi/2) + table.insert(children, mob) end - mob:set_yaw(angle - math.pi/2) - table.insert(children, mob) angle = angle + (math.pi*2) / spawn_count end -- If mother was murdered, children attack the killer after 1 second diff --git a/mods/ENTITIES/mobs_mc/villager_evoker.lua b/mods/ENTITIES/mobs_mc/villager_evoker.lua index 9d465c25d..2dc175341 100644 --- a/mods/ENTITIES/mobs_mc/villager_evoker.lua +++ b/mods/ENTITIES/mobs_mc/villager_evoker.lua @@ -55,14 +55,16 @@ mcl_mobs.register_mob("mobs_mc:evoker", { basepos.y = basepos.y + 1 for i=1, r do local spawnpos = vector.add(basepos, minetest.yaw_to_dir(pr:next(0,360))) - local vex = minetest.add_entity(spawnpos, "mobs_mc:vex") - local ent = vex:get_luaentity() + local vex = mcl_mobs.spawn(spawnpos, "mobs_mc:vex") + if vex then + local ent = vex:get_luaentity() - -- Mark vexes as summoned and start their life clock (they take damage it reaches 0) - ent._summoned = true - ent._lifetimer = pr:next(33, 108) + -- Mark vexes as summoned and start their life clock (they take damage it reaches 0) + ent._summoned = true + ent._lifetimer = pr:next(33, 108) - table.insert(spawned_vexes[self],ent) + table.insert(spawned_vexes[self],ent) + end end end, passive = false, diff --git a/mods/ENVIRONMENT/lightning/init.lua b/mods/ENVIRONMENT/lightning/init.lua index 079c9cb38..d4756bd9f 100644 --- a/mods/ENVIRONMENT/lightning/init.lua +++ b/mods/ENVIRONMENT/lightning/init.lua @@ -19,7 +19,6 @@ local set_node = minetest.set_node local sound_play = minetest.sound_play local add_particlespawner = minetest.add_particlespawner local after = minetest.after -local add_entity = minetest.add_entity local get_objects_inside_radius = minetest.get_objects_inside_radius local get_item_group = minetest.get_item_group @@ -165,7 +164,7 @@ function lightning.strike_func(pos, pos2, objects) -- Events caused by the lightning strike: Fire, damage, mob transformations, rare skeleton spawn - pos2.y = pos2.y + 1/2 + pos2.y = pos2.y + 1 local skeleton_lightning = false if rng:next(1,100) <= 3 then skeleton_lightning = true @@ -174,14 +173,14 @@ function lightning.strike_func(pos, pos2, objects) if get_node(pos2).name == "air" then -- Low chance for a lightning to spawn skeleton horse + skeletons if skeleton_lightning then - add_entity(pos2, "mobs_mc:skeleton_horse") + mcl_mobs.spawn(pos2, "mobs_mc:skeleton_horse") local angle, posadd angle = math.random() * math.pi * 2 for i=1,3 do posadd = { x=math.cos(angle),y=0,z=math.sin(angle) } posadd = vector.normalize(posadd) - local mob = add_entity(vector.add(pos2, posadd), "mobs_mc:skeleton") + local mob = mcl_mobs.spawn(vector.add(pos2, posadd), "mobs_mc:skeleton") if mob then mob:set_yaw(angle-math.pi/2) end From 0b62c827aa965990595471391bc8431acf1d3ada Mon Sep 17 00:00:00 2001 From: teknomunk Date: Fri, 13 Sep 2024 21:31:26 -0500 Subject: [PATCH 13/13] Remove has_room debug data --- mods/ENTITIES/mcl_mobs/spawning.lua | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/mods/ENTITIES/mcl_mobs/spawning.lua b/mods/ENTITIES/mcl_mobs/spawning.lua index 332603392..8436540bf 100644 --- a/mods/ENTITIES/mcl_mobs/spawning.lua +++ b/mods/ENTITIES/mcl_mobs/spawning.lua @@ -1,5 +1,4 @@ --lua locals -local DEBUG = false local math, vector, minetest, mcl_mobs = math, vector, minetest, mcl_mobs local mob_class = mcl_mobs.mob_class @@ -561,20 +560,6 @@ local function has_room(self, pos) local dz = p2.z - p1.z + 1 local found_nodes = minetest.find_nodes_in_area(p1,p2,nodes) or 0 local n = #found_nodes - if DEBUG then - minetest.log(dump({ - cb = cb, - pos = pos, - n = n, - dx = dx, - dy = dy, - dz = dz, - p1 = p1, - p2 = p2, - found_nodes = found_nodes, - nodes = nodes, - })) - end if n == dx * dy * dz then return true end