Merge pull request 'Optimise mobs' (#3720) from optimise_mobs into master

Reviewed-on: https://git.minetest.land/MineClone2/MineClone2/pulls/3720
This commit is contained in:
ancientmarinerdev 2023-05-15 14:03:54 +00:00
commit 6ded4d2322
4 changed files with 144 additions and 123 deletions

@ -111,6 +111,21 @@ function mob_class:get_staticdata()
return minetest.serialize(tmp)
end
local function valid_texture(self, def_textures)
if not self.base_texture then
return false
end
if self.texture_selected then
if #def_textures < self.texture_selected then
self.texture_selected = nil
else
return true
end
end
return false
end
function mob_class:mob_activate(staticdata, def, dtime)
if not self.object:get_pos() or staticdata == "remove" then
mcl_burning.extinguish(self.object)
@ -133,16 +148,20 @@ function mob_class:mob_activate(staticdata, def, dtime)
end
--If textures in definition change, reload textures
if not self.base_texture or (def.textures and table.indexof(def.textures, self.base_texture) == -1) then
if not valid_texture(self, def.textures) then
-- compatiblity with old simple mobs textures
if type(def.textures[1]) == "string" then
def.textures = {def.textures}
end
local c = 1
if #def.textures > c then c = #def.textures end
if not self.texture_selected then
local c = 1
if #def.textures > c then c = #def.textures end
self.texture_selected = math.random(c)
end
self.base_texture = def.textures[math.random(c)]
self.base_texture = def.textures[self.texture_selected]
self.base_mesh = def.mesh
self.base_size = self.visual_size
self.base_colbox = self.collisionbox
@ -297,7 +316,7 @@ end
-- execute current state (stand, walk, run, attacks)
-- returns true if mob has died
function mob_class:do_states(dtime)
function mob_class:do_states(dtime, player_in_active_range)
--if self.can_open_doors then check_doors(self) end
-- knockback timer. set in on_punch
@ -306,6 +325,8 @@ function mob_class:do_states(dtime)
return
end
self:env_danger_movement_checks(player_in_active_range)
if self.state == PATHFINDING then
self:check_gowp(dtime)
elseif self.state == "attack" then
@ -315,7 +336,7 @@ function mob_class:do_states(dtime)
else
if mcl_util.check_dtime_timer(self, dtime, "onstep_dostates", 1) then
if self.state == "stand" then
self:do_states_stand()
self:do_states_stand(player_in_active_range)
elseif self.state == "walk" then
self:do_states_walk()
elseif self.state == "runaway" then
@ -366,30 +387,22 @@ local function on_step_work (self, dtime)
end
if self:falling(pos) then return end
local player_in_active_range = self:player_in_active_range()
self:check_suspend(player_in_active_range)
if not self.fire_resistant then
mcl_burning.tick(self.object, dtime, self)
if not self.object:get_pos() then return end -- mcl_burning.tick may remove object immediately
if self:check_for_death("fire", {type = "fire"}) then
return true
end
end
if self:env_damage (dtime, pos) then return end
if self:step_damage (dtime, pos) then return end
if self.state == "die" then return end
-- End: Death/damage processing
self:check_water_flow()
self:env_danger_movement_checks (dtime)
local player_in_active_range = self:player_in_active_range()
self:check_suspend(player_in_active_range)
self:check_water_flow()
if not self._jumping_cliff then
self._can_jump_cliff = self:can_jump_cliff()
else
self._can_jump_cliff = false
end
-- Follow code is heavy and probably shouldn't run when not in range, but we need to extract the cancel follow stuff
self:check_follow()
self:flop()
self:check_smooth_rotation(dtime)
@ -399,14 +412,28 @@ local function on_step_work (self, dtime)
self:check_head_swivel(dtime)
if self.jump_sound_cooloff > 0 then self.jump_sound_cooloff = self.jump_sound_cooloff - dtime end
self:do_jump()
self:check_runaway_from()
self:monster_attack()
self:npc_attack()
if mcl_util.check_dtime_timer(self, dtime, "onstep_engage", 0.2) then
self:check_follow()
self:check_runaway_from()
self:monster_attack()
self:npc_attack()
end
self:check_herd(dtime)
if self.jump_sound_cooloff > 0 then self.jump_sound_cooloff = self.jump_sound_cooloff - dtime end
self:do_jump()
end
if mcl_util.check_dtime_timer(self, dtime, "onstep_occassional", 1) then
if player_in_active_range then
self:check_item_pickup()
self:set_armor_texture()
self:step_opinion_sound(dtime)
end
self:check_breeding()
end
self:check_aggro(dtime)
@ -415,17 +442,7 @@ local function on_step_work (self, dtime)
if self.do_custom and self.do_custom(self, dtime) == false then return end
if mcl_util.check_dtime_timer(self, dtime, "onstep_occassional", 1) then
self:check_breeding()
if player_in_active_range then
self:check_item_pickup()
self:set_armor_texture()
self:step_opinion_sound(dtime)
end
end
if self:do_states(dtime) then return end
if self:do_states(dtime, player_in_active_range) then return end
if mobs_debug then self:update_tag() end

@ -269,7 +269,7 @@ function mob_class:set_animation(anim, fixed_frame)
if self:flight_check() and self.fly and anim == "walk" then anim = "fly" end
if self.fly and self:flight_check() and anim == "walk" then anim = "fly" end
self._current_animation = self._current_animation or ""
@ -324,12 +324,12 @@ local function who_are_you_looking_at (self)
self._locked_object = nil
end
elseif not self._locked_object then
if math.random(1, 30) then
if mcl_util.check_dtime_timer(self, dtime, "step_look_for_someone", 0.2) then
--minetest.log("Change look check: ".. self.name)
-- For the wither this was 20/60=0.33, so probably need to rebalance and divide rates.
-- but frequency of check isn't good as it is costly. Making others too infrequent requires testing
local chance = 20/self.curiosity
local chance = 150/self.curiosity
if chance < 1 then chance = 1 end
local look_at_player_chance = math.random(chance)

@ -4,8 +4,11 @@ local DEFAULT_FALL_SPEED = -9.81*1.5
local FLOP_HEIGHT = 6
local FLOP_HOR_SPEED = 1.5
local node_snow = "mcl_core:snow"
local CHECK_HERD_FREQUENCY = 4
local PATHFINDING = "gowp"
local node_snow = "mcl_core:snow"
local mobs_griefing = minetest.settings:get_bool("mobs_griefing") ~= false
@ -202,12 +205,8 @@ function mob_class:can_jump_cliff()
end
-- is mob facing a cliff or danger
function mob_class:is_at_cliff_or_danger(can_jump_cliff)
if can_jump_cliff == nil then
can_jump_cliff = self:can_jump_cliff()
end
if self.fear_height == 0 or can_jump_cliff or self._jumping_cliff or not self.object:get_luaentity() then -- 0 for no falling protection!
function mob_class:is_at_cliff_or_danger()
if self.fear_height == 0 or self._jumping_cliff or self._can_jump_cliff or not self.object:get_luaentity() then -- 0 for no falling protection!
return false
end
@ -242,12 +241,16 @@ end
-- copy the 'mob facing cliff_or_danger check' from above, and rework to avoid water
function mob_class:is_at_water_danger(can_jump_cliff)
if can_jump_cliff == nil then
can_jump_cliff = self:can_jump_cliff()
function mob_class:is_at_water_danger()
if self.water_damage == 0 and self.breath_max == -1 then
--minetest.log("Do not need a water check for: " .. self.name)
return
end
if not self.object:get_luaentity() or can_jump_cliff or self._jumping_cliff then
local in_water_danger = self:is_node_waterhazard(self.standing_in) or self:is_node_waterhazard(self.standing_on)
if in_water_danger then return false end -- If you're in trouble, do not stop
if not self.object:get_luaentity() or self._jumping_cliff or self._can_jump_cliff then
return false
end
local yaw = self.object:get_yaw()
@ -262,52 +265,57 @@ function mob_class:is_at_water_danger(can_jump_cliff)
local ypos = pos.y + self.collisionbox[2] -- just above floor
local free_fall, blocker = minetest.line_of_sight(
local los, blocker = minetest.line_of_sight(
vector.new(pos.x + dir_x, ypos, pos.z + dir_z),
vector.new(pos.x + dir_x, ypos - 3, pos.z + dir_z))
if free_fall then
return true
else
if not los then
local bnode = minetest.get_node(blocker)
local waterdanger = self:is_node_waterhazard(bnode.name)
if
waterdanger and (self:is_node_waterhazard(self.standing_in) or self:is_node_waterhazard( self.standing_on)) then
return false
elseif waterdanger and (self:is_node_waterhazard(self.standing_in) or self:is_node_waterhazard(self.standing_on)) == false then
if waterdanger and not in_water_danger then
return true
else
local def = minetest.registered_nodes[bnode.name]
if def and def.walkable then
return false
end
end
end
return false
end
function mob_class:env_danger_movement_checks(dtime)
function mob_class:env_danger_movement_checks(player_in_active_range)
local yaw = 0
local can_jump_cliff = self:can_jump_cliff()
if self.state ~= "attack" and self:is_at_water_danger(can_jump_cliff) then
if math.random(1, 10) <= 6 then
self:set_velocity(0)
self.state = "stand"
self:set_animation( "stand")
if not player_in_active_range then return end
if self.state == PATHFINDING
or self.state == "attack"
or self.state == "stand"
or self.state == "runaway" then
return
end
if self:is_at_water_danger() then
--minetest.log("At water danger for mob, stop?: " .. self.name)
if math.random(1, 10) <= 7 then
if self.state ~= "stand" then
self:set_velocity(0)
self.state = "stand"
self:set_animation( "stand")
end
yaw = yaw + math.random(-0.5, 0.5)
yaw = self:set_yaw( yaw, 8)
return
end
end
if self:is_at_cliff_or_danger(can_jump_cliff) then
self:set_velocity(0)
self.state = "stand"
self:set_animation( "stand")
--[[if self:is_at_cliff_or_danger(can_jump_cliff) then
if self.state ~= "stand" then
self:set_velocity(0)
self.state = "stand"
self:set_animation( "stand")
end
local yaw = self.object:get_yaw() or 0
yaw = self:set_yaw( yaw + 0.78, 8)
end
end--]]
end
-- jump if facing a solid node (not fences or gates)
@ -378,7 +386,7 @@ function mob_class:do_jump()
end
local ndef = minetest.registered_nodes[nod.name]
if self.walk_chance == 0 or ndef and ndef.walkable or self:can_jump_cliff() then
if self.walk_chance == 0 or ndef and ndef.walkable or self._can_jump_cliff then
if minetest.get_item_group(nod.name, "fence") == 0
and minetest.get_item_group(nod.name, "fence_gate") == 0
@ -388,7 +396,7 @@ function mob_class:do_jump()
v.y = self.jump_height + 0.1 * 3
if self:can_jump_cliff() then
if self._can_jump_cliff then
v=vector.multiply(v, vector.new(2.8,1,2.8))
end
@ -623,7 +631,7 @@ function mob_class:check_runaway_from()
end
-- follow player if owner or holding item, if fish outta water then flop
-- follow player if owner or holding item
function mob_class:check_follow()
-- find player to follow
if (self.follow ~= "" or self.order == "follow") and not self.following
@ -724,7 +732,7 @@ function mob_class:flop()
return
elseif self.state == "flop" then
self.state = "stand"
self.object:set_acceleration({x = 0, y = 0, z = 0})
self.object:set_acceleration(vector.zero())
self:set_velocity(0)
end
end
@ -756,7 +764,7 @@ function mob_class:check_herd(dtime)
if self.move_in_group == false then return end
check_herd_timer = check_herd_timer + dtime
if check_herd_timer < 4 then return end
if check_herd_timer < CHECK_HERD_FREQUENCY 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()
@ -887,7 +895,7 @@ function mob_class:do_states_walk()
end
end
function mob_class:do_states_stand()
function mob_class:do_states_stand(player_in_active_range)
local yaw = self.object:get_yaw() or 0
if math.random(1, 4) == 1 then
@ -931,14 +939,16 @@ function mob_class:do_states_stand()
if self.order == "stand" or self.order == "sleep" or self.order == "work" then
else
if self.walk_chance ~= 0
and self.facing_fence ~= true
and math.random(1, 100) <= self.walk_chance
and self:is_at_cliff_or_danger() == false then
if player_in_active_range then
if self.walk_chance ~= 0
and self.facing_fence ~= true
and math.random(1, 100) <= self.walk_chance
and self:is_at_cliff_or_danger() == false then
self:set_velocity(self.walk_velocity)
self.state = "walk"
self:set_animation( "walk")
self:set_velocity(self.walk_velocity)
self.state = "walk"
self:set_animation( "walk")
end
end
end
end

@ -820,11 +820,19 @@ function mob_class:do_env_damage()
return self:check_for_death("unknown", {type = "unknown"})
end
function mob_class:env_damage (dtime, pos)
function mob_class:step_damage (dtime, pos)
if not self.fire_resistant then
mcl_burning.tick(self.object, dtime, self)
if not self.object:get_pos() then return end -- mcl_burning.tick may remove object immediately
if self:check_for_death("fire", {type = "fire"}) then
return true
end
end
-- environmental damage timer (every 1 second)
self.env_damage_timer = self.env_damage_timer + dtime
if self.env_damage_timer > 1 then
self.env_damage_timer = 0
@ -903,47 +911,36 @@ function mob_class:falling(pos)
-- floating in water (or falling)
local v = self.object:get_velocity()
if v then
local new_acceleration
if v.y > 0 then
-- apply gravity when moving up
self.object:set_acceleration({
x = 0,
y = DEFAULT_FALL_SPEED,
z = 0
})
new_acceleration = vector.new(0, DEFAULT_FALL_SPEED, 0)
elseif v.y <= 0 and v.y > self.fall_speed then
-- fall downwards at set speed
self.object:set_acceleration({
x = 0,
y = self.fall_speed,
z = 0
})
new_acceleration = vector.new(0, self.fall_speed, 0)
else
-- stop accelerating once max fall speed hit
self.object:set_acceleration({x = 0, y = 0, z = 0})
new_acceleration =vector.zero()
end
self.object:set_acceleration(new_acceleration)
end
local acc = self.object:get_acceleration()
if minetest.registered_nodes[node_ok(pos).name].groups.lava then
local registered_node = minetest.registered_nodes[node_ok(pos).name]
if registered_node.groups.lava then
if acc and self.floats_on_lava == 1 then
self.object:set_acceleration({
x = 0,
y = -self.fall_speed / (math.max(1, v.y) ^ 2),
z = 0
})
self.object:set_acceleration(vector.new(0, -self.fall_speed / (math.max(1, v.y) ^ 2), 0))
end
end
-- in water then float up
if minetest.registered_nodes[node_ok(pos).name].groups.water then
if registered_node.groups.water then
if acc and self.floats == 1 then
self.object:set_acceleration({
x = 0,
y = -self.fall_speed / (math.max(1, v.y) ^ 2),
z = 0
})
self.object:set_acceleration(vector.new(0, -self.fall_speed / (math.max(1, v.y) ^ 2), 0))
end
else
-- fall damage onto solid ground
@ -1030,9 +1027,6 @@ function mob_class:check_suspend(player_in_active_range)
self.object:set_acceleration(vector.zero())
self.object:set_velocity(vector.zero())
end
if acc.y == 0 and node_under == "air" then
self:falling(pos)
end
end
return true
end