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)
|
||||||
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
|
function mob_class:update_tag() --update nametag and/or the debug box
|
||||||
local tag
|
local tag
|
||||||
if mobs_debug then
|
if mobs_debug then
|
||||||
|
@ -206,7 +216,6 @@ function mob_class:mob_activate(staticdata, def, dtime)
|
||||||
self.standing_in = NODE_IGNORE
|
self.standing_in = NODE_IGNORE
|
||||||
self.standing_on = NODE_IGNORE
|
self.standing_on = NODE_IGNORE
|
||||||
self.standing_under = NODE_IGNORE
|
self.standing_under = NODE_IGNORE
|
||||||
self.standing_body = NODE_IGNORE
|
|
||||||
self.standing_depth = 0
|
self.standing_depth = 0
|
||||||
self.state = self.state or "stand"
|
self.state = self.state or "stand"
|
||||||
self.jump_sound_cooloff = 0 -- used to prevent jump sound from being played too often in short time
|
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.blinktimer = 0
|
||||||
self.blinkstatus = false
|
self.blinkstatus = false
|
||||||
|
|
||||||
|
self.acceleration = vector.zero()
|
||||||
|
|
||||||
self.nametag = self.nametag or def.nametag
|
self.nametag = self.nametag or def.nametag
|
||||||
|
|
||||||
self.object:set_properties(self)
|
self.object:set_properties(self)
|
||||||
|
@ -299,22 +310,22 @@ end
|
||||||
|
|
||||||
local function on_step_work(self, dtime, moveresult)
|
local function on_step_work(self, dtime, moveresult)
|
||||||
local pos = self.object:get_pos()
|
local pos = self.object:get_pos()
|
||||||
if not pos then return end
|
if not pos or self.removed then return end
|
||||||
if self:check_despawn(pos, dtime) then return true end
|
if self:check_despawn(pos, dtime) then return end
|
||||||
if self:outside_limits() then return end
|
if self:outside_limits() then return end
|
||||||
|
|
||||||
-- Update what we know of the mobs environment for physics and movement
|
pos = self:limit_vel_acc_for_large_dtime(pos, dtime, moveresult) -- limit maximum movement to reduce lag effects
|
||||||
self:update_standing()
|
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()
|
local player_in_active_range = self:player_in_active_range()
|
||||||
-- The following functions return true when the mob died and we should stop processing
|
-- 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:check_suspend(player_in_active_range) then return end
|
||||||
if self:gravity_and_floating(pos, dtime, moveresult) then return end
|
if self:gravity_and_floating(pos, dtime, moveresult) then return end
|
||||||
if self:step_damage(dtime, pos) 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
|
if self.state == "die" then return end
|
||||||
self._can_jump_cliff = not self._jumping_cliff and self:can_jump_cliff()
|
self._can_jump_cliff = not self._jumping_cliff and self:can_jump_cliff()
|
||||||
--self:flop()
|
self:flop()
|
||||||
self:smooth_rotation(dtime)
|
self:smooth_rotation(dtime)
|
||||||
|
|
||||||
if player_in_active_range then
|
if player_in_active_range then
|
||||||
|
@ -350,6 +361,7 @@ local function on_step_work(self, dtime, moveresult)
|
||||||
self:smooth_acceleration(dtime)
|
self:smooth_acceleration(dtime)
|
||||||
local cx, cz = self:collision()
|
local cx, cz = self:collision()
|
||||||
self.object:add_velocity(vector.new(cx, 0, cz))
|
self.object:add_velocity(vector.new(cx, 0, cz))
|
||||||
|
self:update_vel_acc(dtime)
|
||||||
if mobs_debug then self:update_tag() 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
|
end
|
||||||
|
@ -416,9 +428,7 @@ local function update_lifetimer(dtime)
|
||||||
timer = 0
|
timer = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
minetest.register_globalstep(function(dtime)
|
minetest.register_globalstep(update_lifetimer)
|
||||||
update_lifetimer(dtime)
|
|
||||||
end)
|
|
||||||
|
|
||||||
minetest.register_chatcommand("clearmobs", {
|
minetest.register_chatcommand("clearmobs", {
|
||||||
privs = { maphack = true },
|
privs = { maphack = true },
|
||||||
|
|
|
@ -3,6 +3,7 @@ local mob_class = mcl_mobs.mob_class
|
||||||
|
|
||||||
local damage_enabled = minetest.settings:get_bool("enable_damage")
|
local damage_enabled = minetest.settings:get_bool("enable_damage")
|
||||||
local mobs_griefing = minetest.settings:get_bool("mobs_griefing") ~= false
|
local mobs_griefing = minetest.settings:get_bool("mobs_griefing") ~= false
|
||||||
|
local mobs_see_through_opaque = mcl_mobs.see_through_opaque
|
||||||
|
|
||||||
-- pathfinding settings
|
-- pathfinding settings
|
||||||
local stuck_timeout = 3 -- how long before mob gets stuck in place and starts searching
|
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)
|
self.path.lastpos = vector_copy(s)
|
||||||
|
|
||||||
local use_pathfind = false
|
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
|
-- im stuck, search for path
|
||||||
if not has_lineofsight then
|
if not has_lineofsight then
|
||||||
|
@ -157,12 +159,7 @@ function mob_class:smart_mobs(s, p, dist, dtime)
|
||||||
|
|
||||||
local dropheight = 12
|
local dropheight = 12
|
||||||
if self.fear_height ~= 0 then dropheight = self.fear_height end
|
if self.fear_height ~= 0 then dropheight = self.fear_height end
|
||||||
local jumpheight = 0
|
local jumpheight = self.jump and floor(self.jump_height + 0.1) or 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
|
|
||||||
self.path.way = minetest.find_path(s, p1, 16, jumpheight, dropheight, "A*_noprefetch")
|
self.path.way = minetest.find_path(s, p1, 16, jumpheight, dropheight, "A*_noprefetch")
|
||||||
|
|
||||||
self.state = ""
|
self.state = ""
|
||||||
|
|
|
@ -75,7 +75,7 @@ function mob_class:mob_sound(soundname, is_opinion, fixed_pitch)
|
||||||
local sound = soundinfo[soundname]
|
local sound = soundinfo[soundname]
|
||||||
if not sound then return end
|
if not sound then return end
|
||||||
if is_opinion and self.opinion_sound_cooloff > 0 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
|
if not fixed_pitch then
|
||||||
pitch = soundinfo.base_pitch or 1
|
pitch = soundinfo.base_pitch or 1
|
||||||
if self.child and not self.sounds_child then pitch = pitch * 1.5 end
|
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 path = minetest.get_modpath(modname)
|
||||||
local S = minetest.get_translator(modname)
|
local S = minetest.get_translator(modname)
|
||||||
mcl_mobs.fallback_node = minetest.registered_aliases["mapgen_dirt"] or "mcl_core:dirt"
|
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.
|
-- used by the libaries below.
|
||||||
-- get node but use fallback for nil or unknown
|
-- get node but use fallback for nil or unknown
|
||||||
|
@ -147,20 +148,25 @@ function mcl_mobs.register_mob(name, def)
|
||||||
end
|
end
|
||||||
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}
|
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>:
|
-- Workaround for <https://github.com/minetest/minetest/issues/5966>:
|
||||||
-- Increase upper Y limit to avoid mobs glitching through solid nodes.
|
-- 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.
|
-- FIXME: Remove workaround if it's no longer needed.
|
||||||
if collisionbox[5] < 0.79 then
|
--if collisionbox[5] < 0.79 then
|
||||||
collisionbox[5] = 0.79
|
-- collisionbox[5] = 0.79
|
||||||
end
|
--end
|
||||||
local final_def = {
|
local final_def = {
|
||||||
use_texture_alpha = def.use_texture_alpha,
|
use_texture_alpha = def.use_texture_alpha,
|
||||||
head_swivel = def.head_swivel or nil, -- bool to activate this function
|
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_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
|
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
|
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
|
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
|
head_yaw = def.head_yaw or "y", -- axis to rotate head on
|
||||||
horizontal_head_height = def.horizontal_head_height or 0,
|
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,
|
spawn_small_alternative = def.spawn_small_alternative,
|
||||||
do_custom = def.do_custom,
|
do_custom = def.do_custom,
|
||||||
detach_child = def.detach_child,
|
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
|
rotate = math.rad(def.rotate or 0), -- 0=front, 90=side, 180=back, 270=side2
|
||||||
lifetimer = def.lifetimer or 57.73,
|
lifetimer = def.lifetimer or 57.73,
|
||||||
hp_min = scale_difficulty(def.hp_min, 5, 1),
|
hp_min = scale_difficulty(def.hp_min, 5, 1),
|
||||||
|
@ -398,11 +404,8 @@ end
|
||||||
|
|
||||||
-- register arrow for shoot attack
|
-- register arrow for shoot attack
|
||||||
function mcl_mobs.register_arrow(name, def)
|
function mcl_mobs.register_arrow(name, def)
|
||||||
|
|
||||||
if not name or not def then return end -- errorcheck
|
if not name or not def then return end -- errorcheck
|
||||||
|
|
||||||
minetest.register_entity(name, {
|
minetest.register_entity(name, {
|
||||||
|
|
||||||
physical = false,
|
physical = false,
|
||||||
visual = def.visual,
|
visual = def.visual,
|
||||||
visual_size = def.visual_size,
|
visual_size = def.visual_size,
|
||||||
|
@ -426,31 +429,21 @@ function mcl_mobs.register_arrow(name, def)
|
||||||
self._puncher = puncher
|
self._puncher = puncher
|
||||||
end,
|
end,
|
||||||
collisionbox = def.collisionbox or {0, 0, 0, 0, 0, 0},
|
collisionbox = def.collisionbox or {0, 0, 0, 0, 0, 0},
|
||||||
automatic_face_movement_dir = def.rotate
|
automatic_face_movement_dir = def.rotate and (def.rotate - (math.pi / 180)) or false,
|
||||||
and (def.rotate - (math.pi / 180)) or false,
|
|
||||||
|
|
||||||
on_activate = def.on_activate,
|
on_activate = def.on_activate,
|
||||||
|
|
||||||
on_step = def.on_step or function(self, dtime)
|
on_step = def.on_step or function(self, dtime)
|
||||||
|
|
||||||
self.timer = self.timer + dtime
|
self.timer = self.timer + dtime
|
||||||
|
|
||||||
local pos = self.object:get_pos()
|
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)
|
mcl_burning.extinguish(self.object)
|
||||||
self.object:remove();
|
self.object:remove();
|
||||||
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
-- does arrow have a tail (fireball)
|
-- does arrow have a tail (fireball)
|
||||||
if def.tail
|
if def.tail == 1 and def.tail_texture then
|
||||||
and def.tail == 1
|
|
||||||
and def.tail_texture then
|
|
||||||
|
|
||||||
minetest.add_particle({
|
minetest.add_particle({
|
||||||
pos = pos,
|
pos = pos,
|
||||||
velocity = {x = 0, y = 0, z = 0},
|
velocity = {x = 0, y = 0, z = 0},
|
||||||
|
@ -464,24 +457,17 @@ function mcl_mobs.register_arrow(name, def)
|
||||||
end
|
end
|
||||||
|
|
||||||
if self.hit_node then
|
if self.hit_node then
|
||||||
|
|
||||||
local node = node_ok(pos).name
|
local node = node_ok(pos).name
|
||||||
|
|
||||||
if minetest.registered_nodes[node].walkable then
|
if minetest.registered_nodes[node].walkable then
|
||||||
|
|
||||||
self.hit_node(self, pos, node)
|
self.hit_node(self, pos, node)
|
||||||
|
|
||||||
if self.drop == true then
|
if self.drop == true then
|
||||||
|
|
||||||
pos.y = pos.y + 1
|
pos.y = pos.y + 1
|
||||||
|
|
||||||
self.lastpos = (self.lastpos or pos)
|
self.lastpos = (self.lastpos or pos)
|
||||||
|
|
||||||
minetest.add_item(self.lastpos, self.object:get_luaentity().name)
|
minetest.add_item(self.lastpos, self.object:get_luaentity().name)
|
||||||
end
|
end
|
||||||
|
|
||||||
self.object:remove();
|
self.object:remove();
|
||||||
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -498,12 +484,8 @@ function mcl_mobs.register_arrow(name, def)
|
||||||
end
|
end
|
||||||
|
|
||||||
if self.hit_player or self.hit_mob or self.hit_object then
|
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
|
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.hit_player(self, object)
|
||||||
self.object:remove();
|
self.object:remove();
|
||||||
return
|
return
|
||||||
|
@ -544,30 +526,20 @@ end
|
||||||
-- * spawn_egg=1: Spawn egg (generic mob, no metadata)
|
-- * spawn_egg=1: Spawn egg (generic mob, no metadata)
|
||||||
-- * spawn_egg=2: Spawn egg (captured/tamed mob, 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)
|
function mcl_mobs.register_egg(mob_id, desc, background_color, overlay_color, addegg, no_creative)
|
||||||
|
local grp = { spawn_egg = 1 }
|
||||||
local grp = {spawn_egg = 1}
|
if no_creative == true then grp.not_in_creative_inventory = 1 end
|
||||||
|
|
||||||
-- 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 invimg = "(spawn_egg.png^[multiply:" .. background_color ..")^(spawn_egg_overlay.png^[multiply:" .. overlay_color .. ")"
|
local invimg = "(spawn_egg.png^[multiply:" .. background_color ..")^(spawn_egg_overlay.png^[multiply:" .. overlay_color .. ")"
|
||||||
if old_spawn_icons then
|
if old_spawn_icons then
|
||||||
local mobname = mob_id:gsub("mobs_mc:","")
|
local fn = "mobs_mc_spawn_icon_" .. mob_id:gsub("mobs_mc:","") .. ".png"
|
||||||
local fn = "mobs_mc_spawn_icon_"..mobname..".png"
|
if mcl_util.file_exists(minetest.get_modpath("mobs_mc").."/textures/"..fn) then invimg = fn end
|
||||||
if mcl_util.file_exists(minetest.get_modpath("mobs_mc").."/textures/"..fn) then
|
|
||||||
invimg = fn
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
if addegg == 1 then
|
if addegg == 1 then
|
||||||
invimg = "mobs_chicken_egg.png^(" .. invimg ..
|
invimg = "mobs_chicken_egg.png^(" .. invimg .. "^[mask:mobs_chicken_egg_overlay.png)"
|
||||||
"^[mask:mobs_chicken_egg_overlay.png)"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- register old stackable mob egg
|
-- register old stackable mob egg
|
||||||
minetest.register_craftitem(mob_id, {
|
minetest.register_craftitem(mob_id, {
|
||||||
|
|
||||||
description = desc,
|
description = desc,
|
||||||
inventory_image = invimg,
|
inventory_image = invimg,
|
||||||
groups = grp,
|
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 dim = mcl_worlds.pos_to_dimension(placer:get_pos())
|
||||||
local mob_light_lvl = {mcl_mobs:mob_light_lvl(itemstack:get_name(),dim)}
|
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
|
-- Handle egg conversion
|
||||||
local convert_to = (minetest.registered_entities[mob_name] or {})._convert_to
|
local convert_to = (minetest.registered_entities[mob_name] or {})._convert_to
|
||||||
if convert_to then mob_name = convert_to end
|
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
|
return itemstack
|
||||||
end
|
end
|
||||||
|
|
||||||
if not minetest.registered_entities[mob_name] then
|
if not minetest.registered_entities[mob_name] then return itemstack end
|
||||||
return itemstack
|
|
||||||
end
|
|
||||||
|
|
||||||
if minetest.settings:get_bool("only_peaceful_mobs", false)
|
if minetest.settings:get_bool("only_peaceful_mobs", false)
|
||||||
and minetest.registered_entities[mob_name].type == "monster" then
|
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
|
-- jump
|
||||||
if ctrl.jump then
|
if ctrl.jump then
|
||||||
if velo.y == 0 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
|
acce_y = acce_y + 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
local math, vector, minetest, mcl_mobs = math, vector, minetest, mcl_mobs
|
local math, vector, minetest, mcl_mobs = math, vector, minetest, mcl_mobs
|
||||||
local mob_class = mcl_mobs.mob_class
|
local mob_class = mcl_mobs.mob_class
|
||||||
local DEFAULT_FALL_SPEED = -9.81*1.5
|
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 FLOP_HOR_SPEED = 1.5
|
||||||
|
|
||||||
local CHECK_HERD_FREQUENCY = 4
|
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 logging = minetest.settings:get_bool("mcl_logging_mobs_movement", true)
|
||||||
local mobs_griefing = minetest.settings:get_bool("mobs_griefing", 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 random = math.random
|
||||||
local sin = math.sin
|
local sin = math.sin
|
||||||
|
@ -31,7 +30,8 @@ local vector_offset = vector.offset
|
||||||
local vector_distance = vector.distance
|
local vector_distance = vector.distance
|
||||||
|
|
||||||
local node_ok = mcl_mobs.node_ok
|
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
|
-- Stop movement and stand
|
||||||
function mob_class:stand()
|
function mob_class:stand()
|
||||||
|
@ -86,12 +86,12 @@ function mob_class:target_visible(origin)
|
||||||
local cbox = self.collisionbox
|
local cbox = self.collisionbox
|
||||||
-- TODO also worth testing midway between feet and head?
|
-- TODO also worth testing midway between feet and head?
|
||||||
-- to top of entity
|
-- 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
|
-- to feed of entity
|
||||||
if self.attack:is_player() then
|
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
|
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
|
end
|
||||||
|
|
||||||
--minetest.log("start targ_head_height: " .. dump(targ_head_height))
|
--minetest.log("start targ_head_height: " .. dump(targ_head_height))
|
||||||
|
@ -111,7 +111,7 @@ end
|
||||||
|
|
||||||
-- check line of sight
|
-- check line of sight
|
||||||
function mob_class:line_of_sight(pos1, pos2, stepsize)
|
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
|
end
|
||||||
|
|
||||||
function mob_class:can_jump_cliff()
|
function mob_class:can_jump_cliff()
|
||||||
|
@ -169,7 +169,7 @@ function mob_class:is_at_cliff_or_danger()
|
||||||
return "free fall"
|
return "free fall"
|
||||||
end
|
end
|
||||||
local height = ypos + 0.4 - blocker.y
|
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 height >= self.fear_height and random() < chance then
|
||||||
if logging then
|
if logging then
|
||||||
minetest.log("action", "[mcl_mobs] "..self.name.." avoiding drop of "..height) --.." chance "..chance)
|
minetest.log("action", "[mcl_mobs] "..self.name.." avoiding drop of "..height) --.." chance "..chance)
|
||||||
|
@ -304,17 +304,12 @@ function mob_class:do_jump()
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
v.y = math.min(v.y, 0) + self.jump_height -- + 0.3
|
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)
|
||||||
--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(-self.fall_speed, math.max(v.y, self.fall_speed))
|
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.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
|
if self.jump_sound_cooloff <= 0 then
|
||||||
self:mob_sound("jump")
|
self:mob_sound("jump")
|
||||||
|
@ -454,7 +449,7 @@ function mob_class:check_runaway_from()
|
||||||
sp = s
|
sp = s
|
||||||
dist = vector_distance(p, s)
|
dist = vector_distance(p, s)
|
||||||
-- choose closest player/mpb to runaway from
|
-- 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
|
-- aim higher to make looking up hills more realistic
|
||||||
min_dist = dist
|
min_dist = dist
|
||||||
min_player = player
|
min_player = player
|
||||||
|
@ -531,29 +526,25 @@ function mob_class:check_follow()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- swimmers flop when out of their element, and swim again when back in
|
||||||
function mob_class:flop()
|
function mob_class:flop()
|
||||||
-- swimmers flop when out of their element, and swim again when back in
|
if not self.fly then return end
|
||||||
if self.fly then
|
if not self:flight_check() then
|
||||||
local s = self.object:get_pos()
|
self.state = "flop"
|
||||||
|
self.acceleration.y = DEFAULT_FALL_SPEED
|
||||||
if self:flight_check(s) == false then
|
local sdef = self.standing_on
|
||||||
self.state = "flop"
|
if sdef and sdef.walkable then -- flop on ground
|
||||||
self.object:set_acceleration(vector_new(0, DEFAULT_FALL_SPEED, 0))
|
if self.object:get_velocity().y == 0 then
|
||||||
|
self:mob_sound("flop")
|
||||||
local p = self.object:get_pos()
|
self.object:add_velocity(vector_new(0, FLOP_VEL, 0))
|
||||||
local sdef = minetest.registered_nodes[node_ok(vector_offset(p, 0, self.collisionbox[2] - 0.2, 0)).name]
|
self:turn_by(TWOPI * random(), 8)
|
||||||
-- Flop on ground
|
self:set_velocity(FLOP_HOR_SPEED)
|
||||||
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
|
|
||||||
end
|
end
|
||||||
|
|
||||||
self:set_animation("stand", true)
|
|
||||||
elseif self.state == "flop" then
|
|
||||||
self:stand()
|
|
||||||
end
|
end
|
||||||
|
self:set_animation("stand", true)
|
||||||
|
elseif self.state == "flop" then
|
||||||
|
--self:stand()
|
||||||
|
self.acceleration.y = 0
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -625,28 +616,33 @@ function mob_class:do_states_walk()
|
||||||
end
|
end
|
||||||
-- facing wall? then turn
|
-- facing wall? then turn
|
||||||
local facing_wall = false
|
local facing_wall = false
|
||||||
local cbox = self.collisionbox
|
-- todo: use moveresult collision info here?
|
||||||
local dir_x, dir_z = -sin(yaw - QUARTERPI) * (cbox[4] + 0.5), cos(yaw - QUARTERPI) * (cbox[4] + 0.5)
|
if self:get_velocity_xyz() < 0.1 then
|
||||||
local nodface = minetest.registered_nodes[minetest.get_node(vector_offset(s, dir_x, cbox[5] - cbox[2], dir_z)).name]
|
facing_wall = true
|
||||||
if nodface and nodface.walkable then
|
elseif not facing_wall then
|
||||||
dir_x, dir_z = -sin(yaw + QUARTERPI) * (cbox[4] + 0.5), cos(yaw + QUARTERPI) * (cbox[4] + 0.5)
|
local cbox = self.collisionbox
|
||||||
nodface = minetest.registered_nodes[minetest.get_node(vector_offset(s, dir_x, cbox[5] - cbox[2], dir_z)).name]
|
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
|
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
|
||||||
end
|
end
|
||||||
if facing_wall then
|
if facing_wall then
|
||||||
if logging then
|
if logging then
|
||||||
minetest.log("action", "[mcl_mobs] "..self.name.." facing a wall, turning.")
|
minetest.log("action", "[mcl_mobs] "..self.name.." facing a wall, turning.")
|
||||||
end
|
end
|
||||||
self:turn_by(TWOPI * (random() - 0.5), 6)
|
self:turn_by(TWOPI * (random() - 0.5), 10)
|
||||||
-- otherwise randomly turn
|
-- otherwise randomly turn
|
||||||
elseif random() <= 0.3 then
|
elseif random() <= 0.3 then
|
||||||
local home = self._home or self._bed
|
local home = self._home or self._bed
|
||||||
if home and random() < 0.1 then
|
if home and random() < 0.1 then
|
||||||
self:turn_in_direction(home.x - s.x, home.z - s.z, 8)
|
self:turn_in_direction(home.x - s.x, home.z - s.z, 8)
|
||||||
else
|
else
|
||||||
self:turn_by(QUARTERPI * (random() - 0.5), 10)
|
self:turn_by(QUARTERPI * (random() - 0.5), 20)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
self:set_velocity(self.walk_velocity)
|
self:set_velocity(self.walk_velocity)
|
||||||
|
@ -687,9 +683,7 @@ function mob_class:do_states_stand(player_in_active_range)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- npc's ordered to stand stay standing
|
-- npc's ordered to stand stay standing
|
||||||
if self.order == "stand" or self.order == "sleep" or self.order == "work" then
|
if self.order ~= "stand" and self.order ~= "sleep" and self.order ~= "work" then
|
||||||
|
|
||||||
else
|
|
||||||
if player_in_active_range then
|
if player_in_active_range then
|
||||||
if self.walk_chance ~= 0
|
if self.walk_chance ~= 0
|
||||||
and self.facing_fence ~= true
|
and self.facing_fence ~= true
|
||||||
|
@ -709,8 +703,7 @@ end
|
||||||
function mob_class:do_states_runaway()
|
function mob_class:do_states_runaway()
|
||||||
self.runaway_timer = self.runaway_timer + 1
|
self.runaway_timer = self.runaway_timer + 1
|
||||||
-- stop after 5 seconds or when at cliff
|
-- stop after 5 seconds or when at cliff
|
||||||
if self.runaway_timer > 5
|
if self.runaway_timer > 5 or self:is_at_cliff_or_danger() then
|
||||||
or self:is_at_cliff_or_danger() then
|
|
||||||
self.runaway_timer = 0
|
self.runaway_timer = 0
|
||||||
self:stand()
|
self:stand()
|
||||||
self:turn_by(PI * (random() + 0.5), 8)
|
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 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
|
||||||
local ENTITY_CRAMMING_MAX = 24
|
local ENTITY_CRAMMING_MAX = 24
|
||||||
local CRAMMING_DAMAGE = 3
|
local CRAMMING_DAMAGE = 3
|
||||||
local DEATH_DELAY = 0.5
|
local DEATH_DELAY = 0.5
|
||||||
|
@ -17,6 +19,7 @@ local abs = math.abs
|
||||||
local atan2 = math.atan2
|
local atan2 = math.atan2
|
||||||
local sin = math.sin
|
local sin = math.sin
|
||||||
local cos = math.cos
|
local cos = math.cos
|
||||||
|
local sqrt = math.sqrt
|
||||||
local node_ok = mcl_mobs.node_ok
|
local node_ok = mcl_mobs.node_ok
|
||||||
|
|
||||||
local PATHFINDING = "gowp"
|
local PATHFINDING = "gowp"
|
||||||
|
@ -48,13 +51,25 @@ end
|
||||||
-- standing_on: node below
|
-- standing_on: node below
|
||||||
-- standing_under: node above
|
-- standing_under: node above
|
||||||
-- these may be "nil" (= ignore) and are otherwise already resolved via minetest.registered_nodes
|
-- these may be "nil" (= ignore) and are otherwise already resolved via minetest.registered_nodes
|
||||||
function mob_class:update_standing()
|
function mob_class:update_standing(pos, moveresult)
|
||||||
local pos = self.object:get_pos()
|
|
||||||
local temp_pos = vector.offset(pos, 0, self.collisionbox[2] + 0.5, 0) -- foot level
|
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
|
self.standing_in = minetest.registered_nodes[minetest.get_node(temp_pos).name] or NODE_IGNORE
|
||||||
temp_pos.y = temp_pos.y - 1.5 -- below
|
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_on_node = minetest.get_node(temp_pos) -- to allow access to param2 in, e.g., stalker
|
||||||
self.standing_height = pos.y - math.ceil(temp_pos.y + 0.5) + self.head_eye_height * 0.75
|
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
|
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
|
self.standing_under = minetest.registered_nodes[minetest.get_node(temp_pos).name] or NODE_IGNORE
|
||||||
end
|
end
|
||||||
|
@ -77,28 +92,20 @@ function mob_class:object_in_range(object)
|
||||||
factor = factors and factors[self.name]
|
factor = factors and factors[self.name]
|
||||||
end
|
end
|
||||||
-- Distance check
|
-- Distance check
|
||||||
local dist
|
local dist = self.view_range
|
||||||
if factor and factor == 0 then
|
if factor then
|
||||||
return false
|
if factor == 0 then return false end
|
||||||
elseif factor then
|
dist = dist * factor
|
||||||
dist = self.view_range * factor
|
|
||||||
else
|
|
||||||
dist = self.view_range
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local p1, p2 = self.object:get_pos(), object:get_pos()
|
local p1, p2 = self.object:get_pos(), object:get_pos()
|
||||||
return p1 and p2 and (vector.distance(p1, p2) <= dist)
|
return p1 and p2 and (vector.distance(p1, p2) <= dist)
|
||||||
end
|
end
|
||||||
|
|
||||||
function mob_class:item_drop(cooked, looting_level)
|
function mob_class:item_drop(cooked, looting_level)
|
||||||
|
|
||||||
if not mobs_drop_items then return end
|
if not mobs_drop_items then return end
|
||||||
|
|
||||||
looting_level = looting_level or 0
|
looting_level = looting_level or 0
|
||||||
|
|
||||||
if (self.child and self.type ~= "monster") then
|
if (self.child and self.type ~= "monster") then return end
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local obj, item, num
|
local obj, item, num
|
||||||
local pos = self.object:get_pos()
|
local pos = self.object:get_pos()
|
||||||
|
@ -160,7 +167,6 @@ end
|
||||||
function mob_class:collision()
|
function mob_class:collision()
|
||||||
local pos = self.object:get_pos()
|
local pos = self.object:get_pos()
|
||||||
if not pos then return 0,0 end
|
if not pos then return 0,0 end
|
||||||
local vel = self.object:get_velocity()
|
|
||||||
local x, z = 0, 0
|
local x, z = 0, 0
|
||||||
local width = -self.collisionbox[1] + self.collisionbox[4] + 0.5
|
local width = -self.collisionbox[1] + self.collisionbox[4] + 0.5
|
||||||
for _,object in pairs(minetest.get_objects_inside_radius(pos, width)) do
|
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 pos2 = object:get_pos()
|
||||||
local vx, vz = pos.x - pos2.x, pos.z - pos2.z
|
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
|
if force > 0 then
|
||||||
force = force * force * (object:is_player() and 2 or 1) -- players push more
|
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"))
|
-- 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
|
self.target_vel = v
|
||||||
end
|
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)
|
-- calculate mob velocity (2d)
|
||||||
function mob_class:get_velocity_xz()
|
function mob_class:get_velocity_xz()
|
||||||
local v = self.object:get_velocity()
|
local v = self.object:get_velocity()
|
||||||
if not v then return 0 end
|
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
|
end
|
||||||
-- legacy API
|
-- legacy API
|
||||||
mob_class.get_velocity = mob_class.get_velocity_xz
|
mob_class.get_velocity = mob_class.get_velocity_xz
|
||||||
|
|
||||||
-- Relative turn, primarily for random turning
|
-- Relative turn, primarily for random turning
|
||||||
-- @param angle number: realative angle, in radians
|
-- @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
|
-- @param dtime deprecated: ignored now, because of smooth rotations
|
||||||
-- @return target angle
|
-- @return target angle
|
||||||
function mob_class:turn_by(angle, delay, dtime)
|
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
|
end
|
||||||
-- Turn into a direction (e.g., to the player, or away)
|
-- Turn into a direction (e.g., to the player, or away)
|
||||||
-- @param dx number: delta in x axis to target
|
-- @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
|
if not self.target_yaw then return end
|
||||||
|
|
||||||
local delay = self.delay
|
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
|
local yaw -- resulting yaw for this tick
|
||||||
if delay and delay > 1 then
|
if delay and delay > 1 then
|
||||||
local dif = (self.target_yaw - initial_yaw + PI) % TWOPI - PI
|
local dif = (self.target_yaw - initial_yaw + PI) % TWOPI - PI
|
||||||
|
@ -286,44 +300,33 @@ function mob_class:smooth_rotation(dtime)
|
||||||
if self.shaking then
|
if self.shaking then
|
||||||
yaw = yaw + (random() * 2 - 1) / 72 * dtime
|
yaw = yaw + (random() * 2 - 1) / 72 * dtime
|
||||||
end
|
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
|
--update_roll() -- Fleckenstein easter egg
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Handling of intentional acceleration by the mob
|
-- Handling of intentional acceleration by the mob
|
||||||
-- its best to place environmental effects afterwards
|
-- 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
|
-- @param dtime number: timestep length
|
||||||
function mob_class:smooth_acceleration(dtime)
|
function mob_class:smooth_acceleration(dtime)
|
||||||
local yaw = self.target_yaw or (self.object:get_yaw() or 0) + self.rotate
|
local yaw = self.target_yaw or (self.object:get_yaw() or 0) + self.rotate
|
||||||
local vel = self.target_vel or 0
|
local vel = self.target_vel or 0
|
||||||
local x, z = -sin(yaw) * vel, cos(yaw) * vel
|
local x, z = -sin(yaw) * vel, cos(yaw) * vel
|
||||||
local v = self.object:get_velocity()
|
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)
|
v.x, v.z = v.x + w * (x - v.x), v.z + w * (z - v.z)
|
||||||
self.object:set_velocity(v)
|
self.object:set_velocity(v)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- are we flying in what we are suppose to? (taikedz)
|
-- are we flying in what we are suppose to? (taikedz)
|
||||||
function mob_class:flight_check()
|
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
|
if not self.fly_in then return false end
|
||||||
|
local nod = self.standing_in.name
|
||||||
local fly_in
|
-- todo: allow flowers etc. for birds
|
||||||
if type(self.fly_in) == "string" then
|
for _,checknode in pairs(self.fly_in) do
|
||||||
fly_in = { self.fly_in }
|
if nod == checknode then return true end
|
||||||
elseif type(self.fly_in) == "table" then
|
|
||||||
fly_in = self.fly_in
|
|
||||||
else
|
|
||||||
return false
|
|
||||||
end
|
end
|
||||||
|
|
||||||
for _,checknode in pairs(fly_in) do
|
|
||||||
if nod == checknode or nod == "ignore" then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -738,10 +741,9 @@ end
|
||||||
function mob_class:check_entity_cramming()
|
function mob_class:check_entity_cramming()
|
||||||
local p = self.object:get_pos()
|
local p = self.object:get_pos()
|
||||||
if not p then return end
|
if not p then return end
|
||||||
local oo = minetest.get_objects_inside_radius(p,1)
|
|
||||||
local mobs = {}
|
local mobs = {}
|
||||||
for i = 1,#oo do
|
for o in minetest.objects_inside_radius(p, 0.5) do
|
||||||
local l = oo[i]:get_luaentity()
|
local l = o:get_luaentity()
|
||||||
if l and l.is_mob and l.health > 0 then table.insert(mobs,l) end
|
if l and l.is_mob and l.health > 0 then table.insert(mobs,l) end
|
||||||
end
|
end
|
||||||
local clear = #mobs < ENTITY_CRAMMING_MAX
|
local clear = #mobs < ENTITY_CRAMMING_MAX
|
||||||
|
@ -769,34 +771,44 @@ end
|
||||||
-- @param moveresult table: minetest engine movement result (collisions)
|
-- @param moveresult table: minetest engine movement result (collisions)
|
||||||
-- @return true if mob died
|
-- @return true if mob died
|
||||||
function mob_class:gravity_and_floating(pos, dtime, moveresult)
|
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 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
|
if self.standing_in == NODE_IGNORE then -- not emerged yet, do not fall
|
||||||
self.object:set_velocity(vector.zero())
|
self.object:set_velocity(vector.zero())
|
||||||
return false
|
return false
|
||||||
end
|
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
|
-- Gravity
|
||||||
--local acc_y = self.fall_speed
|
local acc = vector.new(0, not self.fly and moveresult and moveresult.touching_ground and 0 or self.fall_speed, 0)
|
||||||
local acc_y = moveresult and moveresult.touching_ground and 0 or self.fall_speed
|
self.visc = 1
|
||||||
local visc = 1
|
|
||||||
local vel = self.object:get_velocity() or vector.zero()
|
local vel = self.object:get_velocity() or vector.zero()
|
||||||
local standbody = self.standing_in
|
local standbody = self.standing_in
|
||||||
if standbody.groups.water then
|
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
|
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
|
local w = (self.standing_under.groups.water and 0 or self.standing_height) -- <1 is submerged, >1 is out
|
||||||
acc_y = self.fall_speed * max(-1, min(1, 2 * w - 1)) -- -1 to +1
|
if w > 0.95 and w < 1.05 then
|
||||||
--self.object:set_acceleration(vector.new(0, -self.fall_speed * 0.5, 0))
|
acc.y = 0 -- stabilize floating
|
||||||
|
else
|
||||||
|
acc.y = self.fall_speed * max(-1, min(1, w - 1)) -- -1 to +1
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
self.start_fall_y = nil -- otherwise might receive fall damage on the next jump?
|
||||||
elseif standbody.groups.lava then
|
elseif standbody.groups.lava then
|
||||||
visc = 0.7
|
self.visc = 0.5
|
||||||
if self.floats_on_lava > 0 then
|
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
|
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
|
-- todo: relative to body height?
|
||||||
--self.object:set_acceleration(vector.new(0, -self.fall_speed * 0.5, 0))
|
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
|
end
|
||||||
|
self.start_fall_y = nil -- otherwise might receive fall damage on the next jump?
|
||||||
else
|
else
|
||||||
-- fall damage onto solid ground (bouncy ground will yield vel.y > 0)
|
-- fall damage onto solid ground (bouncy ground will yield vel.y > 0)
|
||||||
if self.fall_damage == 1 and vel.y == 0 then
|
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
|
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)
|
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)
|
-- 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
|
-- but the minetest engine already applied the current velocity on the full timestep
|
||||||
dtime = min(dtime, 0.01)
|
dtime = min(dtime, MAX_DTIME)
|
||||||
-- 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
|
-- 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)
|
self.object:set_velocity(vel)
|
||||||
end
|
end
|
||||||
|
|
||||||
function mob_class:check_water_flow()
|
-- Add water flowing for mobs from mcl_item_entity
|
||||||
-- Add water flowing for mobs from mcl_item_entity
|
function mob_class:check_water_flow(dtime, pos)
|
||||||
local p = self.object:get_pos()
|
local def = self.standing_in
|
||||||
local node = minetest.get_node_or_nil(p)
|
|
||||||
local def = node and minetest.registered_nodes[node.name]
|
|
||||||
|
|
||||||
-- Move item around on flowing liquids
|
-- Move item around on flowing liquids
|
||||||
if def and def.liquidtype == "flowing" then
|
if def and def.liquidtype == "flowing" then
|
||||||
|
|
||||||
--[[ Get flowing direction (function call from flowlib), if there's a liquid.
|
--[[ 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.
|
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. ]]
|
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
|
-- 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
|
if vec.x ~= 0 or vec.y ~= 0 or vec.z ~= 0 then
|
||||||
-- Minecraft Wiki: Flowing speed is "about 1.39 meters per second"
|
-- 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
|
-- Set new item moving speed into the direciton of the liquid
|
||||||
local newv = vector.multiply(vec, f)
|
self.acceleration = self.acceleration + vector.new(vec.x * f, -0.22, vec.z * f)
|
||||||
--self.object:set_acceleration(vector.zero())
|
--self.physical_state = true
|
||||||
self.object:add_velocity(vector.new(newv.x, -0.22, newv.z))
|
--self._flowing = true
|
||||||
|
--self.object:set_properties({ physical = true })
|
||||||
self.physical_state = true
|
|
||||||
self._flowing = true
|
|
||||||
self.object:set_properties({ physical = true })
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
elseif self._flowing == true then
|
--elseif self._flowing == true then
|
||||||
-- Disable flowing physics if not on/in flowing liquid
|
-- -- Disable flowing physics if not on/in flowing liquid
|
||||||
self._flowing = false
|
-- self._flowing = false
|
||||||
return
|
-- return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -83,7 +83,7 @@ mcl_mobs.register_mob("mobs_mc:blaze", {
|
||||||
shoot_offset = 1.0,
|
shoot_offset = 1.0,
|
||||||
passive = false,
|
passive = false,
|
||||||
jump = true,
|
jump = true,
|
||||||
jump_height = 4,
|
jump_height = 1,
|
||||||
fly = true,
|
fly = true,
|
||||||
makes_footstep_sound = false,
|
makes_footstep_sound = false,
|
||||||
fear_height = 0,
|
fear_height = 0,
|
||||||
|
|
|
@ -22,7 +22,7 @@ mcl_mobs.register_mob("mobs_mc:chicken", {
|
||||||
floats = 1,
|
floats = 1,
|
||||||
head_swivel = "head.control",
|
head_swivel = "head.control",
|
||||||
bone_eye_height = 4,
|
bone_eye_height = 4,
|
||||||
head_eye_height = 1.5,
|
head_eye_height = .5,
|
||||||
horizontal_head_height = -.3,
|
horizontal_head_height = -.3,
|
||||||
curiosity = 10,
|
curiosity = 10,
|
||||||
head_yaw="z",
|
head_yaw="z",
|
||||||
|
|
|
@ -77,7 +77,7 @@ mcl_mobs.register_mob("mobs_mc:enderdragon", {
|
||||||
damage = 10,
|
damage = 10,
|
||||||
knock_back = false,
|
knock_back = false,
|
||||||
jump = true,
|
jump = true,
|
||||||
jump_height = 14,
|
jump_height = 10,
|
||||||
fly = true,
|
fly = true,
|
||||||
makes_footstep_sound = false,
|
makes_footstep_sound = false,
|
||||||
dogshoot_switch = 1,
|
dogshoot_switch = 1,
|
||||||
|
|
|
@ -59,7 +59,7 @@ mcl_mobs.register_mob("mobs_mc:ghast", {
|
||||||
dogshoot_count_max =1,
|
dogshoot_count_max =1,
|
||||||
passive = false,
|
passive = false,
|
||||||
jump = true,
|
jump = true,
|
||||||
jump_height = 4,
|
jump_height = 1,
|
||||||
floats=1,
|
floats=1,
|
||||||
fly = true,
|
fly = true,
|
||||||
makes_footstep_sound = false,
|
makes_footstep_sound = false,
|
||||||
|
|
|
@ -158,7 +158,7 @@ local horse = {
|
||||||
floats = 1,
|
floats = 1,
|
||||||
makes_footstep_sound = true,
|
makes_footstep_sound = true,
|
||||||
jump = true,
|
jump = true,
|
||||||
jump_height = 5.75, -- can clear 2.5 blocks
|
jump_height = 2.5, -- can clear 2.5 blocks
|
||||||
drops = {
|
drops = {
|
||||||
{name = "mcl_mobitems:leather",
|
{name = "mcl_mobitems:leather",
|
||||||
chance = 1,
|
chance = 1,
|
||||||
|
@ -560,7 +560,7 @@ donkey.collisionbox = {
|
||||||
horse.collisionbox[6] * d,
|
horse.collisionbox[6] * d,
|
||||||
}
|
}
|
||||||
donkey.jump = true
|
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)
|
mcl_mobs.register_mob("mobs_mc:donkey", donkey)
|
||||||
|
|
|
@ -134,7 +134,7 @@ local slime_big = {
|
||||||
walk_velocity = 1.9,
|
walk_velocity = 1.9,
|
||||||
run_velocity = 1.9,
|
run_velocity = 1.9,
|
||||||
walk_chance = 0,
|
walk_chance = 0,
|
||||||
jump_height = 5.2,
|
jump_height = 1,
|
||||||
fear_height = 0,
|
fear_height = 0,
|
||||||
spawn_small_alternative = "mobs_mc:slime_small",
|
spawn_small_alternative = "mobs_mc:slime_small",
|
||||||
on_die = spawn_children_on_die("mobs_mc:slime_small", 1.0, 1.5),
|
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.reach = 2.25
|
||||||
slime_small.walk_velocity = 1.8
|
slime_small.walk_velocity = 1.8
|
||||||
slime_small.run_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.spawn_small_alternative = "mobs_mc:slime_tiny"
|
||||||
slime_small.on_die = spawn_children_on_die("mobs_mc:slime_tiny", 0.6, 1.0)
|
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)
|
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.walk_velocity = 1.7
|
||||||
slime_tiny.run_velocity = 1.7
|
slime_tiny.run_velocity = 1.7
|
||||||
slime_tiny.jump_height = 3
|
|
||||||
slime_tiny.spawn_small_alternative = nil
|
slime_tiny.spawn_small_alternative = nil
|
||||||
slime_tiny.on_die = nil
|
slime_tiny.on_die = nil
|
||||||
|
|
||||||
|
@ -363,7 +361,7 @@ local magma_cube_big = {
|
||||||
attack_type = "dogfight",
|
attack_type = "dogfight",
|
||||||
passive = false,
|
passive = false,
|
||||||
jump = true,
|
jump = true,
|
||||||
jump_height = 8,
|
jump_height = 4,
|
||||||
walk_chance = 0,
|
walk_chance = 0,
|
||||||
fear_height = 0,
|
fear_height = 0,
|
||||||
spawn_small_alternative = "mobs_mc:magma_cube_small",
|
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.reach = 2.1
|
||||||
magma_cube_small.walk_velocity = .8
|
magma_cube_small.walk_velocity = .8
|
||||||
magma_cube_small.run_velocity = 2.0
|
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.damage = 4
|
||||||
magma_cube_small.reach = 2.75
|
magma_cube_small.reach = 2.75
|
||||||
magma_cube_small.armor = 66
|
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.visual_size = {x=3.125, y=3.125}
|
||||||
magma_cube_tiny.walk_velocity = 1.02
|
magma_cube_tiny.walk_velocity = 1.02
|
||||||
magma_cube_tiny.run_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.damage = 3
|
||||||
magma_cube_tiny.reach = 2
|
magma_cube_tiny.reach = 2
|
||||||
magma_cube_tiny.armor = 50
|
magma_cube_tiny.armor = 50
|
||||||
|
|
|
@ -86,7 +86,7 @@ local spider = {
|
||||||
walk_velocity = 1.3,
|
walk_velocity = 1.3,
|
||||||
run_velocity = 2.4,
|
run_velocity = 2.4,
|
||||||
jump = true,
|
jump = true,
|
||||||
jump_height = 4,
|
jump_height = 1,
|
||||||
view_range = 16,
|
view_range = 16,
|
||||||
floats = 1,
|
floats = 1,
|
||||||
drops = {
|
drops = {
|
||||||
|
|
|
@ -2,16 +2,34 @@
|
||||||
|
|
||||||
local S = minetest.get_translator("mobs_mc")
|
local S = minetest.get_translator("mobs_mc")
|
||||||
|
|
||||||
--###################
|
-- foliage and grass palettes, loaded from mcl_maps
|
||||||
--################### STALKER
|
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 function get_texture(self, prev)
|
||||||
local standing_on = self.standing_on
|
local standing_on = self.standing_on
|
||||||
local texture
|
local texture
|
||||||
local texture_suff = ""
|
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
|
local tiles = standing_on.tiles
|
||||||
if tiles then
|
if tiles then
|
||||||
local tile = tiles[1]
|
local tile = tiles[1]
|
||||||
|
@ -27,21 +45,47 @@ local function get_texture(self, prev)
|
||||||
if not color then
|
if not color then
|
||||||
color = minetest.colorspec_to_colorstring(standing_on.color)
|
color = minetest.colorspec_to_colorstring(standing_on.color)
|
||||||
end
|
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
|
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
|
end
|
||||||
end
|
end
|
||||||
if not texture or texture == "" then
|
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"
|
texture = "vl_stalker_default.png"
|
||||||
end
|
if texture_suff then texture = texture .. texture_suff 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"
|
|
||||||
else
|
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
|
end
|
||||||
return texture
|
return texture
|
||||||
end
|
end
|
||||||
|
|
|
@ -13,14 +13,6 @@ local anti_troll = minetest.settings:get_bool("wither_anti_troll_measures", fals
|
||||||
local WITHER_INIT_BOOM = 7
|
local WITHER_INIT_BOOM = 7
|
||||||
local WITHER_MELEE_COOLDOWN = 3
|
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
|
--################### WITHER
|
||||||
--###################
|
--###################
|
||||||
|
@ -96,7 +88,7 @@ mcl_mobs.register_mob("mobs_mc:wither", {
|
||||||
distance = 60,
|
distance = 60,
|
||||||
},
|
},
|
||||||
jump = true,
|
jump = true,
|
||||||
jump_height = 10,
|
jump_height = 5,
|
||||||
fly = true,
|
fly = true,
|
||||||
makes_footstep_sound = false,
|
makes_footstep_sound = false,
|
||||||
dogshoot_switch = 1, -- unused
|
dogshoot_switch = 1, -- unused
|
||||||
|
@ -260,7 +252,7 @@ mcl_mobs.register_mob("mobs_mc:wither", {
|
||||||
z = p.z - s.z
|
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
|
if p.x > s.x then yaw = yaw +math.pi end
|
||||||
yaw = self:set_yaw( yaw, 0, dtime)
|
yaw = self:set_yaw( yaw, 0, dtime)
|
||||||
|
|
||||||
|
|
|
@ -85,7 +85,7 @@ local zombie = {
|
||||||
fear_height = 4,
|
fear_height = 4,
|
||||||
pathfinding = 1,
|
pathfinding = 1,
|
||||||
jump = true,
|
jump = true,
|
||||||
jump_height = 4,
|
jump_height = 1,
|
||||||
group_attack = { "mobs_mc:zombie", "mobs_mc:baby_zombie", "mobs_mc:husk", "mobs_mc:baby_husk" },
|
group_attack = { "mobs_mc:zombie", "mobs_mc:baby_zombie", "mobs_mc:husk", "mobs_mc:baby_husk" },
|
||||||
drops = drops_zombie,
|
drops = drops_zombie,
|
||||||
animation = {
|
animation = {
|
||||||
|
|
Loading…
Reference in New Issue