diff --git a/mods/ENTITIES/mcl_mobs/api.lua b/mods/ENTITIES/mcl_mobs/api.lua index ea636bd39..4fc6ab246 100644 --- a/mods/ENTITIES/mcl_mobs/api.lua +++ b/mods/ENTITIES/mcl_mobs/api.lua @@ -1,5 +1,4 @@ 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 local PATHFINDING = "gowp" @@ -152,19 +151,17 @@ function mob_class:mob_activate(staticdata, def, dtime) self.base_selbox = self.base_selbox or self.selectionbox or self.base_colbox - local textures = self.base_texture - local mesh = self.base_mesh - local vis_size = self.base_size - local colbox = self.base_colbox - local selbox = self.base_selbox + self.textures = self.gotten and def.gotten_texture or self.base_texture + self.mesh = self.gotten and def.gotten_mesh or self.base_mesh + self.visual_size = self.base_size + self.collisionbox = self.base_colbox + self.selectionbox = 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.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 + if self.child then + self.visual_size = { x = self.base_size.x * .5, y = self.base_size.y * .5 } + self.textures = def.child_texture and def.child_texture[1] or self.textures - colbox = { + self.collisionbox = { self.base_colbox[1] * .5, self.base_colbox[2] * .5, self.base_colbox[3] * .5, @@ -172,7 +169,7 @@ function mob_class:mob_activate(staticdata, def, dtime) self.base_colbox[5] * .5, self.base_colbox[6] * .5 } - selbox = { + self.selectionbox = { self.base_selbox[1] * .5, self.base_selbox[2] * .5, self.base_selbox[3] * .5, @@ -182,8 +179,8 @@ 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 + self.health = (self.health and self.health > 0 and self.health) or math.random(self.hp_min, self.hp_max) + self.breath = self.breath or self.breath_max self.path = {} self.path.way = {} -- path to follow, table of positions @@ -206,14 +203,9 @@ function mob_class:mob_activate(staticdata, def, dtime) self.old_y = self.object:get_pos().y self.old_health = self.health self.sounds.distance = self.sounds.distance or 10 - self.textures = textures - self.mesh = mesh - self.collisionbox = colbox - self.selectionbox = selbox - self.visual_size = vis_size - self.standing_in = NODE_IGNORE - self.standing_on = NODE_IGNORE - self.standing_under = NODE_IGNORE + self.standing_in = mcl_mobs.NODE_IGNORE + self.standing_on = mcl_mobs.NODE_IGNORE + self.standing_under = mcl_mobs.NODE_IGNORE self.standing_depth = 0 self.state = self.state or "stand" self.jump_sound_cooloff = 0 -- used to prevent jump sound from being played too often in short time @@ -317,6 +309,7 @@ local function on_step_work(self, dtime, moveresult) local player_in_active_range = self:player_in_active_range() -- The following functions return true when the mob died and we should stop processing if self:check_suspend(player_in_active_range) then return end + -- initializes self.acceleration: if self:gravity_and_floating(pos, dtime, moveresult) then return end -- keep early, for gravity! if self:check_dying() then return end if self:step_damage(dtime, pos) then return end @@ -360,7 +353,7 @@ local function on_step_work(self, dtime, moveresult) self:smooth_acceleration(dtime) local cx, cz = self:collision() self.object:add_velocity(vector.new(cx, 0, cz)) - self:update_vel_acc(dtime) + self:update_vel_acc(dtime) -- applies self.acceleration if mobs_debug then self:update_tag() end if not self.object:get_luaentity() then return false end end @@ -398,8 +391,12 @@ function mob_class:on_step(dtime, moveresult) -- 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) + local status, retVal + if xpcall then + status, retVal = xpcall(on_step_work, on_step_error_handler, self, dtime, moveresult) + else + status, retVal = pcall(on_step_work, self, dtime, moveresult) + end if status then return retVal end warn_user_error() local pos = self.object:get_pos() @@ -488,16 +485,16 @@ minetest.register_chatcommand("clearmobs", { elseif mob_type == "passive" and o.type ~= "monster" and o.type ~= "npc" then --minetest.log("Match - passive") mob_match = true - else - --minetest.log("No match for type.") + --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 - else - --minetest.log("No match - o.type = ".. tostring(o.type)) - --minetest.log("No match - mob_name = ".. tostring(o.name)) - --minetest.log("No match - mob_type = ".. tostring(mob_name)) + --else + -- minetest.log("No match - o.type = ".. tostring(o.type)) + -- minetest.log("No match - mob_name = ".. tostring(o.name)) + -- minetest.log("No match - mob_type = ".. tostring(mob_name)) end if mob_match then diff --git a/mods/ENTITIES/mcl_mobs/breeding.lua b/mods/ENTITIES/mcl_mobs/breeding.lua index 51fad75db..ea16efe31 100644 --- a/mods/ENTITIES/mcl_mobs/breeding.lua +++ b/mods/ENTITIES/mcl_mobs/breeding.lua @@ -5,7 +5,7 @@ local HORNY_TIME = 30 local HORNY_AGAIN_TIME = 30 -- was 300 or 15*20 local CHILD_GROW_TIME = 60 -local LOGGING_ON = minetest.settings:get_bool("mcl_logging_mobs_villager",false) +local LOGGING_ON = minetest.settings:get_bool("mcl_logging_mobs_villager", false) local LOG_MODULE = "[mcl_mobs]" local function mcl_log (message) @@ -29,18 +29,13 @@ end -- feeding, taming and breeding (thanks blert2112) function mob_class:feed_tame(clicker, feed_count, breed, tame, notake) - if not self.follow then - return false - end - if clicker:get_wielded_item():get_definition()._mcl_not_consumable then - return false - end + if not self.follow then return false end + if clicker:get_wielded_item():get_definition()._mcl_not_consumable then return false end -- can eat/tame with item in hand if self.nofollow or self:follow_holding(clicker) then local consume_food = false -- tame if not still a baby - if tame and not self.child then if not self.owner or self.owner == "" then self.tamed = true @@ -50,7 +45,6 @@ function mob_class:feed_tame(clicker, feed_count, breed, tame, notake) end -- increase health - if self.health < self.hp_max and not consume_food then consume_food = true self.health = math.min(self.health + 4, self.hp_max) @@ -58,15 +52,13 @@ function mob_class:feed_tame(clicker, feed_count, breed, tame, notake) end -- make children grow quicker - - if not consume_food and self.child == true then + if not consume_food and self.child then consume_food = true -- deduct 10% of the time to adulthood self.hornytimer = self.hornytimer + ((CHILD_GROW_TIME - self.hornytimer) * 0.1) end -- breed animals - if breed and not consume_food and self.hornytimer == 0 and not self.horny then self.food = (self.food or 0) + 1 consume_food = true @@ -102,24 +94,15 @@ end -- Spawn a child function mcl_mobs.spawn_child(pos, mob_type) local child = mcl_mobs.spawn(pos, mob_type) - if not child then - return - end + if not child then return end local ent = child:get_luaentity() - mcl_mobs.effect(pos, 15, "mcl_particles_smoke.png", 1, 2, 2, 15, 5) - ent.child = true - - local textures - -- using specific child texture (if found) - if ent.child_texture then - textures = ent.child_texture[1] - end + mcl_mobs.effect(pos, 15, "mcl_particles_smoke.png", 1, 2, 2, 15, 5) -- and resize to half height child:set_properties({ - textures = textures, + textures = ent.child_texture and ent.child_texture[1], visual_size = { x = ent.base_size.x * .5, y = ent.base_size.y * .5, @@ -151,16 +134,12 @@ end -- find two animals of same type and breed if nearby and horny function mob_class:check_breeding() - --mcl_log("In breed function") -- child takes a long time before growing into adult if self.child == true then - -- When a child, hornytimer is used to count age until adulthood self.hornytimer = self.hornytimer + 1 - if self.hornytimer >= CHILD_GROW_TIME then - self.child = false self.hornytimer = 0 @@ -177,11 +156,7 @@ function mob_class:check_breeding() self.on_grown(self) else -- jump when fully grown so as not to fall into ground - self.object:set_velocity({ - x = 0, - y = self.jump_height, - z = 0 - }) + self.object:set_velocity(vector.new(0, self.jump_height, 0)) end self.animation = nil @@ -189,121 +164,79 @@ function mob_class:check_breeding() self._current_animation = nil -- Mobs Redo does nothing otherwise self:set_animation(anim) end - return - else - -- horny animal can mate for HORNY_TIME seconds, - -- afterwards horny animal cannot mate again for HORNY_AGAIN_TIME seconds - if self.horny == true then - self.hornytimer = self.hornytimer + 1 + end + -- horny animal can mate for HORNY_TIME seconds, + -- afterwards horny animal cannot mate again for HORNY_AGAIN_TIME seconds + if self.horny == true then + self.hornytimer = self.hornytimer + 1 - if self.hornytimer >= HORNY_TIME + HORNY_AGAIN_TIME then - self.hornytimer = 0 - self.horny = false - end + if self.hornytimer >= HORNY_TIME + HORNY_AGAIN_TIME then + self.hornytimer = 0 + self.horny = false end end -- find another same animal who is also horny and mate if nearby - if self.horny == true - and self.hornytimer <= HORNY_TIME then - + if self.horny and self.hornytimer <= HORNY_TIME then mcl_log("In breed function. All good. Do the magic.") - local pos = self.object:get_pos() - - mcl_mobs.effect({x = pos.x, y = pos.y + 1, z = pos.z}, 8, "heart.png", 3, 4, 1, 0.1) + mcl_mobs.effect(vector.new(pos.x, pos.y + 1, pos.z), 8, "heart.png", 3, 4, 1, 0.1) local objs = minetest.get_objects_inside_radius(pos, 3) local num = 0 - local ent = nil for n = 1, #objs do - - ent = objs[n]:get_luaentity() + local ent = objs[n]:get_luaentity() -- check for same animal with different colour local canmate = false - if ent then - if ent.name == self.name then canmate = true else local entname = string.split(ent.name,":") local selfname = string.split(self.name,":") - if entname[1] == selfname[1] then entname = string.split(entname[2],"_") selfname = string.split(selfname[2],"_") - if entname[1] == selfname[1] then - canmate = true - end + if entname[1] == selfname[1] then canmate = true end end end end if canmate then mcl_log("In breed function. Can mate.") end - - if ent - and canmate == true - and ent.horny == true - and ent.hornytimer <= HORNY_TIME then + if ent and canmate and ent.horny and ent.hornytimer <= HORNY_TIME then num = num + 1 end -- found your mate? then have a baby if num > 1 then - self.hornytimer = HORNY_TIME + 1 ent.hornytimer = HORNY_TIME + 1 -- spawn baby - - minetest.after(5, function(parent1, parent2, pos) - if not parent1.object:get_luaentity() then - return - end - if not parent2.object:get_luaentity() then - return - end + if not parent1.object:get_luaentity() then return end + if not parent2.object:get_luaentity() then return end mcl_experience.throw_xp(pos, math.random(1, 7) + (parent1._luck or 0) + (parent2._luck or 0)) - -- custom breed function - if parent1.on_breed then - -- when false, skip going any further - if parent1.on_breed(parent1, parent2) == false then - return - end - end - + if parent1.on_breed and not parent1.on_breed(parent1, parent2) then return end + pos = vector.round(pos) local child = mcl_mobs.spawn_child(pos, parent1.name) if not child then return end local ent_c = child:get_luaentity() - - -- Use texture of one of the parents - local p = math.random(1, 2) - if p == 1 then - ent_c.base_texture = parent1.base_texture - else - ent_c.base_texture = parent2.base_texture - end - child:set_properties({ - textures = ent_c.base_texture - }) + ent_c.base_texture = math.random(1, 2) == 1 and parent1.base_texture or parent2.base_texture + child:set_properties({ textures = ent_c.base_texture }) -- tamed and owned by parents' owner ent_c.tamed = true ent_c.owner = parent1.owner end, self, ent, pos) - - num = 0 - break end end @@ -311,9 +244,7 @@ function mob_class:check_breeding() end function mob_class:toggle_sit(clicker,p) - if not self.tamed or self.child or self.owner ~= clicker:get_player_name() then - return - end + if not self.tamed or self.child or self.owner ~= clicker:get_player_name() then return end local pos = self.object:get_pos() local particle if not self.order or self.order == "" or self.order == "sit" then @@ -341,7 +272,7 @@ function mob_class:toggle_sit(clicker,p) -- Display icon to show current order (sit or roam) minetest.add_particle({ pos = vector.add(pos, pp), - velocity = {x=0,y=0.2,z=0}, + velocity = vector.new(0, 0.2, 0), expirationtime = 1, size = 4, texture = particle, diff --git a/mods/ENTITIES/mcl_mobs/combat.lua b/mods/ENTITIES/mcl_mobs/combat.lua index 23dc17c88..b8b9b722b 100644 --- a/mods/ENTITIES/mcl_mobs/combat.lua +++ b/mods/ENTITIES/mcl_mobs/combat.lua @@ -27,10 +27,12 @@ local vector_offset = vector.offset local vector_new = vector.new local vector_copy = vector.copy local vector_distance = vector.distance +local vector_zero = vector.zero +local node_ok = mcl_mobs.node_ok -- TODO: remove -- check if daytime and also if mob is docile during daylight hours function mob_class:day_docile() - return self.docile_by_day == true and self.time_of_day > 0.2 and self.time_of_day < 0.8 + return self.docile_by_day and self.time_of_day > 0.2 and self.time_of_day < 0.8 end -- get this mob to attack the object @@ -60,7 +62,7 @@ local function entity_physics(pos, radius) if dist < 1 then dist = 1 end local damage = floor((4 / dist) * radius) - local ent = objs[n]:get_luaentity() + --local ent = objs[n]:get_luaentity() -- punches work on entities AND players objs[n]:punch(objs[n], 1.0, { @@ -143,7 +145,7 @@ function mob_class:smart_mobs(s, p, dist, dtime) if use_pathfind then -- lets try find a path, first take care of positions -- since pathfinder is very sensitive - local sheight = self.collisionbox[5] - self.collisionbox[2] + --local sheight = self.collisionbox[5] - self.collisionbox[2] -- round position to center of node to avoid stuck in walls -- also adjust height for player models! @@ -381,7 +383,7 @@ function mob_class:npc_attack() p.y = p.y + 1 sp.y = sp.y + 1 - 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, 2) == true then min_dist = dist min_player = obj.object end @@ -605,7 +607,7 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir) local v = self.object:get_velocity() if not v then return end local r = 1.4 - min(punch_interval, 1.4) - local kb = r * (abs(v.x)+abs(v.z)) + local kb = r * sqrt(v.x*v.x+v.z*v.z) local up = 2.625 if die then kb = kb * 1.25 end @@ -633,7 +635,7 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir) kb = kb + (abs(hv.x) + abs(hv.z)) * r end elseif luaentity and luaentity._knockback and die == false then - kb = kb + luaentity._knockback + kb = kb + luaentity._knockback * 0.25 elseif luaentity and luaentity._knockback and die == true then kb = kb + luaentity._knockback * 0.25 end @@ -695,11 +697,8 @@ function mob_class:on_punch(hitter, tflp, tool_capabilities, dir) local alert_pos = hitter:get_pos() if alert_pos then local objs = minetest.get_objects_inside_radius(alert_pos, self.view_range) - local obj = nil - for n = 1, #objs do - obj = objs[n]:get_luaentity() - + local obj = objs[n]:get_luaentity() if obj then -- only alert members of same mob or friends if obj.group_attack @@ -768,17 +767,16 @@ function mob_class:do_states_attack(dtime) 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() + if not s or not p 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 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 + or (self.attack:is_player() and mcl_mobs.invis[self.attack:get_player_name()]) then clear_aggro(self) return @@ -955,7 +953,7 @@ function mob_class:do_states_attack(dtime) self.path.stuck_timer = 0 self.path.following = false -- not stuck anymore - self:set_velocity( 0) + self:set_velocity(0) local attack_frequency = self.attack_frequency or 1 @@ -997,21 +995,23 @@ function mob_class:do_states_attack(dtime) or (self.attack_type == "dogshoot" and (dist > self.reach or dist < self.avoid_distance and self.shooter_avoid_enemy) and self:dogswitch() == 0) then local vec = vector_new(p.x - s.x, p.y - s.y - 1, p.z - s.z) local dist = sqrt(vec.x*vec.x + vec.y*vec.y + vec.z*vec.z) - self:turn_in_direction(vec.x, vec.z, 1) + local dir = -atan2(p.x - s.x, p.z - s.z) + self:set_yaw(dir, 4) if self.strafes then - if not self.strafe_direction then self.strafe_direction = HALFPI end - if random(40) == 1 then self.strafe_direction = self.strafe_direction * -1 end + if not self.strafe_direction then self.strafe_direction = math.random(0, 1) * 2 - 1 end + if random(50) == 1 then self.strafe_direction = -self.strafe_direction end - local dir = -atan2(p.x - s.x, p.z - s.z) - self.acceleration.x = self.acceleration.x - sin(dir + self.strafe_direction) * 8 - self.acceleration.z = self.acceleration.z + cos(dir + self.strafe_direction) * 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 = (self.avoid_distance - dist) / self.avoid_distance - --self:set_velocity(f * self.walk_velocity) --self.object:add_velocity(vector_new(-sin(dir) * f, 0, cos(dir) * f)) - self.acceleration.x = self.acceleration.x - sin(dir) * f * 8 - self.acceleration.z = self.acceleration.z + cos(dir) * f * 8 + if self.avoid_distance and self.shooter_avoid_enemy then + local f = (dist - self.avoid_distance) / self.avoid_distance + f = math.max(-1, math.min(1, f)) + f = f * math.abs(f) + self:set_velocity(f * self.walk_velocity, (1 - math.abs(f)) * self.strafe_direction * self.walk_velocity * 1.5) + elseif dist > 1 then + self:set_velocity(self.walk_velocity) + else + self:set_velocity(0) end else self:set_velocity(0) diff --git a/mods/ENTITIES/mcl_mobs/effects.lua b/mods/ENTITIES/mcl_mobs/effects.lua index 3f1d7605e..e625cf1ff 100644 --- a/mods/ENTITIES/mcl_mobs/effects.lua +++ b/mods/ENTITIES/mcl_mobs/effects.lua @@ -1,6 +1,6 @@ local math, tonumber, vector, minetest, mcl_mobs = math, tonumber, vector, minetest, mcl_mobs local mob_class = mcl_mobs.mob_class -local validate_vector = mcl_util.validate_vector +--local validate_vector = mcl_util.validate_vector local active_particlespawners = {} local disable_blood = minetest.settings:get_bool("mobs_disable_blood") @@ -128,7 +128,7 @@ function mob_class:remove_texture_mod(mod) table.insert(remove, i) end end - for i=#remove, 1 do + for i=#remove, 1, -1 do table.remove(self.texture_mods, remove[i]) end self.object:set_texture_mod(full_mod) @@ -207,7 +207,7 @@ function mob_class:set_animation(anim, fixed_frame) elseif not self.object:get_attach() then self.jockey = nil end - + if self.state == "die" and anim ~= "die" and anim ~= "stand" then return end if self.fly and self:flight_check() and anim == "walk" then anim = "fly" end @@ -275,7 +275,8 @@ function mob_class:check_head_swivel(dtime) who_are_you_looking_at(self, dtime) - local newr, oldp, oldr = vector.zero(), nil, nil + local newr = vector.zero() + local oldp, oldr if self.object.get_bone_override then -- minetest >= 5.9 local ov = self.object:get_bone_override(self.head_swivel) oldp, oldr = ov.position.vec, ov.rotation.vec @@ -327,7 +328,7 @@ function mob_class:check_head_swivel(dtime) elseif not locked_object and math.abs(oldr.y) > 0.05 and math.abs(oldr.x) < 0.05 then newr = vector.multiply(oldr, 0.9) end - + -- 0.02 is about 1.14 degrees tolerance, to update less often local newp = vector.new(0, self.bone_eye_height, self.horizontal_head_height) if math.abs(oldr.x-newr.x) + math.abs(oldr.y-newr.y) + math.abs(oldr.z-newr.z) < 0.02 and vector.equals(oldp, newp) then return end diff --git a/mods/ENTITIES/mcl_mobs/init.lua b/mods/ENTITIES/mcl_mobs/init.lua index 6cef7e1d2..4edd846ff 100644 --- a/mods/ENTITIES/mcl_mobs/init.lua +++ b/mods/ENTITIES/mcl_mobs/init.lua @@ -38,7 +38,7 @@ local function line_of_sight(origin, target, see_through_opaque, liquids) end mcl_mobs.line_of_sight = line_of_sight -local NODE_IGNORE = { name = "ignore", groups = {} } -- fallback for unknown nodes +mcl_mobs.NODE_IGNORE = { name = "ignore", groups = {} } -- fallback for unknown nodes --api and helpers -- effects: sounds and particles mostly @@ -417,7 +417,6 @@ function mcl_mobs.register_arrow(name, def) hit_object = def.hit_object, homing = def.homing, drop = def.drop or false, -- drops arrow as registered item when true - collisionbox = {0, 0, 0, 0, 0, 0}, -- remove box around arrows timer = 0, switch = 0, _lifetime = def._lifetime or 7, diff --git a/mods/ENTITIES/mcl_mobs/items.lua b/mods/ENTITIES/mcl_mobs/items.lua index f2ec4dedd..7365d6168 100644 --- a/mods/ENTITIES/mcl_mobs/items.lua +++ b/mods/ENTITIES/mcl_mobs/items.lua @@ -1,4 +1,4 @@ -local math, vector, minetest, mcl_mobs = math, vector, minetest, mcl_mobs +local minetest, mcl_mobs = minetest, mcl_mobs local mob_class = mcl_mobs.mob_class --- Item and armor management diff --git a/mods/ENTITIES/mcl_mobs/mount.lua b/mods/ENTITIES/mcl_mobs/mount.lua index 826bc5451..7ec7386b2 100644 --- a/mods/ENTITIES/mcl_mobs/mount.lua +++ b/mods/ENTITIES/mcl_mobs/mount.lua @@ -126,7 +126,7 @@ function mcl_mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime) -- jump if ctrl.jump then if velo.y == 0 then - velo.y = velo.y + sqrt(entity.jump_height * 20) + velo.y = velo.y + math.sqrt(entity.jump_height * 20) acce_y = acce_y + 1 end end diff --git a/mods/ENTITIES/mcl_mobs/movement.lua b/mods/ENTITIES/mcl_mobs/movement.lua index c2f91c7c6..f402d54ae 100644 --- a/mods/ENTITIES/mcl_mobs/movement.lua +++ b/mods/ENTITIES/mcl_mobs/movement.lua @@ -8,7 +8,7 @@ local CHECK_HERD_FREQUENCY = 4 local PATHFINDING = "gowp" -local node_snow = "mcl_core:snow" +local NODE_SNOW = "mcl_core:snow" local logging = minetest.settings:get_bool("mcl_logging_mobs_movement", true) local mobs_griefing = minetest.settings:get_bool("mobs_griefing", true) @@ -16,21 +16,16 @@ local mobs_griefing = minetest.settings:get_bool("mobs_griefing", true) local random = math.random 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 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 node_ok = mcl_mobs.node_ok local mobs_see_through_opaque = mcl_mobs.see_through_opaque local line_of_sight = mcl_mobs.line_of_sight @@ -250,16 +245,16 @@ function mob_class:do_jump() -- what is in front of mob? local nod = minetest.get_node(vector_offset(pos, dir_x, 0.5, dir_z)).name - if nod == node_snow then return false end -- no need to jump + if nod == NODE_SNOW then return false end -- no need to jump -- 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 node_top = minetest.get_node(vector_offset(pos, dir_x, 1.5, dir_z)).name - -- TODO: also check above the mob itself? + -- TODO: also check above the mob itself, and check the full mob height? -- we don't attempt to jump if there's a stack of blocks blocking, unless attacking local ntdef = minetest.registered_nodes[node_top] - if ntdef and ntdef.walkable == true and not (self.attack and self.state == "attack") then return false end + if ntdef and ntdef.walkable == true --[[and not (self.attack and self.state == "attack")]] then return false end local ndef = minetest.registered_nodes[nod] if self.walk_chance ~= 0 and not (ndef and ndef.walkable) and not self._can_jump_cliff then return false end @@ -350,7 +345,6 @@ function mob_class:replace_node(pos) on_replace_return = self.on_replace(self, pos, oldnode, newnode) end - if on_replace_return ~= false then if mobs_griefing then minetest.after(self.replace_delay, function() @@ -381,40 +375,32 @@ function mob_class:check_runaway_from() if not self.runaway_from and self.state ~= "flop" then return end local s = self.object:get_pos() - local p, sp, dist - local player, obj, min_player - local type, name = "", "" - local min_dist = self.view_range + 1 + local min_dist, min_player = self.view_range + 1, nil local objs = minetest.get_objects_inside_radius(s, self.view_range) for n = 1, #objs do + local name, player = "", nil 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 - type = "" + 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 + name = "" else player = objs[n] - type = "player" name = "player" end else - obj = objs[n]:get_luaentity() + local obj = objs[n]:get_luaentity() if obj then player = obj.object - type = obj.type name = obj.name or "" end end -- 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 - dist = vector_distance(p, s) + if name ~= "" and name ~= self.name and specific_runaway(self.runaway_from, name) then + local p = player:get_pos() + local dist = vector_distance(p, s) -- choose closest player/mpb to runaway from - if dist < min_dist and line_of_sight(vector_offset(sp, 0, 1, 0), vector_offset(p, 0, 1, 0), self.see_through_opaque or mobs_see_through_opaque, false) then + if dist < min_dist and line_of_sight(vector_offset(s, 0, 1, 0), vector_offset(p, 0, 1, 0), self.see_through_opaque or mobs_see_through_opaque, false) then -- aim higher to make looking up hills more realistic min_dist = dist min_player = player @@ -423,7 +409,7 @@ function mob_class:check_runaway_from() end if min_player then - local lp = player:get_pos() + local lp = min_player:get_pos() self:turn_in_direction(s.x - lp.x, s.z - lp.z, 4) -- away from player self.state = "runaway" self.runaway_timer = 3 @@ -439,10 +425,9 @@ function mob_class:check_follow() and self.state ~= "attack" and self.order ~= "sit" and self.state ~= "runaway" then - local s = self.object:get_pos() local players = minetest.get_connected_players() for n = 1, #players do - if (self:object_in_range(players[n])) and not mcl_mobs.invis[ players[n]:get_player_name() ] then + if self:object_in_range(players[n]) and not mcl_mobs.invis[players[n]:get_player_name()] then self.following = players[n] break end @@ -557,7 +542,7 @@ function mob_class:do_states_walk() -- Better way to find shore - copied from upstream local lp = minetest.find_nodes_in_area_under_air(vector_offset(s, -5, -0.5, -5), vector_offset(s, 5, 1, 5), {"group:solid"}) if #lp == 0 then - local lp = minetest.find_nodes_in_area_under_air(vector_offset(s, -10, -0.5, -10), vector_offset(s, 10, 1, 10), {"group:solid"}) + lp = minetest.find_nodes_in_area_under_air(vector_offset(s, -10, -0.5, -10), vector_offset(s, 10, 1, 10), {"group:solid"}) end -- TODO: use node with smallest change in yaw instead of random? lp = #lp > 0 and lp[random(#lp)] @@ -582,7 +567,7 @@ function mob_class:do_states_walk() -- facing wall? then turn local facing_wall = false -- todo: use moveresult collision info here? - if moveresult and moveresult.collides and self:get_velocity_xyz() < 0.1 then + if self.collides and self:get_velocity_xyz() < 0.1 then facing_wall = true else --if not facing_wall then local cbox = self.collisionbox diff --git a/mods/ENTITIES/mcl_mobs/physics.lua b/mods/ENTITIES/mcl_mobs/physics.lua index 3d4f74ea1..345f3c19e 100644 --- a/mods/ENTITIES/mcl_mobs/physics.lua +++ b/mods/ENTITIES/mcl_mobs/physics.lua @@ -1,6 +1,6 @@ local math, vector, minetest, mcl_mobs = math, vector, minetest, mcl_mobs local mob_class = mcl_mobs.mob_class -local validate_vector = mcl_util.validate_vector +--local validate_vector = mcl_util.validate_vector local MAX_DTIME = 0.25 -- todo: make user configurable? local ACCELERATION_MIX = 1.0 -- how much of acceleration to handle in Lua instead of MTE todo: make user configurable @@ -20,9 +20,10 @@ local atan2 = math.atan2 local sin = math.sin local cos = math.cos local sqrt = math.sqrt -local node_ok = mcl_mobs.node_ok +--local node_ok = mcl_mobs.node_ok +local NODE_IGNORE = mcl_mobs.NODE_IGNORE -local PATHFINDING = "gowp" +--local PATHFINDING = "gowp" 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 @@ -46,10 +47,11 @@ end -- these may be "nil" (= ignore) and are otherwise already resolved via minetest.registered_nodes function mob_class:update_standing(pos, moveresult) local temp_pos = vector.offset(pos, 0, self.collisionbox[2] + 0.5, 0) -- foot level + self.collides = moveresult and moveresult.collides self.standing_in = minetest.registered_nodes[minetest.get_node(temp_pos).name] or NODE_IGNORE temp_pos.y = temp_pos.y - 1.5 -- below self.standing_on_node = minetest.get_node(temp_pos) -- to allow access to param2 in, e.g., stalker - self.standing_on = standing_on or minetest.registered_nodes[self.standing_on_node.name] or NODE_IGNORE + self.standing_on = self.standing_on_node and minetest.registered_nodes[self.standing_on_node.name] or NODE_IGNORE -- sometimes, we may be colliding with a node *not* below us, effectively standing on it instead (e.g., a corner) if not self.standing_on.walkable and moveresult and moveresult.collisions then -- to inspect: minetest.log("action", dump(moveresult):gsub(" *\n\\s*","")) @@ -96,13 +98,10 @@ end function mob_class:item_drop(cooked, looting_level) if not mobs_drop_items then return end + if self.child and self.type ~= "monster" then return end looting_level = looting_level or 0 - if (self.child and self.type ~= "monster") then return end - - local obj, item, num local pos = self.object:get_pos() - self.drops = self.drops or {} for n = 1, #self.drops do @@ -132,7 +131,7 @@ function mob_class:item_drop(cooked, looting_level) end if num > 0 then - item = dropdef.name + local item = dropdef.name if cooked then local output = minetest.get_craft_result({method = "cooking", width = 1, items = {item}}) @@ -142,8 +141,7 @@ function mob_class:item_drop(cooked, looting_level) end for x = 1, num do - obj = minetest.add_item(pos, ItemStack(item .. " 1")) - + local obj = minetest.add_item(pos, ItemStack(item .. " 1")) 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 @@ -184,8 +182,11 @@ function mob_class:collision() end -- move mob in facing direction -function mob_class:set_velocity(v) +-- @param v number: Velocity in direction the mob is facing +-- @param o number, optional: Orthogonal velocity, for strafing +function mob_class:set_velocity(v, o) self.target_vel = v + self.target_orth = o end -- calculate mob velocity (3d) @@ -238,7 +239,7 @@ function mob_class:set_yaw(yaw, delay, dtime) end -- name tag easter egg, test engine capabilities for rolling -local function update_roll() +--[[local function update_roll(self) local is_Fleckenstein = self.nametag == "Fleckenstein" if not is_Fleckenstein and not self.is_Fleckenstein then return end @@ -259,7 +260,7 @@ local function update_roll() self.object:set_pos(pos) end self.is_Fleckenstein = is_Fleckenstein -end +end]] -- Improved smooth rotation -- @param dtime number: timestep length @@ -281,7 +282,7 @@ function mob_class:smooth_rotation(dtime) yaw = yaw + (random() * 2 - 1) / 72 * dtime end if yaw ~= initial_yaw then self.object:set_yaw(yaw - self.rotate) end - --update_roll() -- Fleckenstein easter egg + --update_roll(self) -- Fleckenstein easter egg end -- Handling of intentional acceleration by the mob @@ -289,11 +290,13 @@ end -- TODO: have mobs that acccelerate faster and that accelerate slower? -- @param dtime number: timestep length function mob_class:smooth_acceleration(dtime) - local yaw = self.target_yaw or (self.object:get_yaw() or 0) + self.rotate - local vel = self.target_vel or 0 - local x, z = -sin(yaw) * vel, cos(yaw) * vel + local yaw = self.move_yaw or self.target_yaw or (self.object:get_yaw() or 0) + self.rotate + local vel = self.target_vel or 0 -- can also stop + local x, z = -sin(yaw), cos(yaw) + local orth = self.target_orth or 0 -- strafing + x, z = x * vel + z * orth, z * vel + x * orth local v = self.object:get_velocity() - local w = min(dtime * 5, 1) + local w = 10 * min(dtime, 0.1) -- 0.1 time to fully change direction / accelerate v.x, v.z = v.x + w * (x - v.x), v.z + w * (z - v.z) self.object:set_velocity(v) end @@ -796,7 +799,6 @@ function mob_class:limit_vel_acc_for_large_dtime(pos, dtime, moveresult) end --- Update velocity and acceleration at the end of our movement logic --- function mob_class:update_vel_acc(dtime) local vel = self.object:get_velocity() --vel.x, vel.y, vel.z = vel.x * visc, (vel.y + acc.y * dtime) * visc, vel.z * visc @@ -834,7 +836,7 @@ function mob_class:check_water_flow(dtime, pos) -- Just to make sure we don't manipulate the speed for no reason if vec.x ~= 0 or vec.y ~= 0 or vec.z ~= 0 then -- Minecraft Wiki: Flowing speed is "about 1.39 meters per second" - local f = 8 -- but we have acceleration ehre, not velocity. Was: 1.39 + local f = 10 -- but we have acceleration here, not velocity. Was: 1.39 -- Set new item moving speed into the direciton of the liquid self.acceleration = self.acceleration + vector.new(vec.x * f, -0.22, vec.z * f) --self.physical_state = true