Move projectile stick logic into vl_projectile
This commit is contained in:
parent
b2c6d4f8de
commit
1a4c5c8749
|
@ -36,65 +36,6 @@ S("Arrows might get stuck on solid blocks and can be retrieved again. They are a
|
||||||
})
|
})
|
||||||
|
|
||||||
-- Destroy arrow entity self at pos and drops it as an item
|
-- Destroy arrow entity self at pos and drops it as an item
|
||||||
local function replace_with_item_drop(self, pos)
|
|
||||||
if not minetest.is_creative_enabled("") then
|
|
||||||
local item = minetest.add_item(pos, "mcl_bows:arrow")
|
|
||||||
item:set_velocity(vector.zero())
|
|
||||||
item:set_yaw(self.object:get_yaw())
|
|
||||||
end
|
|
||||||
|
|
||||||
mcl_burning.extinguish(self.object)
|
|
||||||
self._removed = true
|
|
||||||
self.object:remove()
|
|
||||||
end
|
|
||||||
|
|
||||||
local function stuck_arrow_on_step(self, dtime)
|
|
||||||
self._stucktimer = self._stucktimer + dtime
|
|
||||||
self._stuckrechecktimer = self._stuckrechecktimer + dtime
|
|
||||||
if self._stucktimer > ARROW_TIMEOUT then
|
|
||||||
mcl_burning.extinguish(self.object)
|
|
||||||
self._removed = true
|
|
||||||
self.object:remove()
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local pos = self.object:get_pos()
|
|
||||||
if not pos then return end
|
|
||||||
|
|
||||||
-- Drop arrow as item when it is no longer stuck
|
|
||||||
-- FIXME: Arrows are a bit slow to react and continue to float in mid air for a few seconds.
|
|
||||||
if self._stuckrechecktimer > STUCK_RECHECK_TIME then
|
|
||||||
local stuckin_def = self._stuckin and minetest.registered_nodes[minetest.get_node(self._stuckin).name]
|
|
||||||
-- TODO: In MC, arrow just falls down without turning into an item
|
|
||||||
if stuckin_def and stuckin_def.walkable == false then
|
|
||||||
replace_with_item_drop(self, pos)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
self._stuckrechecktimer = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Pickup arrow if player is nearby (not in Creative Mode)
|
|
||||||
local objects = minetest.get_objects_inside_radius(pos, 1)
|
|
||||||
for _,obj in ipairs(objects) do
|
|
||||||
if obj:is_player() then
|
|
||||||
if self._collectable and not minetest.is_creative_enabled(obj:get_player_name()) then
|
|
||||||
local arrow_item = self._arrow_item
|
|
||||||
if arrow_item and minetest.registered_items[arrow_item] and obj:get_inventory():room_for_item("main", arrow_item) then
|
|
||||||
obj:get_inventory():add_item("main", arrow_item)
|
|
||||||
minetest.sound_play("item_drop_pickup", {
|
|
||||||
pos = pos,
|
|
||||||
max_hear_distance = 16,
|
|
||||||
gain = 1.0,
|
|
||||||
}, true)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
mcl_burning.extinguish(self.object)
|
|
||||||
self.object:remove()
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local arrow_entity = {
|
local arrow_entity = {
|
||||||
physical = true,
|
physical = true,
|
||||||
pointable = false,
|
pointable = false,
|
||||||
|
@ -137,17 +78,12 @@ local arrow_entity = {
|
||||||
end,
|
end,
|
||||||
tracer_texture = "mobs_mc_arrow_particle.png",
|
tracer_texture = "mobs_mc_arrow_particle.png",
|
||||||
behaviors = {
|
behaviors = {
|
||||||
|
vl_projectile.sticks,
|
||||||
vl_projectile.burns,
|
vl_projectile.burns,
|
||||||
vl_projectile.has_tracer,
|
vl_projectile.has_tracer,
|
||||||
|
|
||||||
-- Custom arrow behaviors
|
-- Custom arrow behaviors
|
||||||
function(self, dtime)
|
function(self, dtime)
|
||||||
-- Stuck handling
|
|
||||||
if self._stuck then
|
|
||||||
stuck_arrow_on_step(self, dtime)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local pos = self.object:get_pos()
|
local pos = self.object:get_pos()
|
||||||
self._allow_punch = self._allow_punch or not self._owner or not self._startpos or pos and vector.distance(self._startpos, pos) > 1.5
|
self._allow_punch = self._allow_punch or not self._owner or not self._startpos or pos and vector.distance(self._startpos, pos) > 1.5
|
||||||
|
|
||||||
|
@ -184,57 +120,6 @@ local arrow_entity = {
|
||||||
return {{name="mcl_bows_hit_other", gain=0.3}, {pos=self.object:get_pos(), max_hear_distance=16}, true}
|
return {{name="mcl_bows_hit_other", gain=0.3}, {pos=self.object:get_pos(), max_hear_distance=16}, true}
|
||||||
end
|
end
|
||||||
},
|
},
|
||||||
on_collide_with_solid = function(self, pos, node, node_def)
|
|
||||||
local vel = self.object:get_velocity()
|
|
||||||
local dpos = vector.round(pos) -- digital pos
|
|
||||||
|
|
||||||
-- Check for the node to which the arrow is pointing
|
|
||||||
local dir
|
|
||||||
if math.abs(vel.y) < 0.00001 then
|
|
||||||
if self._last_pos.y < pos.y then
|
|
||||||
dir = vector.new(0, 1, 0)
|
|
||||||
else
|
|
||||||
dir = vector.new(0, -1, 0)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
dir = minetest.facedir_to_dir(minetest.dir_to_facedir(minetest.yaw_to_dir(self.object:get_yaw()-YAW_OFFSET)))
|
|
||||||
end
|
|
||||||
self._stuckin = vector.add(dpos, dir)
|
|
||||||
|
|
||||||
local snode = minetest.get_node(self._stuckin)
|
|
||||||
local sdef = minetest.registered_nodes[snode.name]
|
|
||||||
|
|
||||||
-- If node is non-walkable, unknown or ignore, don't make arrow stuck.
|
|
||||||
-- This causes a deflection in the engine.
|
|
||||||
if not sdef or sdef.walkable == false or snode.name == "ignore" then
|
|
||||||
self._stuckin = nil
|
|
||||||
if self._deflection_cooloff <= 0 then
|
|
||||||
-- Lose 1/3 of velocity on deflection
|
|
||||||
local newvel = vector.multiply(vel, 0.6667)
|
|
||||||
|
|
||||||
self.object:set_velocity(newvel)
|
|
||||||
-- Reset deflection cooloff timer to prevent many deflections happening in quick succession
|
|
||||||
self._deflection_cooloff = 1.0
|
|
||||||
end
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Node was walkable, make arrow stuck
|
|
||||||
self._stuck = true
|
|
||||||
self._stucktimer = 0
|
|
||||||
self._stuckrechecktimer = 0
|
|
||||||
|
|
||||||
self.object:set_velocity(vector.zero())
|
|
||||||
self.object:set_acceleration(vector.zero())
|
|
||||||
|
|
||||||
minetest.sound_play({name="mcl_bows_hit_other", gain=0.3}, {pos=self.object:get_pos(), max_hear_distance=16}, true)
|
|
||||||
|
|
||||||
-- Temporary handler here to test moving this to node definitions.
|
|
||||||
-- TODO: move to vl_projectile when the stuck logic gets moved there and before merging
|
|
||||||
-- Trigger hits on the node the projectile hit
|
|
||||||
local hook = sdef._vl_projectile and sdef._vl_projectile.on_collide
|
|
||||||
if hook then hook(self, self._stuckin, snode, sdef) end
|
|
||||||
end,
|
|
||||||
on_collide_with_entity = function(self, pos, obj)
|
on_collide_with_entity = function(self, pos, obj)
|
||||||
local is_player = obj:is_player()
|
local is_player = obj:is_player()
|
||||||
local lua = obj:get_luaentity()
|
local lua = obj:get_luaentity()
|
||||||
|
|
|
@ -67,6 +67,8 @@ standard behaviors provided with the API:
|
||||||
* `vl_projectile.collides_with_entities`: handles collisions between projectiles and entities by checking nearby entities
|
* `vl_projectile.collides_with_entities`: handles collisions between projectiles and entities by checking nearby entities
|
||||||
* `vl_projectile.has_tracer`: projectile will have a tracer trail when thrown/shot. Projectile can define
|
* `vl_projectile.has_tracer`: projectile will have a tracer trail when thrown/shot. Projectile can define
|
||||||
`_vl_projectile.hide_tracer = function(self)` to conditionally hide the tracer.
|
`_vl_projectile.hide_tracer = function(self)` to conditionally hide the tracer.
|
||||||
|
* `vl_projectile.sticks`: projectile will stick into nodes. Forces `_vl_projectile.sticks_in_nodes = true`
|
||||||
|
and `_vl_projectile.survive_collision = true`.
|
||||||
* `vl_projectile.raycast_collides_with_entities`: handles collisions between projectils and entities by performing a raycast
|
* `vl_projectile.raycast_collides_with_entities`: handles collisions between projectils and entities by performing a raycast
|
||||||
check along the path of movement.
|
check along the path of movement.
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,8 @@ local vl_physics_path = minetest.get_modpath("vl_physics")
|
||||||
local DEBUG = false
|
local DEBUG = false
|
||||||
local YAW_OFFSET = -math.pi/2
|
local YAW_OFFSET = -math.pi/2
|
||||||
local GRAVITY = tonumber(minetest.settings:get("movement_gravity"))
|
local GRAVITY = tonumber(minetest.settings:get("movement_gravity"))
|
||||||
|
local STUCK_TIMEOUT = 60
|
||||||
|
local STUCK_RECHECK_TIME = 0.25
|
||||||
local enable_pvp = minetest.settings:get_bool("enable_pvp")
|
local enable_pvp = minetest.settings:get_bool("enable_pvp")
|
||||||
|
|
||||||
function mod.projectile_physics(obj, entity_def, v, a)
|
function mod.projectile_physics(obj, entity_def, v, a)
|
||||||
|
@ -87,7 +89,9 @@ function mod.update_projectile(self, dtime)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if not self._stuck then
|
||||||
mod.projectile_physics(self.object, entity_def)
|
mod.projectile_physics(self.object, entity_def)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function damage_particles(pos, is_critical)
|
local function damage_particles(pos, is_critical)
|
||||||
|
@ -229,6 +233,85 @@ function mod.has_tracer(self, dtime, entity_def, projectile_def)
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function mod.replace_with_item_drop(self, pos, entity_def, projectile_def)
|
||||||
|
local item = self._arrow_item or projectile_def.item
|
||||||
|
|
||||||
|
if self._collectable and not minetest.is_creative_enabled("") then
|
||||||
|
local item = minetest.add_item(pos, item)
|
||||||
|
item:set_velocity(vector.zero())
|
||||||
|
item:set_yaw(self.object:get_yaw())
|
||||||
|
end
|
||||||
|
|
||||||
|
mcl_burning.extinguish(self.object)
|
||||||
|
self._removed = true
|
||||||
|
self.object:remove()
|
||||||
|
end
|
||||||
|
|
||||||
|
local function stuck_on_step(self, dtime, entity_def, projectile_def)
|
||||||
|
-- Don't process objects that have been removed
|
||||||
|
local pos = self.object:get_pos()
|
||||||
|
if not pos then return true end
|
||||||
|
|
||||||
|
self._stucktimer = self._stucktimer + dtime
|
||||||
|
if self._stucktimer > STUCK_TIMEOUT then
|
||||||
|
mcl_burning.extinguish(self.object)
|
||||||
|
self._removed = true
|
||||||
|
self.object:remove()
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Drop arrow as item when it is no longer stuck
|
||||||
|
-- TODO: revist after observer rework
|
||||||
|
self._stuckrechecktimer = self._stuckrechecktimer + dtime
|
||||||
|
if self._stuckrechecktimer > 1 then
|
||||||
|
self._stuckrechecktimer = 0
|
||||||
|
if self._stuckin then
|
||||||
|
local node = minetest.get_node(self._stuckin)
|
||||||
|
local node_def = minetest.registered_nodes[node.name]
|
||||||
|
if node_def and node_def.walkable == false then
|
||||||
|
mod.replace_with_item_drop(self, pos, entity_def, projectile_def)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Pickup arrow if player is nearby (not in Creative Mode)
|
||||||
|
local objects = minetest.get_objects_inside_radius(pos, 1)
|
||||||
|
for i = 1,#objects do
|
||||||
|
obj = objects[i]
|
||||||
|
if obj:is_player() then
|
||||||
|
if self._collectable and not minetest.is_creative_enabled(obj:get_player_name()) then
|
||||||
|
local arrow_item = self._arrow_item
|
||||||
|
if arrow_item and minetest.registered_items[arrow_item] and obj:get_inventory():room_for_item("main", arrow_item) then
|
||||||
|
obj:get_inventory():add_item("main", arrow_item)
|
||||||
|
|
||||||
|
minetest.sound_play("item_drop_pickup", {
|
||||||
|
pos = pos,
|
||||||
|
max_hear_distance = 16,
|
||||||
|
gain = 1.0,
|
||||||
|
}, true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
mcl_burning.extinguish(self.object)
|
||||||
|
self.object:remove()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mod.sticks(self, dtime, entity_def, projectile_def)
|
||||||
|
-- Force the projectile to survive collisions (Otherwise, the projectile can't stick in nodes)
|
||||||
|
projectile_def.survive_collision = true
|
||||||
|
projectile_def.sticks_in_nodes = true
|
||||||
|
|
||||||
|
-- Stuck handling
|
||||||
|
if self._stuck then
|
||||||
|
return stuck_on_step(self, dtime, entity_def, projectile_def)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
function mod.collides_with_solids(self, dtime, entity_def, projectile_def)
|
function mod.collides_with_solids(self, dtime, entity_def, projectile_def)
|
||||||
local pos = self.object:get_pos()
|
local pos = self.object:get_pos()
|
||||||
if not pos then return end
|
if not pos then return end
|
||||||
|
@ -264,6 +347,55 @@ function mod.collides_with_solids(self, dtime, entity_def, projectile_def)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Handle sticking in nodes
|
||||||
|
if projectile_def.sticks_in_nodes then
|
||||||
|
local vel = self.object:get_velocity()
|
||||||
|
local dpos = vector.round(pos) -- digital pos
|
||||||
|
|
||||||
|
-- Check for the node to which the arrow is pointing
|
||||||
|
local dir
|
||||||
|
if math.abs(vel.y) < 0.00001 then
|
||||||
|
if self._last_pos.y < pos.y then
|
||||||
|
dir = vector.new(0, 1, 0)
|
||||||
|
else
|
||||||
|
dir = vector.new(0, -1, 0)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
dir = minetest.facedir_to_dir(minetest.dir_to_facedir(minetest.yaw_to_dir(self.object:get_yaw()-YAW_OFFSET)))
|
||||||
|
end
|
||||||
|
self._stuckin = vector.add(dpos, dir)
|
||||||
|
|
||||||
|
local snode = minetest.get_node(self._stuckin)
|
||||||
|
local sdef = minetest.registered_nodes[snode.name]
|
||||||
|
|
||||||
|
-- If node is non-walkable, unknown or ignore, don't make arrow stuck.
|
||||||
|
-- This causes a deflection in the engine.
|
||||||
|
if not sdef or sdef.walkable == false or snode.name == "ignore" then
|
||||||
|
self._stuckin = nil
|
||||||
|
if self._deflection_cooloff <= 0 then
|
||||||
|
-- Lose 1/3 of velocity on deflection
|
||||||
|
local newvel = vector.multiply(vel, 0.6667)
|
||||||
|
|
||||||
|
self.object:set_velocity(newvel)
|
||||||
|
-- Reset deflection cooloff timer to prevent many deflections happening in quick succession
|
||||||
|
self._deflection_cooloff = 1.0
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Node was walkable, make arrow stuck
|
||||||
|
self._stuck = true
|
||||||
|
self._stucktimer = 0
|
||||||
|
self._stuckrechecktimer = 0
|
||||||
|
|
||||||
|
self.object:set_velocity(vector.zero())
|
||||||
|
self.object:set_acceleration(vector.zero())
|
||||||
|
|
||||||
|
-- Trigger hits on the node the projectile hit
|
||||||
|
local hook = sdef._vl_projectile and sdef._vl_projectile.on_collide
|
||||||
|
if hook then hook(self, self._stuckin, snode, sdef) end
|
||||||
|
end
|
||||||
|
|
||||||
-- Call entity collied hook
|
-- Call entity collied hook
|
||||||
local hook = projectile_def.on_collide_with_solid
|
local hook = projectile_def.on_collide_with_solid
|
||||||
if hook then hook(self, pos, node, node_def) end
|
if hook then hook(self, pos, node, node_def) end
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
name = vl_projectile
|
name = vl_projectile
|
||||||
depends = mcl_util
|
depends = mcl_util
|
||||||
optional_depends = vl_physics, mcl_shields, mcl_burning
|
optional_depends = vl_physics, mcl_shields, mcl_burning, mcl_util
|
||||||
|
|
Loading…
Reference in New Issue