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