diff --git a/mods/ENTITIES/mcl_mobs/api.lua b/mods/ENTITIES/mcl_mobs/api.lua index a5af89690..a9a1a0dad 100644 --- a/mods/ENTITIES/mcl_mobs/api.lua +++ b/mods/ENTITIES/mcl_mobs/api.lua @@ -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 diff --git a/mods/ENTITIES/mcl_mobs/effects.lua b/mods/ENTITIES/mcl_mobs/effects.lua index 696994d0d..1a1578834 100644 --- a/mods/ENTITIES/mcl_mobs/effects.lua +++ b/mods/ENTITIES/mcl_mobs/effects.lua @@ -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) diff --git a/mods/ENTITIES/mcl_mobs/movement.lua b/mods/ENTITIES/mcl_mobs/movement.lua index 469ac9e85..377e25f6e 100644 --- a/mods/ENTITIES/mcl_mobs/movement.lua +++ b/mods/ENTITIES/mcl_mobs/movement.lua @@ -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 diff --git a/mods/ENTITIES/mcl_mobs/physics.lua b/mods/ENTITIES/mcl_mobs/physics.lua index 2859cfe98..090b921c7 100644 --- a/mods/ENTITIES/mcl_mobs/physics.lua +++ b/mods/ENTITIES/mcl_mobs/physics.lua @@ -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