diff --git a/mods/ENTITIES/mcl_mobs/api.lua b/mods/ENTITIES/mcl_mobs/api.lua index 6e877cbce..5d2b86517 100644 --- a/mods/ENTITIES/mcl_mobs/api.lua +++ b/mods/ENTITIES/mcl_mobs/api.lua @@ -1,29 +1,24 @@ local mob_class = mcl_mobs.mob_class local mob_class_meta = {__index = mcl_mobs.mob_class} local math, vector, minetest, mcl_mobs = math, vector, minetest, mcl_mobs --- API for Mobs Redo: VoxeLibre Edition local PATHFINDING = "gowp" local CRASH_WARN_FREQUENCY = 60 local LIFETIMER_DISTANCE = 47 +local MAPGEN_LIMIT = mcl_vars.mapgen_limit +local MAPGEN_MOB_LIMIT = MAPGEN_LIMIT - 90 +-- 30927 seems to be the edge of the world, so could be closer, but this is safer --- Localize local S = minetest.get_translator("mcl_mobs") -local DEVELOPMENT = minetest.settings:get_bool("mcl_development",false) - -- Invisibility mod check mcl_mobs.invis = {} local remove_far = true local mobs_debug = minetest.settings:get_bool("mobs_debug", false) -- Shows helpful debug info above each mob -local spawn_logging = minetest.settings:get_bool("mcl_logging_mobs_spawn", false) - -local MAPGEN_LIMIT = mcl_vars.mapgen_limit -local MAPGEN_MOB_LIMIT = MAPGEN_LIMIT - 90 --- 30927 seems to be the edge of the world, so could be closer, but this is safer - +local spawn_logging = minetest.settings:get_bool("mcl_logging_mobs_spawn", true) +local DEVELOPMENT = minetest.settings:get_bool("mcl_development", false) -- Peaceful mode message so players will know there are no monsters if minetest.settings:get_bool("only_peaceful_mobs", false) then @@ -36,10 +31,7 @@ end function mob_class:update_tag() --update nametag and/or the debug box local tag if mobs_debug then - local name = self.name - if self.nametag and self.nametag ~= "" then - name = self.nametag - end + local name = self.nametag ~= "" and self.nametag or self.name tag = "name = '"..tostring(name).."'\n".. "state = '"..tostring(self.state).."'\n".. "order = '"..tostring(self.order).."'\n".. @@ -56,9 +48,7 @@ function mob_class:update_tag() --update nametag and/or the debug box else tag = self.nametag end - self.object:set_properties({ - nametag = tag, - }) + self.object:set_properties({ nametag = tag }) end function mob_class:jock_to(mob, reletive_pos, rot) @@ -74,19 +64,15 @@ function mob_class:jock_to(mob, reletive_pos, rot) end function mob_class:get_staticdata() - for _,p in pairs(minetest.get_connected_players()) do self:remove_particlespawners(p:get_player_name()) end -- remove mob when out of range unless tamed - if remove_far - and self:despawn_allowed() - and self.lifetimer <= 20 then + if remove_far and self:despawn_allowed() and self.lifetimer <= 20 then if spawn_logging then 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 end @@ -95,17 +81,9 @@ function mob_class:get_staticdata() self.state = "stand" local tmp = {} - for tag, stat in pairs(self) do - local t = type(stat) - - if t ~= "function" - and t ~= "nil" - and t ~= "userdata" - and tag ~= "_cmi_components" then - tmp[tag] = self[tag] - end + if t ~= "function" and t ~= "nil" and t ~= "userdata" and tag ~= "_cmi_components" then tmp[tag] = self[tag] end end tmp._mcl_potions = self._mcl_potions @@ -120,10 +98,7 @@ function mob_class:get_staticdata() end local function valid_texture(self, def_textures) - if not self.base_texture then - return false - end - + 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 @@ -148,32 +123,18 @@ function mob_class:mob_activate(staticdata, def, dtime) end local tmp = minetest.deserialize(staticdata) - if tmp then -- Patch incorrectly converted mobs - if tmp.base_mesh ~= minetest.registered_entities[self.name].mesh then - mcl_mobs.strip_staticdata(tmp) - end - - for _,stat in pairs(tmp) do - self[_] = stat - end + if tmp.base_mesh ~= minetest.registered_entities[self.name].mesh then mcl_mobs.strip_staticdata(tmp) end + for _, stat in pairs(tmp) do self[_] = stat end end --If textures in definition change, reload textures 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 - - 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 + if type(def.textures[1]) == "string" then def.textures = {def.textures} end + self.texture_selected = self.texture_selected or math.random(#def.textures) self.base_texture = def.textures[self.texture_selected] self.base_mesh = def.mesh self.base_size = self.visual_size @@ -181,9 +142,7 @@ function mob_class:mob_activate(staticdata, def, dtime) self.base_selbox = self.selectionbox end - if not self.base_selbox then - self.base_selbox = self.selectionbox or self.base_colbox - end + self.base_selbox = self.base_selbox or self.selectionbox or self.base_colbox local textures = self.base_texture local mesh = self.base_mesh @@ -191,26 +150,11 @@ function mob_class:mob_activate(staticdata, def, dtime) local colbox = self.base_colbox local selbox = self.base_selbox - if self.gotten == true - and def.gotten_texture then - textures = def.gotten_texture - end - - if self.gotten == true - and def.gotten_mesh then - mesh = def.gotten_mesh - end - + if self.gotten == true and def.gotten_texture then textures = def.gotten_texture end + if self.gotten == true and def.gotten_mesh then mesh = def.gotten_mesh end if self.child == true then - - vis_size = { - x = self.base_size.x * .5, - y = self.base_size.y * .5, - } - - if def.child_texture then - textures = def.child_texture[1] - end + vis_size = { x = self.base_size.x * .5, y = self.base_size.y * .5 } + if def.child_texture then textures = def.child_texture[1] end colbox = { self.base_colbox[1] * .5, @@ -230,16 +174,12 @@ function mob_class:mob_activate(staticdata, def, dtime) } end - if self.health == 0 then - self.health = math.random (self.hp_min, self.hp_max) - end - if self.breath == nil then - self.breath = self.breath_max - end + if self.health == 0 then self.health = math.random(self.hp_min, self.hp_max) end + if self.breath == nil then self.breath = self.breath_max end self.path = {} self.path.way = {} -- path to follow, table of positions - self.path.lastpos = {x = 0, y = 0, z = 0} + self.path.lastpos = vector.zero() self.path.stuck = false self.path.following = false -- currently following path? self.path.stuck_timer = 0 -- if stuck for too long search for path @@ -276,19 +216,7 @@ function mob_class:mob_activate(staticdata, def, dtime) self.blinktimer = 0 self.blinkstatus = false - if not self.nametag then - self.nametag = def.nametag - end - if not self.custom_visual_size then - self.visual_size = nil - self.base_size = self.visual_size - if self.child then - self.visual_size = { - x = self.visual_size.x * 0.5, - y = self.visual_size.y * 0.5, - } - end - end + self.nametag = self.nametag or def.nametag self.object:set_properties(self) self:set_yaw(math.random() * math.pi * 2, 6) @@ -296,22 +224,14 @@ function mob_class:mob_activate(staticdata, def, dtime) self._current_animation = nil self:set_animation("stand") - if self.riden_by_jock then --- Keep this function before self.on_spawn() is run. self.object:remove() return end + if self.on_spawn and not self.on_spawn_run and self.on_spawn(self) then self.on_spawn_run = true end - if self.on_spawn and not self.on_spawn_run then - if self.on_spawn(self) then - self.on_spawn_run = true - end - end - - if not self.wears_armor and self.armor_list then - self.armor_list = nil - end + if not self.wears_armor and self.armor_list then self.armor_list = nil end if not self._run_armor_init and self.wears_armor then self.armor_list={helmet="",chestplate="",boots="",leggings=""} @@ -319,15 +239,10 @@ function mob_class:mob_activate(staticdata, def, dtime) self._run_armor_init = true end - if not self._mcl_potions then - self._mcl_potions = {} - end + if not self._mcl_potions then self._mcl_potions = {} end mcl_potions._load_entity_effects(self) - - if def.after_activate then - def.after_activate(self, staticdata, def, dtime) - end + if def.after_activate then def.after_activate(self, staticdata, def, dtime) end end -- execute current state (stand, walk, run, attacks) @@ -346,9 +261,7 @@ function mob_class:do_states(dtime, player_in_active_range) if self.state == PATHFINDING then self:check_gowp(dtime) elseif self.state == "attack" then - if self:do_states_attack(dtime) then - return true - end + if self:do_states_attack(dtime) then return true end else if mcl_util.check_dtime_timer(self, dtime, "onstep_dostates", 1) then if self.state == "stand" then @@ -364,34 +277,28 @@ end function mob_class:outside_limits() local pos = self.object:get_pos() - if pos then - local posx = math.abs(pos.x) - local posy = math.abs(pos.y) - local posz = math.abs(pos.z) - if posx > MAPGEN_MOB_LIMIT or posy > MAPGEN_MOB_LIMIT or posz > MAPGEN_MOB_LIMIT then - --minetest.log("action", "Getting close to limits of worldgen: " .. minetest.pos_to_string(pos)) - if posx > MAPGEN_LIMIT or posy > MAPGEN_LIMIT or posz > MAPGEN_LIMIT then - minetest.log("action", "Warning mob past limits of worldgen: " .. minetest.pos_to_string(pos)) - else - if self.state ~= "stand" then - minetest.log("action", "Warning mob close to limits of worldgen: " .. minetest.pos_to_string(pos)) - self.state = "stand" - self:set_animation("stand") - self.object:set_acceleration(vector.zero()) - self.object:set_velocity(vector.zero()) - end + if not pos then return end + local posx, posy, posz = math.abs(pos.x), math.abs(pos.y), math.abs(pos.z) + if posx > MAPGEN_MOB_LIMIT or posy > MAPGEN_MOB_LIMIT or posz > MAPGEN_MOB_LIMIT then + --minetest.log("action", "Getting close to limits of worldgen: " .. minetest.pos_to_string(pos)) + if posx > MAPGEN_LIMIT or posy > MAPGEN_LIMIT or posz > MAPGEN_LIMIT then + minetest.log("action", "Warning mob past limits of worldgen: " .. minetest.pos_to_string(pos)) + else + if self.state ~= "stand" then + minetest.log("action", "Warning mob close to limits of worldgen: " .. minetest.pos_to_string(pos)) + self.state = "stand" + self:set_animation("stand") + self.object:set_acceleration(vector.zero()) + self.object:set_velocity(vector.zero()) end - return true end + return true end end - - local function on_step_work(self, dtime, moveresult) local pos = self.object:get_pos() if not pos then return end - if self:check_despawn(pos, dtime) then return true end if self:outside_limits() then return end @@ -403,25 +310,20 @@ local function on_step_work(self, dtime, moveresult) end if self:falling(pos, moveresult) then return end - if self:step_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 local player_in_active_range = self:player_in_active_range() self:check_suspend(player_in_active_range) - self:check_water_flow() - self._can_jump_cliff = not self._jumping_cliff and self:can_jump_cliff() - self:flop() - self:check_smooth_rotation(dtime) if player_in_active_range then self:set_animation_speed() -- set animation speed relative to velocity - self:check_head_swivel(dtime) if mcl_util.check_dtime_timer(self, dtime, "onstep_engage", 0.2) then @@ -438,89 +340,68 @@ local function on_step_work(self, dtime, moveresult) 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) - self:check_particlespawners(dtime) - if self.do_custom and self.do_custom(self, dtime) == false then return end - if self:do_states(dtime, player_in_active_range) then return end - if mobs_debug then self:update_tag() end - - if not self.object:get_luaentity() then - return false - end + if not self.object:get_luaentity() then return false end end local last_crash_warn_time = 0 -local function log_error (stack_trace, info, info2) +local function log_error(stack_trace, info, info2) minetest.log("action", "--- Bug report start (please provide a few lines before this also for context) ---") minetest.log("action", "Error: " .. stack_trace) minetest.log("action", "Bug info: " .. info) - if info2 then - minetest.log("action", "Bug info additional: " .. info2) - end + if info2 then minetest.log("action", "Bug info additional: " .. info2) end minetest.log("action", "--- Bug report end ---") end local function warn_user_error () local current_time = os.time() local time_since_warning = current_time - last_crash_warn_time - --minetest.log("previous_crash_time: " .. current_time) --minetest.log("last_crash_time: " .. last_crash_warn_time) --minetest.log("time_since_warning: " .. time_since_warning) - if time_since_warning > CRASH_WARN_FREQUENCY then last_crash_warn_time = current_time minetest.log("A game crashing bug was prevented. Please provide debug.log information to VoxeLibre dev team for investigation. (Search for: --- Bug report start)") end end -local on_step_error_handler = function () - warn_user_error () +local on_step_error_handler = function() + warn_user_error() local info = debug.getinfo(1, "SnlufL") log_error(tostring(debug.traceback()), dump(info)) end - - -- main mob function function mob_class:on_step(dtime, moveresult) - if DEVELOPMENT then - return on_step_work(self, dtime, moveresult) - end + -- allow crash in development mode + if DEVELOPMENT then return on_step_work(self, dtime, moveresult) end -- Removed as bundled Lua (5.1 doesn't support xpcall) --local status, retVal = xpcall(on_step_work, on_step_error_handler, self, dtime) local status, retVal = pcall(on_step_work, self, dtime, moveresult) - if status then - return retVal - end - warn_user_error () + if status then return retVal end + warn_user_error() local pos = self.object:get_pos() if pos then local node = minetest.get_node(pos) - if node and node.name == "ignore" then - minetest.log("warning", "Pos is ignored: " .. dump(pos)) - end + if node and node.name == "ignore" then minetest.log("warning", "Pos is ignored: " .. dump(pos)) end end - log_error (dump(retVal), dump(pos), dump(self)) + log_error(dump(retVal), dump(pos), dump(self)) end local timer = 0 - local function update_lifetimer(dtime) timer = timer + dtime if timer < 1 then return end @@ -541,7 +422,6 @@ minetest.register_globalstep(function(dtime) update_lifetimer(dtime) end) - minetest.register_chatcommand("clearmobs", { privs = { maphack = true }, params = "[all|monster|passive| [|nametagged|tamed]]", @@ -554,11 +434,7 @@ minetest.register_chatcommand("clearmobs", { S("Default usage. Clearing hostile mobs. For more options please type: /help clearmobs")) end local mob, unsafe = param:match("^([%w]+)[ ]?([%w%d]*)$") - - local all = false - local nametagged = false - local tamed = false - + local all, nametagged, tamed = false, false, false local mob_name, mob_type, range -- Param 1 resolve @@ -572,12 +448,7 @@ minetest.register_chatcommand("clearmobs", { end --minetest.log ("mob: [" .. mob .. "]") else - --minetest.log("No valid first param") - if default then - --minetest.log("Use default") - mob_type = "monster" - end - --return + if default then mob_type = "monster" end end -- Param 2 resolve @@ -594,7 +465,6 @@ minetest.register_chatcommand("clearmobs", { end local p = minetest.get_player_by_name(player) - for _,o in pairs(minetest.luaentities) do if o and o.is_mob then local mob_match = false @@ -603,7 +473,6 @@ minetest.register_chatcommand("clearmobs", { --minetest.log("Match - All mobs specified") mob_match = true elseif mob_type then - --minetest.log("Match - o.type: ".. tostring(o.type)) --minetest.log("mob_type: ".. tostring(mob_type)) if mob_type == "monster" and o.type == mob_type then @@ -615,7 +484,6 @@ minetest.register_chatcommand("clearmobs", { else --minetest.log("No match for type.") end - elseif mob_name and (o.name == mob_name or string.find(o.name, mob_name)) then --minetest.log("Match - mob_name = ".. tostring(o.name)) mob_match = true @@ -626,35 +494,16 @@ minetest.register_chatcommand("clearmobs", { end if mob_match then - local in_range = true - if (not range or range <= 0 ) then - in_range = true - else - if ( vector.distance(p:get_pos(),o.object:get_pos()) <= range ) then - in_range = true - else - --minetest.log("Out of range") - in_range = false - end - end - - --minetest.log("o.nametag: ".. tostring(o.nametag)) - + local in_range = (not range or range <= 0) or vector.distance(p:get_pos(), o.object:get_pos()) <= range if nametagged then - if o.nametag then - --minetest.log("Namedtagged and it has a name tag. Kill it") - o.object:remove() - end + if o.nametag then o.object:remove() end elseif tamed then - if o.tamed then - --minetest.log("Tamed. Kill it") - o.object:remove() - end + if o.tamed then o.object:remove() end elseif in_range and (not o.nametag or o.nametag == "") and not o.tamed then - --minetest.log("No nametag or tamed. Kill it") o.object:remove() end end end end -end}) + end +}) diff --git a/mods/ENTITIES/mcl_mobs/combat.lua b/mods/ENTITIES/mcl_mobs/combat.lua index 2bcf706ce..7b22e5aaf 100644 --- a/mods/ENTITIES/mcl_mobs/combat.lua +++ b/mods/ENTITIES/mcl_mobs/combat.lua @@ -12,30 +12,29 @@ local enable_pathfinding = true local TIME_TO_FORGET_TARGET = 15 local PI = math.pi +local HALFPI = PI * 0.5 local random = math.random +local min = math.min +local floor = math.floor +local ceil = math.ceil +local abs = math.abs +local cos = math.cos +local sin = math.sin +local atan2 = math.atan2 +local vector_offset = vector.offset +local vector_new = vector.new +local vector_copy = vector.copy +local vector_distance = vector.distance -- check if daytime and also if mob is docile during daylight hours function mob_class:day_docile() - if self.docile_by_day == false then - return false - elseif self.docile_by_day == true - and self.time_of_day > 0.2 - and self.time_of_day < 0.8 then - return true - end + return self.docile_by_day == true and self.time_of_day > 0.2 and self.time_of_day < 0.8 end -- get this mob to attack the object function mob_class:do_attack(object) - - if self.state == "attack" or self.state == "die" then - return - end - - - if object:is_player() and not minetest.settings:get_bool("enable_damage") then - return - end + if self.state == "attack" or self.state == "die" then return end + if object:is_player() and not minetest.settings:get_bool("enable_damage") then return end self.attack = object self.state = "attack" @@ -47,21 +46,18 @@ function mob_class:do_attack(object) end -- blast damage to entities nearby -local function entity_physics(pos,radius) - +local function entity_physics(pos, radius) radius = radius * 2 local objs = minetest.get_objects_inside_radius(pos, radius) local obj_pos, dist - for n = 1, #objs do - obj_pos = objs[n]:get_pos() - dist = vector.distance(pos, obj_pos) + dist = vector_distance(pos, obj_pos) if dist < 1 then dist = 1 end - local damage = math.floor((4 / dist) * radius) + local damage = floor((4 / dist) * radius) local ent = objs[n]:get_luaentity() -- punches work on entities AND players @@ -79,75 +75,57 @@ local height_switcher = false -- path finding and smart mob routine by rnd, line_of_sight and other edits by Elkien3 function mob_class:smart_mobs(s, p, dist, dtime) - local s1 = self.path.lastpos - local target_pos = self.attack:get_pos() -- is it becoming stuck? - if math.abs(s1.x - s.x) + math.abs(s1.z - s.z) < .5 then + if abs(s1.x - s.x) + abs(s1.z - s.z) < .5 then self.path.stuck_timer = self.path.stuck_timer + dtime else self.path.stuck_timer = 0 end - self.path.lastpos = {x = s.x, y = s.y, z = s.z} + self.path.lastpos = vector_copy(s) local use_pathfind = false - local has_lineofsight = minetest.line_of_sight( - {x = s.x, y = (s.y) + .5, z = s.z}, - {x = target_pos.x, y = (target_pos.y) + 1.5, z = target_pos.z}, .2) + local has_lineofsight = minetest.line_of_sight(vector_offset(s, 0, .5, 0), vector_offset(target_pos, 0, 1.5, 0), .2) -- im stuck, search for path if not has_lineofsight then - if los_switcher == true then use_pathfind = true los_switcher = false end -- cannot see target! else if los_switcher == false then - los_switcher = true use_pathfind = false - minetest.after(1, function(self) - if not self.object:get_luaentity() then - return - end + if not self.object:get_luaentity() then return end if has_lineofsight then self.path.following = false end end, self) end -- can see target! end if (self.path.stuck_timer > stuck_timeout and not self.path.following) then - use_pathfind = true self.path.stuck_timer = 0 - minetest.after(1, function(self) - if not self.object:get_luaentity() then - return - end + if not self.object:get_luaentity() then return end if has_lineofsight then self.path.following = false end end, self) end if (self.path.stuck_timer > stuck_path_timeout and self.path.following) then - use_pathfind = true self.path.stuck_timer = 0 - minetest.after(1, function(self) - if not self.object:get_luaentity() then - return - end + if not self.object:get_luaentity() then return end if has_lineofsight then self.path.following = false end end, self) end - if math.abs(vector.subtract(s,target_pos).y) > self.stepheight then - + if abs(s.y - target_pos.y) > self.stepheight then if height_switcher then use_pathfind = true height_switcher = false @@ -166,28 +144,21 @@ function mob_class:smart_mobs(s, p, dist, dtime) -- round position to center of node to avoid stuck in walls -- also adjust height for player models! - s.x = math.floor(s.x + 0.5) - s.z = math.floor(s.z + 0.5) + s.x, s.z = floor(s.x + 0.5), floor(s.z + 0.5) - local ssight, sground = minetest.line_of_sight(s, { - x = s.x, y = s.y - 4, z = s.z}, 1) + local ssight, sground = minetest.line_of_sight(s, vector_offset(s, 0, -4, 0), 1) -- determine node above ground - if not ssight then - s.y = sground.y + 1 - end + if not ssight then s.y = sground.y + 1 end local p1 = self.attack:get_pos() - - p1.x = math.floor(p1.x + 0.5) - p1.y = math.floor(p1.y + 0.5) - p1.z = math.floor(p1.z + 0.5) + p1 = vector_new(floor(p1.x + 0.5), floor(p1.y + 0.5), floor(p1.z + 0.5)) local dropheight = 12 if self.fear_height ~= 0 then dropheight = self.fear_height end local jumpheight = 0 if self.jump and self.jump_height >= 4 then - jumpheight = math.min(math.ceil(self.jump_height / 4), 4) + jumpheight = min(ceil(self.jump_height * 0.25), 4) elseif self.stepheight > 0.5 then jumpheight = 1 end @@ -198,34 +169,27 @@ function mob_class:smart_mobs(s, p, dist, dtime) -- no path found, try something else if not self.path.way then - self.path.following = false -- lets make way by digging/building if not accessible if self.pathfinding == 2 and mobs_griefing then - -- is player higher than mob? if s.y < p1.y then - -- build upwards if not minetest.is_protected(s, "") then - local ndef1 = minetest.registered_nodes[self.standing_in] - if ndef1 and (ndef1.buildable_to or ndef1.groups.liquid) then - minetest.set_node(s, {name = mcl_mobs.fallback_node}) end end - local sheight = math.ceil(self.collisionbox[5]) + 1 + local sheight = ceil(self.collisionbox[5]) + 1 -- assume mob is 2 blocks high so it digs above its head s.y = s.y + sheight -- remove one block above to make room to jump if not minetest.is_protected(s, "") then - local node1 = node_ok(s, "air").name local ndef1 = minetest.registered_nodes[node1] @@ -235,32 +199,21 @@ function mob_class:smart_mobs(s, p, dist, dtime) and not ndef1.groups.level and not ndef1.groups.unbreakable and not ndef1.groups.liquid then - minetest.set_node(s, {name = "air"}) minetest.add_item(s, ItemStack(node1)) - end end s.y = s.y - sheight - self.object:set_pos({x = s.x, y = s.y + 2, z = s.z}) - + self.object:set_pos(vector_offset(s, 0, 2, 0)) else -- dig 2 blocks to make door toward player direction - - local yaw1 = self.object:get_yaw() + math.pi / 2 - local p1 = { - x = s.x + math.cos(yaw1), - y = s.y, - z = s.z + math.sin(yaw1) - } + local yaw1 = self.object:get_yaw() + HALFPI + local p1 = vector_offset(s, cos(yaw1), 0, sin(yaw1)) if not minetest.is_protected(p1, "") then - local node1 = node_ok(p1, "air").name local ndef1 = minetest.registered_nodes[node1] - - if node1 ~= "air" - and node1 ~= "ignore" + if node1 ~= "air" and node1 ~= "ignore" and ndef1 and not ndef1.groups.level and not ndef1.groups.unbreakable @@ -274,8 +227,7 @@ function mob_class:smart_mobs(s, p, dist, dtime) node1 = node_ok(p1, "air").name ndef1 = minetest.registered_nodes[node1] - if node1 ~= "air" - and node1 ~= "ignore" + if node1 ~= "air" and node1 ~= "ignore" and ndef1 and not ndef1.groups.level and not ndef1.groups.unbreakable @@ -309,28 +261,19 @@ end -- specific attacks local specific_attack = function(list, what) - -- no list so attack default (player, animals etc.) - if list == nil then - return true - end + if list == nil then return true end -- found entity on list to attack? for no = 1, #list do - - if list[no] == what then - return true - end + if list[no] == what then return true end end - return false end -- find someone to attack function mob_class:monster_attack() - if not damage_enabled or self.passive ~= false or self.state == "attack" or self:day_docile() then - return - end + if not damage_enabled or self.passive ~= false or self.state == "attack" or self:day_docile() then return end local s = self.object:get_pos() local p, sp, dist @@ -384,7 +327,7 @@ function mob_class:monster_attack() p = player:get_pos() sp = s - dist = vector.distance(p, s) + dist = vector_distance(p, s) -- aim higher to make looking up hills more realistic p.y = p.y + 1 @@ -406,7 +349,7 @@ function mob_class:monster_attack() end end if not min_player and #blacklist_attack > 0 then - min_player=blacklist_attack[math.random(#blacklist_attack)] + min_player=blacklist_attack[random(#blacklist_attack)] end -- attack player if min_player then @@ -417,7 +360,6 @@ end -- npc, find closest monster to attack function mob_class:npc_attack() - if self.type ~= "npc" or not self.attacks_monsters or self.state == "attack" then @@ -436,7 +378,7 @@ function mob_class:npc_attack() p = obj.object:get_pos() sp = s - local dist = vector.distance(p, s) + local dist = vector_distance(p, s) -- aim higher to make looking up hills more realistic p.y = p.y + 1 @@ -458,7 +400,6 @@ end -- dogshoot attack switch and counter function function mob_class:dogswitch(dtime) - -- switch mode not activated if not self.dogshoot_switch or not dtime then @@ -467,10 +408,8 @@ function mob_class:dogswitch(dtime) self.dogshoot_count = self.dogshoot_count + dtime - if (self.dogshoot_switch == 1 - and self.dogshoot_count > self.dogshoot_count_max) - or (self.dogshoot_switch == 2 - and self.dogshoot_count > self.dogshoot_count2_max) then + if (self.dogshoot_switch == 1 and self.dogshoot_count > self.dogshoot_count_max) + or (self.dogshoot_switch == 2 and self.dogshoot_count > self.dogshoot_count2_max) then self.dogshoot_count = 0 @@ -517,13 +456,9 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir) if is_player then -- is mob out of reach? - if vector.distance(mob_pos, player_pos) > 3 then - return - end + if vector_distance(mob_pos, player_pos) > 3 then return end -- is mob protected? - if self.protected and minetest.is_protected(mob_pos, hitter:get_player_name()) then - return - end + if self.protected and minetest.is_protected(mob_pos, hitter:get_player_name()) then return end mcl_potions.update_haste_and_fatigue(hitter) end @@ -532,13 +467,10 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir) local time_diff = time_now - self.invul_timestamp -- check for invulnerability time in microseconds (0.5 second) - if time_diff <= 500000 and time_diff >= 0 then - return - end + if time_diff <= 500000 and time_diff >= 0 then return end -- custom punch function if self.do_punch then - -- when false skip going any further if self.do_punch(self, hitter, tflp, tool_capabilities, dir) == false then return @@ -554,15 +486,11 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir) local time_now = minetest.get_us_time() if is_player then - if minetest.is_creative_enabled(hitter:get_player_name()) then - self.health = 0 - end - + if minetest.is_creative_enabled(hitter:get_player_name()) then self.health = 0 end -- set/update 'drop xp' timestamp if hitted by player self.xp_timestamp = time_now end - -- punch interval local weapon = hitter:get_wielded_item() local punch_interval = 1.4 @@ -583,18 +511,10 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir) end - for group,_ in pairs( (tool_capabilities.damage_groups or {}) ) do - + for group,_ in pairs((tool_capabilities.damage_groups or {}) ) do tmp = tflp / (tool_capabilities.full_punch_interval or 1.4) - - if tmp < 0 then - tmp = 0.0 - elseif tmp > 1 then - tmp = 1.0 - end - - damage = damage + (tool_capabilities.damage_groups[group] or 0) - * tmp * ((armor[group] or 0) / 100.0) + tmp = tmp < 0 and 0 or (tmp > 1 and 1 or tmp) + damage = damage + (tool_capabilities.damage_groups[group] or 0) * tmp * ((armor[group] or 0) / 100.0) end -- strength and weakness effects @@ -613,9 +533,7 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir) -- check for tool immunity or special damage for n = 1, #self.immune_to do - if self.immune_to[n][1] == weapon:get_name() then - damage = self.immune_to[n][2] or 0 break end @@ -623,7 +541,7 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir) -- healing if damage <= -1 then - self.health = self.health - math.floor(damage) + self.health = self.health - floor(damage) return end @@ -643,7 +561,7 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir) local weapon = hitter:get_wielded_item(player) local def = weapon:get_definition() if def.tool_capabilities and def.tool_capabilities.punch_attack_uses then - local wear = math.floor(65535/tool_capabilities.punch_attack_uses) + local wear = floor(65535/tool_capabilities.punch_attack_uses) weapon:add_wear(wear) tt.reload_itemstack_description(weapon) -- update tooltip hitter:set_wielded_item(weapon) @@ -654,14 +572,12 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir) local die = false - if damage >= 0 then -- only play hit sound and show blood effects if damage is 1 or over; lower to 0.1 to ensure armor works appropriately. if damage >= 0.1 then -- weapon sounds if weapon:get_definition().sounds ~= nil then - - local s = math.random(0, #weapon:get_definition().sounds) + local s = random(0, #weapon:get_definition().sounds) minetest.sound_play(weapon:get_definition().sounds[s], { object = self.object, --hitter, @@ -691,24 +607,18 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir) if self.knock_back and tflp >= punch_interval then -- direction error check - dir = dir or {x = 0, y = 0, z = 0} + dir = dir or vector_zero() local v = self.object:get_velocity() if not v then return end - local r = 1.4 - math.min(punch_interval, 1.4) - local kb = r * (math.abs(v.x)+math.abs(v.z)) + local r = 1.4 - min(punch_interval, 1.4) + local kb = r * (abs(v.x)+abs(v.z)) local up = 2.625 - if die==true then - kb=kb*1.25 - end + if die then kb = kb * 1.25 end -- if already in air then dont go up anymore when hit - if math.abs(v.y) > 0.1 - or self.fly then - up = 0 - end - + if abs(v.y) > 0.1 or self.fly then up = 0 end -- check if tool already has specific knockback value if tool_capabilities.damage_groups["knockback"] then @@ -717,21 +627,17 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir) kb = kb * 1.25 end - - local luaentity - if hitter then - luaentity = hitter:get_luaentity() - end + local luaentity = hitter and hitter:get_luaentity() if hitter and is_player then local wielditem = hitter:get_wielded_item() kb = kb + 9 * mcl_enchanting.get_enchantment(wielditem, "knockback") -- add player velocity to mob knockback local hv = hitter:get_velocity() local dir_dot = (hv.x * dir.x) + (hv.z * dir.z) - local player_mag = math.sqrt((hv.x * hv.x) + (hv.z * hv.z)) - local mob_mag = math.sqrt((v.x * v.x) + (v.z * v.z)) + local player_mag = ((hv.x * hv.x) + (hv.z * hv.z))^0.5 + local mob_mag = ((v.x * v.x) + (v.z * v.z))^0.5 if dir_dot > 0 and mob_mag <= player_mag * 0.625 then - kb = kb + ((math.abs(hv.x) + math.abs(hv.z)) * r) + kb = kb + (abs(hv.x) + abs(hv.z)) * r end elseif luaentity and luaentity._knockback and die == false then kb = kb + luaentity._knockback @@ -742,9 +648,9 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir) self._turn_to=self.object:get_yaw()-1.57 self.frame_speed_multiplier=2.3 if self.animation.run_end then - self:set_animation( "run") + self:set_animation("run") elseif self.animation.walk_end then - self:set_animation( "walk") + self:set_animation("walk") end minetest.after(0.2, function() if self and self.object then @@ -752,11 +658,7 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir) self._kb_turn = false end end) - self.object:add_velocity({ - x = dir.x * kb, - y = up*2, - z = dir.z * kb - }) + self.object:add_velocity(vector_new(dir.x * kb, up*2, dir.z * kb )) self.pause_timer = 0.25 end @@ -803,7 +705,6 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir) local obj = nil for n = 1, #objs do - obj = objs[n]:get_luaentity() if obj then @@ -835,11 +736,7 @@ end function mob_class:check_aggro(dtime) if not self._aggro or not self.attack then return end - - if not self._check_aggro_timer then - self._check_aggro_timer = 0 - end - + if not self._check_aggro_timer then self._check_aggro_timer = 0 end if self._check_aggro_timer > 5 then self._check_aggro_timer = 0 @@ -847,7 +744,7 @@ function mob_class:check_aggro(dtime) -- TODO consider removing this in favour of what is done in do_states_attack -- Attack is dropped in do_states_attack if out of range, so won't even trigger here -- I do not think this code does anything. Are mobs still loaded in at 128? - if not self.attack:get_pos() or vector.distance(self.attack:get_pos(),self.object:get_pos()) > 128 then + if not self.attack:get_pos() or vector_distance(self.attack:get_pos(),self.object:get_pos()) > 128 then self._aggro = nil self.attack = nil self.state = "stand" @@ -875,15 +772,12 @@ end function mob_class:do_states_attack (dtime) self.timer = self.timer + dtime - if self.timer > 100 then - self.timer = 1 - end + if self.timer > 100 then self.timer = 1 end local s = self.object:get_pos() if not s then return end local p = self.attack:get_pos() or s - local yaw = self.object:get_yaw() or 0 -- stop attacking if player invisible or out of range @@ -915,17 +809,15 @@ function mob_class:do_states_attack (dtime) end -- calculate distance from mob and enemy - local dist = vector.distance(p, s) + local dist = vector_distance(p, s) if self.attack_type == "explode" then - if target_line_of_sight then self:turn_in_direction(p.x - s.x, p.z - s.z, 1, dtime) end local node_break_radius = self.explosion_radius or 1 - local entity_damage_radius = self.explosion_damage_radius - or (node_break_radius * 2) + 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 target_line_of_sight then @@ -952,9 +844,9 @@ function mob_class:do_states_attack (dtime) end if self.animation and self.animation.run_start then - self:set_animation( "run") + self:set_animation("run") else - self:set_animation( "walk") + self:set_animation("walk") end if self.v_start then @@ -997,80 +889,47 @@ function mob_class:do_states_attack (dtime) 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) + if self.fly and dist > self.reach then + local p1, p2 = s, p + local me_y, p_y = floor(p1.y), 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 - }) - + self.object:set_velocity(vector_new(v.x, 1 * self.walk_velocity, v.z)) elseif me_y > p_y then - - self.object:set_velocity({ - x = v.x, - y = -1 * self.walk_velocity, - z = v.z - }) + self.object:set_velocity(vector_new(v.x, -1 * self.walk_velocity, v.z)) end else if me_y < p_y then - - self.object:set_velocity({ - x = v.x, - y = 0.01, - z = v.z - }) - + self.object:set_velocity(vector_new(v.x, 0.01, v.z)) elseif me_y > p_y then - - self.object:set_velocity({ - x = v.x, - y = -0.01, - z = v.z - }) + self.object:set_velocity(vector_new(v.x, -0.01, v.z)) end end - end -- rnd: new movement direction - if self.path.following - and self.path.way - and self.attack_type ~= "dogshoot" then - + 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 + 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 + if abs(p1.x - s.x) + 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} + p = vector_copy(p1) end self:turn_in_direction(p.x - s.x, p.z - s.z, 1, dtime) @@ -1111,19 +970,13 @@ function mob_class:do_states_attack (dtime) self.timer = 0 if not self.custom_attack then - if self.double_melee_attack and math.random(1, 2) == 1 then + if self.double_melee_attack and 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 + if self:line_of_sight(vector_offset(p, 0, .5, 0), vector_offset(s, 0, .5, 0)) == true then self:mob_sound("attack") -- punch player (or what player is attached to) @@ -1149,54 +1002,30 @@ function mob_class:do_states_attack (dtime) 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 vec = { - x = p.x - s.x, - y = p.y - s.y, - z = p.z - s.z - } - local dist = (vec.x^2 + vec.y^2 + vec.z^2)^0.5 + local vec = vector_new(p.x - s.x, p.y - s.y - 1, p.z - s.z) + local dist = (vec.x*vec.x + vec.y*vec.y + vec.z*vec.z)^0.5 self:turn_in_direction(vec.x, vec.z, 1, dtime) - local stay_away_from_player = vector.zero() - - --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 + if not self.strafe_direction then self.strafe_direction = HALFPI end + if random(40) == 1 then self.strafe_direction = self.strafe_direction * -1 end - local dir = vector.rotate_around_axis(vector.direction(s, p), vector.new(0,1,0), self.strafe_direction) - local dir2 = vector.multiply(dir, 0.3 * self.walk_velocity) - - if dir2 and stay_away_from_player then - self.acc = vector.add(dir2, stay_away_from_player) + local dir = -atan2(p.x - s.x, p.z - s.z) + self.acc = vector_new(-sin(dir + self.strafe_direction) * 0.8, 0, cos(dir + self.strafe_direction) * 0.8) + --stay away from player so as to shoot them + if self.avoid_distance and dist < self.avoid_distance and self.shooter_avoid_enemy then + local f = 0.3 * (self.avoid_distance - dist) / self.avoid_distance + self.acc.x, self.acc.z = self.acc.x - sin(dir) * f, self.acc.z + cos(dir) * f end else - self:set_velocity( 0) + 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 + p.y = p.y + (self.collisionbox[2] + self.collisionbox[5]) * 0.5 + if self.shoot_interval and self.timer > self.shoot_interval and random(1, 100) <= 60 + and not minetest.raycast(vector_offset(p, 0, self.shoot_offset, 0), vector_offset(self.attack:get_pos(), 0, 1.5, 0), false, false):next() then self.timer = 0 self:set_animation( "shoot") @@ -1205,7 +1034,6 @@ function mob_class:do_states_attack (dtime) -- Shoot arrow if minetest.registered_entities[self.arrow] then - local arrow, ent local v = 1 if not self.shoot_arrow then @@ -1215,9 +1043,7 @@ function mob_class:do_states_attack (dtime) end) arrow = minetest.add_entity(p, self.arrow) ent = arrow:get_luaentity() - if ent.velocity then - v = ent.velocity - end + v = ent.velocity or v ent.switch = 1 ent.owner_id = tostring(self.object) -- add unique owner id to arrow @@ -1231,9 +1057,7 @@ function mob_class:do_states_attack (dtime) -- offset makes shoot aim accurate vec.y = vec.y + self.shoot_offset - vec.x = vec.x * (v / dist) - vec.y = vec.y * (v / dist) - vec.z = vec.z * (v / dist) + vec.x, vec.y, vec.z = vec.x * (v / dist), vec.y * (v / dist), vec.z * (v / dist) if self.shoot_arrow then vec = vector.normalize(vec) self:shoot_arrow(p, vec) @@ -1242,13 +1066,9 @@ function mob_class:do_states_attack (dtime) end end end - elseif self.attack_type == "custom" and self.attack_state then self.attack_state(self, dtime) end - if self.on_attack then - self.on_attack(self, dtime) - end - + if self.on_attack then self.on_attack(self, dtime) end end diff --git a/mods/ENTITIES/mcl_mobs/init.lua b/mods/ENTITIES/mcl_mobs/init.lua index 1fcbb944a..3ac42ff9b 100644 --- a/mods/ENTITIES/mcl_mobs/init.lua +++ b/mods/ENTITIES/mcl_mobs/init.lua @@ -6,6 +6,19 @@ local modname = minetest.get_current_modname() local path = minetest.get_modpath(modname) local S = minetest.get_translator(modname) mcl_mobs.fallback_node = minetest.registered_aliases["mapgen_dirt"] or "mcl_core:dirt" + +-- used by the libaries below. +-- get node but use fallback for nil or unknown +local node_ok = function(pos, fallback) + fallback = fallback or mcl_mobs.fallback_node + local node = minetest.get_node_or_nil(pos) + if node and minetest.registered_nodes[node.name] then + return node + end + return minetest.registered_nodes[fallback] +end +mcl_mobs.node_ok = node_ok + --api and helpers -- effects: sounds and particles mostly dofile(path .. "/effects.lua") @@ -19,10 +32,9 @@ dofile(path .. "/items.lua") dofile(path .. "/pathfinding.lua") -- combat: attack logic dofile(path .. "/combat.lua") --- the enity functions themselves +-- the entity functions themselves dofile(path .. "/api.lua") - --utility functions dofile(path .. "/breeding.lua") dofile(path .. "/spawning.lua") @@ -37,16 +49,6 @@ local old_spawn_icons = minetest.settings:get_bool("mcl_old_spawn_icons",false) local extended_pet_control = minetest.settings:get_bool("mcl_extended_pet_control",true) local difficulty = tonumber(minetest.settings:get("mob_difficulty")) or 1.0 --- get node but use fallback for nil or unknown -local node_ok = function(pos, fallback) - fallback = fallback or mcl_mobs.fallback_node - local node = minetest.get_node_or_nil(pos) - if node and minetest.registered_nodes[node.name] then - return node - end - return minetest.registered_nodes[fallback] -end - --#### REGISTER FUNCS -- Code to execute before custom on_rightclick handling @@ -114,14 +116,8 @@ function mcl_mobs.register_mob(name, def) mcl_mobs.spawning_mobs[name] = true mcl_mobs.registered_mobs[name] = def - local can_despawn - if def.can_despawn ~= nil then - can_despawn = def.can_despawn - elseif def.spawn_class == "passive" then - can_despawn = false - else - can_despawn = true - end + local can_despawn = def.can_despawn + if def.can_despawn == nil then can_despawn = def.spawn_class ~= "passive" end local function scale_difficulty(value, default, min, special) if (not value) or (value == default) or (value == special) then diff --git a/mods/ENTITIES/mcl_mobs/mod.conf b/mods/ENTITIES/mcl_mobs/mod.conf index 927c1c905..8e5c8a261 100644 --- a/mods/ENTITIES/mcl_mobs/mod.conf +++ b/mods/ENTITIES/mcl_mobs/mod.conf @@ -1,5 +1,5 @@ name = mcl_mobs -author = PilzAdam +author = PilzAdam, kno10 description = Adds a mob API for mods to add animals or monsters, etc. depends = mcl_particles, mcl_luck optional_depends = mcl_weather, mcl_explosions, mcl_hunger, mcl_worlds, invisibility, lucky_block, cmi, doc_identifier, mcl_armor, mcl_portals, mcl_experience, mcl_sculk diff --git a/mods/ENTITIES/mcl_mobs/movement.lua b/mods/ENTITIES/mcl_mobs/movement.lua index ffd8bd885..614146726 100644 --- a/mods/ENTITIES/mcl_mobs/movement.lua +++ b/mods/ENTITIES/mcl_mobs/movement.lua @@ -18,10 +18,17 @@ local sin = math.sin local cos = math.cos local abs = math.abs local floor = math.floor +local atan2 = math.atan2 local PI = math.pi local TWOPI = 2 * math.pi -local PIHALF = 0.5 * math.pi -local PIQUARTER = 0.25 * math.pi +local HALFPI = 0.5 * math.pi +local QUARTERPI = 0.25 * math.pi + +local vector_new = vector.new +local vector_zero = vector.zero +local vector_copy = vector.copy +local vector_offset = vector.offset +local vector_distance = vector.distance local registered_fallback_node = minetest.registered_nodes[mcl_mobs.fallback_node] @@ -35,32 +42,16 @@ end -- get node but use fallback for nil or unknown local node_ok = function(pos, fallback) local node = minetest.get_node_or_nil(pos) - if node and minetest.registered_nodes[node.name] then - return node - end - if fallback then - return minetest.registered_nodes[fallback] - else - return registered_fallback_node - end + if node and minetest.registered_nodes[node.name] then return node end + return fallback and minetest.registered_nodes[fallback] or registered_fallback_node end -- Returns true is node can deal damage to self function mob_class:is_node_dangerous(nodename) local nn = nodename - if self.lava_damage > 0 then - if minetest.get_item_group(nn, "lava") ~= 0 then - return true - end - end - if self.fire_damage > 0 then - if minetest.get_item_group(nn, "fire") ~= 0 then - return true - end - end - if minetest.registered_nodes[nn] and minetest.registered_nodes[nn].damage_per_second and minetest.registered_nodes[nn].damage_per_second > 0 then - return true - end + if self.lava_damage > 0 and minetest.get_item_group(nn, "lava") ~= 0 then return true end + if self.fire_damage > 0 and minetest.get_item_group(nn, "fire") ~= 0 then return true end + if minetest.registered_nodes[nn] and minetest.registered_nodes[nn].damage_per_second and minetest.registered_nodes[nn].damage_per_second > 0 then return true end return false end @@ -68,29 +59,19 @@ end -- Returns true if node is a water hazard function mob_class:is_node_waterhazard(nodename) local nn = nodename - if self.water_damage > 0 then - if minetest.get_item_group(nn, "water") ~= 0 then - return true - end - end - if minetest.registered_nodes[nn] and (minetest.registered_nodes[nn].drowning or 0) > 0 then - if self.breath_max ~= -1 then - -- check if the mob is water-breathing _and_ the block is water; only return true if neither is the case - -- this will prevent water-breathing mobs to classify water or e.g. sand below them as dangerous - if not self.breathes_in_water and minetest.get_item_group(nn, "water") ~= 0 then - return true - end - end - end + if self.water_damage > 0 and minetest.get_item_group(nn, "water") ~= 0 then return true end + if minetest.registered_nodes[nn] and (minetest.registered_nodes[nn].drowning or 0) > 0 + and self.breath_max ~= -1 + -- check if the mob is water-breathing _and_ the block is water; only return true if neither is the case + -- this will prevent water-breathing mobs to classify water or e.g. sand below them as dangerous + and not self.breathes_in_water and minetest.get_item_group(nn, "water") ~= 0 then return true end return false end -local function raycast_line_of_sight (origin, target) +local function raycast_line_of_sight(origin, target) local raycast = minetest.raycast(origin, target, false, true) - local los_blocked = false - for hitpoint in raycast do if hitpoint.type == "node" then --TODO type object could block vision, for example chests @@ -110,13 +91,11 @@ end function mob_class:target_visible(origin) if not origin then return end - if not self.attack then return end local target_pos = self.attack:get_pos() - if not target_pos then return end - local origin_eye_pos = vector.offset(origin, 0, self.head_eye_height, 0) + local origin_eye_pos = vector_offset(origin, 0, self.head_eye_height, 0) --minetest.log("origin: " .. dump(origin)) --minetest.log("origin_eye_pos: " .. dump(origin_eye_pos)) @@ -124,11 +103,11 @@ function mob_class:target_visible(origin) local targ_head_height, targ_feet_height local cbox = self.collisionbox if self.attack:is_player() then - targ_head_height = vector.offset(target_pos, 0, cbox[5], 0) + targ_head_height = vector_offset(target_pos, 0, cbox[5], 0) targ_feet_height = target_pos -- Cbox would put feet under ground which interferes with ray else - targ_head_height = vector.offset(target_pos, 0, cbox[5], 0) - targ_feet_height = vector.offset(target_pos, 0, cbox[2], 0) + targ_head_height = vector_offset(target_pos, 0, cbox[5], 0) + targ_feet_height = vector_offset(target_pos, 0, cbox[2], 0) end --minetest.log("start targ_head_height: " .. dump(targ_head_height)) @@ -148,7 +127,6 @@ end -- check line of sight (BrunoMine) function mob_class:line_of_sight(pos1, pos2, stepsize) - stepsize = stepsize or 1 local s, pos = minetest.line_of_sight(pos1, pos2, stepsize) @@ -159,8 +137,7 @@ function mob_class:line_of_sight(pos1, pos2, stepsize) end -- New pos1 to be analyzed - local npos1 = {x = pos1.x, y = pos1.y, z = pos1.z} - + local npos1 = vector_copy(pos1) local r, pos = minetest.line_of_sight(npos1, pos2, stepsize) -- Checks the return @@ -170,23 +147,19 @@ function mob_class:line_of_sight(pos1, pos2, stepsize) local nn = minetest.get_node(pos).name -- Target Distance (td) to travel - local td = vector.distance(pos1, pos2) + local td = vector_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 'normal' nodebox. - while minetest.registered_nodes[nn] - and minetest.registered_nodes[nn].walkable == false do - + 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 + if td < ad + stepsize then return true end -- Reached the target -- Moves the analyzed pos - local d = vector.distance(pos1, pos2) + local d = vector_distance(pos1, pos2) npos1.x = ((pos2.x - pos1.x) / d * stepsize) + pos1.x npos1.y = ((pos2.y - pos1.y) / d * stepsize) + pos1.y @@ -224,15 +197,15 @@ function mob_class:can_jump_cliff() local dir_z = cos(yaw) * (cbox[4] + 0.5) --is there nothing under the block in front? if so jump the gap. - local node_low = node_ok({ x = pos.x + dir_x*0.6, y = pos.y - 0.5, z = pos.z + dir_z*0.6 }, "air") + local node_low = node_ok(vector_offset(pos, dir_x * 0.6, -0.5, dir_z * 0.6), "air") -- next is solid, no need to jump if minetest.registered_nodes[node_low.name] and minetest.registered_nodes[node_low.name].walkable == true then self._jumping_cliff = false return false end - local node_far = node_ok({ x = pos.x + dir_x*1.6, y = pos.y - 0.5, z = pos.z + dir_z*1.6 }, "air") - local node_far2 = node_ok({ x = pos.x + dir_x*2.5, y = pos.y - 0.5, z = pos.z + dir_z*2.5 }, "air") + local node_far = node_ok(vector_offset(pos, dir_x * 1.6, -0.5, dir_z * 1.6), "air") + local node_far2 = node_ok(vector_offset(pos, dir_x * 2.5, -0.5, dir_z * 2.5), "air") -- TODO: also check there is air above these nodes? -- some place to land on @@ -272,8 +245,8 @@ function mob_class:is_at_cliff_or_danger() local ypos = pos.y + cbox[2] + 0.1 -- just above floor local free_fall, blocker = minetest.line_of_sight( - vector.new(pos.x + dir_x, ypos, pos.z + dir_z), - vector.new(pos.x + dir_x, floor(ypos - self.fear_height), pos.z + dir_z)) + vector_new(pos.x + dir_x, ypos, pos.z + dir_z), + vector_new(pos.x + dir_x, floor(ypos - self.fear_height), pos.z + dir_z)) if free_fall then return "free fall" @@ -328,8 +301,8 @@ function mob_class:is_at_water_danger() local ypos = pos.y + cbox[2] + 0.1 -- just above floor 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)) + vector_new(pos.x + dir_x, ypos, pos.z + dir_z), + vector_new(pos.x + dir_x, ypos - 3, pos.z + dir_z)) if not los then local bnode = minetest.get_node(blocker) @@ -374,22 +347,12 @@ end -- jump if facing a solid node (not fences or gates) function mob_class:do_jump() - if not self.jump - or self.jump_height == 0 - or self.fly - or self.order == "stand" then - return false - end - + if not self.jump or self.jump_height == 0 or self.fly or self.order == "stand" then return false end self.facing_fence = false self._jumping_cliff = false -- something stopping us while moving? - if self.state ~= "stand" - and self:get_velocity() > 0.5 - and self.object:get_velocity().y ~= 0 then - return false - end + if self.state ~= "stand" and self:get_velocity() > 0.5 and self.object:get_velocity().y ~= 0 then return false end local pos = self.object:get_pos() local v = self.object:get_velocity() @@ -399,10 +362,9 @@ function mob_class:do_jump() -- what is mob standing on? pos.y = pos.y + cbox[2] - local nodBelow = node_ok({ x = pos.x, y = pos.y - 0.2, z = pos.z }) - if minetest.registered_nodes[nodBelow.name].walkable == false and not in_water then - return false - end + local node_below = node_ok(vector_offset(pos, 0, -0.2, 0)) + local nbdef = minetest.registered_nodes[node_below.name] + if nbdef and nbdef.walkable == false and not in_water then return false end local yaw = self.object:get_yaw() @@ -411,27 +373,23 @@ function mob_class:do_jump() local dir_z = cos(yaw) * (cbox[4] + 0.5) + v.z * 0.25 -- what is in front of mob? - local nod = node_ok(vector.new(pos.x + dir_x, pos.y + 0.5, pos.z + dir_z)) + local nod = node_ok(vector_offset(pos, dir_x, 0.5, dir_z)) -- this is used to detect if there's a block on top of the block in front of the mob. -- If there is, there is no point in jumping as we won't manage. - local nodTop = node_ok(vector.new(pos.x + dir_x, pos.y + 1.5, pos.z + dir_z), "air") + local node_top = node_ok(vector_offset(pos, dir_x, 1.5, dir_z), "air") -- TODO: also check above the mob itself? -- we don't attempt to jump if there's a stack of blocks blocking, unless attacking - if minetest.registered_nodes[nodTop.name].walkable == true and not (self.attack and self.state == "attack") then - return false - end + local ntdef = minetest.registered_nodes[node_top.name] + if ntdef and ntdef.walkable == true and not (self.attack and self.state == "attack") then return false end -- thin blocks that do not need to be jumped - if nod.name == node_snow then - return false - end + if nod.name == node_snow then return false end local ndef = minetest.registered_nodes[nod.name] - if self.walk_chance ~= 0 and not (ndef and ndef.walkable) and not self._can_jump_cliff then - return false - end + if self.walk_chance ~= 0 and not (ndef and ndef.walkable) and not self._can_jump_cliff then return false end + if minetest.get_item_group(nod.name, "fence") ~= 0 or minetest.get_item_group(nod.name, "fence_gate") ~= 0 or minetest.get_item_group(nod.name, "wall") ~= 0 then @@ -441,9 +399,9 @@ function mob_class:do_jump() v.y = self.jump_height + 0.3 if in_water then - v=vector.multiply(v, vector.new(1.2,1.5,1.2)) + v.x, v.y, v.z = v.x * 1.2, v.y * 1.5, v.z * 1.2 elseif self._can_jump_cliff then - v=vector.multiply(v, vector.new(2.5,1.1,2.5)) + v.x, v.y, v.z = v.x * 2.5, v.y * 1.1, v.z * 2.5 end self:set_animation("jump") -- only when defined @@ -451,10 +409,8 @@ function mob_class:do_jump() -- when in air move forward local forward = function(self, v) - if not self.object or not self.object:get_luaentity() or self.state == "die" then - return - end - self.object:set_acceleration(vector.new(v.x * 2, DEFAULT_FALL_SPEED, v.z * 2)) + if not self.object or not self.object:get_luaentity() or self.state == "die" then return end + self.object:set_acceleration(vector_new(v.x * 2, DEFAULT_FALL_SPEED, v.z * 2)) end -- trying multiple time helps mobs jump minetest.after(0.1, forward, self, v) @@ -483,28 +439,19 @@ end -- should mob follow what I'm holding ? function mob_class:follow_holding(clicker) if self.nofollow then return false end - - if mcl_mobs.invis[clicker:get_player_name()] then - return false - end + if mcl_mobs.invis[clicker:get_player_name()] then return false end local item = clicker:get_wielded_item() local t = type(self.follow) - -- single item - if t == "string" - and item:get_name() == self.follow then + if t == "string" and item:get_name() == self.follow then return true - -- multiple items elseif t == "table" then for no = 1, #self.follow do - if self.follow[no] == item:get_name() then - return true - end + if self.follow[no] == item:get_name() then return true end end end - return false end @@ -537,7 +484,6 @@ function mob_class:replace_node(pos) local node = minetest.get_node(pos) if node.name == what then - local oldnode = {name = what, param2 = node.param2} local newnode = {name = with, param2 = node.param2} local on_replace_return = false @@ -547,7 +493,6 @@ function mob_class:replace_node(pos) if on_replace_return ~= false then - if mobs_griefing then minetest.after(self.replace_delay, function() if self and self.object and self.object:get_velocity() and self.health > 0 then @@ -561,32 +506,20 @@ end -- specific runaway local specific_runaway = function(list, what) - if type(list) ~= "table" then - list = {} - end - -- no list so do not run - if list == nil then - return false - end - + if list == nil then return false end + if type(list) ~= "table" then list = {} end -- found entity on list to attack? for no = 1, #list do - - if list[no] == what then - return true - end + if list[no] == what then return true end end - return false end -- find someone to runaway from function mob_class:check_runaway_from() - if not self.runaway_from and self.state ~= "flop" then - return - end + if not self.runaway_from and self.state ~= "flop" then return end local s = self.object:get_pos() local p, sp, dist @@ -596,9 +529,7 @@ function mob_class:check_runaway_from() local objs = minetest.get_objects_inside_radius(s, self.view_range) for n = 1, #objs do - if objs[n]:is_player() then - if mcl_mobs.invis[ objs[n]:get_player_name() ] or self.owner == objs[n]:get_player_name() or (not self:object_in_range(objs[n])) then @@ -610,7 +541,6 @@ function mob_class:check_runaway_from() end else obj = objs[n]:get_luaentity() - if obj then player = obj.object type = obj.type @@ -621,20 +551,13 @@ function mob_class:check_runaway_from() -- find specific mob to runaway from if name ~= "" and name ~= self.name and specific_runaway(self.runaway_from, name) then - p = player:get_pos() sp = s - - -- aim higher to make looking up hills more realistic - p.y = p.y + 1 - sp.y = sp.y + 1 - - dist = vector.distance(p, s) - - + dist = vector_distance(p, s) -- choose closest player/mpb to runaway from if dist < min_dist - and self:line_of_sight(sp, p, 2) == true then + and self:line_of_sight(vector_offset(sp, 0, 1, 0), vector_offset(p, 0, 1, 0), 2) == true then + -- aim higher to make looking up hills more realistic min_dist = dist min_player = player end @@ -670,7 +593,6 @@ function mob_class:check_follow() if self.type == "npc" and self.order == "follow" and self.state ~= "attack" and self.order ~= "sit" and self.owner ~= "" then - if self.following and self.owner and self.owner ~= self.following:get_player_name() then self.following = nil end @@ -686,13 +608,8 @@ function mob_class:check_follow() -- follow that thing if self.following then local s = self.object:get_pos() - - local p - if self.following:is_player() then - p = self.following:get_pos() - elseif self.following.object then - p = self.following.object:get_pos() - end + local p = self.following:is_player() and self.following:get_pos() + or self.following.object and self.following.object:get_pos() if p then if (not self:object_in_range(self.following)) then @@ -701,7 +618,7 @@ function mob_class:check_follow() self:turn_in_direction(p.x - s.x, p.z - s.z, 2.35) -- anyone but standing npc's can move along - local dist = vector.distance(p, s) + local dist = vector_distance(p, s) if dist > 3 and self.order ~= "stand" then self:set_velocity(self.follow_velocity) if self.walk_chance ~= 0 then @@ -724,19 +641,15 @@ function mob_class:flop() if self:flight_check(s) == false then self.state = "flop" - self.object:set_acceleration({x = 0, y = DEFAULT_FALL_SPEED, z = 0}) + self.object:set_acceleration(vector_new(0, DEFAULT_FALL_SPEED, 0)) local p = self.object:get_pos() - local sdef = minetest.registered_nodes[node_ok(vector.add(p, vector.new(0,self.collisionbox[2]-0.2,0))).name] + local sdef = minetest.registered_nodes[node_ok(vector_offset(p, 0, self.collisionbox[2] - 0.2, 0)).name] -- Flop on ground if sdef and sdef.walkable then if self.object:get_velocity().y < 0.1 then self:mob_sound("flop") - self.object:set_velocity({ - x = (random() * 2 - 1) * FLOP_HOR_SPEED, - y = FLOP_HEIGHT, - z = (random() * 2 - 1) * FLOP_HOR_SPEED, - }) + self.object:set_velocity(vector_new((random() * 2 - 1) * FLOP_HOR_SPEED, FLOP_HEIGHT, (random() * 2 - 1) * FLOP_HOR_SPEED)) end end @@ -744,7 +657,7 @@ function mob_class:flop() return elseif self.state == "flop" then self.state = "stand" - self.object:set_acceleration(vector.zero()) + self.object:set_acceleration(vector_zero()) self:set_velocity(0) end end @@ -754,7 +667,7 @@ function mob_class:go_to_pos(b) if not self then return end if not b then return end local s = self.object:get_pos() - if vector.distance(b,s) < .5 then return true end + if vector_distance(b,s) < .5 then return true end if b.y > s.y then self:do_jump() end self:turn_in_direction(b.x - s.x, b.z - s.z, 2) self:set_velocity(self.walk_velocity) @@ -784,18 +697,11 @@ function mob_class:check_herd(dtime) end function mob_class:teleport(target) - if self.do_teleport then - if self.do_teleport(self, target) == false then - return - end - end + if self.do_teleport then return self.do_teleport(self, target) end end function mob_class:animate_walk_or_fly() - if self:flight_check() - and self.animation - and self.animation.fly_start - and self.animation.fly_end then + 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") @@ -855,18 +761,13 @@ function mob_class:do_states_walk() self:stand() self:turn_by(PI * (random() - 0.5), 6) return - elseif logging then - minetest.log("action", "[mcl_mobs] "..self.name.." ignores the danger "..tostring(danger)) end end -- If mob in or on dangerous block, look for land if self:is_node_dangerous(self.standing_in) or self:is_node_waterhazard(self.standing_in) or not self.fly and (self:is_node_dangerous(self.standing_on) or self:is_node_waterhazard(self.standing_on)) then -- Better way to find shore - copied from upstream - local lp = minetest.find_nodes_in_area_under_air( - vector.new(s.x - 5, s.y - 0.5, s.z - 5), - vector.new(s.x + 5, s.y + 1, s.z + 5), - {"group:solid"}) + local lp = minetest.find_nodes_in_area_under_air(vector_offset(s, -5, -0.5, -5), vector_offset(s, 5, 1, 5), {"group:solid"}) -- TODO: use node with smallest change in yaw? lp = #lp > 0 and lp[random(#lp)] @@ -891,13 +792,13 @@ function mob_class:do_states_walk() -- facing wall? then turn local facing_wall = false local cbox = self.collisionbox - local dir_x = -sin(yaw - PIQUARTER) * (cbox[4] + 0.5) - local dir_z = cos(yaw - PIQUARTER) * (cbox[4] + 0.5) - local nodface = node_ok({ x = s.x + dir_x, y = s.y + cbox[5] - cbox[2], z = s.z + dir_z }) + local dir_x = -sin(yaw - QUARTERPI) * (cbox[4] + 0.5) + local dir_z = cos(yaw - QUARTERPI) * (cbox[4] + 0.5) + local nodface = node_ok(vector_offset(s, dir_x, cbox[5] - cbox[2], dir_z)) if minetest.registered_nodes[nodface.name] and minetest.registered_nodes[nodface.name].walkable == true then - dir_x = -sin(yaw + PIQUARTER) * (cbox[4] + 0.5) - dir_z = cos(yaw + PIQUARTER) * (cbox[4] + 0.5) - nodface = node_ok({ x = s.x + dir_x, y = s.y + cbox[5] - cbox[2], z = s.z + dir_z }) + dir_x = -sin(yaw + QUARTERPI) * (cbox[4] + 0.5) + dir_z = cos(yaw + QUARTERPI) * (cbox[4] + 0.5) + nodface = node_ok(vector_offset(s, dir_x, cbox[5] - cbox[2], dir_z)) if minetest.registered_nodes[nodface.name] and minetest.registered_nodes[nodface.name].walkable == true then facing_wall = true end @@ -913,7 +814,7 @@ function mob_class:do_states_walk() if home and random() < 0.1 then self:turn_in_direction(home.x - s.x, home.z - s.z, 8) else - self:turn_by(PIQUARTER * (random() - 0.5), 10) + self:turn_by(QUARTERPI * (random() - 0.5), 10) end end self:set_velocity(self.walk_velocity) @@ -941,7 +842,7 @@ function mob_class:do_states_stand(player_in_active_range) if home and random() < 0.3 then self:turn_in_direction(home.x - s.x, home.z - s.z, 8) else - self:turn_by(PIHALF * (random() - 0.5), 10) + self:turn_by(HALFPI * (random() - 0.5), 10) end end end diff --git a/mods/ENTITIES/mcl_mobs/physics.lua b/mods/ENTITIES/mcl_mobs/physics.lua index d914ee39a..df7ee5dc7 100644 --- a/mods/ENTITIES/mcl_mobs/physics.lua +++ b/mods/ENTITIES/mcl_mobs/physics.lua @@ -7,8 +7,17 @@ local CRAMMING_DAMAGE = 3 local DEATH_DELAY = 0.5 local DEFAULT_FALL_SPEED = -9.81*1.5 local PI = math.pi -local TWOPI = 2 * PI +local HALFPI = 0.5 * PI +local TWOPI = 2 * PI -- aka tau, but not very common +local random = math.random +local min = math.min +local max = math.max +local floor = math.floor +local abs = math.abs local atan2 = math.atan2 +local sin = math.sin +local cos = math.cos +local node_ok = mcl_mobs.node_ok local PATHFINDING = "gowp" local mobs_debug = minetest.settings:get_bool("mobs_debug", false) @@ -16,20 +25,6 @@ local mobs_drop_items = minetest.settings:get_bool("mobs_drop_items") ~= false local mob_active_range = tonumber(minetest.settings:get("mcl_mob_active_range")) or 48 local show_health = false --- get node but use fallback for nil or unknown -local node_ok = function(pos, fallback) - - fallback = fallback or mcl_mobs.fallback_node - - local node = minetest.get_node_or_nil(pos) - - if node and minetest.registered_nodes[node.name] then - return node - end - - return minetest.registered_nodes[fallback] -end - -- check if within physical map limits (-30911 to 30927) local function within_limits(pos, radius) local wmin, wmax = -30912, 30928 @@ -111,24 +106,21 @@ function mob_class:item_drop(cooked, looting_level) local num = 0 local do_common_looting = (looting_level > 0 and looting_type == "common") - if math.random() < chance then - num = math.random(dropdef.min or 1, dropdef.max or 1) + if random() < chance then + num = random(dropdef.min or 1, dropdef.max or 1) elseif not dropdef.looting_ignore_chance then do_common_looting = false end if do_common_looting then - num = num + math.floor(math.random(0, looting_level) + 0.5) + num = num + floor(random(0, looting_level) + 0.5) end if num > 0 then item = dropdef.name if cooked then - - local output = minetest.get_craft_result({ - method = "cooking", width = 1, items = {item}}) - + local output = minetest.get_craft_result({method = "cooking", width = 1, items = {item}}) if output and output.item and not output.item:is_empty() then item = output.item:get_name() end @@ -136,12 +128,12 @@ function mob_class:item_drop(cooked, looting_level) for x = 1, num do obj = minetest.add_item(pos, ItemStack(item .. " " .. 1)) - end - if obj and obj:get_luaentity() then - obj:set_velocity(vector.new((math.random() - 0.5) * 1.5, 6, (math.random() - 0.5) * 1.5)) - elseif obj then - obj:remove() -- item does not exist + if obj and obj:get_luaentity() then + obj:set_velocity(vector.new((random() - 0.5) * 1.5, 6, (random() - 0.5) * 1.5)) + elseif obj then + obj:remove() -- item does not exist + end end end end @@ -199,22 +191,18 @@ function mob_class:set_velocity(v) end if v > 0 then local yaw = (self.object:get_yaw() or 0) + self.rotate - local x = ((-math.sin(yaw) * v) + c_x) * .4 - local z = (( math.cos(yaw) * v) + c_z) * .4 + local x = ((-sin(yaw) * v) + c_x) * .4 + local z = (( cos(yaw) * v) + c_z) * .4 if not self.acc then self.acc = vector.new(x, 0, z) else - self.acc.x = x - self.acc.y = 0 - self.acc.z = z + self.acc.x, self.acc.y, self.acc.z = x, 0, z end else -- allow standing mobs to be pushed if not self.acc then self.acc = vector.new(c_x * .2, 0, c_z * .2) else - self.acc.x = c_x * .2 - self.acc.y = 0 - self.acc.z = c_z * .2 + self.acc.x, self.acc.y, self.acc.z = c_x * .2, 0, c_z * .2 end end end @@ -237,7 +225,7 @@ function mob_class:update_roll() local cbox = table.copy(self.collisionbox) local acbox = self.object:get_properties().collisionbox - if math.abs(cbox[2] - acbox[2]) > 0.1 then + if abs(cbox[2] - acbox[2]) > 0.1 then was_Fleckenstein = true end @@ -264,7 +252,7 @@ function mob_class:turn_by(angle, delay, dtime) end -- Turn into a direction (e.g., to the player, or away) function mob_class:turn_in_direction(dx, dz, delay, dtime) - if math.abs(dx) == 0 and math.abs(dz) == 0 then return self.object:get_yaw() + self.rotate end + if abs(dx) == 0 and abs(dz) == 0 then return self.object:get_yaw() + self.rotate end return self:set_yaw(-atan2(dx, dz) - self.rotate, delay, dtime) end -- set and return valid yaw @@ -282,6 +270,7 @@ function mob_class:check_smooth_rotation(dtime) self:set_yaw(self._turn_to, .1) self._turn_to = nil end + if not self.target_yaw then return end local delay = self.delay local initial_yaw = self.object:get_yaw() or 0 @@ -299,7 +288,7 @@ function mob_class:check_smooth_rotation(dtime) end --[[ needed? if self.acc then local change = yaw - initial_yaw - local si, co = math.sin(change), math.cos(change) + local si, co = sin(change), cos(change) self.acc.x, self.acc.y = co * self.acc.x - si * self.acc.y, si * self.acc.x + co * self.acc.y end ]]-- self.object:set_yaw(yaw) @@ -416,7 +405,7 @@ function mob_class:check_for_death(cause, cmi_cause) if ((not self.child) or self.type ~= "animal") and (minetest.get_us_time() - self.xp_timestamp <= math.huge) then local pos = self.object:get_pos() - local xp_amount = math.random(self.xp_min, self.xp_max) + local xp_amount = random(self.xp_min, self.xp_max) if not mcl_sculk.handle_death(pos, xp_amount) then --minetest.log("Xp not thrown") @@ -489,7 +478,7 @@ function mob_class:check_for_death(cause, cmi_cause) elseif self.animation and self.animation.die_start and self.animation.die_end then local frames = self.animation.die_end - self.animation.die_start local speed = self.animation.die_speed or 15 - length = math.max(frames / speed, 0) + DEATH_DELAY + length = max(frames / speed, 0) + DEATH_DELAY self:set_animation( "die") else length = 1 + DEATH_DELAY @@ -718,7 +707,7 @@ function mob_class:do_env_damage() end if drowning then - self.breath = math.max(0, self.breath - 1) + self.breath = max(0, self.breath - 1) mcl_mobs.effect(pos, 2, "bubble.png", nil, nil, 1, nil) if self.breath <= 0 then local dmg @@ -735,7 +724,7 @@ function mob_class:do_env_damage() return true end else - self.breath = math.min(self.breath_max, self.breath + 1) + self.breath = min(self.breath_max, self.breath + 1) end end @@ -801,7 +790,7 @@ end function mob_class:damage_mob(reason,damage) if not self.health then return end - damage = math.floor(damage) + damage = floor(damage) if damage > 0 then self.health = self.health - damage @@ -850,49 +839,42 @@ function mob_class:falling(pos, moveresult) if self.fly and self.state ~= "die" then return end if not self.fall_speed then self.fall_speed = DEFAULT_FALL_SPEED end + -- Gravity + local v = self.object:get_velocity() + if v then + if v.y > 0 or (v.y <= 0 and v.y > self.fall_speed) then + -- fall downwards at set speed + if moveresult and moveresult.touching_ground then + -- when touching ground, retain a minimal gravity to keep the touching_ground flag + -- but also to not get upwards acceleration with large dtime when on bouncy ground + self.object:set_acceleration(vector.new(0, self.fall_speed * 0.01, 0)) + else + self.object:set_acceleration(vector.new(0, self.fall_speed, 0)) + end + else + -- stop accelerating once max fall speed hit + self.object:set_acceleration(vector.zero()) + end + end + if mcl_portals ~= nil then if mcl_portals.nether_portal_cooloff(self.object) then return false -- mob has teleported through Nether portal - it's 99% not falling end end - -- floating in water (or falling) - local v = self.object:get_velocity() - if v then - local new_acceleration - if v.y > 0 and v.y < -0.5 * DEFAULT_FALL_SPEED then - -- when moving up, always use gravity - new_acceleration = vector.new(0, 0.5 * DEFAULT_FALL_SPEED, 0) - elseif v.y <= 0 and v.y > 0.5 * self.fall_speed then - -- fall downwards at set speed - if moveresult and moveresult.touching_ground then - -- when touching ground, retain a minimal gravity to keep the touching_ground flag - -- but also to not get upwards acceleration with large dtime when on bouncy ground - new_acceleration = vector.new(0, self.fall_speed * 0.01, 0) - else - new_acceleration = vector.new(0, self.fall_speed, 0) - end - else - -- stop accelerating once max fall speed hit - new_acceleration = vector.zero() - end - self.object:set_acceleration(new_acceleration) - end - - local acc = self.object:get_acceleration() - 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(vector.new(0, -self.fall_speed / math.max(1, v.y^2), 0)) + if self.floats_on_lava == 1 then + self.object:set_acceleration(vector.new(0, -self.fall_speed / max(1, v.y^2), 0)) end end -- in water then float up if registered_node.groups.water then - if acc and self.floats == 1 and minetest.registered_nodes[node_ok(vector.offset(pos,0,self.collisionbox[5] -0.25,0)).name].groups.water then - self.object:set_acceleration(vector.new(0, -self.fall_speed / math.max(1, v.y^2), 0)) + if self.floats == 1 and minetest.registered_nodes[node_ok(vector.offset(pos,0,self.collisionbox[5] -0.25,0)).name].groups.water then + self.object:set_acceleration(vector.new(0, -self.fall_speed / max(1, v.y^2), 0)) end else -- fall damage onto solid ground @@ -952,7 +934,7 @@ function mob_class:check_dying() if ((self.state and self.state=="die") or self:check_for_death()) and not self.animation.die_end then local rot = self.object:get_rotation() if rot then - rot.z = ((math.pi/2-rot.z)*.2)+rot.z + rot.z = ((HALFPI - rot.z) * .2) + rot.z self.object:set_rotation(rot) end return true