Merge pull request 'Split global cap for peaceful and hostile. Introduce underground water, ambient water, axolotl cap. Slight peaceful spawn balancing.' (#3765) from spawning_cap_tweaks into master

Reviewed-on: https://git.minetest.land/MineClone2/MineClone2/pulls/3765
This commit is contained in:
ancientmarinerdev 2023-06-01 20:08:53 +00:00
commit 5cbb56d71b
7 changed files with 96 additions and 50 deletions

View file

@ -27,8 +27,11 @@ local table_remove = table.remove
local pairs = pairs local pairs = pairs
local LOGGING_ON = minetest.settings:get_bool("mcl_logging_mobs_spawning", false) local LOGGING_ON = minetest.settings:get_bool("mcl_logging_mobs_spawning", false)
local function mcl_log (message) local function mcl_log (message, property)
if LOGGING_ON then if LOGGING_ON then
if property then
message = message .. ": " .. dump(property)
end
mcl_util.mcl_log (message, "[Mobs spawn]", true) mcl_util.mcl_log (message, "[Mobs spawn]", true)
end end
end end
@ -52,21 +55,25 @@ local MOB_CAP_INNER_RADIUS = 32
local aoc_range = 136 local aoc_range = 136
local MISSING_CAP_DEFAULT = 15 local MISSING_CAP_DEFAULT = 15
local MOBS_CAP_CLOSE = 5 local MOBS_CAP_CLOSE = 10
local SPAWN_MAPGEN_LIMIT = mcl_vars.mapgen_limit - 150 local SPAWN_MAPGEN_LIMIT = mcl_vars.mapgen_limit - 150
local mob_cap = { local mob_cap = {
hostile = tonumber(minetest.settings:get("mcl_mob_cap_monster")) or 70, hostile = tonumber(minetest.settings:get("mcl_mob_cap_monster")) or 70,
passive = tonumber(minetest.settings:get("mcl_mob_cap_animal")) or 13, passive = tonumber(minetest.settings:get("mcl_mob_cap_animal")) or 10,
ambient = tonumber(minetest.settings:get("mcl_mob_cap_ambient")) or 15, ambient = tonumber(minetest.settings:get("mcl_mob_cap_ambient")) or 15,
water = tonumber(minetest.settings:get("mcl_mob_cap_water")) or 8, 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 water_ambient = tonumber(minetest.settings:get("mcl_mob_cap_water_ambient")) or 20,
water_underground = tonumber(minetest.settings:get("mcl_mob_cap_water_underground")) or 5,
axolotl = tonumber(minetest.settings:get("mcl_mob_cap_axolotl")) or 2, -- TODO should be 5 when lush caves added
player = tonumber(minetest.settings:get("mcl_mob_cap_player")) or 75, player = tonumber(minetest.settings:get("mcl_mob_cap_player")) or 75,
global_hostile = tonumber(minetest.settings:get("mcl_mob_cap_hostile")) or 300,
global_non_hostile = tonumber(minetest.settings:get("mcl_mob_cap_non_hostile")) or 300,
total = tonumber(minetest.settings:get("mcl_mob_cap_total")) or 500, 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_percentage_spawned = tonumber(minetest.settings:get("mcl_mob_peaceful_percentage_spawned")) or 30
local peaceful_group_percentage_spawned = tonumber(minetest.settings:get("mcl_mob_peaceful_group_percentage_spawned")) or 15 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 local hostile_group_percentage_spawned = tonumber(minetest.settings:get("mcl_mob_hostile_group_percentage_spawned")) or 20
@ -340,15 +347,32 @@ local function count_mobs_all(categorise_by, pos)
end end
local function count_mobs_total_cap(mob_type) local function count_mobs_total_cap(mob_type)
local total = 0
local num = 0 local num = 0
local hostile = 0
local non_hostile = 0
for _,l in pairs(minetest.luaentities) do for _,l in pairs(minetest.luaentities) do
if l.is_mob then if l.is_mob then
if ( mob_type == nil or l.type == mob_type ) and l.can_despawn and not l.nametag then total = total + 1
local nametagged = l.nametag and l.nametag ~= ""
if ( mob_type == nil or l.type == mob_type ) and not nametagged then
if l.spawn_class == "hostile" then
hostile = hostile + 1
else
non_hostile = non_hostile + 1
end
num = num + 1 num = num + 1
else
mcl_log("l.name", l.name)
mcl_log("l.nametag", l.nametag)
end end
end end
end end
return num mcl_log("Total mobs", total)
mcl_log("hostile", hostile)
mcl_log("non_hostile", non_hostile)
return num, non_hostile, hostile
end end
local function output_mob_stats(mob_counts, total_mobs, chat_display) local function output_mob_stats(mob_counts, total_mobs, chat_display)
@ -704,13 +728,13 @@ local function spawn_check(pos, spawn_def)
--mcl_log("Level 3 spawn check passed") --mcl_log("Level 3 spawn check passed")
return true return true
else else
mcl_log("Spawn check level 3 failed") --mcl_log("Spawn check level 3 failed")
end end
else else
mcl_log("Spawn check level 2 failed") --mcl_log("Spawn check level 2 failed")
end end
else else
mcl_log("Spawn check level 1 failed") --mcl_log("Spawn check level 1 failed")
end end
return false return false
end end
@ -831,7 +855,7 @@ if mobs_spawn then
-- Get pos to spawn, x and z are randomised, y is range -- Get pos to spawn, x and z are randomised, y is range
local function mob_cap_space (pos, mob_type, mob_counts_close, mob_counts_wide) local function mob_cap_space (pos, mob_type, mob_counts_close, mob_counts_wide, cap_space_hostile, cap_space_non_hostile)
-- Some mob examples -- Some mob examples
--type = "monster", spawn_class = "hostile", --type = "monster", spawn_class = "hostile",
@ -847,9 +871,18 @@ if mobs_spawn then
mob_total_wide = 0 mob_total_wide = 0
end end
local cap_space_wide = type_cap - mob_total_wide local cap_space_wide = math.max(type_cap - mob_total_wide, 0)
if cap_space_wide < 1 then
cap_space_wide = 0 mcl_log("mob_type", mob_type)
mcl_log("cap_space_wide", cap_space_wide)
local cap_space_available = 0
if mob_type == "hostile" then
mcl_log("cap_space_global", cap_space_hostile)
cap_space_available = math.min(cap_space_hostile, cap_space_wide)
else
mcl_log("cap_space_global", cap_space_non_hostile)
cap_space_available = math.min(cap_space_non_hostile, cap_space_wide)
end end
local mob_total_close = mob_counts_close[mob_type] local mob_total_close = mob_counts_close[mob_type]
@ -858,12 +891,11 @@ if mobs_spawn then
mob_total_close = 0 mob_total_close = 0
end end
local cap_space_close = close_zone_cap - mob_total_close local cap_space_close = math.max(close_zone_cap - mob_total_close, 0)
if cap_space_close < 1 then cap_space_available = math.min(cap_space_available, cap_space_close)
cap_space_close = 0
end
--mcl_log("spawn_class: " .. spawn_class) mcl_log("cap_space_close", cap_space_close)
mcl_log("cap_space_available", cap_space_available)
if false and mob_type == "water" then if false and mob_type == "water" then
mcl_log("mob_type: " .. mob_type .. " and pos: " .. minetest.pos_to_string(pos)) mcl_log("mob_type: " .. mob_type .. " and pos: " .. minetest.pos_to_string(pos))
@ -873,7 +905,7 @@ if mobs_spawn then
mcl_log("cap_space_close: " .. cap_space_close) mcl_log("cap_space_close: " .. cap_space_close)
end end
return cap_space_wide, cap_space_close return cap_space_available
end end
local function find_spawning_position(pos, max_times) local function find_spawning_position(pos, max_times)
@ -884,7 +916,7 @@ if mobs_spawn then
local y_min, y_max = decypher_limits(pos.y) local y_min, y_max = decypher_limits(pos.y)
mcl_log("mapgen_limit: " .. SPAWN_MAPGEN_LIMIT) --mcl_log("mapgen_limit: " .. SPAWN_MAPGEN_LIMIT)
local i = 0 local i = 0
repeat repeat
local goal_pos = get_next_mob_spawn_pos(pos) local goal_pos = get_next_mob_spawn_pos(pos)
@ -916,7 +948,7 @@ if mobs_spawn then
return spawning_position return spawning_position
end end
local function spawn_a_mob(pos) local function spawn_a_mob(pos, cap_space_hostile, cap_space_non_hostile)
--create a disconnected clone of the spawn dictionary, prevents memory leak --create a disconnected clone of the spawn dictionary, prevents memory leak
local mob_library_worker_table = table_copy(spawn_dictionary) local mob_library_worker_table = table_copy(spawn_dictionary)
@ -954,22 +986,18 @@ if mobs_spawn then
if mob_def and mob_def.name and minetest.registered_entities[mob_def.name] then if mob_def and mob_def.name and minetest.registered_entities[mob_def.name] then
local mob_def_ent = minetest.registered_entities[mob_def.name] 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 local mob_spawn_class = mob_def_ent.spawn_class
--mcl_log("mob_spawn_class: " .. mob_spawn_class) local cap_space_available = mob_cap_space (spawning_position, mob_spawn_class, mob_counts_close, mob_counts_wide, cap_space_hostile, cap_space_non_hostile)
local cap_space_wide, cap_space_close = mob_cap_space (spawning_position, mob_spawn_class, mob_counts_close, mob_counts_wide) if cap_space_available > 0 then
if cap_space_close > 0 and cap_space_wide > 0 then
--mcl_log("Cap space available") --mcl_log("Cap space available")
-- Spawn caps for animals and water creatures fill up rapidly. Need to throttle this somewhat -- 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. -- for performance and for early game challenge. We don't want to reduce hostiles though.
local spawn_hostile = (mob_spawn_class == "hostile") local spawn_hostile = (mob_spawn_class == "hostile")
local spawn_passive = (mob_spawn_class ~= "hostile") and math.random(100) < peaceful_percentage_spawned 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_passive: " .. tostring(spawn_passive))
--mcl_log("Spawn_hostile: " .. tostring(spawn_hostile)) --mcl_log("Spawn_hostile: " .. tostring(spawn_hostile))
@ -1000,13 +1028,10 @@ if mobs_spawn then
local group_min = mob_def_ent.spawn_in_group_min or 1 local group_min = mob_def_ent.spawn_in_group_min or 1
if not group_min then group_min = 1 end if not group_min then group_min = 1 end
local amount_to_spawn = math.random(group_min,spawn_in_group) 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("Spawning quantity: " .. amount_to_spawn)
mcl_log("Throttle amount to cap space: " .. cap_space_wide) amount_to_spawn = math.min(amount_to_spawn, cap_space_available)
amount_to_spawn = cap_space_wide mcl_log("throttled spawning quantity: " .. amount_to_spawn)
end
if logging then 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)) 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))
@ -1021,7 +1046,7 @@ if mobs_spawn then
if spawned then if spawned then
--mcl_log("We have spawned") --mcl_log("We have spawned")
mob_counts_close, mob_counts_wide, total_mobs = count_mobs_all("type", pos) mob_counts_close, mob_counts_wide, total_mobs = count_mobs_all("spawn_class", pos)
local new_spawning_position = find_spawning_position(pos, FIND_SPAWN_POS_RETRIES_SUCCESS_RESPIN) local new_spawning_position = find_spawning_position(pos, FIND_SPAWN_POS_RETRIES_SUCCESS_RESPIN)
if new_spawning_position then if new_spawning_position then
mcl_log("Setting new spawning position") mcl_log("Setting new spawning position")
@ -1034,7 +1059,7 @@ if mobs_spawn then
--mcl_log("Spawn check failed") --mcl_log("Spawn check failed")
end end
else else
mcl_log("Cap space full") --mcl_log("Cap space full")
end end
end end
@ -1054,7 +1079,13 @@ if mobs_spawn then
timer = 0 timer = 0
local players = get_connected_players() local players = get_connected_players()
local total_mobs = count_mobs_total_cap() local total_mobs, total_non_hostile, total_hostile = count_mobs_total_cap()
local cap_space_hostile = math.max(mob_cap.global_hostile - total_hostile, 0)
local cap_space_non_hostile = math.max(mob_cap.global_non_hostile - total_non_hostile, 0)
mcl_log("global cap_space_hostile", cap_space_hostile)
mcl_log("global cap_space_non_hostile", cap_space_non_hostile)
if total_mobs > mob_cap.total or total_mobs > #players * mob_cap.player then 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.") minetest.log("action","[mcl_mobs] global mob cap reached. no cycle spawning.")
return return
@ -1065,7 +1096,7 @@ if mobs_spawn then
local dimension = mcl_worlds.pos_to_dimension(pos) local dimension = mcl_worlds.pos_to_dimension(pos)
-- ignore void and unloaded area -- ignore void and unloaded area
if dimension ~= "void" and dimension ~= "default" then if dimension ~= "void" and dimension ~= "default" then
spawn_a_mob(pos) spawn_a_mob(pos, cap_space_hostile, cap_space_non_hostile)
end end
end end
end) end)

View file

@ -2,7 +2,7 @@ local S = minetest.get_translator(minetest.get_current_modname())
local axolotl = { local axolotl = {
type = "animal", type = "animal",
spawn_class = "water", spawn_class = "axolotl",
can_despawn = true, can_despawn = true,
passive = false, passive = false,
hp_min = 14, hp_min = 14,

View file

@ -31,7 +31,7 @@ local S = minetest.get_translator(minetest.get_current_modname())
local cod = { local cod = {
type = "animal", type = "animal",
spawn_class = "water", spawn_class = "water_ambient",
can_despawn = true, can_despawn = true,
passive = true, passive = true,
hp_min = 3, hp_min = 3,

View file

@ -31,7 +31,7 @@ end
mcl_mobs.register_mob("mobs_mc:glow_squid", { mcl_mobs.register_mob("mobs_mc:glow_squid", {
type = "animal", type = "animal",
spawn_class = "water", spawn_class = "water_underground",
can_despawn = true, can_despawn = true,
passive = true, passive = true,
hp_min = 10, hp_min = 10,

View file

@ -11,7 +11,7 @@ local S = minetest.get_translator(minetest.get_current_modname())
local salmon = { local salmon = {
type = "animal", type = "animal",
spawn_class = "water", spawn_class = "water_ambient",
can_despawn = true, can_despawn = true,
passive = true, passive = true,
hp_min = 3, hp_min = 3,

View file

@ -59,7 +59,7 @@ end
local tropical_fish = { local tropical_fish = {
type = "animal", type = "animal",
spawn_class = "water", spawn_class = "water_ambient",
can_despawn = true, can_despawn = true,
passive = true, passive = true,
hp_min = 3, hp_min = 3,

View file

@ -121,8 +121,8 @@ mobs_griefing (Mobs change blocks) bool true
# If enabled, mobs won't damage particles when they got hurt. # If enabled, mobs won't damage particles when they got hurt.
mobs_disable_blood (Disable mob damage particles) bool false mobs_disable_blood (Disable mob damage particles) bool false
#Percentage of peaceful spawn attempts that succeed (default:35) #Percentage of peaceful spawn attempts that succeed (default:30)
mcl_mob_peaceful_percentage_spawned (Peaceful percentage success) int 35 0 100 mcl_mob_peaceful_percentage_spawned (Peaceful percentage success) int 30 0 100
#Percentage of peaceful spawn attempts that are group spawns (default:15) #Percentage of peaceful spawn attempts that are group spawns (default:15)
mcl_mob_peaceful_group_percentage_spawned (Peaceful group percentage) int 15 0 100 mcl_mob_peaceful_group_percentage_spawned (Peaceful group percentage) int 15 0 100
@ -130,21 +130,36 @@ mcl_mob_peaceful_group_percentage_spawned (Peaceful group percentage) int 15 0 1
#Percentage of hostile spawn attempts that are group spawns (default:20) #Percentage of hostile spawn attempts that are group spawns (default:20)
mcl_mob_hostile_group_percentage_spawned (Hostile group percentage) int 20 0 100 mcl_mob_hostile_group_percentage_spawned (Hostile group percentage) int 20 0 100
#Maximum amount mobs (default:500) #Maximum amount of mobs (default:500)
mcl_mob_cap_total (Global mob cap) int 500 0 2048 mcl_mob_cap_total (Global mob cap) int 500 0 2048
#Maximum amount of hostile mobs (default:300)
mcl_mob_cap_hostile (Global hostile mob cap) int 300 0 2048
#Maximum amount of non-hostile mobs (default:300)
mcl_mob_cap_non_hostile (Global non-hostile mob cap) int 300 0 2048
#Maximum amount of mobs per player (default:75) #Maximum amount of mobs per player (default:75)
mcl_mob_cap_player (Mob cap per player) int 75 0 2048 mcl_mob_cap_player (Mob cap per player) int 75 0 2048
#Maximum amount of monsters that will spawn near a player (default:70) #Maximum amount of monsters that will spawn near a player (default:70)
mcl_mob_cap_monster (Mob cap monsters) int 70 0 2048 mcl_mob_cap_monster (Mob cap monsters) int 70 0 2048
#Maximum amount of animals that will spawn near a player (default:13) #Maximum amount of animals that will spawn near a player (default:10)
mcl_mob_cap_animal (Mob cap animals) int 13 0 1024 mcl_mob_cap_animal (Mob cap animals) int 10 0 1024
#Maximum amount of water mobs that will spawn near a player (default:8) #Maximum amount of water mobs that will spawn near a player (default:8)
mcl_mob_cap_water (Mob cap water) int 8 0 1024 mcl_mob_cap_water (Mob cap water) int 8 0 1024
#Maximum amount of ambient water mobs that will spawn near a player (default:20)
mcl_mob_cap_water_ambient (Mob cap ambient water) int 20 0 1024
#Maximum amount of underground water mobs that will spawn near a player (default:5)
mcl_mob_cap_water_underground (Mob cap underground water) int 5 0 1024
#Maximum amount of axolotl mobs that will spawn near a player (default:5)
mcl_mob_cap_axolotl (Mob cap axolotl) int 5 0 1024
#Maximum amount of ambient mobs that will spawn near a player (default:15) #Maximum amount of ambient mobs that will spawn near a player (default:15)
mcl_mob_cap_ambient (Mob cap ambient mobs) int 15 0 1024 mcl_mob_cap_ambient (Mob cap ambient mobs) int 15 0 1024