|
|
|
@ -24,25 +24,60 @@ local vector_floor = vector.floor
|
|
|
|
|
|
|
|
|
|
local table_copy = table.copy
|
|
|
|
|
local table_remove = table.remove
|
|
|
|
|
|
|
|
|
|
local pairs = pairs
|
|
|
|
|
|
|
|
|
|
local LOGGING_ON = minetest.settings:get_bool("mcl_logging_mobs_spawning", false)
|
|
|
|
|
local function mcl_log (message)
|
|
|
|
|
if LOGGING_ON then
|
|
|
|
|
mcl_util.mcl_log (message, "[Mobs spawn]", true)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local dbg_spawn_attempts = 0
|
|
|
|
|
local dbg_spawn_succ = 0
|
|
|
|
|
local dbg_spawn_counts = {}
|
|
|
|
|
-- range for mob count
|
|
|
|
|
local aoc_range = 136
|
|
|
|
|
|
|
|
|
|
local remove_far = true
|
|
|
|
|
|
|
|
|
|
local WAIT_FOR_SPAWN_ATTEMPT = 10
|
|
|
|
|
local FIND_SPAWN_POS_RETRIES = 16
|
|
|
|
|
local FIND_SPAWN_POS_RETRIES_SUCCESS_RESPIN = 8
|
|
|
|
|
|
|
|
|
|
local MOB_SPAWN_ZONE_INNER = 24
|
|
|
|
|
local MOB_SPAWN_ZONE_MIDDLE = 32
|
|
|
|
|
local MOB_SPAWN_ZONE_OUTER = 128
|
|
|
|
|
|
|
|
|
|
-- range for mob count
|
|
|
|
|
local MOB_CAP_INNER_RADIUS = 32
|
|
|
|
|
local aoc_range = 136
|
|
|
|
|
|
|
|
|
|
local MISSING_CAP_DEFAULT = 15
|
|
|
|
|
local MOBS_CAP_CLOSE = 5
|
|
|
|
|
|
|
|
|
|
local SPAWN_MAPGEN_LIMIT = mcl_vars.mapgen_limit - 150
|
|
|
|
|
|
|
|
|
|
local mob_cap = {
|
|
|
|
|
monster = tonumber(minetest.settings:get("mcl_mob_cap_monster")) or 70,
|
|
|
|
|
animal = tonumber(minetest.settings:get("mcl_mob_cap_animal")) or 10,
|
|
|
|
|
hostile = tonumber(minetest.settings:get("mcl_mob_cap_monster")) or 70,
|
|
|
|
|
passive = tonumber(minetest.settings:get("mcl_mob_cap_animal")) or 13,
|
|
|
|
|
ambient = tonumber(minetest.settings:get("mcl_mob_cap_ambient")) or 15,
|
|
|
|
|
water = tonumber(minetest.settings:get("mcl_mob_cap_water")) or 5, --currently unused
|
|
|
|
|
water = tonumber(minetest.settings:get("mcl_mob_cap_water")) or 8,
|
|
|
|
|
water_ambient = tonumber(minetest.settings:get("mcl_mob_cap_water_ambient")) or 20, --currently unused
|
|
|
|
|
player = tonumber(minetest.settings:get("mcl_mob_cap_player")) or 75,
|
|
|
|
|
total = tonumber(minetest.settings:get("mcl_mob_cap_total")) or 500,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
local peaceful_percentage_spawned = tonumber(minetest.settings:get("mcl_mob_peaceful_percentage_spawned")) or 35
|
|
|
|
|
local peaceful_group_percentage_spawned = tonumber(minetest.settings:get("mcl_mob_peaceful_group_percentage_spawned")) or 15
|
|
|
|
|
local hostile_group_percentage_spawned = tonumber(minetest.settings:get("mcl_mob_hostile_group_percentage_spawned")) or 20
|
|
|
|
|
|
|
|
|
|
mcl_log("Mob cap hostile: " .. mob_cap.hostile)
|
|
|
|
|
mcl_log("Mob cap water: " .. mob_cap.water)
|
|
|
|
|
mcl_log("Mob cap passive: " .. mob_cap.passive)
|
|
|
|
|
|
|
|
|
|
mcl_log("Percentage of peacefuls spawned: " .. peaceful_percentage_spawned)
|
|
|
|
|
mcl_log("Percentage of peaceful spawns are group: " .. peaceful_group_percentage_spawned)
|
|
|
|
|
mcl_log("Percentage of hostile spawns are group: " .. hostile_group_percentage_spawned)
|
|
|
|
|
|
|
|
|
|
--do mobs spawn?
|
|
|
|
|
local mobs_spawn = minetest.settings:get_bool("mobs_spawn", true) ~= false
|
|
|
|
|
local spawn_protected = minetest.settings:get_bool("mobs_spawn_protected") ~= false
|
|
|
|
@ -250,22 +285,58 @@ local function count_mobs_total(mob_type)
|
|
|
|
|
return num
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local function count_mobs_all()
|
|
|
|
|
local mobs_found = {}
|
|
|
|
|
local function count_mobs_add_entry (mobs_list, mob_cat)
|
|
|
|
|
if mobs_list[mob_cat] then
|
|
|
|
|
mobs_list[mob_cat] = mobs_list[mob_cat] + 1
|
|
|
|
|
else
|
|
|
|
|
mobs_list[mob_cat] = 1
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--categorise_by can be name or type or spawn_class
|
|
|
|
|
local function count_mobs_all(categorise_by, pos)
|
|
|
|
|
local mobs_found_wide = {}
|
|
|
|
|
local mobs_found_close = {}
|
|
|
|
|
|
|
|
|
|
local num = 0
|
|
|
|
|
for _,entity in pairs(minetest.luaentities) do
|
|
|
|
|
if entity.is_mob then
|
|
|
|
|
local mob_type = entity.type -- animal / monster / npc
|
|
|
|
|
local mob_name = entity.name
|
|
|
|
|
if mobs_found[mob_name] then
|
|
|
|
|
mobs_found[mob_name] = mobs_found[mob_name] + 1
|
|
|
|
|
if entity and entity.is_mob then
|
|
|
|
|
|
|
|
|
|
local add_entry = false
|
|
|
|
|
--local mob_type = entity.type -- animal / monster / npc
|
|
|
|
|
local mob_cat = entity[categorise_by]
|
|
|
|
|
|
|
|
|
|
if pos then
|
|
|
|
|
local mob_pos = entity.object:get_pos()
|
|
|
|
|
if mob_pos then
|
|
|
|
|
local distance = vector.distance(pos, mob_pos)
|
|
|
|
|
--mcl_log("distance: ".. distance)
|
|
|
|
|
if distance <= MOB_SPAWN_ZONE_MIDDLE then
|
|
|
|
|
--mcl_log("distance is close")
|
|
|
|
|
count_mobs_add_entry (mobs_found_close, mob_cat)
|
|
|
|
|
count_mobs_add_entry (mobs_found_wide, mob_cat)
|
|
|
|
|
add_entry = true
|
|
|
|
|
elseif distance <= MOB_SPAWN_ZONE_OUTER then
|
|
|
|
|
--mcl_log("distance is wide")
|
|
|
|
|
count_mobs_add_entry (mobs_found_wide, mob_cat)
|
|
|
|
|
add_entry = true
|
|
|
|
|
else
|
|
|
|
|
mobs_found[mob_name] = 1
|
|
|
|
|
--mcl_log("mob_pos: " .. minetest.pos_to_string(mob_pos))
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
count_mobs_add_entry (mobs_found_wide, mob_cat)
|
|
|
|
|
add_entry = true
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if add_entry then
|
|
|
|
|
num = num + 1
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
return mobs_found, num
|
|
|
|
|
end
|
|
|
|
|
--mcl_log("num: ".. num)
|
|
|
|
|
return mobs_found_close, mobs_found_wide, num
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local function count_mobs_total_cap(mob_type)
|
|
|
|
@ -280,6 +351,32 @@ local function count_mobs_total_cap(mob_type)
|
|
|
|
|
return num
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local function output_mob_stats(mob_counts, total_mobs, chat_display)
|
|
|
|
|
if (total_mobs) then
|
|
|
|
|
local total_output = "Total mobs found: " .. total_mobs
|
|
|
|
|
if chat_display then
|
|
|
|
|
minetest.log(total_output)
|
|
|
|
|
else
|
|
|
|
|
minetest.log("action", total_output)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
end
|
|
|
|
|
local detailed = ""
|
|
|
|
|
if mob_counts then
|
|
|
|
|
for k, v1 in pairs(mob_counts) do
|
|
|
|
|
detailed = detailed .. tostring(k) .. ": " .. tostring(v1) .. "; "
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
if detailed and detailed ~= "" then
|
|
|
|
|
if chat_display then
|
|
|
|
|
minetest.log(detailed)
|
|
|
|
|
else
|
|
|
|
|
minetest.log("action", detailed)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- global functions
|
|
|
|
|
|
|
|
|
|
function mcl_mobs:spawn_abm_check(pos, node, name)
|
|
|
|
@ -422,18 +519,23 @@ function mcl_mobs:spawn_specific(name, dimension, type_of_spawning, biomes, min_
|
|
|
|
|
summary_chance = summary_chance + chance
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
local two_pi = 2 * math.pi
|
|
|
|
|
local function get_next_mob_spawn_pos(pos)
|
|
|
|
|
local distance = math_random(25, 32)
|
|
|
|
|
-- TODO We should consider spawning something a little further away sporadically.
|
|
|
|
|
-- It would be good for sky farms and variance, rather than all being on the 24 - 32 block away radius
|
|
|
|
|
local distance = math_random(MOB_SPAWN_ZONE_INNER, MOB_SPAWN_ZONE_MIDDLE)
|
|
|
|
|
local angle = math_random() * two_pi
|
|
|
|
|
|
|
|
|
|
-- TODO Floor xoff and zoff and add 0.5 so it tries to spawn in the middle of the square. Less failed attempts.
|
|
|
|
|
local xoff = math_round(distance * math_cos(angle))
|
|
|
|
|
local yoff = math_round(distance * math_sin(angle))
|
|
|
|
|
return vector.offset(pos, xoff, 0, yoff)
|
|
|
|
|
local zoff = math_round(distance * math_sin(angle))
|
|
|
|
|
return vector.offset(pos, xoff, 0, zoff)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local function decypher_limits(posy)
|
|
|
|
|
posy = math_floor(posy)
|
|
|
|
|
return posy - 32, posy + 32
|
|
|
|
|
return posy - MOB_SPAWN_ZONE_MIDDLE, posy + MOB_SPAWN_ZONE_MIDDLE
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--a simple helper function for mob_spawn
|
|
|
|
@ -484,7 +586,9 @@ local function has_room(self,pos)
|
|
|
|
|
return true
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local function spawn_check(pos,spawn_def,ignore_caps)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
local function spawn_check(pos, spawn_def)
|
|
|
|
|
if not spawn_def then return end
|
|
|
|
|
dbg_spawn_attempts = dbg_spawn_attempts + 1
|
|
|
|
|
local dimension = mcl_worlds.pos_to_dimension(pos)
|
|
|
|
@ -507,17 +611,8 @@ local function spawn_check(pos,spawn_def,ignore_caps)
|
|
|
|
|
local is_leaf = get_item_group(gotten_node, "leaves") ~= 0
|
|
|
|
|
local is_bedrock = gotten_node == "mcl_core:bedrock"
|
|
|
|
|
local is_grass = minetest.get_item_group(gotten_node,"grass_block") ~= 0
|
|
|
|
|
local mob_count_wide = 0
|
|
|
|
|
|
|
|
|
|
local mob_count = 0
|
|
|
|
|
if not ignore_caps then
|
|
|
|
|
mob_count = count_mobs(pos,32,mob_type)
|
|
|
|
|
mob_count_wide = count_mobs(pos,aoc_range,mob_type)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if pos and spawn_def
|
|
|
|
|
and ( mob_count_wide < (mob_cap[mob_type] or 15) )
|
|
|
|
|
and ( mob_count < 5 )
|
|
|
|
|
and pos.y >= spawn_def.min_height
|
|
|
|
|
and pos.y <= spawn_def.max_height
|
|
|
|
|
and spawn_def.dimension == dimension
|
|
|
|
@ -553,8 +648,7 @@ function mcl_mobs.spawn(pos,id)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
local function spawn_group(p,mob,spawn_on,group_max,group_min)
|
|
|
|
|
if not group_min then group_min = 1 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
|
|
|
|
|
table.shuffle(nn)
|
|
|
|
@ -562,9 +656,10 @@ local function spawn_group(p,mob,spawn_on,group_max,group_min)
|
|
|
|
|
nn = {}
|
|
|
|
|
table.insert(nn,p)
|
|
|
|
|
end
|
|
|
|
|
for i = 1, math.random(group_min,group_max) do
|
|
|
|
|
|
|
|
|
|
for i = 1, amount_to_spawn do
|
|
|
|
|
local sp = vector.offset(nn[math.random(#nn)],0,1,0)
|
|
|
|
|
if spawn_check(nn[math.random(#nn)],mob,true) then
|
|
|
|
|
if spawn_check(nn[math.random(#nn)],mob) then
|
|
|
|
|
if mob.type_of_spawning == "water" then
|
|
|
|
|
sp = get_water_spawn(sp)
|
|
|
|
|
end
|
|
|
|
@ -655,25 +750,113 @@ if mobs_spawn then
|
|
|
|
|
-- Get pos to spawn, x and z are randomised, y is range
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
local function spawn_a_mob(pos, dimension, y_min, y_max)
|
|
|
|
|
--create a disconnected clone of the spawn dictionary
|
|
|
|
|
--prevents memory leak
|
|
|
|
|
local mob_library_worker_table = table_copy(spawn_dictionary)
|
|
|
|
|
local function mob_cap_space (pos, mob_type, mob_counts_close, mob_counts_wide)
|
|
|
|
|
|
|
|
|
|
-- Some mob examples
|
|
|
|
|
--type = "monster", spawn_class = "hostile",
|
|
|
|
|
--type = "animal", spawn_class = "passive",
|
|
|
|
|
--local cod = { type = "animal", spawn_class = "water",
|
|
|
|
|
|
|
|
|
|
local type_cap = mob_cap[mob_type] or MISSING_CAP_DEFAULT
|
|
|
|
|
local close_zone_cap = MOBS_CAP_CLOSE
|
|
|
|
|
|
|
|
|
|
local mob_total_wide = mob_counts_wide[mob_type]
|
|
|
|
|
if not mob_total_wide then
|
|
|
|
|
--mcl_log("none of type found. set as 0")
|
|
|
|
|
mob_total_wide = 0
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local cap_space_wide = type_cap - mob_total_wide
|
|
|
|
|
if cap_space_wide < 1 then
|
|
|
|
|
cap_space_wide = 0
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local mob_total_close = mob_counts_close[mob_type]
|
|
|
|
|
if not mob_total_close then
|
|
|
|
|
--mcl_log("none of type found. set as 0")
|
|
|
|
|
mob_total_close = 0
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local cap_space_close = close_zone_cap - mob_total_close
|
|
|
|
|
if cap_space_close < 1 then
|
|
|
|
|
cap_space_close = 0
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
--mcl_log("spawn_class: " .. spawn_class)
|
|
|
|
|
|
|
|
|
|
if false and mob_type == "water" then
|
|
|
|
|
mcl_log("mob_type: " .. mob_type .. " and pos: " .. minetest.pos_to_string(pos))
|
|
|
|
|
mcl_log("wide: " .. mob_total_wide .. "/" .. type_cap)
|
|
|
|
|
mcl_log("cap_space_wide: " .. cap_space_wide)
|
|
|
|
|
mcl_log("close: " .. mob_total_close .. "/" .. close_zone_cap)
|
|
|
|
|
mcl_log("cap_space_close: " .. cap_space_close)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
return cap_space_wide, cap_space_close
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local function find_spawning_position(pos, max_times)
|
|
|
|
|
local spawning_position
|
|
|
|
|
|
|
|
|
|
local max_loops = 1
|
|
|
|
|
if max_times then max_loops = max_times end
|
|
|
|
|
|
|
|
|
|
local y_min, y_max = decypher_limits(pos.y)
|
|
|
|
|
|
|
|
|
|
mcl_log("mapgen_limit: " .. SPAWN_MAPGEN_LIMIT)
|
|
|
|
|
local i = 0
|
|
|
|
|
repeat
|
|
|
|
|
local goal_pos = get_next_mob_spawn_pos(pos)
|
|
|
|
|
--grab mob that fits into the spawning location
|
|
|
|
|
--randomly grab a mob, don't exclude any possibilities
|
|
|
|
|
|
|
|
|
|
if math.abs(goal_pos.x) <= SPAWN_MAPGEN_LIMIT and math.abs(pos.y) <= SPAWN_MAPGEN_LIMIT and math.abs(goal_pos.z) <= SPAWN_MAPGEN_LIMIT then
|
|
|
|
|
local spawning_position_list = find_nodes_in_area_under_air(
|
|
|
|
|
{x = goal_pos.x, y = y_min, z = goal_pos.z},
|
|
|
|
|
{x = goal_pos.x, y = y_max, z = goal_pos.z},
|
|
|
|
|
{"group:solid", "group:water", "group:lava"}
|
|
|
|
|
)
|
|
|
|
|
if #spawning_position_list <= 0 then return end
|
|
|
|
|
local spawning_position = spawning_position_list[math_random(1, #spawning_position_list)]
|
|
|
|
|
if #spawning_position_list > 0 then
|
|
|
|
|
mcl_log("Spawning positions available: " .. minetest.pos_to_string(goal_pos))
|
|
|
|
|
spawning_position = spawning_position_list[math_random(1, #spawning_position_list)]
|
|
|
|
|
else
|
|
|
|
|
mcl_log("Spawning position isn't good. Do not spawn: " .. minetest.pos_to_string(goal_pos))
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
else
|
|
|
|
|
mcl_log("Pos outside mapgen limits: " .. minetest.pos_to_string(goal_pos))
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
i = i + 1
|
|
|
|
|
if i >= max_loops then
|
|
|
|
|
mcl_log("Cancel finding spawn positions at: " .. max_loops)
|
|
|
|
|
break
|
|
|
|
|
end
|
|
|
|
|
until spawning_position
|
|
|
|
|
return spawning_position
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local function spawn_a_mob(pos)
|
|
|
|
|
--create a disconnected clone of the spawn dictionary, prevents memory leak
|
|
|
|
|
local mob_library_worker_table = table_copy(spawn_dictionary)
|
|
|
|
|
|
|
|
|
|
local spawning_position = find_spawning_position(pos, FIND_SPAWN_POS_RETRIES)
|
|
|
|
|
if not spawning_position then
|
|
|
|
|
minetest.log("action", "[Mobs spawn] Cannot find a valid spawn position after retries: " .. FIND_SPAWN_POS_RETRIES)
|
|
|
|
|
return
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
local mob_counts_close, mob_counts_wide, total_mobs = count_mobs_all("spawn_class", spawning_position)
|
|
|
|
|
--output_mob_stats(mob_counts_close, total_mobs)
|
|
|
|
|
--output_mob_stats(mob_counts_wide)
|
|
|
|
|
|
|
|
|
|
--grab mob that fits into the spawning location
|
|
|
|
|
--randomly grab a mob, don't exclude any possibilities
|
|
|
|
|
perlin_noise = perlin_noise or minetest_get_perlin(noise_params)
|
|
|
|
|
local noise = perlin_noise:get_3d(spawning_position)
|
|
|
|
|
local current_summary_chance = summary_chance
|
|
|
|
|
|
|
|
|
|
table.shuffle(mob_library_worker_table)
|
|
|
|
|
|
|
|
|
|
while #mob_library_worker_table > 0 do
|
|
|
|
|
local mob_chance_offset = (math_round(noise * current_summary_chance + 12345) % current_summary_chance) + 1
|
|
|
|
|
local mob_index = 1
|
|
|
|
@ -684,13 +867,32 @@ if mobs_spawn then
|
|
|
|
|
mob_chance = mob_library_worker_table[mob_index].chance
|
|
|
|
|
step_chance = step_chance + mob_chance
|
|
|
|
|
end
|
|
|
|
|
local mob_def = mob_library_worker_table[mob_index]
|
|
|
|
|
--minetest.log(mob_def.name.." "..step_chance.. " "..mob_chance)
|
|
|
|
|
|
|
|
|
|
local mob_def = mob_library_worker_table[mob_index]
|
|
|
|
|
if mob_def and mob_def.name and minetest.registered_entities[mob_def.name] then
|
|
|
|
|
local spawn_in_group = minetest.registered_entities[mob_def.name].spawn_in_group or 4
|
|
|
|
|
local spawn_in_group_min = minetest.registered_entities[mob_def.name].spawn_in_group_min or 1
|
|
|
|
|
local mob_type = minetest.registered_entities[mob_def.name].type
|
|
|
|
|
if spawn_check(spawning_position,mob_def) then
|
|
|
|
|
|
|
|
|
|
local mob_def_ent = minetest.registered_entities[mob_def.name]
|
|
|
|
|
--local mob_type = mob_def_ent.type
|
|
|
|
|
local mob_spawn_class = mob_def_ent.spawn_class
|
|
|
|
|
|
|
|
|
|
--mcl_log("mob_spawn_class: " .. mob_spawn_class)
|
|
|
|
|
|
|
|
|
|
local cap_space_wide, cap_space_close = mob_cap_space (spawning_position, mob_spawn_class, mob_counts_close, mob_counts_wide)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if cap_space_close > 0 and cap_space_wide > 0 then
|
|
|
|
|
--mcl_log("Cap space available")
|
|
|
|
|
|
|
|
|
|
-- Spawn caps for animals and water creatures fill up rapidly. Need to throttle this somewhat
|
|
|
|
|
-- for performance and for early game challenge. We don't want to reduce hostiles though.
|
|
|
|
|
local spawn_hostile = (mob_spawn_class == "hostile")
|
|
|
|
|
local spawn_passive = (mob_spawn_class ~= "hostile") and math.random(100) < peaceful_percentage_spawned
|
|
|
|
|
-- or not hostile
|
|
|
|
|
--mcl_log("Spawn_passive: " .. tostring(spawn_passive))
|
|
|
|
|
--mcl_log("Spawn_hostile: " .. tostring(spawn_hostile))
|
|
|
|
|
|
|
|
|
|
if (spawn_hostile or spawn_passive) and spawn_check(spawning_position,mob_def) then
|
|
|
|
|
if mob_def.type_of_spawning == "water" then
|
|
|
|
|
spawning_position = get_water_spawn(spawning_position)
|
|
|
|
|
if not spawning_position then
|
|
|
|
@ -698,25 +900,62 @@ if mobs_spawn then
|
|
|
|
|
return
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
if minetest.registered_entities[mob_def.name].can_spawn and not minetest.registered_entities[mob_def.name].can_spawn(spawning_position) then
|
|
|
|
|
if mob_def_ent.can_spawn and not mob_def_ent.can_spawn(spawning_position) then
|
|
|
|
|
minetest.log("warning","[mcl_mobs] mob "..mob_def.name.." refused to spawn at "..minetest.pos_to_string(vector.round(spawning_position)))
|
|
|
|
|
return
|
|
|
|
|
end
|
|
|
|
|
--everything is correct, spawn mob
|
|
|
|
|
local object
|
|
|
|
|
if spawn_in_group and ( mob_type ~= "monster" or math.random(5) == 1 ) then
|
|
|
|
|
if logging then
|
|
|
|
|
minetest.log("action", "[mcl_mobs] A group of mob " .. mob_def.name .. " spawns on " ..minetest.get_node(vector.offset(spawning_position,0,-1,0)).name .." at " .. minetest.pos_to_string(spawning_position, 1))
|
|
|
|
|
end
|
|
|
|
|
object = spawn_group(spawning_position,mob_def,{minetest.get_node(vector.offset(spawning_position,0,-1,0)).name},spawn_in_group,spawn_in_group_min)
|
|
|
|
|
|
|
|
|
|
--everything is correct, spawn mob
|
|
|
|
|
local spawn_in_group = mob_def_ent.spawn_in_group or 4
|
|
|
|
|
|
|
|
|
|
local spawn_group_hostile = (mob_spawn_class == "hostile") and (math.random(100) < hostile_group_percentage_spawned)
|
|
|
|
|
local spawn_group_passive = (mob_spawn_class ~= "hostile") and (math.random(100) < peaceful_group_percentage_spawned)
|
|
|
|
|
|
|
|
|
|
mcl_log("spawn_group_hostile: " .. tostring(spawn_group_hostile))
|
|
|
|
|
mcl_log("spawn_group_passive: " .. tostring(spawn_group_passive))
|
|
|
|
|
|
|
|
|
|
local spawned
|
|
|
|
|
if spawn_in_group and (spawn_group_hostile or spawn_group_passive) then
|
|
|
|
|
local group_min = mob_def_ent.spawn_in_group_min or 1
|
|
|
|
|
if not group_min then group_min = 1 end
|
|
|
|
|
|
|
|
|
|
local amount_to_spawn = math.random(group_min,spawn_in_group)
|
|
|
|
|
|
|
|
|
|
if amount_to_spawn > cap_space_wide then
|
|
|
|
|
mcl_log("Spawning quantity: " .. amount_to_spawn)
|
|
|
|
|
mcl_log("Throttle amount to cap space: " .. cap_space_wide)
|
|
|
|
|
amount_to_spawn = cap_space_wide
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if logging then
|
|
|
|
|
minetest.log("action", "[mcl_mobs] A group of " ..amount_to_spawn .. " " .. mob_def.name .. " mob spawns on " ..minetest.get_node(vector.offset(spawning_position,0,-1,0)).name .." at " .. minetest.pos_to_string(spawning_position, 1))
|
|
|
|
|
end
|
|
|
|
|
spawned = spawn_group(spawning_position,mob_def,{minetest.get_node(vector.offset(spawning_position,0,-1,0)).name}, amount_to_spawn)
|
|
|
|
|
else
|
|
|
|
|
if logging then
|
|
|
|
|
minetest.log("action", "[mcl_mobs] Mob " .. mob_def.name .. " spawns on " ..minetest.get_node(vector.offset(spawning_position,0,-1,0)).name .." at ".. minetest.pos_to_string(spawning_position, 1))
|
|
|
|
|
end
|
|
|
|
|
object = mcl_mobs.spawn(spawning_position, mob_def.name)
|
|
|
|
|
spawned = mcl_mobs.spawn(spawning_position, mob_def.name)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
if spawned then
|
|
|
|
|
--mcl_log("We have spawned")
|
|
|
|
|
mob_counts_close, mob_counts_wide, total_mobs = count_mobs_all("type", pos)
|
|
|
|
|
local new_spawning_position = find_spawning_position(pos, FIND_SPAWN_POS_RETRIES_SUCCESS_RESPIN)
|
|
|
|
|
if new_spawning_position then
|
|
|
|
|
mcl_log("Setting new spawning position")
|
|
|
|
|
spawning_position = new_spawning_position
|
|
|
|
|
else
|
|
|
|
|
mcl_log("Cannot set new spawning position")
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
mcl_log("Spawn check failed")
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
mcl_log("Cap space full")
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
end
|
|
|
|
|
current_summary_chance = current_summary_chance - mob_chance
|
|
|
|
|
table_remove(mob_library_worker_table, mob_index)
|
|
|
|
@ -728,22 +967,24 @@ if mobs_spawn then
|
|
|
|
|
|
|
|
|
|
local timer = 0
|
|
|
|
|
minetest.register_globalstep(function(dtime)
|
|
|
|
|
|
|
|
|
|
timer = timer + dtime
|
|
|
|
|
if timer < 10 then return end
|
|
|
|
|
if timer < WAIT_FOR_SPAWN_ATTEMPT then return end
|
|
|
|
|
timer = 0
|
|
|
|
|
|
|
|
|
|
local players = get_connected_players()
|
|
|
|
|
local total_mobs = count_mobs_total_cap()
|
|
|
|
|
if total_mobs > mob_cap.total or total_mobs > #players * mob_cap.player then
|
|
|
|
|
minetest.log("action","[mcl_mobs] global mob cap reached. no cycle spawning.")
|
|
|
|
|
return
|
|
|
|
|
end --mob cap per player
|
|
|
|
|
|
|
|
|
|
for _, player in pairs(players) do
|
|
|
|
|
local pos = player:get_pos()
|
|
|
|
|
local dimension = mcl_worlds.pos_to_dimension(pos)
|
|
|
|
|
-- ignore void and unloaded area
|
|
|
|
|
if dimension ~= "void" and dimension ~= "default" then
|
|
|
|
|
local y_min, y_max = decypher_limits(pos.y)
|
|
|
|
|
spawn_a_mob(pos, dimension, y_min, y_max)
|
|
|
|
|
spawn_a_mob(pos)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end)
|
|
|
|
@ -775,27 +1016,16 @@ function mob_class:check_despawn(pos, dtime)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
minetest.register_chatcommand("mobstats",{
|
|
|
|
|
privs = { debug = true },
|
|
|
|
|
func = function(n,param)
|
|
|
|
|
minetest.chat_send_player(n,dump(dbg_spawn_counts))
|
|
|
|
|
--minetest.chat_send_player(n,dump(dbg_spawn_counts))
|
|
|
|
|
local pos = minetest.get_player_by_name(n):get_pos()
|
|
|
|
|
minetest.chat_send_player(n,"mobs within 32 radius of player:"..count_mobs(pos,32))
|
|
|
|
|
minetest.chat_send_player(n,"total mobs:"..count_mobs_total())
|
|
|
|
|
minetest.chat_send_player(n,"spawning attempts since server start:"..dbg_spawn_attempts)
|
|
|
|
|
minetest.chat_send_player(n,"successful spawns since server start:"..dbg_spawn_succ)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
local mob_counts, total_mobs = count_mobs_all()
|
|
|
|
|
if (total_mobs) then
|
|
|
|
|
minetest.log("action", "Total mobs found: " .. total_mobs)
|
|
|
|
|
end
|
|
|
|
|
if mob_counts then
|
|
|
|
|
for k, v1 in pairs(mob_counts) do
|
|
|
|
|
minetest.log("action", "k: " .. tostring(k))
|
|
|
|
|
minetest.log("action", "v1: " .. tostring(v1))
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
minetest.chat_send_player(n,"mobs: within 32 radius of player/total loaded :"..count_mobs(pos,MOB_CAP_INNER_RADIUS) .. "/" .. count_mobs_total())
|
|
|
|
|
minetest.chat_send_player(n,"spawning attempts since server start:" .. dbg_spawn_succ .. "/" .. dbg_spawn_attempts)
|
|
|
|
|
|
|
|
|
|
local mob_counts_close, mob_counts_wide, total_mobs = count_mobs_all("name") -- Can use "type"
|
|
|
|
|
output_mob_stats(mob_counts_wide, total_mobs, true)
|
|
|
|
|
end
|
|
|
|
|
})
|
|
|
|
|