diff --git a/api.lua b/api.lua index ee42180..c6acdf5 100644 --- a/api.lua +++ b/api.lua @@ -11,7 +11,7 @@ local use_mc2 = minetest.get_modpath("mcl_core") -- Global mobs = { mod = "redo", - version = "20231028", + version = "20231105", translate = S, invis = minetest.global_exists("invisibility") and invisibility or {}, node_snow = minetest.registered_aliases["mapgen_snow"] @@ -90,7 +90,8 @@ local pathfinder_enable = settings:get_bool("mob_pathfinder_enable") or true local pathfinding_stuck_timeout = tonumber( settings:get("mob_pathfinding_stuck_timeout")) or 3.0 -- how long will mob follow path before giving up -local pathfinding_stuck_path_timeout = tonumber(settings:get("mob_pathfinding_stuck_path_timeout")) or 5.0 +local pathfinding_stuck_path_timeout = tonumber( + settings:get("mob_pathfinding_stuck_path_timeout")) or 5.0 -- which algorithm to use, Dijkstra(default) or A*_noprefetch or A* -- fix settings not allowing "*" local pathfinding_algorithm = settings:get("mob_pathfinding_algorithm") or "Dijkstra" @@ -199,21 +200,20 @@ local mob_class_meta = {__index = mob_class} -- play sound function mob_class:mob_sound(sound) - if sound then + if not sound then return end - -- higher pitch for a child - local pitch = self.child and 1.5 or 1.0 + -- higher pitch for a child + local pitch = self.child and 1.5 or 1.0 - -- a little random pitch to be different - pitch = pitch + random(-10, 10) * 0.005 + -- a little random pitch to be different + pitch = pitch + random(-10, 10) * 0.005 - minetest.sound_play(sound, { - object = self.object, - gain = 1.0, - max_hear_distance = (self.sounds and self.sounds.distance) or 10, - pitch = pitch - }, true) - end + minetest.sound_play(sound, { + object = self.object, + gain = 1.0, + max_hear_distance = (self.sounds and self.sounds.distance) or 10, + pitch = pitch + }, true) end @@ -450,75 +450,8 @@ function mobs:set_animation(entity, anim) end --- check line of sight (BrunoMine) -local function line_of_sight(self, pos1, pos2, stepsize) - - stepsize = stepsize or 1 - - local s = minetest.line_of_sight(pos1, pos2, stepsize) - - -- normal walking and flying mobs can see you through air - if s == true then - return true - end - - -- New pos1 to be analyzed - local npos1 = {x = pos1.x, y = pos1.y, z = pos1.z} - - local r, pos = minetest.line_of_sight(npos1, pos2, stepsize) - - -- Checks the return - if r == true then return true end - - -- Nodename found - local nn = minetest.get_node(pos).name - - -- Target Distance (td) to travel - local td = get_distance(pos1, pos2) - - -- Actual Distance (ad) traveled - local ad = 0 - - -- It continues to advance in the line of sight in search of a real - -- obstruction which counts as 'walkable' nodebox. - while minetest.registered_nodes[nn] - and minetest.registered_nodes[nn].walkable == false do - - -- Check if you can still move forward - if td < ad + stepsize then - return true -- Reached the target - end - - -- Moves the analyzed pos - local d = get_distance(pos1, pos2) - - npos1.x = ((pos2.x - pos1.x) / d * stepsize) + pos1.x - npos1.y = ((pos2.y - pos1.y) / d * stepsize) + pos1.y - npos1.z = ((pos2.z - pos1.z) / d * stepsize) + pos1.z - - -- NaN checks - if d == 0 - or npos1.x ~= npos1.x or npos1.y ~= npos1.y or npos1.z ~= npos1.z then - return false - end - - ad = ad + stepsize - - -- scan again - r, pos = minetest.line_of_sight(npos1, pos2, stepsize) - - if r == true then return true end - - -- New Nodename found - nn = minetest.get_node(pos).name - end - - return false -end - - -- check line of sight using raycasting (thanks Astrobe) -local function ray_line_of_sight(self, pos1, pos2) +function mob_class:line_of_sight(pos1, pos2) local ray = minetest.raycast(pos1, pos2, true, false) local thing = ray:next() @@ -541,19 +474,9 @@ local function ray_line_of_sight(self, pos1, pos2) return true end - -function mob_class:line_of_sight(pos1, pos2, stepsize) - - if minetest.raycast then -- only use if minetest 5.0 is detected - return ray_line_of_sight(self, pos1, pos2) - end - - return line_of_sight(self, pos1, pos2, stepsize) -end - -- global function [deprecated] -function mobs:line_of_sight(entity, pos1, pos2, stepsize) - return entity:line_of_sight(pos1, pos2, stepsize) +function mobs:line_of_sight(entity, pos1, pos2) + return entity:line_of_sight(pos1, pos2) end @@ -715,7 +638,6 @@ end -- Thanks Wuzzy for the editable settings - local HORNY_TIME = 30 local HORNY_AGAIN_TIME = 60 * 5 -- 5 minutes local CHILD_GROW_TIME = 60 * 20 -- 20 minutes @@ -1017,7 +939,7 @@ function mobs:node_ok(pos, fallback) end --- Returns true is node can deal damage to self +-- Returns true if node can deal damage to self function mobs:is_node_dangerous(mob_object, nodename) if mob_object.water_damage > 0 @@ -1134,7 +1056,7 @@ function mob_class:do_env_damage() end -- lava damage - elseif self.lava_damage ~= 0 and self:is_inside("group:lava") then -- nodef.groups.lava then + elseif self.lava_damage ~= 0 and self:is_inside("group:lava") then self.health = self.health - self.lava_damage @@ -1146,7 +1068,7 @@ function mob_class:do_env_damage() end -- fire damage - elseif self.fire_damage ~= 0 and self:is_inside("group:fire") then -- nodef.groups.fire then + elseif self.fire_damage ~= 0 and self:is_inside("group:fire") then self.health = self.health - self.fire_damage @@ -1221,7 +1143,7 @@ function mob_class:do_env_damage() if self.suffocation == true then damage = 2 else - damage = (self.suffocation or 2) + damage = self.suffocation end self.health = self.health - damage @@ -1331,9 +1253,7 @@ local function entity_physics(pos, radius) obj_pos = objs[n]:get_pos() - dist = get_distance(pos, obj_pos) - - if dist < 1 then dist = 1 end + dist = max(1, get_distance(pos, obj_pos)) local damage = floor((4 / dist) * radius) @@ -2001,7 +1921,7 @@ function mob_class:general_attack() -- choose closest player to attack that isnt self if dist ~= 0 and dist < min_dist - and self:line_of_sight(sp, p, 2) == true + and self:line_of_sight(sp, p) == true and not is_peaceful_player(player) then min_dist = dist min_player = player @@ -2037,7 +1957,6 @@ function mob_class:do_runaway_from() if is_invisible(self, pname) or self.owner == pname then - name = "" else player = objs[n] @@ -2066,7 +1985,7 @@ function mob_class:do_runaway_from() dist = get_distance(p, s) -- choose closest player/mob to runaway from - if dist < min_dist and self:line_of_sight(sp, p, 2) == true then + if dist < min_dist and self:line_of_sight(sp, p) == true then min_dist = dist min_player = player end @@ -2238,6 +2157,21 @@ function mob_class:dogswitch(dtime) end +-- stop attack +function mob_class:stop_attack() + + self.attack = nil + self.following = nil + self.v_start = false + self.timer = 0 + self.blinktimer = 0 + self.path.way = nil + self:set_velocity(0) + self.state = "stand" + self:set_animation("stand", true) +end + + -- execute current state (stand, walk, run, attacks) function mob_class:do_states(dtime) @@ -2415,19 +2349,28 @@ function mob_class:do_states(dtime) --print(" ** stop attacking **", self.name, self.health, dist, self.view_range) - self.attack = nil - self.following = nil - self.v_start = false - self.timer = 0 - self.blinktimer = 0 - self.path.way = nil - self:set_velocity(0) - self.state = "stand" - self:set_animation("stand", true) + self:stop_attack() return end + -- check enemy is in sight + local in_sight = self:line_of_sight( + {x = s.x, y = s.y + 0.5, z = s.z}, + {x = p.x, y = p.y + 0.5, z = p.z}) + + -- stop attacking when enemy not seen for 11 seconds + if not in_sight then + + self.target_time_lost = (self.target_time_lost or 0) + dtime + + if self.target_time_lost > 11 then + self:stop_attack() + end + else + self.target_time_lost = 0 + end + if self.attack_type == "explode" then self:yaw_to_pos(p) @@ -2442,7 +2385,7 @@ function mob_class:do_states(dtime) -- 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 + and in_sight then self.v_start = true self.timer = 0 @@ -2454,7 +2397,7 @@ function mob_class:do_states(dtime) -- stop timer if out of reach or direct line of sight elseif self.allow_fuse_reset and self.v_start - and (dist > self.reach or not self:line_of_sight(s, p, 2)) then + and (dist > self.reach or not in_sight) then --print("=== explosion timer stopped") @@ -2502,10 +2445,9 @@ function mob_class:do_states(dtime) local pos = self.object:get_pos() - -- dont damage anything if area protected or next to waterpathfinding_max_jump + -- dont damage anything if area protected or next to water if minetest.find_node_near(pos, 1, {"group:water"}) or minetest.is_protected(pos, "") then - node_break_radius = 1 end @@ -3521,7 +3463,6 @@ function mob_class:on_step(dtime, moveresult) return end - -- attack timer self.timer = self.timer + dtime -- never go over 100 @@ -4156,7 +4097,7 @@ function mobs:spawn_specific(name, nodes, neighbors, min_light, max_light, inter end --- compatibility with older mob registration +-- compatibility with older mob registration [DEPRECATED] function mobs:register_spawn(name, nodes, max_light, min_light, chance, active_object_count, max_height, day_toggle) @@ -4202,7 +4143,8 @@ function mobs:register_arrow(name, def) textures = def.textures, collisionbox = def.collisionbox or {-.1, -.1, -.1, .1, .1, .1}, glow = def.glow, - automatic_face_movement_dir = def.rotate and (def.rotate - (pi / 180)) or false, + automatic_face_movement_dir = def.rotate + and (def.rotate - (pi / 180)) or false, }, velocity = def.velocity, @@ -4364,7 +4306,6 @@ end -- Register spawn eggs - -- Note: This also introduces the “spawn_egg” group: -- * spawn_egg=1: Spawn egg (generic mob, no metadata) -- * spawn_egg=2: Spawn egg (captured/tamed mob, metadata) diff --git a/api.txt b/api.txt index 83af0cb..9a21022 100644 --- a/api.txt +++ b/api.txt @@ -395,13 +395,14 @@ another mod entirely, replace mob_class with the mob entity variable: mob_class:mob_sound(sound) -- play sound at mob position mob_class:do_attack(player) -- if not already attacking, attack object given +mob_class:stop_attack() -- stops mob attacking mob_class:collision() -- checks for player collision with mob and returns {x, z} vector mob_class:set_velocity(velocity) -- move at velocity in the facing direction mob_class:get_velocity() -- returns mob speed value mob_class:set_yaw(yaw, delay) -- change mob yaw, delay is for smooth rotation (default:0) mob_class:yaw_to_pos(pos, delay) -- rotates mod to look at position and returns new yaw mob_class:set_animation(animation, force) -- set mob animation -mob_class:line_of_sight(pos1, pos2, stepsize) -- internal line of sight function +mob_class:line_of_sight(pos1, pos2) -- internal line of sight function mob_class:attempt_flight_correction(override) -- check for stuck flying mobs and fix mob_class:update_tag(newname) -- update nametag or give new name mob_class:do_jump() -- check if mob can jump then jump