From 8c648d1fc3e627abd5e8371e07832bb92915205a Mon Sep 17 00:00:00 2001 From: ancientmarinerdev Date: Mon, 2 Jan 2023 00:00:40 +0000 Subject: [PATCH] Refactor mob_step and do_states --- mods/ENTITIES/mcl_mobs/api.lua | 126 ++--- mods/ENTITIES/mcl_mobs/combat.lua | 394 +++++++++++++++ mods/ENTITIES/mcl_mobs/effects.lua | 1 + mods/ENTITIES/mcl_mobs/movement.lua | 756 +++++++--------------------- mods/ENTITIES/mcl_mobs/physics.lua | 30 ++ 5 files changed, 666 insertions(+), 641 deletions(-) diff --git a/mods/ENTITIES/mcl_mobs/api.lua b/mods/ENTITIES/mcl_mobs/api.lua index a898ed9794..46e113c54d 100644 --- a/mods/ENTITIES/mcl_mobs/api.lua +++ b/mods/ENTITIES/mcl_mobs/api.lua @@ -297,51 +297,67 @@ function mob_class:mob_activate(staticdata, def, dtime) end end +-- execute current state (stand, walk, run, attacks) +-- returns true if mob has died +function mob_class:do_states(dtime) + --if self.can_open_doors then check_doors(self) end + if self.state == "stand" then + self:do_states_stand() + elseif self.state == PATHFINDING then + self:check_gowp(dtime) + elseif self.state == "walk" then + self:do_state_walk() + elseif self.state == "runaway" then + -- runaway when punched + self:do_states_runaway() + elseif self.state == "attack" then + -- attack routines (explode, dogfight, shoot, dogshoot) + if self:do_state_attack(dtime) then + return true + end + end +end + +local function update_timers (self, dtime) + -- knockback timer + if self.pause_timer > 0 then + self.pause_timer = self.pause_timer - dtime + return true + end + + -- attack timer + self.timer = self.timer + dtime + + if self.state ~= "attack" and self.state ~= PATHFINDING then + if self.timer < 1 then + return true + end + self.timer = 0 + end + + -- never go over 100 + if self.timer > 100 then + self.timer = 1 + end +end -- main mob function function mob_class:on_step(dtime) self.lifetimer = self.lifetimer - dtime + local pos = self.object:get_pos() if not pos then return end + if self:check_despawn(pos) then return true end - local d = 0.85 - if self:check_dying() then d = 0.92 end - - local v = self.object:get_velocity() - if v then - --diffuse object velocity - self.object:set_velocity({x = v.x*d, y = v.y, z = v.z*d}) - end - + self:slow_mob() if self:falling(pos) then return end self:check_suspend() self:check_water_flow() - local yaw = 0 - if self:is_at_water_danger() and self.state ~= "attack" then - if math.random(1, 10) <= 6 then - self:set_velocity(0) - self.state = "stand" - self:set_animation( "stand") - yaw = yaw + math.random(-0.5, 0.5) - yaw = self:set_yaw( yaw, 8) - end - else - if self.move_in_group ~= false then - self:check_herd(dtime) - end - end - - if self:is_at_cliff_or_danger() then - self:set_velocity(0) - self.state = "stand" - self:set_animation( "stand") - local yaw = self.object:get_yaw() or 0 - yaw = self:set_yaw( yaw + 0.78, 8) - end + self:env_danger_movement_checks (dtime) if not self.fire_resistant then mcl_burning.tick(self.object, dtime, self) @@ -378,63 +394,23 @@ function mob_class:on_step(dtime) -- run custom function (defined in mob lua file) if self.do_custom then - - -- when false skip going any further if self.do_custom(self, dtime) == false then return end end - -- knockback timer - if self.pause_timer > 0 then + if update_timers(self, dtime) then return end - self.pause_timer = self.pause_timer - dtime - - return - end - - -- attack timer - self.timer = self.timer + dtime - - if self.state ~= "attack" and self.state ~= PATHFINDING then - if self.timer < 1 then - return - end - self.timer = 0 - end self:check_particlespawners(dtime) self:check_item_pickup() - -- never go over 100 - if self.timer > 100 then - self.timer = 1 - end - - -- mob plays random sound at times + -- mob plays random sound at times. Should be 120. Zombie and mob farms are ridiculous if math.random(1, 70) == 1 then self:mob_sound("random", true) end - -- environmental damage timer (every 1 second) - self.env_damage_timer = self.env_damage_timer + dtime - - if (self.state == "attack" and self.env_damage_timer > 1) - or self.state ~= "attack" then - self:check_entity_cramming() - self.env_damage_timer = 0 - - -- check for environmental damage (water, fire, lava etc.) - if self:do_env_damage() then - return - end - - -- node replace check (cow eats grass etc.) - self:replace(pos) - end - - if self:do_states(dtime) then - return - end + if self:env_damage (dtime, pos) then return end + if self:do_states(dtime) then return end if not self.object:get_luaentity() then return false diff --git a/mods/ENTITIES/mcl_mobs/combat.lua b/mods/ENTITIES/mcl_mobs/combat.lua index 5577a43612..24eb2f290c 100644 --- a/mods/ENTITIES/mcl_mobs/combat.lua +++ b/mods/ENTITIES/mcl_mobs/combat.lua @@ -794,3 +794,397 @@ function mob_class:check_aggro(dtime) end self._check_aggro_timer = self._check_aggro_timer + dtime end + +function mob_class:do_state_attack (dtime) + local yaw = self.object:get_yaw() or 0 + + local s = self.object:get_pos() + local p = self.attack:get_pos() or s + + -- stop attacking if player invisible or out of range + if not self.attack + or not self.attack:get_pos() + or not self:object_in_range(self.attack) + or self.attack:get_hp() <= 0 + or (self.attack:is_player() and mcl_mobs.invis[ self.attack:get_player_name() ]) then + + self.state = "stand" + self:set_velocity( 0) + self:set_animation( "stand") + self.attack = nil + self.v_start = false + self.timer = 0 + self.blinktimer = 0 + self.path.way = nil + + return + end + + -- calculate distance from mob and enemy + local dist = vector.distance(p, s) + + if self.attack_type == "explode" then + + local vec = { + x = p.x - s.x, + z = p.z - s.z + } + + yaw = (atan(vec.z / vec.x) +math.pi/ 2) - self.rotate + + if p.x > s.x then yaw = yaw +math.pi end + + yaw = self:set_yaw( yaw, 0, dtime) + + local node_break_radius = self.explosion_radius or 1 + local entity_damage_radius = self.explosion_damage_radius + or (node_break_radius * 2) + + -- start timer when in reach and line of sight + if not self.v_start + and dist <= self.reach + and self:line_of_sight( s, p, 2) then + + self.v_start = true + self.timer = 0 + self.blinktimer = 0 + self:mob_sound("fuse", nil, false) + + -- stop timer if out of reach or direct line of sight + elseif self.allow_fuse_reset + and self.v_start + and (dist >= self.explosiontimer_reset_radius + or not self:line_of_sight( s, p, 2)) then + self.v_start = false + self.timer = 0 + self.blinktimer = 0 + self.blinkstatus = false + self:remove_texture_mod("^[brighten") + end + + -- walk right up to player unless the timer is active + if self.v_start and (self.stop_to_explode or dist < self.reach) then + self:set_velocity( 0) + else + self:set_velocity( self.run_velocity) + end + + if self.animation and self.animation.run_start then + self:set_animation( "run") + else + self:set_animation( "walk") + end + + if self.v_start then + + self.timer = self.timer + dtime + self.blinktimer = (self.blinktimer or 0) + dtime + + if self.blinktimer > 0.2 then + + self.blinktimer = 0 + + if self.blinkstatus then + self:remove_texture_mod("^[brighten") + else + self:add_texture_mod("^[brighten") + end + + self.blinkstatus = not self.blinkstatus + end + + if self.timer > self.explosion_timer then + + local pos = self.object:get_pos() + + if mobs_griefing and not minetest.is_protected(pos, "") then + mcl_explosions.explode(mcl_util.get_object_center(self.object), self.explosion_strength, { drop_chance = 1.0 }, self.object) + else + minetest.sound_play(self.sounds.explode, { + pos = pos, + gain = 1.0, + max_hear_distance = self.sounds.distance or 32 + }, true) + self:entity_physics(pos,entity_damage_radius) + mcl_mobs.effect(pos, 32, "mcl_particles_smoke.png", nil, nil, node_break_radius, 1, 0) + end + mcl_burning.extinguish(self.object) + self.object:remove() + + return true + end + end + + elseif self.attack_type == "dogfight" + or (self.attack_type == "dogshoot" and self:dogswitch(dtime) == 2) and (dist >= self.avoid_distance or not self.shooter_avoid_enemy) + or (self.attack_type == "dogshoot" and dist <= self.reach and self:dogswitch() == 0) then + + if self.fly + and dist > self.reach then + + local p1 = s + local me_y = math.floor(p1.y) + local p2 = p + local p_y = math.floor(p2.y + 1) + local v = self.object:get_velocity() + + if self:flight_check( s) then + + if me_y < p_y then + + self.object:set_velocity({ + x = v.x, + y = 1 * self.walk_velocity, + z = v.z + }) + + elseif me_y > p_y then + + self.object:set_velocity({ + x = v.x, + y = -1 * self.walk_velocity, + z = v.z + }) + end + else + if me_y < p_y then + + self.object:set_velocity({ + x = v.x, + y = 0.01, + z = v.z + }) + + elseif me_y > p_y then + + self.object:set_velocity({ + x = v.x, + y = -0.01, + z = v.z + }) + end + end + + end + + -- rnd: new movement direction + if self.path.following + and self.path.way + and self.attack_type ~= "dogshoot" then + + -- no paths longer than 50 + if #self.path.way > 50 + or dist < self.reach then + self.path.following = false + return + end + + local p1 = self.path.way[1] + + if not p1 then + self.path.following = false + return + end + + if math.abs(p1.x-s.x) + math.abs(p1.z - s.z) < 0.6 then + -- reached waypoint, remove it from queue + table.remove(self.path.way, 1) + end + + -- set new temporary target + p = {x = p1.x, y = p1.y, z = p1.z} + end + + local vec = { + x = p.x - s.x, + z = p.z - s.z + } + + yaw = (atan(vec.z / vec.x) + math.pi / 2) - self.rotate + + if p.x > s.x then yaw = yaw + math.pi end + + yaw = self:set_yaw( yaw, 0, dtime) + + -- move towards enemy if beyond mob reach + if dist > self.reach then + + -- path finding by rnd + if self.pathfinding -- only if mob has pathfinding enabled + and enable_pathfinding then + + self:smart_mobs(s, p, dist, dtime) + end + + if self:is_at_cliff_or_danger() then + + self:set_velocity( 0) + self:set_animation( "stand") + local yaw = self.object:get_yaw() or 0 + yaw = self:set_yaw( yaw + 0.78, 8) + else + + if self.path.stuck then + self:set_velocity( self.walk_velocity) + else + self:set_velocity( self.run_velocity) + end + + if self.animation and self.animation.run_start then + self:set_animation( "run") + else + self:set_animation( "walk") + end + end + + else -- rnd: if inside reach range + + self.path.stuck = false + self.path.stuck_timer = 0 + self.path.following = false -- not stuck anymore + + self:set_velocity( 0) + + if not self.custom_attack then + + if self.timer > 1 then + + self.timer = 0 + + if self.double_melee_attack + and math.random(1, 2) == 1 then + self:set_animation( "punch2") + else + self:set_animation( "punch") + end + + local p2 = p + local s2 = s + + p2.y = p2.y + .5 + s2.y = s2.y + .5 + + if self:line_of_sight( p2, s2) == true then + + -- play attack sound + self:mob_sound("attack") + + -- punch player (or what player is attached to) + local attached = self.attack:get_attach() + if attached then + self.attack = attached + end + self.attack:punch(self.object, 1.0, { + full_punch_interval = 1.0, + damage_groups = {fleshy = self.damage} + }, nil) + end + end + else -- call custom attack every second + if self.custom_attack + and self.timer > 1 then + + self.timer = 0 + + self.custom_attack(self, p) + end + end + end + + elseif self.attack_type == "shoot" + or (self.attack_type == "dogshoot" and self:dogswitch(dtime) == 1) + or (self.attack_type == "dogshoot" and (dist > self.reach or dist < self.avoid_distance and self.shooter_avoid_enemy) and self:dogswitch() == 0) then + + p.y = p.y - .5 + s.y = s.y + .5 + + local dist = vector.distance(p, s) + local vec = { + x = p.x - s.x, + y = p.y - s.y, + z = p.z - s.z + } + + yaw = (atan(vec.z / vec.x) +math.pi/ 2) - self.rotate + + if p.x > s.x then yaw = yaw +math.pi end + + yaw = self:set_yaw( yaw, 0, dtime) + + local stay_away_from_player = vector.new(0,0,0) + + --strafe back and fourth + + --stay away from player so as to shoot them + if dist < self.avoid_distance and self.shooter_avoid_enemy then + self:set_animation( "shoot") + stay_away_from_player=vector.multiply(vector.direction(p, s), 0.33) + end + + if self.strafes then + if not self.strafe_direction then + self.strafe_direction = 1.57 + end + if math.random(40) == 1 then + self.strafe_direction = self.strafe_direction*-1 + end + self.acc = vector.add(vector.multiply(vector.rotate_around_axis(vector.direction(s, p), vector.new(0,1,0), self.strafe_direction), 0.3*self.walk_velocity), stay_away_from_player) + else + self:set_velocity( 0) + end + + local p = self.object:get_pos() + p.y = p.y + (self.collisionbox[2] + self.collisionbox[5]) / 2 + + if self.shoot_interval + and self.timer > self.shoot_interval + and not minetest.raycast(vector.add(p, vector.new(0,self.shoot_offset,0)), vector.add(self.attack:get_pos(), vector.new(0,1.5,0)), false, false):next() + and math.random(1, 100) <= 60 then + + self.timer = 0 + self:set_animation( "shoot") + + -- play shoot attack sound + self:mob_sound("shoot_attack") + + -- Shoot arrow + if minetest.registered_entities[self.arrow] then + + local arrow, ent + local v = 1 + if not self.shoot_arrow then + self.firing = true + minetest.after(1, function() + self.firing = false + end) + arrow = minetest.add_entity(p, self.arrow) + ent = arrow:get_luaentity() + if ent.velocity then + v = ent.velocity + end + ent.switch = 1 + ent.owner_id = tostring(self.object) -- add unique owner id to arrow + + -- important for mcl_shields + ent._shooter = self.object + ent._saved_shooter_pos = self.object:get_pos() + end + + local amount = (vec.x * vec.x + vec.y * vec.y + vec.z * vec.z) ^ 0.5 + -- offset makes shoot aim accurate + vec.y = vec.y + self.shoot_offset + vec.x = vec.x * (v / amount) + vec.y = vec.y * (v / amount) + vec.z = vec.z * (v / amount) + if self.shoot_arrow then + vec = vector.normalize(vec) + self:shoot_arrow(p, vec) + else + arrow:set_velocity(vec) + end + end + end + else + + end +end diff --git a/mods/ENTITIES/mcl_mobs/effects.lua b/mods/ENTITIES/mcl_mobs/effects.lua index 476c134691..81ebdbd2e1 100644 --- a/mods/ENTITIES/mcl_mobs/effects.lua +++ b/mods/ENTITIES/mcl_mobs/effects.lua @@ -116,6 +116,7 @@ function mob_class:mob_sound(soundname, is_opinion, fixed_pitch) -- randomize the pitch a bit pitch = pitch + math.random(-10, 10) * 0.005 end + -- Should be 0.1 to 0.2 for mobs. Cow and zombie farms loud. At least have cool down. minetest.sound_play(sound, { object = self.object, gain = 1.0, diff --git a/mods/ENTITIES/mcl_mobs/movement.lua b/mods/ENTITIES/mcl_mobs/movement.lua index 2e8b4198c2..753f792819 100644 --- a/mods/ENTITIES/mcl_mobs/movement.lua +++ b/mods/ENTITIES/mcl_mobs/movement.lua @@ -269,6 +269,31 @@ function mob_class:is_at_water_danger() return false end +function mob_class:env_danger_movement_checks(dtime) + local yaw = 0 + if self:is_at_water_danger() and self.state ~= "attack" then + if math.random(1, 10) <= 6 then + self:set_velocity(0) + self.state = "stand" + self:set_animation( "stand") + yaw = yaw + math.random(-0.5, 0.5) + yaw = self:set_yaw( yaw, 8) + end + else + if self.move_in_group ~= false then + self:check_herd(dtime) + end + end + + if self:is_at_cliff_or_danger() then + self:set_velocity(0) + self.state = "stand" + self:set_animation( "stand") + local yaw = self.object:get_yaw() or 0 + yaw = self:set_yaw( yaw + 0.78, 8) + end +end + -- jump if facing a solid node (not fences or gates) function mob_class:do_jump() if not self.jump @@ -769,591 +794,190 @@ function mob_class:teleport(target) end end --- execute current state (stand, walk, run, attacks) --- returns true if mob has died -function mob_class:do_states(dtime) - --if self.can_open_doors then check_doors(self) end - +function mob_class:do_state_walk() local yaw = self.object:get_yaw() or 0 - if self.state == "stand" then - if math.random(1, 4) == 1 then + local s = self.object:get_pos() + local lp = nil + + -- is there something I need to avoid? + if (self.water_damage > 0 + and self.lava_damage > 0) + or self.breath_max ~= -1 then + lp = minetest.find_node_near(s, 1, {"group:water", "group:lava"}) + elseif self.water_damage > 0 then + lp = minetest.find_node_near(s, 1, {"group:water"}) + elseif self.lava_damage > 0 then + lp = minetest.find_node_near(s, 1, {"group:lava"}) + elseif self.fire_damage > 0 then + lp = minetest.find_node_near(s, 1, {"group:fire"}) + end + + local is_in_danger = false + if lp then + -- If mob in or on dangerous block, look for land + if (self:is_node_dangerous(self.standing_in) or + self:is_node_dangerous(self.standing_on)) or (self:is_node_waterhazard(self.standing_in) or self:is_node_waterhazard(self.standing_on)) and (not self.fly) then + is_in_danger = true + + -- If mob in or on dangerous block, look for land + if is_in_danger then + -- Better way to find shore - copied from upstream + lp = minetest.find_nodes_in_area_under_air( + {x = s.x - 5, y = s.y - 0.5, z = s.z - 5}, + {x = s.x + 5, y = s.y + 1, z = s.z + 5}, + {"group:solid"}) + + lp = #lp > 0 and lp[math.random(#lp)] + + -- did we find land? + if lp then + + local vec = { + x = lp.x - s.x, + z = lp.z - s.z + } + + yaw = (atan(vec.z / vec.x) +math.pi/ 2) - self.rotate + + + if lp.x > s.x then yaw = yaw +math.pi end + + -- look towards land and move in that direction + yaw = self:set_yaw( yaw, 6) + self:set_velocity(self.walk_velocity) - local s = self.object:get_pos() - local objs = minetest.get_objects_inside_radius(s, 3) - local lp - for n = 1, #objs do - if objs[n]:is_player() then - lp = objs[n]:get_pos() - break end end - -- look at any players nearby, otherwise turn randomly - if lp and self.look_at_players then - - local vec = { - x = lp.x - s.x, - z = lp.z - s.z - } - - yaw = (atan(vec.z / vec.x) +math.pi/ 2) - self.rotate - - if lp.x > s.x then yaw = yaw +math.pi end - else - yaw = yaw + math.random(-0.5, 0.5) - end - - yaw = self:set_yaw( yaw, 8) - end - if self.order == "sit" then - self:set_animation( "sit") - self:set_velocity(0) - else - self:set_animation( "stand") - self:set_velocity(0) - end - - -- npc's ordered to stand stay standing - 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 - - self:set_velocity(self.walk_velocity) - self.state = "walk" - self:set_animation( "walk") - end - end - - elseif self.state == PATHFINDING then - self:check_gowp(dtime) - - elseif self.state == "walk" then - local s = self.object:get_pos() - local lp = nil - - -- is there something I need to avoid? - if (self.water_damage > 0 - and self.lava_damage > 0) - or self.breath_max ~= -1 then - - lp = minetest.find_node_near(s, 1, {"group:water", "group:lava"}) - - elseif self.water_damage > 0 then - - lp = minetest.find_node_near(s, 1, {"group:water"}) - - elseif self.lava_damage > 0 then - - lp = minetest.find_node_near(s, 1, {"group:lava"}) - - elseif self.fire_damage > 0 then - - lp = minetest.find_node_near(s, 1, {"group:fire"}) - - end - - local is_in_danger = false - if lp then - -- If mob in or on dangerous block, look for land - if (self:is_node_dangerous(self.standing_in) or - self:is_node_dangerous(self.standing_on)) or (self:is_node_waterhazard(self.standing_in) or self:is_node_waterhazard(self.standing_on)) and (not self.fly) then - is_in_danger = true - - -- If mob in or on dangerous block, look for land - if is_in_danger then - -- Better way to find shore - copied from upstream - lp = minetest.find_nodes_in_area_under_air( - {x = s.x - 5, y = s.y - 0.5, z = s.z - 5}, - {x = s.x + 5, y = s.y + 1, z = s.z + 5}, - {"group:solid"}) - - lp = #lp > 0 and lp[math.random(#lp)] - - -- did we find land? - if lp then - - local vec = { - x = lp.x - s.x, - z = lp.z - s.z - } - - yaw = (atan(vec.z / vec.x) +math.pi/ 2) - self.rotate - - - if lp.x > s.x then yaw = yaw +math.pi end - - -- look towards land and move in that direction - yaw = self:set_yaw( yaw, 6) - self:set_velocity(self.walk_velocity) - - end - end - -- A danger is near but mob is not inside - else + else - -- Randomly turn - if math.random(1, 100) <= 30 then - yaw = yaw + math.random(-0.5, 0.5) - yaw = self:set_yaw( yaw, 8) - end + -- Randomly turn + if math.random(1, 100) <= 30 then + yaw = yaw + math.random(-0.5, 0.5) + yaw = self:set_yaw( yaw, 8) end + end - yaw = self:set_yaw( yaw, 8) + yaw = self:set_yaw( yaw, 8) -- otherwise randomly turn - elseif math.random(1, 100) <= 30 then - yaw = yaw + math.random(-0.5, 0.5) - yaw = self:set_yaw( yaw, 8) - end + elseif math.random(1, 100) <= 30 then + yaw = yaw + math.random(-0.5, 0.5) + yaw = self:set_yaw( yaw, 8) + end - -- stand for great fall or danger or fence in front - local cliff_or_danger = false - if is_in_danger then - cliff_or_danger = self:is_at_cliff_or_danger() - end - if self.facing_fence == true - or cliff_or_danger - or math.random(1, 100) <= 30 then + -- stand for great fall or danger or fence in front + local cliff_or_danger = false + if is_in_danger then + cliff_or_danger = self:is_at_cliff_or_danger() + end + if self.facing_fence == true + or cliff_or_danger + or math.random(1, 100) <= 30 then - self:set_velocity(0) - self.state = "stand" - self:set_animation( "stand") - local yaw = self.object:get_yaw() or 0 - yaw = self:set_yaw( yaw + 0.78, 8) + self:set_velocity(0) + self.state = "stand" + self:set_animation( "stand") + local yaw = self.object:get_yaw() or 0 + yaw = self:set_yaw( yaw + 0.78, 8) + else + + self:set_velocity(self.walk_velocity) + + if self:flight_check() + and self.animation + and self.animation.fly_start + and self.animation.fly_end then + self:set_animation( "fly") else - - self:set_velocity(self.walk_velocity) - - if self:flight_check() - and self.animation - and self.animation.fly_start - and self.animation.fly_end then - self:set_animation( "fly") - else - self:set_animation( "walk") - end - end - - -- runaway when punched - elseif self.state == "runaway" then - - self.runaway_timer = self.runaway_timer + 1 - - -- stop after 5 seconds or when at cliff - if self.runaway_timer > 5 - or self:is_at_cliff_or_danger() then - self.runaway_timer = 0 - self:set_velocity(0) - self.state = "stand" - self:set_animation( "stand") - local yaw = self.object:get_yaw() or 0 - yaw = self:set_yaw( yaw + 0.78, 8) - else - self:set_velocity( self.run_velocity) - self:set_animation( "run") - end - - -- attack routines (explode, dogfight, shoot, dogshoot) - elseif self.state == "attack" then - - local s = self.object:get_pos() - local p = self.attack:get_pos() or s - - -- stop attacking if player invisible or out of range - if not self.attack - or not self.attack:get_pos() - or not self:object_in_range(self.attack) - or self.attack:get_hp() <= 0 - or (self.attack:is_player() and mcl_mobs.invis[ self.attack:get_player_name() ]) then - - self.state = "stand" - self:set_velocity( 0) - self:set_animation( "stand") - self.attack = nil - self.v_start = false - self.timer = 0 - self.blinktimer = 0 - self.path.way = nil - - return - end - - -- calculate distance from mob and enemy - local dist = vector.distance(p, s) - - if self.attack_type == "explode" then - - local vec = { - x = p.x - s.x, - z = p.z - s.z - } - - yaw = (atan(vec.z / vec.x) +math.pi/ 2) - self.rotate - - if p.x > s.x then yaw = yaw +math.pi end - - yaw = self:set_yaw( yaw, 0, dtime) - - local node_break_radius = self.explosion_radius or 1 - local entity_damage_radius = self.explosion_damage_radius - or (node_break_radius * 2) - - -- start timer when in reach and line of sight - if not self.v_start - and dist <= self.reach - and self:line_of_sight( s, p, 2) then - - self.v_start = true - self.timer = 0 - self.blinktimer = 0 - self:mob_sound("fuse", nil, false) - - -- stop timer if out of reach or direct line of sight - elseif self.allow_fuse_reset - and self.v_start - and (dist >= self.explosiontimer_reset_radius - or not self:line_of_sight( s, p, 2)) then - self.v_start = false - self.timer = 0 - self.blinktimer = 0 - self.blinkstatus = false - self:remove_texture_mod("^[brighten") - end - - -- walk right up to player unless the timer is active - if self.v_start and (self.stop_to_explode or dist < self.reach) then - self:set_velocity( 0) - else - self:set_velocity( self.run_velocity) - end - - if self.animation and self.animation.run_start then - self:set_animation( "run") - else - self:set_animation( "walk") - end - - if self.v_start then - - self.timer = self.timer + dtime - self.blinktimer = (self.blinktimer or 0) + dtime - - if self.blinktimer > 0.2 then - - self.blinktimer = 0 - - if self.blinkstatus then - self:remove_texture_mod("^[brighten") - else - self:add_texture_mod("^[brighten") - end - - self.blinkstatus = not self.blinkstatus - end - - if self.timer > self.explosion_timer then - - local pos = self.object:get_pos() - - if mobs_griefing and not minetest.is_protected(pos, "") then - mcl_explosions.explode(mcl_util.get_object_center(self.object), self.explosion_strength, { drop_chance = 1.0 }, self.object) - else - minetest.sound_play(self.sounds.explode, { - pos = pos, - gain = 1.0, - max_hear_distance = self.sounds.distance or 32 - }, true) - self:entity_physics(pos,entity_damage_radius) - mcl_mobs.effect(pos, 32, "mcl_particles_smoke.png", nil, nil, node_break_radius, 1, 0) - end - mcl_burning.extinguish(self.object) - self.object:remove() - - return true - end - end - - elseif self.attack_type == "dogfight" - or (self.attack_type == "dogshoot" and self:dogswitch(dtime) == 2) and (dist >= self.avoid_distance or not self.shooter_avoid_enemy) - or (self.attack_type == "dogshoot" and dist <= self.reach and self:dogswitch() == 0) then - - if self.fly - and dist > self.reach then - - local p1 = s - local me_y = math.floor(p1.y) - local p2 = p - local p_y = math.floor(p2.y + 1) - local v = self.object:get_velocity() - - if self:flight_check( s) then - - if me_y < p_y then - - self.object:set_velocity({ - x = v.x, - y = 1 * self.walk_velocity, - z = v.z - }) - - elseif me_y > p_y then - - self.object:set_velocity({ - x = v.x, - y = -1 * self.walk_velocity, - z = v.z - }) - end - else - if me_y < p_y then - - self.object:set_velocity({ - x = v.x, - y = 0.01, - z = v.z - }) - - elseif me_y > p_y then - - self.object:set_velocity({ - x = v.x, - y = -0.01, - z = v.z - }) - end - end - - end - - -- rnd: new movement direction - if self.path.following - and self.path.way - and self.attack_type ~= "dogshoot" then - - -- no paths longer than 50 - if #self.path.way > 50 - or dist < self.reach then - self.path.following = false - return - end - - local p1 = self.path.way[1] - - if not p1 then - self.path.following = false - return - end - - if math.abs(p1.x-s.x) + math.abs(p1.z - s.z) < 0.6 then - -- reached waypoint, remove it from queue - table.remove(self.path.way, 1) - end - - -- set new temporary target - p = {x = p1.x, y = p1.y, z = p1.z} - end - - local vec = { - x = p.x - s.x, - z = p.z - s.z - } - - yaw = (atan(vec.z / vec.x) + math.pi / 2) - self.rotate - - if p.x > s.x then yaw = yaw + math.pi end - - yaw = self:set_yaw( yaw, 0, dtime) - - -- move towards enemy if beyond mob reach - if dist > self.reach then - - -- path finding by rnd - if self.pathfinding -- only if mob has pathfinding enabled - and enable_pathfinding then - - self:smart_mobs(s, p, dist, dtime) - end - - if self:is_at_cliff_or_danger() then - - self:set_velocity( 0) - self:set_animation( "stand") - local yaw = self.object:get_yaw() or 0 - yaw = self:set_yaw( yaw + 0.78, 8) - else - - if self.path.stuck then - self:set_velocity( self.walk_velocity) - else - self:set_velocity( self.run_velocity) - end - - if self.animation and self.animation.run_start then - self:set_animation( "run") - else - self:set_animation( "walk") - end - end - - else -- rnd: if inside reach range - - self.path.stuck = false - self.path.stuck_timer = 0 - self.path.following = false -- not stuck anymore - - self:set_velocity( 0) - - if not self.custom_attack then - - if self.timer > 1 then - - self.timer = 0 - - if self.double_melee_attack - and math.random(1, 2) == 1 then - self:set_animation( "punch2") - else - self:set_animation( "punch") - end - - local p2 = p - local s2 = s - - p2.y = p2.y + .5 - s2.y = s2.y + .5 - - if self:line_of_sight( p2, s2) == true then - - -- play attack sound - self:mob_sound("attack") - - -- punch player (or what player is attached to) - local attached = self.attack:get_attach() - if attached then - self.attack = attached - end - self.attack:punch(self.object, 1.0, { - full_punch_interval = 1.0, - damage_groups = {fleshy = self.damage} - }, nil) - end - end - else -- call custom attack every second - if self.custom_attack - and self.timer > 1 then - - self.timer = 0 - - self.custom_attack(self, p) - end - end - end - - elseif self.attack_type == "shoot" - or (self.attack_type == "dogshoot" and self:dogswitch(dtime) == 1) - or (self.attack_type == "dogshoot" and (dist > self.reach or dist < self.avoid_distance and self.shooter_avoid_enemy) and self:dogswitch() == 0) then - - p.y = p.y - .5 - s.y = s.y + .5 - - local dist = vector.distance(p, s) - local vec = { - x = p.x - s.x, - y = p.y - s.y, - z = p.z - s.z - } - - yaw = (atan(vec.z / vec.x) +math.pi/ 2) - self.rotate - - if p.x > s.x then yaw = yaw +math.pi end - - yaw = self:set_yaw( yaw, 0, dtime) - - local stay_away_from_player = vector.new(0,0,0) - - --strafe back and fourth - - --stay away from player so as to shoot them - if dist < self.avoid_distance and self.shooter_avoid_enemy then - self:set_animation( "shoot") - stay_away_from_player=vector.multiply(vector.direction(p, s), 0.33) - end - - if self.strafes then - if not self.strafe_direction then - self.strafe_direction = 1.57 - end - if math.random(40) == 1 then - self.strafe_direction = self.strafe_direction*-1 - end - self.acc = vector.add(vector.multiply(vector.rotate_around_axis(vector.direction(s, p), vector.new(0,1,0), self.strafe_direction), 0.3*self.walk_velocity), stay_away_from_player) - else - self:set_velocity( 0) - end - - local p = self.object:get_pos() - p.y = p.y + (self.collisionbox[2] + self.collisionbox[5]) / 2 - - if self.shoot_interval - and self.timer > self.shoot_interval - and not minetest.raycast(vector.add(p, vector.new(0,self.shoot_offset,0)), vector.add(self.attack:get_pos(), vector.new(0,1.5,0)), false, false):next() - and math.random(1, 100) <= 60 then - - self.timer = 0 - self:set_animation( "shoot") - - -- play shoot attack sound - self:mob_sound("shoot_attack") - - -- Shoot arrow - if minetest.registered_entities[self.arrow] then - - local arrow, ent - local v = 1 - if not self.shoot_arrow then - self.firing = true - minetest.after(1, function() - self.firing = false - end) - arrow = minetest.add_entity(p, self.arrow) - ent = arrow:get_luaentity() - if ent.velocity then - v = ent.velocity - end - ent.switch = 1 - ent.owner_id = tostring(self.object) -- add unique owner id to arrow - - -- important for mcl_shields - ent._shooter = self.object - ent._saved_shooter_pos = self.object:get_pos() - end - - local amount = (vec.x * vec.x + vec.y * vec.y + vec.z * vec.z) ^ 0.5 - -- offset makes shoot aim accurate - vec.y = vec.y + self.shoot_offset - vec.x = vec.x * (v / amount) - vec.y = vec.y * (v / amount) - vec.z = vec.z * (v / amount) - if self.shoot_arrow then - vec = vector.normalize(vec) - self:shoot_arrow(p, vec) - else - arrow:set_velocity(vec) - end - end - end - else - + self:set_animation( "walk") end end end +function mob_class:do_states_stand() + local yaw = self.object:get_yaw() or 0 + + if math.random(1, 4) == 1 then + + local s = self.object:get_pos() + local objs = minetest.get_objects_inside_radius(s, 3) + local lp + for n = 1, #objs do + if objs[n]:is_player() then + lp = objs[n]:get_pos() + break + end + end + + -- look at any players nearby, otherwise turn randomly + if lp and self.look_at_players then + + local vec = { + x = lp.x - s.x, + z = lp.z - s.z + } + + yaw = (atan(vec.z / vec.x) +math.pi/ 2) - self.rotate + + if lp.x > s.x then yaw = yaw +math.pi end + else + yaw = yaw + math.random(-0.5, 0.5) + end + + yaw = self:set_yaw( yaw, 8) + end + if self.order == "sit" then + self:set_animation( "sit") + self:set_velocity(0) + else + self:set_animation( "stand") + self:set_velocity(0) + end + + -- npc's ordered to stand stay standing + 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 + + self:set_velocity(self.walk_velocity) + self.state = "walk" + self:set_animation( "walk") + end + end +end + +function mob_class:do_states_runaway() + local yaw = self.object:get_yaw() or 0 + + self.runaway_timer = self.runaway_timer + 1 + + -- stop after 5 seconds or when at cliff + if self.runaway_timer > 5 + or self:is_at_cliff_or_danger() then + self.runaway_timer = 0 + self:set_velocity(0) + self.state = "stand" + self:set_animation( "stand") + local yaw = self.object:get_yaw() or 0 + yaw = self:set_yaw( yaw + 0.78, 8) + else + self:set_velocity( self.run_velocity) + self:set_animation( "run") + end +end + + + + + + function mob_class:check_smooth_rotation(dtime) -- smooth rotation by ThomasMonroe314 if self._turn_to then diff --git a/mods/ENTITIES/mcl_mobs/physics.lua b/mods/ENTITIES/mcl_mobs/physics.lua index af206c41c6..0dfc3a800f 100644 --- a/mods/ENTITIES/mcl_mobs/physics.lua +++ b/mods/ENTITIES/mcl_mobs/physics.lua @@ -183,6 +183,17 @@ function mob_class:collision() return({x,z}) end +function mob_class:slow_mob() + local d = 0.85 + if self:check_dying() then d = 0.92 end + + local v = self.object:get_velocity() + if v then + --diffuse object velocity + self.object:set_velocity({x = v.x*d, y = v.y, z = v.z*d}) + end +end + -- move mob in facing direction function mob_class:set_velocity(v) local c_x, c_y = 0, 0 @@ -778,6 +789,25 @@ function mob_class:do_env_damage() return self:check_for_death("", {type = "unknown"}) end +function mob_class:env_damage (dtime, pos) + -- environmental damage timer (every 1 second) + self.env_damage_timer = self.env_damage_timer + dtime + + if (self.state == "attack" and self.env_damage_timer > 1) + or self.state ~= "attack" then + self:check_entity_cramming() + self.env_damage_timer = 0 + + -- check for environmental damage (water, fire, lava etc.) + if self:do_env_damage() then + return true + end + + -- node replace check (cow eats grass etc.) + self:replace(pos) + end +end + function mob_class:damage_mob(reason,damage) if not self.health then return end damage = math.floor(damage)