Arrows: Fix reverse flying arrows after long fly

This commit is contained in:
Wuzzy 2018-05-12 07:00:16 +02:00
parent fab5d17a5d
commit c7949bf703
2 changed files with 60 additions and 21 deletions

@ -62,8 +62,6 @@ minetest.register_node("mcl_bows:arrow_box", {
groups = {not_in_creative_inventory=1, dig_immediate=3}, groups = {not_in_creative_inventory=1, dig_immediate=3},
}) })
-- FIXME: Arrow velocity is a bit strange. If the arrow flies VERY long, the acceleration can cause the velocity to become negative
-- and the arrow flies backwards.
local ARROW_ENTITY={ local ARROW_ENTITY={
physical = true, physical = true,
visual = "wielditem", visual = "wielditem",
@ -80,6 +78,9 @@ local ARROW_ENTITY={
_stuckrechecktimer=nil,-- An additional timer for periodically re-checking the stuck status of an arrow _stuckrechecktimer=nil,-- An additional timer for periodically re-checking the stuck status of an arrow
_stuckin=nil, --Position of node in which arow is stuck. _stuckin=nil, --Position of node in which arow is stuck.
_shooter=nil, -- ObjectRef of player or mob who shot it _shooter=nil, -- ObjectRef of player or mob who shot it
_viscosity=0, -- Viscosity of node the arrow is currently in
_deflection_cooloff=0, -- Cooloff timer after an arrow deflection, to prevent many deflections in quick succession
} }
-- Destroy arrow entity self at pos and drops it as an item -- Destroy arrow entity self at pos and drops it as an item
@ -145,6 +146,10 @@ ARROW_ENTITY.on_step = function(self, dtime)
local closest_distance local closest_distance
local ok = false local ok = false
if self._deflection_cooloff > 0 then
self._deflection_cooloff = self._deflection_cooloff - dtime
end
-- Iterate through all objects and remember the closest attackable object -- Iterate through all objects and remember the closest attackable object
for k, obj in pairs(objs) do for k, obj in pairs(objs) do
-- Arrows can only damage players and mobs -- Arrows can only damage players and mobs
@ -210,7 +215,8 @@ ARROW_ENTITY.on_step = function(self, dtime)
if self._lastpos.x~=nil and not self._stuck then if self._lastpos.x~=nil and not self._stuck then
local def = minetest.registered_nodes[node.name] local def = minetest.registered_nodes[node.name]
local vel = self.object:get_velocity() local vel = self.object:get_velocity()
-- Arrow has stopped in one axis, so it probably hit something -- Arrow has stopped in one axis, so it probably hit something.
-- This detection is a bit clunky, but sadly, MT does not offer a direct collision detection for us. :-(
if (math.abs(vel.x) < 0.0001) or (math.abs(vel.z) < 0.0001) or (math.abs(vel.y) < 0.00001) then if (math.abs(vel.x) < 0.0001) or (math.abs(vel.z) < 0.0001) or (math.abs(vel.y) < 0.00001) then
-- Check for the node to which the arrow is pointing -- Check for the node to which the arrow is pointing
local dir local dir
@ -231,25 +237,56 @@ ARROW_ENTITY.on_step = function(self, dtime)
-- This causes a deflection in the engine. -- This causes a deflection in the engine.
if not sdef or sdef.walkable == false or snode.name == "ignore" then if not sdef or sdef.walkable == false or snode.name == "ignore" then
self._stuckin = nil self._stuckin = nil
-- Lose 1/3 of velocity on deflection if self._deflection_cooloff <= 0 then
self.object:set_velocity(vector.multiply(vel, 0.6667)) -- Lose 1/3 of velocity on deflection
return self.object:set_velocity(vector.multiply(vel, 0.6667))
end
-- Node was walkable, make arrow stuck -- Just some dirty hack to make sure the arrow has a minimum direction to
self._stuck = true -- avoid triggering the stuck detection again.
self._stucktimer = 0 vel = self.object:get_velocity()
self._stuckrechecktimer = 0 if math.abs(vel.x) < 0.0001 then
if pos.x < self._lastpos.x then
vel.x = 0.01
else
vel.x = -0.01
end
end
if math.abs(vel.z) < 0.0001 then
if pos.z < self._lastpos.z then
vel.z = 0.01
else
vel.z = -0.01
end
end
if math.abs(vel.y) < 0.00001 then
if pos.y < self._lastpos.y then
vel.y = 0.001
else
vel.y = -0.001
end
end
self.object:set_velocity(vel)
self.object:set_yaw(minetest.dir_to_yaw(vel)+YAW_OFFSET)
-- Reset deflection cooloff timer to prevent many deflections happening in quick succession
self._deflection_cooloff = 0.2
end
else
self.object:set_velocity({x=0, y=0, z=0}) -- Node was walkable, make arrow stuck
self.object:set_acceleration({x=0, y=0, z=0}) self._stuck = true
self._stucktimer = 0
self._stuckrechecktimer = 0
-- Push the button! Push, push, push the button! self.object:set_velocity({x=0, y=0, z=0})
if mod_button and minetest.get_item_group(node.name, "button") > 0 and minetest.get_item_group(node.name, "button_push_by_arrow") == 1 then self.object:set_acceleration({x=0, y=0, z=0})
local bdir = minetest.wallmounted_to_dir(node.param2)
-- Check the button orientation -- Push the button! Push, push, push the button!
if vector.equals(vector.add(dpos, bdir), self._stuckin) then if mod_button and minetest.get_item_group(node.name, "button") > 0 and minetest.get_item_group(node.name, "button_push_by_arrow") == 1 then
mesecon.push_button(dpos, node) local bdir = minetest.wallmounted_to_dir(node.param2)
-- Check the button orientation
if vector.equals(vector.add(dpos, bdir), self._stuckin) then
mesecon.push_button(dpos, node)
end
end end
end end
elseif (def and def.liquidtype ~= "none") then elseif (def and def.liquidtype ~= "none") then
@ -258,6 +295,8 @@ ARROW_ENTITY.on_step = function(self, dtime)
if not v then if not v then
v = 0 v = 0
end end
local old_v = self._viscosity
self._viscosity = v
local vpenalty = math.max(0.1, 0.98 - 0.1 * v) local vpenalty = math.max(0.1, 0.98 - 0.1 * v)
if math.abs(vel.x) > 0.001 then if math.abs(vel.x) > 0.001 then
vel.x = vel.x * vpenalty vel.x = vel.x * vpenalty

@ -39,8 +39,8 @@ mcl_bows.shoot_arrow = function(arrow_item, pos, dir, yaw, shooter, power, damag
if damage == nil then if damage == nil then
damage = 3 damage = 3
end end
obj:setvelocity({x=dir.x*power, y=dir.y*power, z=dir.z*power}) obj:set_velocity({x=dir.x*power, y=dir.y*power, z=dir.z*power})
obj:setacceleration({x=dir.x*-3, y=-GRAVITY, z=dir.z*-3}) obj:set_acceleration({x=0, y=-GRAVITY, z=0})
obj:setyaw(yaw-math.pi/2) obj:setyaw(yaw-math.pi/2)
local le = obj:get_luaentity() local le = obj:get_luaentity()
le._shooter = shooter le._shooter = shooter