mirror of
https://git.minetest.land/VoxeLibre/VoxeLibre.git
synced 2024-12-23 16:39:33 +01:00
Merge pull request 'Some structure- and general mob fixes' (#2746) from fix_structmob_despawn into master
Reviewed-on: https://git.minetest.land/MineClone2/MineClone2/pulls/2746
This commit is contained in:
commit
10a857bb3f
10 changed files with 184 additions and 67 deletions
|
@ -204,6 +204,13 @@ local disable_physics = function(object, luaentity, ignore_check, reset_movement
|
|||
end
|
||||
end
|
||||
|
||||
local function player_in_active_range(self)
|
||||
for _,p in pairs(minetest.get_connected_players()) do
|
||||
if vector.distance(self.object:get_pos(),p:get_pos()) <= 38 then return true end
|
||||
-- slightly larger than the mc 32 since mobs spawn on that circle and easily stand still immediately right after spawning.
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- play sound
|
||||
local mob_sound = function(self, soundname, is_opinion, fixed_pitch)
|
||||
|
@ -1375,7 +1382,6 @@ end
|
|||
|
||||
-- jump if facing a solid node (not fences or gates)
|
||||
local do_jump = function(self)
|
||||
|
||||
if not self.jump
|
||||
or self.jump_height == 0
|
||||
or self.fly
|
||||
|
@ -1443,7 +1449,7 @@ local do_jump = function(self)
|
|||
|
||||
local v = self.object:get_velocity()
|
||||
|
||||
v.y = self.jump_height
|
||||
v.y = self.jump_height + 0.1
|
||||
|
||||
set_animation(self, "jump") -- only when defined
|
||||
|
||||
|
@ -3129,6 +3135,31 @@ local function check_item_pickup(self)
|
|||
end
|
||||
end
|
||||
|
||||
local check_herd_timer = 0
|
||||
local function check_herd(self,dtime)
|
||||
local pos = self.object:get_pos()
|
||||
if not pos then return end
|
||||
check_herd_timer = check_herd_timer + dtime
|
||||
if check_herd_timer < 4 then return end
|
||||
check_herd_timer = 0
|
||||
for _,o in pairs(minetest.get_objects_inside_radius(pos,self.view_range)) do
|
||||
local l = o:get_luaentity()
|
||||
local p,y
|
||||
if l and l.is_mob and l.name == self.name then
|
||||
if self.horny and l.horny then
|
||||
p = l.object:get_pos()
|
||||
else
|
||||
y = o:get_yaw()
|
||||
end
|
||||
if p then
|
||||
go_to_pos(self,p)
|
||||
elseif y then
|
||||
set_yaw(self,y)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function damage_mob(self,reason,damage)
|
||||
if not self.health then return end
|
||||
damage = floor(damage)
|
||||
|
@ -3570,7 +3601,7 @@ local mob_staticdata = function(self)
|
|||
and ((not self.nametag) or (self.nametag == ""))
|
||||
and self.lifetimer <= 20 then
|
||||
if spawn_logging then
|
||||
minetest.log("action", "[mcl_mobs] Mob "..tostring(self.name).." despawns in mob_staticdata at "..minetest.pos_to_string(vector.round(self.object:get_pos())))
|
||||
minetest.log("action", "[mcl_mobs] Mob "..tostring(self.name).." despawns at "..minetest.pos_to_string(vector.round(self.object:get_pos())) .. " - out of range")
|
||||
end
|
||||
|
||||
return "remove"-- nil
|
||||
|
@ -3794,6 +3825,35 @@ end
|
|||
-- main mob function
|
||||
local mob_step = function(self, dtime)
|
||||
self.lifetimer = self.lifetimer - dtime
|
||||
|
||||
local pos = self.object:get_pos()
|
||||
-- Despawning: when lifetimer expires, remove mob
|
||||
if remove_far
|
||||
and self.can_despawn == true
|
||||
and ((not self.nametag) or (self.nametag == ""))
|
||||
and self.state ~= "attack"
|
||||
and self.following == nil then
|
||||
if self.despawn_immediately or self.lifetimer <= 0 then
|
||||
if spawn_logging then
|
||||
minetest.log("action", "[mcl_mobs] Mob "..self.name.." despawns at "..minetest.pos_to_string(pos, 1) .. " lifetimer ran out")
|
||||
end
|
||||
mcl_burning.extinguish(self.object)
|
||||
self.object:remove()
|
||||
return
|
||||
elseif self.lifetimer <= 10 then
|
||||
if random(10) < 4 then
|
||||
self.despawn_immediately = true
|
||||
else
|
||||
self.lifetimer = 20
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not player_in_active_range(self) then
|
||||
set_animation(self, "stand", true)
|
||||
self.object:set_velocity(vector.new(0,0,0))
|
||||
return
|
||||
end
|
||||
check_item_pickup(self)
|
||||
check_aggro(self,dtime)
|
||||
particlespawner_check(self,dtime)
|
||||
|
@ -3803,7 +3863,6 @@ local mob_step = function(self, dtime)
|
|||
if not self.object:get_pos() then return end
|
||||
end
|
||||
|
||||
local pos = self.object:get_pos()
|
||||
local yaw = 0
|
||||
|
||||
if mobs_debug then
|
||||
|
@ -4016,6 +4075,10 @@ local mob_step = function(self, dtime)
|
|||
yaw = yaw + random(-0.5, 0.5)
|
||||
yaw = set_yaw(self, yaw, 8)
|
||||
end
|
||||
else
|
||||
if self.move_in_group ~= false then
|
||||
check_herd(self,dtime)
|
||||
end
|
||||
end
|
||||
|
||||
-- Add water flowing for mobs from mcl_item_entity
|
||||
|
@ -4064,28 +4127,6 @@ local mob_step = function(self, dtime)
|
|||
local yaw = self.object:get_yaw() or 0
|
||||
yaw = set_yaw(self, yaw + 0.78, 8)
|
||||
end
|
||||
|
||||
-- Despawning: when lifetimer expires, remove mob
|
||||
if remove_far
|
||||
and self.can_despawn == true
|
||||
and ((not self.nametag) or (self.nametag == ""))
|
||||
and self.state ~= "attack"
|
||||
and self.following == nil then
|
||||
if self.despawn_immediately or self.lifetimer <= 0 then
|
||||
if spawn_logging then
|
||||
minetest.log("action", "[mcl_mobs] Mob "..self.name.." despawns in mob_step at "..minetest.pos_to_string(pos, 1))
|
||||
end
|
||||
mcl_burning.extinguish(self.object)
|
||||
self.object:remove()
|
||||
return
|
||||
elseif self.lifetimer <= 10 then
|
||||
if random(10) < 4 then
|
||||
self.despawn_immediately = true
|
||||
else
|
||||
self.lifetimer = 20
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
--lua locals
|
||||
local minetest,vector,math,table = minetest,vector,math,table
|
||||
local get_node = minetest.get_node
|
||||
local get_item_group = minetest.get_item_group
|
||||
local get_node_light = minetest.get_node_light
|
||||
|
@ -25,15 +26,18 @@ local table_remove = table.remove
|
|||
local pairs = pairs
|
||||
local dbg_spawn_attempts = 0
|
||||
local dbg_spawn_succ = 0
|
||||
local dbg_spawn_counts = {}
|
||||
-- range for mob count
|
||||
local aoc_range = 136
|
||||
|
||||
local mob_cap = {
|
||||
monster = minetest.settings:get_bool("mcl_mob_cap_monster") or 70,
|
||||
animal = minetest.settings:get_bool("mcl_mob_cap_animal") or 10,
|
||||
ambient = minetest.settings:get_bool("mcl_mob_cap_ambient") or 15,
|
||||
water = minetest.settings:get_bool("mcl_mob_cap_water") or 5, --currently unused
|
||||
water_ambient = minetest.settings:get_bool("mcl_mob_cap_water_ambient") or 20, --currently unused
|
||||
monster = tonumber(minetest.settings:get("mcl_mob_cap_monster")) or 70,
|
||||
animal = tonumber(minetest.settings:get("mcl_mob_cap_animal")) or 10,
|
||||
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_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,
|
||||
}
|
||||
|
||||
--do mobs spawn?
|
||||
|
@ -237,6 +241,17 @@ local function count_mobs_total(mob_type)
|
|||
return num
|
||||
end
|
||||
|
||||
local function count_mobs_total_cap(mob_type)
|
||||
local num = 0
|
||||
for _,l in pairs(minetest.luaentities) do
|
||||
if l.is_mob then
|
||||
if ( mob_type == nil or l.type == mob_type ) and l.can_despawn and not l.nametag then
|
||||
num = num + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
return num
|
||||
end
|
||||
|
||||
-- global functions
|
||||
|
||||
|
@ -418,11 +433,38 @@ local function get_water_spawn(p)
|
|||
end
|
||||
end
|
||||
|
||||
local function spawn_check(pos,spawn_def)
|
||||
local function has_room(self,pos)
|
||||
local cb = self.collisionbox
|
||||
local nodes = {}
|
||||
if self.fly_in then
|
||||
local t = type(self.fly_in)
|
||||
if t == "table" then
|
||||
nodes = table.copy(self.fly_in)
|
||||
elseif t == "string" then
|
||||
table.insert(nodes,self.fly_in)
|
||||
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
|
||||
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
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local function spawn_check(pos,spawn_def,ignore_caps)
|
||||
if not spawn_def then return end
|
||||
dbg_spawn_attempts = dbg_spawn_attempts + 1
|
||||
local dimension = mcl_worlds.pos_to_dimension(pos)
|
||||
local mob_type = minetest.registered_entities[spawn_def.name].type
|
||||
local mob_def = minetest.registered_entities[spawn_def.name]
|
||||
local mob_type = mob_def.type
|
||||
local gotten_node = get_node(pos).name
|
||||
local gotten_biome = minetest.get_biome_data(pos)
|
||||
if not gotten_node or not gotten_biome then return end
|
||||
|
@ -435,25 +477,29 @@ local function spawn_check(pos,spawn_def)
|
|||
is_ground = minetest.get_item_group(gotten_node,"solid") ~= 0
|
||||
end
|
||||
pos.y = pos.y + 1
|
||||
local has_room = #minetest.find_nodes_in_area(pos,vector.offset(pos,0,1,0),{"air"}) or 0 >= 2
|
||||
local is_water = get_item_group(gotten_node, "water") ~= 0
|
||||
local is_lava = get_item_group(gotten_node, "lava") ~= 0
|
||||
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 = count_mobs(pos,aoc_range,mob_type)
|
||||
local mob_count = count_mobs(pos,32,mob_type)
|
||||
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 ( 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
|
||||
and biome_check(spawn_def.biomes, gotten_biome)
|
||||
and (is_ground or spawn_def.type_of_spawning ~= "ground")
|
||||
and (spawn_def.type_of_spawning ~= "ground" or not is_leaf)
|
||||
and (spawn_def.type_of_spawning ~= "ground" or has_room)
|
||||
and has_room(mob_def,pos)
|
||||
and (spawn_def.check_position and spawn_def.check_position(pos) or true)
|
||||
and (not is_farm_animal(spawn_def.name) or is_grass)
|
||||
and (spawn_def.type_of_spawning ~= "water" or is_water)
|
||||
|
@ -473,6 +519,11 @@ function mcl_mobs.spawn(pos,id)
|
|||
if not def or (def.can_spawn and not def.can_spawn(pos)) or not def.is_mob then
|
||||
return false
|
||||
end
|
||||
if not dbg_spawn_counts[def.name] then
|
||||
dbg_spawn_counts[def.name] = 1
|
||||
else
|
||||
dbg_spawn_counts[def.name] = dbg_spawn_counts[def.name] + 1
|
||||
end
|
||||
return minetest.add_entity(pos, def.name)
|
||||
end
|
||||
|
||||
|
@ -488,7 +539,7 @@ local function spawn_group(p,mob,spawn_on,group_max,group_min)
|
|||
end
|
||||
for i = 1, math.random(group_min,group_max) do
|
||||
local sp = vector.offset(nn[math.random(#nn)],0,1,0)
|
||||
if spawn_check(nn[math.random(#nn)],mob) then
|
||||
if spawn_check(nn[math.random(#nn)],mob,true) then
|
||||
if mob.type_of_spawning == "water" then
|
||||
sp = get_water_spawn(sp)
|
||||
end
|
||||
|
@ -609,38 +660,36 @@ if mobs_spawn then
|
|||
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 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
|
||||
if mob_def.type_of_spawning == "water" then
|
||||
spawning_position = get_water_spawn(spawning_position)
|
||||
if not spawning_position then
|
||||
return
|
||||
end
|
||||
end
|
||||
if minetest.registered_entities[mob_def.name].can_spawn and not minetest.registered_entities[mob_def.name].can_spawn(pos) then
|
||||
if mob_def.type_of_spawning == "water" then
|
||||
spawning_position = get_water_spawn(spawning_position)
|
||||
if not spawning_position then
|
||||
minetest.log("warning","[mcl_mobs] no water spawn for mob "..mob_def.name.." found at "..minetest.pos_to_string(vector.round(pos)))
|
||||
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 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)
|
||||
|
||||
else
|
||||
if logging then
|
||||
minetest.log("action", "[mcl_mobs] Mob " .. mob_def.name .. " spawns at " .. minetest.pos_to_string(spawning_position, 1))
|
||||
end
|
||||
object = mcl_mobs.spawn(spawning_position, mob_def.name)
|
||||
end
|
||||
if minetest.registered_entities[mob_def.name].can_spawn and not minetest.registered_entities[mob_def.name].can_spawn(pos) then
|
||||
minetest.log("warning","[mcl_mobs] mob "..mob_def.name.." refused to spawn at "..minetest.pos_to_string(vector.round(pos)))
|
||||
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)
|
||||
|
||||
|
||||
if object then
|
||||
return mob_def.on_spawn and mob_def.on_spawn(object, spawning_position)
|
||||
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)
|
||||
end
|
||||
end
|
||||
current_summary_chance = current_summary_chance - mob_chance
|
||||
table_remove(mob_library_worker_table, mob_index)
|
||||
|
@ -655,7 +704,13 @@ if mobs_spawn then
|
|||
timer = timer + dtime
|
||||
if timer < 10 then return end
|
||||
timer = 0
|
||||
for _, player in pairs(get_connected_players()) do
|
||||
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
|
||||
|
@ -672,6 +727,7 @@ end
|
|||
minetest.register_chatcommand("mobstats",{
|
||||
privs = { debug = true },
|
||||
func = function(n,param)
|
||||
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())
|
||||
|
|
|
@ -11,6 +11,8 @@ local cow_def = {
|
|||
xp_min = 1,
|
||||
xp_max = 3,
|
||||
collisionbox = {-0.45, -0.01, -0.45, 0.45, 1.39, 0.45},
|
||||
spawn_in_group = 8,
|
||||
spawn_in_group_min = 3,
|
||||
visual = "mesh",
|
||||
mesh = "mobs_mc_cow.b3d",
|
||||
textures = { {
|
||||
|
|
|
@ -157,6 +157,16 @@ cat.on_rightclick = function(self, clicker)
|
|||
|
||||
end
|
||||
|
||||
cat.on_spawn = function(self)
|
||||
if self.owner == "!witch!" then
|
||||
self._texture = {"mobs_mc_cat_black.png"}
|
||||
end
|
||||
if not self._texture then
|
||||
self._texture = cat.textures[math.random(#cat.textures)]
|
||||
end
|
||||
self.object:set_properties({textures = {self._texture}})
|
||||
end
|
||||
|
||||
mcl_mobs:register_mob("mobs_mc:cat", cat)
|
||||
|
||||
local base_spawn_chance = 5000
|
||||
|
|
|
@ -28,6 +28,7 @@ pillager = {
|
|||
shoot_interval = 3,
|
||||
shoot_offset = 1.5,
|
||||
armor = {fleshy = 100},
|
||||
can_despawn = false,
|
||||
collisionbox = {-0.3, -0.01, -0.3, 0.3, 1.98, 0.3},
|
||||
pathfinding = 1,
|
||||
group_attack = true,
|
||||
|
|
|
@ -15,6 +15,7 @@ mcl_mobs:register_mob("mobs_mc:evoker", {
|
|||
description = S("Evoker"),
|
||||
type = "monster",
|
||||
spawn_class = "hostile",
|
||||
can_despawn = false,
|
||||
physical = true,
|
||||
pathfinding = 1,
|
||||
hp_min = 24,
|
||||
|
|
|
@ -16,6 +16,7 @@ mcl_mobs:register_mob("mobs_mc:witch", {
|
|||
description = S("Witch"),
|
||||
type = "monster",
|
||||
spawn_class = "hostile",
|
||||
can_despawn = false,
|
||||
hp_min = 26,
|
||||
hp_max = 26,
|
||||
xp_min = 5,
|
||||
|
|
|
@ -103,7 +103,6 @@ baby_pigman.description = S("Baby Zombie Pigman")
|
|||
baby_pigman.collisionbox = {-0.25, -0.01, -0.25, 0.25, 0.94, 0.25}
|
||||
baby_pigman.xp_min = 13
|
||||
baby_pigman.xp_max = 13
|
||||
baby_pigman.visual_size = {x=pigman.visual_size.x/2, y=pigman.visual_size.y/2}
|
||||
baby_pigman.textures = { {
|
||||
"mobs_mc_zombie_pigman.png", --baby
|
||||
"default_tool_goldsword.png", --sword
|
||||
|
|
|
@ -69,12 +69,12 @@ mcl_structures.register_structure("pillager_outpost",{
|
|||
if not peaceful then
|
||||
if sp and #sp > 0 then
|
||||
for i=1,5 do
|
||||
local pos = sp[pr:next(1,#sp)]
|
||||
local pos = vector.offset(sp[pr:next(1,#sp)],0,1,0)
|
||||
if pos then
|
||||
minetest.add_entity(pos,"mobs_mc:pillager")
|
||||
end
|
||||
end
|
||||
local pos = sp[pr:next(1,#sp)]
|
||||
local pos = vector.offset(sp[pr:next(1,#sp)],0,1,0)
|
||||
if pos then
|
||||
minetest.add_entity(pos,"mobs_mc:evoker")
|
||||
end
|
||||
|
|
|
@ -99,6 +99,12 @@ mobs_griefing (Mobs change blocks) bool true
|
|||
# If enabled, mobs won't damage particles when they got hurt.
|
||||
mobs_disable_blood (Disable mob damage particles) bool false
|
||||
|
||||
#Maximum amount mobs (default:500)
|
||||
mcl_mob_cap_total (Global mob cap) int 500 0 2048
|
||||
|
||||
#Maximum amount of mobs per player (default:75)
|
||||
mcl_mob_cap_player (Mob cap per player) int 75 0 2048
|
||||
|
||||
#Maximum amount of monsters that will spawn near a player (default:70)
|
||||
mcl_mob_cap_monster (Mob cap monsters) int 70 0 2048
|
||||
|
||||
|
|
Loading…
Reference in a new issue