Making movement work better.
This commit is contained in:
parent
19c1863100
commit
8c38745f6c
|
@ -28,6 +28,16 @@ if minetest.settings:get_bool("only_peaceful_mobs", false) then
|
|||
end)
|
||||
end
|
||||
|
||||
function mob_class:safe_remove()
|
||||
self.removed = true
|
||||
minetest.after(0,function(obj)
|
||||
if obj and obj:get_pos() then
|
||||
mcl_burning.extinguish(obj)
|
||||
obj:remove()
|
||||
end
|
||||
end,self.object)
|
||||
end
|
||||
|
||||
function mob_class:update_tag() --update nametag and/or the debug box
|
||||
local tag
|
||||
if mobs_debug then
|
||||
|
@ -206,7 +216,6 @@ function mob_class:mob_activate(staticdata, def, dtime)
|
|||
self.standing_in = NODE_IGNORE
|
||||
self.standing_on = NODE_IGNORE
|
||||
self.standing_under = NODE_IGNORE
|
||||
self.standing_body = 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
|
||||
|
@ -220,6 +229,8 @@ function mob_class:mob_activate(staticdata, def, dtime)
|
|||
self.blinktimer = 0
|
||||
self.blinkstatus = false
|
||||
|
||||
self.acceleration = vector.zero()
|
||||
|
||||
self.nametag = self.nametag or def.nametag
|
||||
|
||||
self.object:set_properties(self)
|
||||
|
@ -299,22 +310,22 @@ 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 not pos or self.removed then return end
|
||||
if self:check_despawn(pos, dtime) then return end
|
||||
if self:outside_limits() then return end
|
||||
|
||||
-- Update what we know of the mobs environment for physics and movement
|
||||
self:update_standing()
|
||||
pos = self:limit_vel_acc_for_large_dtime(pos, dtime, moveresult) -- limit maximum movement to reduce lag effects
|
||||
self:update_standing(pos, moveresult) -- update what we know of the mobs environment for physics and movement
|
||||
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
|
||||
if self:gravity_and_floating(pos, dtime, moveresult) then return end
|
||||
if self:step_damage(dtime, pos) then return end
|
||||
self:check_water_flow()
|
||||
self:check_water_flow(dtime, pos)
|
||||
|
||||
if self.state == "die" then return end
|
||||
self._can_jump_cliff = not self._jumping_cliff and self:can_jump_cliff()
|
||||
--self:flop()
|
||||
self:flop()
|
||||
self:smooth_rotation(dtime)
|
||||
|
||||
if player_in_active_range then
|
||||
|
@ -350,6 +361,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)
|
||||
if mobs_debug then self:update_tag() end
|
||||
if not self.object:get_luaentity() then return false end
|
||||
end
|
||||
|
@ -416,9 +428,7 @@ local function update_lifetimer(dtime)
|
|||
timer = 0
|
||||
end
|
||||
|
||||
minetest.register_globalstep(function(dtime)
|
||||
update_lifetimer(dtime)
|
||||
end)
|
||||
minetest.register_globalstep(update_lifetimer)
|
||||
|
||||
minetest.register_chatcommand("clearmobs", {
|
||||
privs = { maphack = true },
|
||||
|
|
|
@ -3,6 +3,7 @@ local mob_class = mcl_mobs.mob_class
|
|||
|
||||
local damage_enabled = minetest.settings:get_bool("enable_damage")
|
||||
local mobs_griefing = minetest.settings:get_bool("mobs_griefing") ~= false
|
||||
local mobs_see_through_opaque = mcl_mobs.see_through_opaque
|
||||
|
||||
-- pathfinding settings
|
||||
local stuck_timeout = 3 -- how long before mob gets stuck in place and starts searching
|
||||
|
@ -89,7 +90,8 @@ function mob_class:smart_mobs(s, p, dist, dtime)
|
|||
self.path.lastpos = vector_copy(s)
|
||||
|
||||
local use_pathfind = false
|
||||
local has_lineofsight = self:line_of_sight(vector_offset(s, 0, .5, 0), vector_offset(target_pos, 0, 1.5, 0), self.see_through_opaque or mobs_see_through, false)
|
||||
local has_lineofsight = self:line_of_sight(vector_offset(s, 0, .5, 0), vector_offset(target_pos, 0, 1.5, 0),
|
||||
self.see_through_opaque or mobs_see_through_opaque, false)
|
||||
|
||||
-- im stuck, search for path
|
||||
if not has_lineofsight then
|
||||
|
@ -157,12 +159,7 @@ function mob_class:smart_mobs(s, p, dist, dtime)
|
|||
|
||||
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 = min(ceil(self.jump_height * 0.25), 4)
|
||||
elseif self.stepheight > 0.5 then
|
||||
jumpheight = 1
|
||||
end
|
||||
local jumpheight = self.jump and floor(self.jump_height + 0.1) or 0
|
||||
self.path.way = minetest.find_path(s, p1, 16, jumpheight, dropheight, "A*_noprefetch")
|
||||
|
||||
self.state = ""
|
||||
|
|
|
@ -75,7 +75,7 @@ function mob_class:mob_sound(soundname, is_opinion, fixed_pitch)
|
|||
local sound = soundinfo[soundname]
|
||||
if not sound then return end
|
||||
if is_opinion and self.opinion_sound_cooloff > 0 then return end
|
||||
local pitch = fixed_pitch
|
||||
local pitch
|
||||
if not fixed_pitch then
|
||||
pitch = soundinfo.base_pitch or 1
|
||||
if self.child and not self.sounds_child then pitch = pitch * 1.5 end
|
||||
|
|
|
@ -6,6 +6,7 @@ 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"
|
||||
mcl_mobs.see_through_opaque = minetest.settings:get_bool("mobs_see_through_opaque", false)
|
||||
|
||||
-- used by the libaries below.
|
||||
-- get node but use fallback for nil or unknown
|
||||
|
@ -147,20 +148,25 @@ function mcl_mobs.register_mob(name, def)
|
|||
end
|
||||
end
|
||||
|
||||
if type(def.fly_in) == "string" then
|
||||
def.fly_in = { def.fly_in }
|
||||
end
|
||||
|
||||
local collisionbox = def.collisionbox or {-0.25, -0.25, -0.25, 0.25, 0.25, 0.25}
|
||||
-- Workaround for <https://github.com/minetest/minetest/issues/5966>:
|
||||
-- Increase upper Y limit to avoid mobs glitching through solid nodes.
|
||||
-- Removed now, as this was supposedly fixed in 5.3.0?
|
||||
-- FIXME: Remove workaround if it's no longer needed.
|
||||
if collisionbox[5] < 0.79 then
|
||||
collisionbox[5] = 0.79
|
||||
end
|
||||
--if collisionbox[5] < 0.79 then
|
||||
-- collisionbox[5] = 0.79
|
||||
--end
|
||||
local final_def = {
|
||||
use_texture_alpha = def.use_texture_alpha,
|
||||
head_swivel = def.head_swivel or nil, -- bool to activate this function
|
||||
head_yaw_offset = math.rad(def.head_yaw_offset or 0), -- for wonkey model bones
|
||||
head_pitch_multiplier = def.head_pitch_multiplier or 1, --for inverted pitch
|
||||
bone_eye_height = def.bone_eye_height or 1.4, -- head bone offset
|
||||
head_eye_height = def.head_eye_height or def.bone_eye_height or 0, -- how hight aproximatly the mobs head is fromm the ground to tell the mob how high to look up at the player
|
||||
head_eye_height = def.head_eye_height or def.bone_eye_height or 0, -- how high aproximately the mobs head is from the ground to tell the mob how high to look up at the player
|
||||
curiosity = def.curiosity or 1, -- how often mob will look at player on idle
|
||||
head_yaw = def.head_yaw or "y", -- axis to rotate head on
|
||||
horizontal_head_height = def.horizontal_head_height or 0,
|
||||
|
@ -179,7 +185,7 @@ function mcl_mobs.register_mob(name, def)
|
|||
spawn_small_alternative = def.spawn_small_alternative,
|
||||
do_custom = def.do_custom,
|
||||
detach_child = def.detach_child,
|
||||
jump_height = def.jump_height or 4, -- was 6
|
||||
jump_height = def.jump_height or 1,
|
||||
rotate = math.rad(def.rotate or 0), -- 0=front, 90=side, 180=back, 270=side2
|
||||
lifetimer = def.lifetimer or 57.73,
|
||||
hp_min = scale_difficulty(def.hp_min, 5, 1),
|
||||
|
@ -398,11 +404,8 @@ end
|
|||
|
||||
-- register arrow for shoot attack
|
||||
function mcl_mobs.register_arrow(name, def)
|
||||
|
||||
if not name or not def then return end -- errorcheck
|
||||
|
||||
minetest.register_entity(name, {
|
||||
|
||||
physical = false,
|
||||
visual = def.visual,
|
||||
visual_size = def.visual_size,
|
||||
|
@ -426,31 +429,21 @@ function mcl_mobs.register_arrow(name, def)
|
|||
self._puncher = puncher
|
||||
end,
|
||||
collisionbox = def.collisionbox or {0, 0, 0, 0, 0, 0},
|
||||
automatic_face_movement_dir = def.rotate
|
||||
and (def.rotate - (math.pi / 180)) or false,
|
||||
automatic_face_movement_dir = def.rotate and (def.rotate - (math.pi / 180)) or false,
|
||||
|
||||
on_activate = def.on_activate,
|
||||
|
||||
on_step = def.on_step or function(self, dtime)
|
||||
|
||||
self.timer = self.timer + dtime
|
||||
|
||||
local pos = self.object:get_pos()
|
||||
|
||||
if self.switch == 0
|
||||
or self.timer > self._lifetime
|
||||
or not within_limits(pos, 0) then
|
||||
if self.switch == 0 or self.timer > self._lifetime or not within_limits(pos, 0) then
|
||||
mcl_burning.extinguish(self.object)
|
||||
self.object:remove();
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
-- does arrow have a tail (fireball)
|
||||
if def.tail
|
||||
and def.tail == 1
|
||||
and def.tail_texture then
|
||||
|
||||
if def.tail == 1 and def.tail_texture then
|
||||
minetest.add_particle({
|
||||
pos = pos,
|
||||
velocity = {x = 0, y = 0, z = 0},
|
||||
|
@ -464,24 +457,17 @@ function mcl_mobs.register_arrow(name, def)
|
|||
end
|
||||
|
||||
if self.hit_node then
|
||||
|
||||
local node = node_ok(pos).name
|
||||
|
||||
if minetest.registered_nodes[node].walkable then
|
||||
|
||||
self.hit_node(self, pos, node)
|
||||
|
||||
if self.drop == true then
|
||||
|
||||
pos.y = pos.y + 1
|
||||
|
||||
self.lastpos = (self.lastpos or pos)
|
||||
|
||||
minetest.add_item(self.lastpos, self.object:get_luaentity().name)
|
||||
end
|
||||
|
||||
self.object:remove();
|
||||
|
||||
return
|
||||
end
|
||||
end
|
||||
|
@ -498,12 +484,8 @@ function mcl_mobs.register_arrow(name, def)
|
|||
end
|
||||
|
||||
if self.hit_player or self.hit_mob or self.hit_object then
|
||||
|
||||
for _,object in pairs(minetest.get_objects_inside_radius(pos, 1.5)) do
|
||||
|
||||
if self.hit_player
|
||||
and object:is_player() then
|
||||
|
||||
if self.hit_player and object:is_player() then
|
||||
self.hit_player(self, object)
|
||||
self.object:remove();
|
||||
return
|
||||
|
@ -544,30 +526,20 @@ end
|
|||
-- * spawn_egg=1: Spawn egg (generic mob, no metadata)
|
||||
-- * spawn_egg=2: Spawn egg (captured/tamed mob, metadata)
|
||||
function mcl_mobs.register_egg(mob_id, desc, background_color, overlay_color, addegg, no_creative)
|
||||
|
||||
local grp = {spawn_egg = 1}
|
||||
|
||||
-- do NOT add this egg to creative inventory (e.g. dungeon master)
|
||||
if no_creative == true then
|
||||
grp.not_in_creative_inventory = 1
|
||||
end
|
||||
local grp = { spawn_egg = 1 }
|
||||
if no_creative == true then grp.not_in_creative_inventory = 1 end
|
||||
|
||||
local invimg = "(spawn_egg.png^[multiply:" .. background_color ..")^(spawn_egg_overlay.png^[multiply:" .. overlay_color .. ")"
|
||||
if old_spawn_icons then
|
||||
local mobname = mob_id:gsub("mobs_mc:","")
|
||||
local fn = "mobs_mc_spawn_icon_"..mobname..".png"
|
||||
if mcl_util.file_exists(minetest.get_modpath("mobs_mc").."/textures/"..fn) then
|
||||
invimg = fn
|
||||
end
|
||||
local fn = "mobs_mc_spawn_icon_" .. mob_id:gsub("mobs_mc:","") .. ".png"
|
||||
if mcl_util.file_exists(minetest.get_modpath("mobs_mc").."/textures/"..fn) then invimg = fn end
|
||||
end
|
||||
if addegg == 1 then
|
||||
invimg = "mobs_chicken_egg.png^(" .. invimg ..
|
||||
"^[mask:mobs_chicken_egg_overlay.png)"
|
||||
invimg = "mobs_chicken_egg.png^(" .. invimg .. "^[mask:mobs_chicken_egg_overlay.png)"
|
||||
end
|
||||
|
||||
-- register old stackable mob egg
|
||||
minetest.register_craftitem(mob_id, {
|
||||
|
||||
description = desc,
|
||||
inventory_image = invimg,
|
||||
groups = grp,
|
||||
|
@ -604,9 +576,6 @@ function mcl_mobs.register_egg(mob_id, desc, background_color, overlay_color, ad
|
|||
local dim = mcl_worlds.pos_to_dimension(placer:get_pos())
|
||||
local mob_light_lvl = {mcl_mobs:mob_light_lvl(itemstack:get_name(),dim)}
|
||||
|
||||
--minetest.log("min light: " .. mob_light_lvl[1])
|
||||
--minetest.log("max light: " .. mob_light_lvl[2])
|
||||
|
||||
-- Handle egg conversion
|
||||
local convert_to = (minetest.registered_entities[mob_name] or {})._convert_to
|
||||
if convert_to then mob_name = convert_to end
|
||||
|
@ -618,9 +587,7 @@ function mcl_mobs.register_egg(mob_id, desc, background_color, overlay_color, ad
|
|||
return itemstack
|
||||
end
|
||||
|
||||
if not minetest.registered_entities[mob_name] then
|
||||
return itemstack
|
||||
end
|
||||
if not minetest.registered_entities[mob_name] then return itemstack end
|
||||
|
||||
if minetest.settings:get_bool("only_peaceful_mobs", false)
|
||||
and minetest.registered_entities[mob_name].type == "monster" then
|
||||
|
|
|
@ -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 + entity.jump_height
|
||||
velo.y = velo.y + sqrt(entity.jump_height * 20)
|
||||
acce_y = acce_y + 1
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
local math, vector, minetest, mcl_mobs = math, vector, minetest, mcl_mobs
|
||||
local mob_class = mcl_mobs.mob_class
|
||||
local DEFAULT_FALL_SPEED = -9.81*1.5
|
||||
local FLOP_HEIGHT = 6
|
||||
local FLOP_VEL = math.sqrt(1.5 * 20) -- 1.5 blocks
|
||||
local FLOP_HOR_SPEED = 1.5
|
||||
|
||||
local CHECK_HERD_FREQUENCY = 4
|
||||
|
@ -12,7 +12,6 @@ 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)
|
||||
local mobs_see_through_opaque = minetest.settings:get_bool("mobs_see_through_opaque", false)
|
||||
|
||||
local random = math.random
|
||||
local sin = math.sin
|
||||
|
@ -31,7 +30,8 @@ local vector_offset = vector.offset
|
|||
local vector_distance = vector.distance
|
||||
|
||||
local node_ok = mcl_mobs.node_ok
|
||||
local see_through_opaque = mcl_mobs.see_through_opaque
|
||||
local mobs_see_through_opaque = mcl_mobs.see_through_opaque
|
||||
local line_of_sight = mcl_mobs.line_of_sight
|
||||
|
||||
-- Stop movement and stand
|
||||
function mob_class:stand()
|
||||
|
@ -86,12 +86,12 @@ function mob_class:target_visible(origin)
|
|||
local cbox = self.collisionbox
|
||||
-- TODO also worth testing midway between feet and head?
|
||||
-- to top of entity
|
||||
if line_of_sight(origin_eye_pos, vector_offset(target_pos, 0, cbox[5], 0), mobs_see_through_opaque, true) then return true end
|
||||
if line_of_sight(origin_eye_pos, vector_offset(target_pos, 0, cbox[5], 0), self.see_through_opaque or mobs_see_through_opaque, true) then return true end
|
||||
-- to feed of entity
|
||||
if self.attack:is_player() then
|
||||
if line_of_sight(origin_eye_pos, target_pos, mobs_see_through_opaque, true) then return true end -- Cbox would put feet under ground which interferes with ray
|
||||
if line_of_sight(origin_eye_pos, target_pos, self.see_through_opaque or mobs_see_through_opaque, true) then return true end -- Cbox would put feet under ground which interferes with ray
|
||||
else
|
||||
if line_of_sight(origin_eye_pos, vector_offset(target_pos, 0, cbox[2], 0), mobs_see_through_opaque, true) then return true end
|
||||
if line_of_sight(origin_eye_pos, vector_offset(target_pos, 0, cbox[2], 0), self.see_through_opaque or mobs_see_through_opaque, true) then return true end
|
||||
end
|
||||
|
||||
--minetest.log("start targ_head_height: " .. dump(targ_head_height))
|
||||
|
@ -111,7 +111,7 @@ end
|
|||
|
||||
-- check line of sight
|
||||
function mob_class:line_of_sight(pos1, pos2, stepsize)
|
||||
return line_of_sight(pos1, pos2, mobs_see_through_opaque, true)
|
||||
return line_of_sight(pos1, pos2, self.see_through_opaque or mobs_see_through_opaque, true)
|
||||
end
|
||||
|
||||
function mob_class:can_jump_cliff()
|
||||
|
@ -169,7 +169,7 @@ function mob_class:is_at_cliff_or_danger()
|
|||
return "free fall"
|
||||
end
|
||||
local height = ypos + 0.4 - blocker.y
|
||||
local chance = (self.jump_height or 4) * 0.25 / (height * height)
|
||||
local chance = self.jump_height / (height * height)
|
||||
if height >= self.fear_height and random() < chance then
|
||||
if logging then
|
||||
minetest.log("action", "[mcl_mobs] "..self.name.." avoiding drop of "..height) --.." chance "..chance)
|
||||
|
@ -304,17 +304,12 @@ function mob_class:do_jump()
|
|||
return false
|
||||
end
|
||||
|
||||
v.y = math.min(v.y, 0) + self.jump_height -- + 0.3
|
||||
--if in_water then
|
||||
-- v.x, v.y, v.z = v.x * 1.2, v.y + self.jump_height * 0.5, v.z * 1.2
|
||||
--elseif self._can_jump_cliff then
|
||||
-- v.x, v.y, v.z = v.x * 2.5, v.y + self.jump_height * 0.1, v.z * 2.5
|
||||
--end
|
||||
if in_water or self._cam_jump_cliff then v.y = v.y + self.jump_height * 0.25 end
|
||||
|
||||
v.y = math.min(v.y, 0) + math.sqrt(self.jump_height * 20) + (in_water or self._can_jump_cliff and 0.5 or 0)
|
||||
v.y = math.min(-self.fall_speed, math.max(v.y, self.fall_speed))
|
||||
self:set_animation("jump") -- only when defined
|
||||
self.object:set_velocity(v)
|
||||
self:set_animation("run")
|
||||
self:set_animation("jump") -- only when defined
|
||||
self:set_velocity(self.run_velocity)
|
||||
|
||||
if self.jump_sound_cooloff <= 0 then
|
||||
self:mob_sound("jump")
|
||||
|
@ -454,7 +449,7 @@ function mob_class:check_runaway_from()
|
|||
sp = s
|
||||
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), mobs_see_through_opaque, false) then
|
||||
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
|
||||
-- aim higher to make looking up hills more realistic
|
||||
min_dist = dist
|
||||
min_player = player
|
||||
|
@ -531,29 +526,25 @@ function mob_class:check_follow()
|
|||
end
|
||||
end
|
||||
|
||||
-- swimmers flop when out of their element, and swim again when back in
|
||||
function mob_class:flop()
|
||||
-- swimmers flop when out of their element, and swim again when back in
|
||||
if self.fly then
|
||||
local s = self.object:get_pos()
|
||||
|
||||
if self:flight_check(s) == false then
|
||||
self.state = "flop"
|
||||
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_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(vector_new((random() * 2 - 1) * FLOP_HOR_SPEED, FLOP_HEIGHT, (random() * 2 - 1) * FLOP_HOR_SPEED))
|
||||
end
|
||||
if not self.fly then return end
|
||||
if not self:flight_check() then
|
||||
self.state = "flop"
|
||||
self.acceleration.y = DEFAULT_FALL_SPEED
|
||||
local sdef = self.standing_on
|
||||
if sdef and sdef.walkable then -- flop on ground
|
||||
if self.object:get_velocity().y == 0 then
|
||||
self:mob_sound("flop")
|
||||
self.object:add_velocity(vector_new(0, FLOP_VEL, 0))
|
||||
self:turn_by(TWOPI * random(), 8)
|
||||
self:set_velocity(FLOP_HOR_SPEED)
|
||||
end
|
||||
|
||||
self:set_animation("stand", true)
|
||||
elseif self.state == "flop" then
|
||||
self:stand()
|
||||
end
|
||||
self:set_animation("stand", true)
|
||||
elseif self.state == "flop" then
|
||||
--self:stand()
|
||||
self.acceleration.y = 0
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -625,28 +616,33 @@ function mob_class:do_states_walk()
|
|||
end
|
||||
-- facing wall? then turn
|
||||
local facing_wall = false
|
||||
local cbox = self.collisionbox
|
||||
local dir_x, dir_z = -sin(yaw - QUARTERPI) * (cbox[4] + 0.5), cos(yaw - QUARTERPI) * (cbox[4] + 0.5)
|
||||
local nodface = minetest.registered_nodes[minetest.get_node(vector_offset(s, dir_x, cbox[5] - cbox[2], dir_z)).name]
|
||||
if nodface and nodface.walkable then
|
||||
dir_x, dir_z = -sin(yaw + QUARTERPI) * (cbox[4] + 0.5), cos(yaw + QUARTERPI) * (cbox[4] + 0.5)
|
||||
nodface = minetest.registered_nodes[minetest.get_node(vector_offset(s, dir_x, cbox[5] - cbox[2], dir_z)).name]
|
||||
-- todo: use moveresult collision info here?
|
||||
if self:get_velocity_xyz() < 0.1 then
|
||||
facing_wall = true
|
||||
elseif not facing_wall then
|
||||
local cbox = self.collisionbox
|
||||
local dir_x, dir_z = -sin(yaw - QUARTERPI) * (cbox[4] + 0.5), cos(yaw - QUARTERPI) * (cbox[4] + 0.5)
|
||||
local nodface = minetest.registered_nodes[minetest.get_node(vector_offset(s, dir_x, (cbox[5] - cbox[2]) * 0.5, dir_z)).name]
|
||||
if nodface and nodface.walkable then
|
||||
facing_wall = true
|
||||
dir_x, dir_z = -sin(yaw + QUARTERPI) * (cbox[4] + 0.5), cos(yaw + QUARTERPI) * (cbox[4] + 0.5)
|
||||
nodface = minetest.registered_nodes[minetest.get_node(vector_offset(s, dir_x, (cbox[5] - cbox[2]) * 0.5, dir_z)).name]
|
||||
if nodface and nodface.walkable then
|
||||
facing_wall = true
|
||||
end
|
||||
end
|
||||
end
|
||||
if facing_wall then
|
||||
if logging then
|
||||
minetest.log("action", "[mcl_mobs] "..self.name.." facing a wall, turning.")
|
||||
end
|
||||
self:turn_by(TWOPI * (random() - 0.5), 6)
|
||||
self:turn_by(TWOPI * (random() - 0.5), 10)
|
||||
-- otherwise randomly turn
|
||||
elseif random() <= 0.3 then
|
||||
local home = self._home or self._bed
|
||||
if home and random() < 0.1 then
|
||||
self:turn_in_direction(home.x - s.x, home.z - s.z, 8)
|
||||
else
|
||||
self:turn_by(QUARTERPI * (random() - 0.5), 10)
|
||||
self:turn_by(QUARTERPI * (random() - 0.5), 20)
|
||||
end
|
||||
end
|
||||
self:set_velocity(self.walk_velocity)
|
||||
|
@ -687,9 +683,7 @@ function mob_class:do_states_stand(player_in_active_range)
|
|||
end
|
||||
|
||||
-- npc's ordered to stand stay standing
|
||||
if self.order == "stand" or self.order == "sleep" or self.order == "work" then
|
||||
|
||||
else
|
||||
if self.order ~= "stand" and self.order ~= "sleep" and self.order ~= "work" then
|
||||
if player_in_active_range then
|
||||
if self.walk_chance ~= 0
|
||||
and self.facing_fence ~= true
|
||||
|
@ -709,8 +703,7 @@ end
|
|||
function mob_class:do_states_runaway()
|
||||
self.runaway_timer = self.runaway_timer + 1
|
||||
-- stop after 5 seconds or when at cliff
|
||||
if self.runaway_timer > 5
|
||||
or self:is_at_cliff_or_danger() then
|
||||
if self.runaway_timer > 5 or self:is_at_cliff_or_danger() then
|
||||
self.runaway_timer = 0
|
||||
self:stand()
|
||||
self:turn_by(PI * (random() + 0.5), 8)
|
||||
|
|
|
@ -2,6 +2,8 @@ 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 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
|
||||
local ENTITY_CRAMMING_MAX = 24
|
||||
local CRAMMING_DAMAGE = 3
|
||||
local DEATH_DELAY = 0.5
|
||||
|
@ -17,6 +19,7 @@ local abs = math.abs
|
|||
local atan2 = math.atan2
|
||||
local sin = math.sin
|
||||
local cos = math.cos
|
||||
local sqrt = math.sqrt
|
||||
local node_ok = mcl_mobs.node_ok
|
||||
|
||||
local PATHFINDING = "gowp"
|
||||
|
@ -48,13 +51,25 @@ end
|
|||
-- standing_on: node below
|
||||
-- standing_under: node above
|
||||
-- these may be "nil" (= ignore) and are otherwise already resolved via minetest.registered_nodes
|
||||
function mob_class:update_standing()
|
||||
local pos = self.object:get_pos()
|
||||
function mob_class:update_standing(pos, moveresult)
|
||||
local temp_pos = vector.offset(pos, 0, self.collisionbox[2] + 0.5, 0) -- foot level
|
||||
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 = minetest.registered_nodes[minetest.get_node(temp_pos).name] or NODE_IGNORE
|
||||
self.standing_height = pos.y - math.ceil(temp_pos.y + 0.5) + self.head_eye_height * 0.75
|
||||
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
|
||||
-- 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*",""))
|
||||
for _, c in ipairs(moveresult.collisions) do
|
||||
if c.axis == "y" and c.type == "node" and c.old_velocity.y < 0 then
|
||||
self.standing_on_node = minetest.get_node(c.node_pos)
|
||||
self.standing_on = minetest.registered_nodes[self.standing_on_node.name]
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
-- approximate height of head over ground:
|
||||
self.standing_height = pos.y - math.floor(temp_pos.y + 0.5) - 0.5 + self.head_eye_height * 0.9
|
||||
temp_pos.y = temp_pos.y + 2 -- at +1 = above
|
||||
self.standing_under = minetest.registered_nodes[minetest.get_node(temp_pos).name] or NODE_IGNORE
|
||||
end
|
||||
|
@ -77,28 +92,20 @@ function mob_class:object_in_range(object)
|
|||
factor = factors and factors[self.name]
|
||||
end
|
||||
-- Distance check
|
||||
local dist
|
||||
if factor and factor == 0 then
|
||||
return false
|
||||
elseif factor then
|
||||
dist = self.view_range * factor
|
||||
else
|
||||
dist = self.view_range
|
||||
local dist = self.view_range
|
||||
if factor then
|
||||
if factor == 0 then return false end
|
||||
dist = dist * factor
|
||||
end
|
||||
|
||||
local p1, p2 = self.object:get_pos(), object:get_pos()
|
||||
return p1 and p2 and (vector.distance(p1, p2) <= dist)
|
||||
end
|
||||
|
||||
function mob_class:item_drop(cooked, looting_level)
|
||||
|
||||
if not mobs_drop_items then return end
|
||||
|
||||
looting_level = looting_level or 0
|
||||
|
||||
if (self.child and self.type ~= "monster") then
|
||||
return
|
||||
end
|
||||
if (self.child and self.type ~= "monster") then return end
|
||||
|
||||
local obj, item, num
|
||||
local pos = self.object:get_pos()
|
||||
|
@ -160,7 +167,6 @@ end
|
|||
function mob_class:collision()
|
||||
local pos = self.object:get_pos()
|
||||
if not pos then return 0,0 end
|
||||
local vel = self.object:get_velocity()
|
||||
local x, z = 0, 0
|
||||
local width = -self.collisionbox[1] + self.collisionbox[4] + 0.5
|
||||
for _,object in pairs(minetest.get_objects_inside_radius(pos, width)) do
|
||||
|
@ -172,7 +178,7 @@ function mob_class:collision()
|
|||
|
||||
local pos2 = object:get_pos()
|
||||
local vx, vz = pos.x - pos2.x, pos.z - pos2.z
|
||||
local force = width - (vx*vx+vz*vz)^0.5
|
||||
local force = width - sqrt(vx*vx+vz*vz)
|
||||
if force > 0 then
|
||||
force = force * force * (object:is_player() and 2 or 1) -- players push more
|
||||
-- minetest.log("mob push force "..force.." "..tostring(self.name).." by "..tostring(ent and ent.name or "player"))
|
||||
|
@ -202,22 +208,30 @@ function mob_class:set_velocity(v)
|
|||
self.target_vel = v
|
||||
end
|
||||
|
||||
-- calculate mob velocity (3d)
|
||||
function mob_class:get_velocity_xyz()
|
||||
local v = self.object:get_velocity()
|
||||
if not v then return 0 end
|
||||
local x, y, z = v.x, v.y, v.z
|
||||
return sqrt(x*x + y*y + z*z)
|
||||
end
|
||||
-- calculate mob velocity (2d)
|
||||
function mob_class:get_velocity_xz()
|
||||
local v = self.object:get_velocity()
|
||||
if not v then return 0 end
|
||||
return (v.x*v.x + v.z*v.z)^0.5
|
||||
local x, z = v.x, v.z
|
||||
return sqrt(x*x + z*z)
|
||||
end
|
||||
-- legacy API
|
||||
mob_class.get_velocity = mob_class.get_velocity_xz
|
||||
|
||||
-- Relative turn, primarily for random turning
|
||||
-- @param angle number: realative angle, in radians
|
||||
-- @param dtime deprecated: ignored now, because of smooth rotations
|
||||
-- @param delay number: time needed to turn
|
||||
-- @param dtime deprecated: ignored now, because of smooth rotations
|
||||
-- @return target angle
|
||||
function mob_class:turn_by(angle, delay, dtime)
|
||||
return self:set_yaw((self.object:get_yaw() or 0) + angle, delay, dtime)
|
||||
return self:set_yaw((self.object:get_yaw() or 0) + self.rotate + angle, delay, dtime)
|
||||
end
|
||||
-- Turn into a direction (e.g., to the player, or away)
|
||||
-- @param dx number: delta in x axis to target
|
||||
|
@ -273,7 +287,7 @@ function mob_class:smooth_rotation(dtime)
|
|||
if not self.target_yaw then return end
|
||||
|
||||
local delay = self.delay
|
||||
local initial_yaw = self.object:get_yaw() or 0
|
||||
local initial_yaw = (self.object:get_yaw() or 0) + self.rotate
|
||||
local yaw -- resulting yaw for this tick
|
||||
if delay and delay > 1 then
|
||||
local dif = (self.target_yaw - initial_yaw + PI) % TWOPI - PI
|
||||
|
@ -286,44 +300,33 @@ function mob_class:smooth_rotation(dtime)
|
|||
if self.shaking then
|
||||
yaw = yaw + (random() * 2 - 1) / 72 * dtime
|
||||
end
|
||||
if yaw ~= initial_yaw then self.object:set_yaw(yaw) end
|
||||
if yaw ~= initial_yaw then self.object:set_yaw(yaw - self.rotate) end
|
||||
--update_roll() -- Fleckenstein easter egg
|
||||
end
|
||||
|
||||
-- Handling of intentional acceleration by the mob
|
||||
-- its best to place environmental effects afterwards
|
||||
-- TODO: have mobs that act faster and that act slower?
|
||||
-- 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 v = self.object:get_velocity()
|
||||
local w = min(1, dtime * 10)
|
||||
local w = min(dtime * 5, 1)
|
||||
v.x, v.z = v.x + w * (x - v.x), v.z + w * (z - v.z)
|
||||
self.object:set_velocity(v)
|
||||
end
|
||||
|
||||
-- are we flying in what we are suppose to? (taikedz)
|
||||
function mob_class:flight_check()
|
||||
if not self.standin_in or not self.standing_on then return true end -- nil check
|
||||
if not self.standing_in or self.standing_in.name == "ignore" then return true end -- unknown?
|
||||
if not self.fly_in then return false end
|
||||
|
||||
local fly_in
|
||||
if type(self.fly_in) == "string" then
|
||||
fly_in = { self.fly_in }
|
||||
elseif type(self.fly_in) == "table" then
|
||||
fly_in = self.fly_in
|
||||
else
|
||||
return false
|
||||
local nod = self.standing_in.name
|
||||
-- todo: allow flowers etc. for birds
|
||||
for _,checknode in pairs(self.fly_in) do
|
||||
if nod == checknode then return true end
|
||||
end
|
||||
|
||||
for _,checknode in pairs(fly_in) do
|
||||
if nod == checknode or nod == "ignore" then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
|
@ -738,10 +741,9 @@ end
|
|||
function mob_class:check_entity_cramming()
|
||||
local p = self.object:get_pos()
|
||||
if not p then return end
|
||||
local oo = minetest.get_objects_inside_radius(p,1)
|
||||
local mobs = {}
|
||||
for i = 1,#oo do
|
||||
local l = oo[i]:get_luaentity()
|
||||
for o in minetest.objects_inside_radius(p, 0.5) do
|
||||
local l = o:get_luaentity()
|
||||
if l and l.is_mob and l.health > 0 then table.insert(mobs,l) end
|
||||
end
|
||||
local clear = #mobs < ENTITY_CRAMMING_MAX
|
||||
|
@ -769,34 +771,44 @@ end
|
|||
-- @param moveresult table: minetest engine movement result (collisions)
|
||||
-- @return true if mob died
|
||||
function mob_class:gravity_and_floating(pos, dtime, moveresult)
|
||||
if self._just_portaled then
|
||||
self.start_fall_y = nil -- reset fall damage
|
||||
end
|
||||
if self.fly and self.state ~= "die" then return end
|
||||
if not self.fall_speed then self.fall_speed = DEFAULT_FALL_SPEED end -- TODO: move to initialization code only?
|
||||
if self.standing_in == NODE_IGNORE then -- not emerged yet, do not fall
|
||||
self.object:set_velocity(vector.zero())
|
||||
return false
|
||||
end
|
||||
self.object:set_properties({ nametag = "on: "..self.standing_on.name.."\nin: "..self.standing_in.name.."\n "..tostring(self.standing_height) })
|
||||
-- self.object:set_properties({ nametag = "on: "..self.standing_on.name.."\nin: "..self.standing_in.name.."\n "..tostring(self.standing_height) })
|
||||
|
||||
-- Gravity
|
||||
--local acc_y = self.fall_speed
|
||||
local acc_y = moveresult and moveresult.touching_ground and 0 or self.fall_speed
|
||||
local visc = 1
|
||||
local acc = vector.new(0, not self.fly and moveresult and moveresult.touching_ground and 0 or self.fall_speed, 0)
|
||||
self.visc = 1
|
||||
local vel = self.object:get_velocity() or vector.zero()
|
||||
local standbody = self.standing_in
|
||||
if standbody.groups.water then
|
||||
visc = 0.8
|
||||
self.visc = 0.4
|
||||
if self.floats > 0 then --and minetest.registered_nodes[node_ok(vector.offset(pos, 0, self.collisionbox[5] - 0.25, 0)).name].groups.water then
|
||||
local w = self.standing_under.groups.water and 0 or self.standing_height -- 0 is submerged, 1 is out
|
||||
acc_y = self.fall_speed * max(-1, min(1, 2 * w - 1)) -- -1 to +1
|
||||
--self.object:set_acceleration(vector.new(0, -self.fall_speed * 0.5, 0))
|
||||
local w = (self.standing_under.groups.water and 0 or self.standing_height) -- <1 is submerged, >1 is out
|
||||
if w > 0.95 and w < 1.05 then
|
||||
acc.y = 0 -- stabilize floating
|
||||
else
|
||||
acc.y = self.fall_speed * max(-1, min(1, w - 1)) -- -1 to +1
|
||||
end
|
||||
end
|
||||
self.start_fall_y = nil -- otherwise might receive fall damage on the next jump?
|
||||
elseif standbody.groups.lava then
|
||||
visc = 0.7
|
||||
self.visc = 0.5
|
||||
if self.floats_on_lava > 0 then
|
||||
local w = self.standing_under.groups.water and 0 or self.standing_height -- 0 is submerged, 1 is out
|
||||
acc_y = self.fall_speed * max(-1, min(1, 2 * w - 1)) -- -1 to +1
|
||||
--self.object:set_acceleration(vector.new(0, -self.fall_speed * 0.5, 0))
|
||||
-- todo: relative to body height?
|
||||
if w > 0.95 and w < 1.05 then
|
||||
acc.y = 0
|
||||
else
|
||||
acc.y = self.fall_speed * max(-1, min(1, w - 1)) -- -1 to +1
|
||||
end
|
||||
end
|
||||
self.start_fall_y = nil -- otherwise might receive fall damage on the next jump?
|
||||
else
|
||||
-- fall damage onto solid ground (bouncy ground will yield vel.y > 0)
|
||||
if self.fall_damage == 1 and vel.y == 0 then
|
||||
|
@ -819,50 +831,112 @@ function mob_class:gravity_and_floating(pos, dtime, moveresult)
|
|||
end
|
||||
end
|
||||
end
|
||||
--vel.x, vel.y, vel.z = vel.x * visc, (vel.y + acc_y * dtime) * visc, vel.z * visc
|
||||
self.acceleration = acc
|
||||
end
|
||||
|
||||
--- Limit the velocity and acceleration of a mob applied by MTE
|
||||
-- This is an attempt to solve mobs trampolining on water.
|
||||
-- The problem is when a large timestep occurs, acceleration and velocity is applied
|
||||
-- by the minetest engine (MTE) for a long timestep. If a mob enters or leaves water
|
||||
-- during this time (or a similar transition occurs), this can become wildly inaccurate.
|
||||
-- A mob slightly above water will fall deep into water, or may fly high into
|
||||
-- the air because of updrift.
|
||||
function mob_class:limit_vel_acc_for_large_dtime(pos, dtime, moveresult)
|
||||
-- hack not in use:
|
||||
if ACCELERATION_MIX == 0 and dtime < MAX_DTIME then return pos end
|
||||
local edtime, rdtime = dtime, 0 -- effective dtime and reverted dtime
|
||||
if dtime >= MAX_DTIME then
|
||||
edtime, rdtime = MAX_DTIME, dtime - MAX_DTIME
|
||||
end
|
||||
|
||||
local vel = self.object:get_velocity()
|
||||
local acc = self.object:get_acceleration()
|
||||
-- revert excess movement and acceleration from MTE
|
||||
if rdtime > 0 and not (moveresult and moveresult.collides) then
|
||||
vel = vel - acc * rdtime
|
||||
pos = pos - (vel - acc * rdtime * 0.5) * rdtime -- at average velocity during excess
|
||||
end
|
||||
-- apply the missing lua part of acceleration:
|
||||
if ACCELERATION_MIX > 0 and self.acceleration then
|
||||
local dx, dy, dz = self.acceleration.x, self.acceleration.y, self.acceleration.z
|
||||
-- use collision information:
|
||||
if moveresult and moveresult.collisions then
|
||||
for _, c in ipairs(moveresult.collisions) do
|
||||
if c.axis == "y" then
|
||||
if c.old_velocity.y < 0 and dy < 0 then dy = 0 end
|
||||
if c.old_velocity.y > 0 and dy > 0 then dy = 0 end
|
||||
elseif c.axis == "x" then
|
||||
if c.old_velocity.x < 0 and dx < 0 then dx = 0 end
|
||||
if c.old_velocity.x > 0 and dx > 0 then dx = 0 end
|
||||
elseif c.axis == "z" then
|
||||
if c.old_velocity.z < 0 and dz < 0 then dz = 0 end
|
||||
if c.old_velocity.z > 0 and dz > 0 then dz = 0 end
|
||||
end
|
||||
end
|
||||
end
|
||||
vel.x = vel.x + dx * edtime * ACCELERATION_MIX
|
||||
vel.y = vel.y + dy * edtime * ACCELERATION_MIX
|
||||
vel.z = vel.z + dz * edtime * ACCELERATION_MIX
|
||||
-- because we cannot check for collission, we simply allow the extra acceleration to lag a timestep:
|
||||
-- pos = pos + self.acceleration * edtime * 0.5 * rdtime
|
||||
end
|
||||
self.object:set_velocity(vel)
|
||||
self.object:set_pos(pos)
|
||||
return pos
|
||||
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
|
||||
vel.y = max(min(vel.y, -self.fall_speed), self.fall_speed)
|
||||
|
||||
-- Cap dtime to reduce bopping on water (hence we also do not use minetest acceleration)
|
||||
-- but the minetest engine already applied the current velocity on the full timestep
|
||||
dtime = min(dtime, 0.01)
|
||||
-- ideally, we could use: self.object:set_acceleration(vector.new(0, acc_y * visc, 0))
|
||||
vel.y = vel.y + acc_y * dtime -- apply acceleration in LUA, with limited dtime
|
||||
dtime = min(dtime, MAX_DTIME)
|
||||
|
||||
-- Slowdown in liquids:
|
||||
if self.visc then
|
||||
-- TODO: only on y, or also apply to vel.x, vel.z, acceleration?
|
||||
vel.y = vel.y * self.visc^(dtime*10)
|
||||
-- vel = vel * self.visc^(dtime*10)
|
||||
end
|
||||
|
||||
-- acceleration:
|
||||
if self.acceleration and ACCELERATION_MIX < 1 then
|
||||
self.object:set_acceleration(self.acceleration * (1 - ACCELERATION_MIX))
|
||||
-- the remaining part is applied after the dtime step
|
||||
end
|
||||
|
||||
vel.y = vel.y * visc
|
||||
self.object:set_velocity(vel)
|
||||
end
|
||||
|
||||
function mob_class:check_water_flow()
|
||||
-- Add water flowing for mobs from mcl_item_entity
|
||||
local p = self.object:get_pos()
|
||||
local node = minetest.get_node_or_nil(p)
|
||||
local def = node and minetest.registered_nodes[node.name]
|
||||
|
||||
-- Add water flowing for mobs from mcl_item_entity
|
||||
function mob_class:check_water_flow(dtime, pos)
|
||||
local def = self.standing_in
|
||||
-- Move item around on flowing liquids
|
||||
if def and def.liquidtype == "flowing" then
|
||||
|
||||
--[[ Get flowing direction (function call from flowlib), if there's a liquid.
|
||||
NOTE: According to Qwertymine, flowlib.quickflow is only reliable for liquids with a flowing distance of 7.
|
||||
Luckily, this is exactly what we need if we only care about water, which has this flowing distance. ]]
|
||||
local vec = flowlib.quick_flow(p, node)
|
||||
local vec = flowlib.quick_flow(pos, minetest.get_node(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 = 1.39
|
||||
local f = 8 -- but we have acceleration ehre, not velocity. Was: 1.39
|
||||
-- Set new item moving speed into the direciton of the liquid
|
||||
local newv = vector.multiply(vec, f)
|
||||
--self.object:set_acceleration(vector.zero())
|
||||
self.object:add_velocity(vector.new(newv.x, -0.22, newv.z))
|
||||
|
||||
self.physical_state = true
|
||||
self._flowing = true
|
||||
self.object:set_properties({ physical = true })
|
||||
self.acceleration = self.acceleration + vector.new(vec.x * f, -0.22, vec.z * f)
|
||||
--self.physical_state = true
|
||||
--self._flowing = true
|
||||
--self.object:set_properties({ physical = true })
|
||||
return
|
||||
end
|
||||
elseif self._flowing == true then
|
||||
-- Disable flowing physics if not on/in flowing liquid
|
||||
self._flowing = false
|
||||
return
|
||||
--elseif self._flowing == true then
|
||||
-- -- Disable flowing physics if not on/in flowing liquid
|
||||
-- self._flowing = false
|
||||
-- return
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -83,7 +83,7 @@ mcl_mobs.register_mob("mobs_mc:blaze", {
|
|||
shoot_offset = 1.0,
|
||||
passive = false,
|
||||
jump = true,
|
||||
jump_height = 4,
|
||||
jump_height = 1,
|
||||
fly = true,
|
||||
makes_footstep_sound = false,
|
||||
fear_height = 0,
|
||||
|
|
|
@ -22,7 +22,7 @@ mcl_mobs.register_mob("mobs_mc:chicken", {
|
|||
floats = 1,
|
||||
head_swivel = "head.control",
|
||||
bone_eye_height = 4,
|
||||
head_eye_height = 1.5,
|
||||
head_eye_height = .5,
|
||||
horizontal_head_height = -.3,
|
||||
curiosity = 10,
|
||||
head_yaw="z",
|
||||
|
|
|
@ -77,7 +77,7 @@ mcl_mobs.register_mob("mobs_mc:enderdragon", {
|
|||
damage = 10,
|
||||
knock_back = false,
|
||||
jump = true,
|
||||
jump_height = 14,
|
||||
jump_height = 10,
|
||||
fly = true,
|
||||
makes_footstep_sound = false,
|
||||
dogshoot_switch = 1,
|
||||
|
|
|
@ -59,7 +59,7 @@ mcl_mobs.register_mob("mobs_mc:ghast", {
|
|||
dogshoot_count_max =1,
|
||||
passive = false,
|
||||
jump = true,
|
||||
jump_height = 4,
|
||||
jump_height = 1,
|
||||
floats=1,
|
||||
fly = true,
|
||||
makes_footstep_sound = false,
|
||||
|
|
|
@ -158,7 +158,7 @@ local horse = {
|
|||
floats = 1,
|
||||
makes_footstep_sound = true,
|
||||
jump = true,
|
||||
jump_height = 5.75, -- can clear 2.5 blocks
|
||||
jump_height = 2.5, -- can clear 2.5 blocks
|
||||
drops = {
|
||||
{name = "mcl_mobitems:leather",
|
||||
chance = 1,
|
||||
|
@ -560,7 +560,7 @@ donkey.collisionbox = {
|
|||
horse.collisionbox[6] * d,
|
||||
}
|
||||
donkey.jump = true
|
||||
donkey.jump_height = 3.75 -- can clear 1 block height
|
||||
donkey.jump_height = 1 -- can clear 1 block height
|
||||
|
||||
|
||||
mcl_mobs.register_mob("mobs_mc:donkey", donkey)
|
||||
|
|
|
@ -134,7 +134,7 @@ local slime_big = {
|
|||
walk_velocity = 1.9,
|
||||
run_velocity = 1.9,
|
||||
walk_chance = 0,
|
||||
jump_height = 5.2,
|
||||
jump_height = 1,
|
||||
fear_height = 0,
|
||||
spawn_small_alternative = "mobs_mc:slime_small",
|
||||
on_die = spawn_children_on_die("mobs_mc:slime_small", 1.0, 1.5),
|
||||
|
@ -156,7 +156,6 @@ slime_small.damage = 3
|
|||
slime_small.reach = 2.25
|
||||
slime_small.walk_velocity = 1.8
|
||||
slime_small.run_velocity = 1.8
|
||||
slime_small.jump_height = 4.3
|
||||
slime_small.spawn_small_alternative = "mobs_mc:slime_tiny"
|
||||
slime_small.on_die = spawn_children_on_die("mobs_mc:slime_tiny", 0.6, 1.0)
|
||||
mcl_mobs.register_mob("mobs_mc:slime_small", slime_small)
|
||||
|
@ -181,7 +180,6 @@ slime_tiny.drops = {
|
|||
}
|
||||
slime_tiny.walk_velocity = 1.7
|
||||
slime_tiny.run_velocity = 1.7
|
||||
slime_tiny.jump_height = 3
|
||||
slime_tiny.spawn_small_alternative = nil
|
||||
slime_tiny.on_die = nil
|
||||
|
||||
|
@ -363,7 +361,7 @@ local magma_cube_big = {
|
|||
attack_type = "dogfight",
|
||||
passive = false,
|
||||
jump = true,
|
||||
jump_height = 8,
|
||||
jump_height = 4,
|
||||
walk_chance = 0,
|
||||
fear_height = 0,
|
||||
spawn_small_alternative = "mobs_mc:magma_cube_small",
|
||||
|
@ -386,7 +384,7 @@ magma_cube_small.damage = 3
|
|||
magma_cube_small.reach = 2.1
|
||||
magma_cube_small.walk_velocity = .8
|
||||
magma_cube_small.run_velocity = 2.0
|
||||
magma_cube_small.jump_height = 6
|
||||
magma_cube_small.jump_height = 2
|
||||
magma_cube_small.damage = 4
|
||||
magma_cube_small.reach = 2.75
|
||||
magma_cube_small.armor = 66
|
||||
|
@ -407,7 +405,7 @@ magma_cube_tiny.collisionbox = {-0.2505, -0.01, -0.2505, 0.2505, 0.50, 0.2505, r
|
|||
magma_cube_tiny.visual_size = {x=3.125, y=3.125}
|
||||
magma_cube_tiny.walk_velocity = 1.02
|
||||
magma_cube_tiny.run_velocity = 1.02
|
||||
magma_cube_tiny.jump_height = 4
|
||||
magma_cube_tiny.jump_height = 1
|
||||
magma_cube_tiny.damage = 3
|
||||
magma_cube_tiny.reach = 2
|
||||
magma_cube_tiny.armor = 50
|
||||
|
|
|
@ -86,7 +86,7 @@ local spider = {
|
|||
walk_velocity = 1.3,
|
||||
run_velocity = 2.4,
|
||||
jump = true,
|
||||
jump_height = 4,
|
||||
jump_height = 1,
|
||||
view_range = 16,
|
||||
floats = 1,
|
||||
drops = {
|
||||
|
|
|
@ -2,16 +2,34 @@
|
|||
|
||||
local S = minetest.get_translator("mobs_mc")
|
||||
|
||||
--###################
|
||||
--################### STALKER
|
||||
--###################
|
||||
-- foliage and grass palettes, loaded from mcl_maps
|
||||
local palettes = {}
|
||||
|
||||
local function load_json_file(name)
|
||||
local file = assert(io.open(name, "r"))
|
||||
local data = minetest.parse_json(file:read("*all"))
|
||||
file:close()
|
||||
return data
|
||||
end
|
||||
local mapmodpath = minetest.get_modpath("mcl_maps")
|
||||
if mapmodpath then
|
||||
for k,v in pairs(load_json_file(mapmodpath .. "/palettes_grass.json")) do
|
||||
palettes[k] = v
|
||||
end
|
||||
for k,v in pairs(load_json_file(mapmodpath .. "/palettes_foliage.json")) do
|
||||
palettes[k] = v
|
||||
end
|
||||
for k,v in pairs(load_json_file(mapmodpath .. "/palettes_water.json")) do
|
||||
palettes[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function get_texture(self, prev)
|
||||
local standing_on = self.standing_on
|
||||
local texture
|
||||
local texture_suff = ""
|
||||
if standing_on and standing_on.groups.solid then
|
||||
if standing_on and (standing_on.walkable or standing_on.groups.liquid) then
|
||||
local tiles = standing_on.tiles
|
||||
if tiles then
|
||||
local tile = tiles[1]
|
||||
|
@ -27,21 +45,47 @@ local function get_texture(self, prev)
|
|||
if not color then
|
||||
color = minetest.colorspec_to_colorstring(standing_on.color)
|
||||
end
|
||||
-- handle param2
|
||||
if standing_on.palette and self.standing_on_node then
|
||||
local param2
|
||||
if standing_on.paramtype2 == "color" then
|
||||
param2 = self.standing_on_node.param2
|
||||
elseif standing_on.paramtype2 == "colorfacedir" then
|
||||
param2 = math.floor(self.standing_on_node.param2 / 8)
|
||||
elseif standing_on.paramtype2 == "colorwallmounted" then
|
||||
param2 = math.floor(self.standing_on_node.param2 / 32)
|
||||
elseif standing_on.paramtype2 == "color4dir" then
|
||||
param2 = math.floor(self.standing_on_node.param2 / 64)
|
||||
elseif standing_on.paramtype2 == "colordegrotate" then
|
||||
param2 = math.floor(self.standing_on_node.param2 / 8)
|
||||
end
|
||||
local palette = palettes[standing_on.palette]
|
||||
local oldcol = color
|
||||
if param2 and palette then
|
||||
local c = palette[param2 + 1]
|
||||
if c then color = minetest.rgba(c[1], c[2], c[3], c[4]) end
|
||||
end
|
||||
end
|
||||
if color then
|
||||
texture_suff = "^[multiply:" .. color .. "^[hsl:0:0:20"
|
||||
texture_suff = "^[multiply:" .. color .. "^[contrast:20:10" --"^[hsl:0:0:20"
|
||||
end
|
||||
end
|
||||
end
|
||||
if not texture or texture == "" then
|
||||
if prev then return prev end
|
||||
-- try to keep last texture when, e.g., falling
|
||||
if prev and (not (not self.attack)) == (string.find(prev, "vl_mobs_starker_overlay_angry.png") ~= nil) then
|
||||
return prev
|
||||
end
|
||||
texture = "vl_stalker_default.png"
|
||||
end
|
||||
texture = texture:gsub("([\\^:\\[])","\\%1") -- escape texture modifiers
|
||||
texture = "([combine:16x24:0,0=(" .. texture .. "):0,16=(" .. texture ..")".. texture_suff
|
||||
if self.attack then
|
||||
texture = texture .. ")^vl_mobs_stalker_overlay_angry.png"
|
||||
if texture_suff then texture = texture .. texture_suff end
|
||||
else
|
||||
texture = texture .. ")^vl_mobs_stalker_overlay.png"
|
||||
texture = texture:gsub("([\\^:\\[])", "\\%1") -- escape texture modifiers
|
||||
texture = "(vl_stalker_default.png^[combine:16x24:0,0=(" .. texture .. "):0,16=(" .. texture .. ")" .. texture_suff .. ")"
|
||||
end
|
||||
if self.attack then
|
||||
texture = texture .. "^vl_mobs_stalker_overlay_angry.png"
|
||||
else
|
||||
texture = texture .. "^vl_mobs_stalker_overlay.png"
|
||||
end
|
||||
return texture
|
||||
end
|
||||
|
|
|
@ -13,14 +13,6 @@ local anti_troll = minetest.settings:get_bool("wither_anti_troll_measures", fals
|
|||
local WITHER_INIT_BOOM = 7
|
||||
local WITHER_MELEE_COOLDOWN = 3
|
||||
|
||||
local function atan(x)
|
||||
if not x or x ~= x then
|
||||
return 0
|
||||
else
|
||||
return math.atan(x)
|
||||
end
|
||||
end
|
||||
|
||||
--###################
|
||||
--################### WITHER
|
||||
--###################
|
||||
|
@ -96,7 +88,7 @@ mcl_mobs.register_mob("mobs_mc:wither", {
|
|||
distance = 60,
|
||||
},
|
||||
jump = true,
|
||||
jump_height = 10,
|
||||
jump_height = 5,
|
||||
fly = true,
|
||||
makes_footstep_sound = false,
|
||||
dogshoot_switch = 1, -- unused
|
||||
|
@ -260,7 +252,7 @@ mcl_mobs.register_mob("mobs_mc:wither", {
|
|||
z = p.z - s.z
|
||||
}
|
||||
|
||||
local yaw = (atan(vec.z / vec.x) +math.pi/ 2) - self.rotate
|
||||
local yaw = (atan2(vec.z, vec.x) +math.pi/ 2) - self.rotate
|
||||
if p.x > s.x then yaw = yaw +math.pi end
|
||||
yaw = self:set_yaw( yaw, 0, dtime)
|
||||
|
||||
|
|
|
@ -85,7 +85,7 @@ local zombie = {
|
|||
fear_height = 4,
|
||||
pathfinding = 1,
|
||||
jump = true,
|
||||
jump_height = 4,
|
||||
jump_height = 1,
|
||||
group_attack = { "mobs_mc:zombie", "mobs_mc:baby_zombie", "mobs_mc:husk", "mobs_mc:baby_husk" },
|
||||
drops = drops_zombie,
|
||||
animation = {
|
||||
|
|
Loading…
Reference in New Issue