mirror of
https://git.minetest.land/VoxeLibre/VoxeLibre.git
synced 2024-11-16 16:11:06 +01:00
More refactoring to generalize arrow code into projectile
This commit is contained in:
parent
92e4185032
commit
1d4784e26a
2 changed files with 153 additions and 108 deletions
|
@ -56,26 +56,6 @@ S("Arrows might get stuck on solid blocks and can be retrieved again. They are a
|
|||
end,
|
||||
})
|
||||
|
||||
local function damage_particles(pos, is_critical)
|
||||
if is_critical then
|
||||
minetest.add_particlespawner({
|
||||
amount = 15,
|
||||
time = 0.1,
|
||||
minpos = vector.offset(pos, -0.5, -0.5, -0.5),
|
||||
maxpos = vector.offset(pos, 0.5, 0.5, 0.5),
|
||||
minvel = vector.new(-0.1, -0.1, -0.1),
|
||||
maxvel = vector.new(0.1, 0.1, 0.1),
|
||||
minexptime = 1,
|
||||
maxexptime = 2,
|
||||
minsize = 1.5,
|
||||
maxsize = 1.5,
|
||||
collisiondetection = false,
|
||||
vertical = false,
|
||||
texture = "mcl_particles_crit.png^[colorize:#bc7a57:127",
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
-- Destroy arrow entity self at pos and drops it as an item
|
||||
local function spawn_item(self, pos)
|
||||
if not minetest.is_creative_enabled("") then
|
||||
|
@ -165,9 +145,28 @@ vl_projectile.register("mcl_bows:arrow_entity", {
|
|||
|
||||
_vl_projectile = {
|
||||
survive_collision = true,
|
||||
sticks_in_players = true,
|
||||
damage_groups = function(self)
|
||||
return { fleshy = self._damage }
|
||||
end,
|
||||
behaviors = {
|
||||
vl_projectile.raycast_collides_with_entities,
|
||||
vl_projectile.collides_with_solids,
|
||||
vl_projectile.raycast_collides_with_entities,
|
||||
},
|
||||
allow_punching = function(self, entity_def, projectile_def, entity)
|
||||
local lua = entity:get_luaentity()
|
||||
if lua and lua.name == "mobs_mc:rover" then return false end
|
||||
|
||||
return true
|
||||
end,
|
||||
sounds = {
|
||||
on_entity_collision = function(self, _, _, obj)
|
||||
if obj:is_player() then
|
||||
return {{name="mcl_bows_hit_player", gain=0.1}, {to_player=self._shooter:get_player_name()}, true}
|
||||
end
|
||||
|
||||
return {{name="mcl_bows_hit_other", gain=0.3}, {pos=self.object:get_pos(), max_hear_distance=16}, true}
|
||||
end
|
||||
},
|
||||
on_collide_with_solid = function(self, pos, node, node_def)
|
||||
local def = node_def
|
||||
|
@ -252,6 +251,7 @@ vl_projectile.register("mcl_bows:arrow_entity", {
|
|||
|
||||
if obj:get_hp() > 0 then
|
||||
-- Check if there is no solid node between arrow and object
|
||||
-- TODO: remove. this code should never occur if vl_projectile is working correctly
|
||||
local ray = minetest.raycast(self.object:get_pos(), obj:get_pos(), true)
|
||||
for pointed_thing in ray do
|
||||
if pointed_thing.type == "object" and pointed_thing.ref == obj then
|
||||
|
@ -269,78 +269,6 @@ vl_projectile.register("mcl_bows:arrow_entity", {
|
|||
end
|
||||
end
|
||||
|
||||
-- Punch target object but avoid hurting enderman.
|
||||
if not lua or lua.name ~= "mobs_mc:rover" then
|
||||
if not self._in_player then
|
||||
damage_particles(vector.add(pos, vector.multiply(self.object:get_velocity(), 0.1)), self._is_critical)
|
||||
end
|
||||
if mcl_burning.is_burning(self.object) then
|
||||
mcl_burning.set_on_fire(obj, 5)
|
||||
end
|
||||
if not self._in_player and not self._blocked then
|
||||
obj:punch(self.object, 1.0, {
|
||||
full_punch_interval=1.0,
|
||||
damage_groups={fleshy=self._damage},
|
||||
}, self.object:get_velocity())
|
||||
|
||||
if obj:is_player() then
|
||||
if not mcl_shields.is_blocking(obj) then
|
||||
local placement
|
||||
self._placement = math.random(1, 2)
|
||||
if self._placement == 1 then
|
||||
placement = "front"
|
||||
else
|
||||
placement = "back"
|
||||
end
|
||||
self._in_player = true
|
||||
if self._placement == 2 then
|
||||
self._rotation_station = 90
|
||||
else
|
||||
self._rotation_station = -90
|
||||
end
|
||||
self._y_position = random_arrow_positions("y", placement)
|
||||
self._x_position = random_arrow_positions("x", placement)
|
||||
if self._y_position > 6 and self._x_position < 2 and self._x_position > -2 then
|
||||
self._attach_parent = "Head"
|
||||
self._y_position = self._y_position - 6
|
||||
elseif self._x_position > 2 then
|
||||
self._attach_parent = "Arm_Right"
|
||||
self._y_position = self._y_position - 3
|
||||
self._x_position = self._x_position - 2
|
||||
elseif self._x_position < -2 then
|
||||
self._attach_parent = "Arm_Left"
|
||||
self._y_position = self._y_position - 3
|
||||
self._x_position = self._x_position + 2
|
||||
else
|
||||
self._attach_parent = "Body"
|
||||
end
|
||||
self._z_rotation = math.random(-30, 30)
|
||||
self._y_rotation = math.random( -30, 30)
|
||||
self.object:set_attach(
|
||||
obj, self._attach_parent,
|
||||
vector.new(self._x_position, self._y_position, random_arrow_positions("z", placement)),
|
||||
vector.new(0, self._rotation_station + self._y_rotation, self._z_rotation)
|
||||
)
|
||||
else
|
||||
self._blocked = true
|
||||
self.object:set_velocity(vector.multiply(self.object:get_velocity(), -0.25))
|
||||
end
|
||||
minetest.after(150, function()
|
||||
self.object:remove()
|
||||
end)
|
||||
else
|
||||
self.object:remove()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if is_player then
|
||||
if self._shooter and self._shooter:is_player() and not self._in_player and not self._blocked then
|
||||
-- “Ding” sound for hitting another player
|
||||
minetest.sound_play({name="mcl_bows_hit_player", gain=0.1}, {to_player=self._shooter:get_player_name()}, true)
|
||||
end
|
||||
end
|
||||
|
||||
if lua then
|
||||
local entity_name = lua.name
|
||||
-- Achievement for hitting skeleton, wither skeleton or stray (TODO) with an arrow at least 50 meters away
|
||||
|
@ -352,9 +280,6 @@ vl_projectile.register("mcl_bows:arrow_entity", {
|
|||
end
|
||||
end
|
||||
end
|
||||
if not self._in_player and not self._blocked then
|
||||
minetest.sound_play({name="mcl_bows_hit_other", gain=0.3}, {pos=self.object:get_pos(), max_hear_distance=16}, true)
|
||||
end
|
||||
end
|
||||
|
||||
if not obj:is_player() then
|
||||
|
@ -374,8 +299,8 @@ vl_projectile.register("mcl_bows:arrow_entity", {
|
|||
self._time_in_air = self._time_in_air + dtime
|
||||
|
||||
local pos = self.object:get_pos()
|
||||
local dpos = vector.round(vector.new(pos)) -- digital pos
|
||||
local node = minetest.get_node(dpos)
|
||||
--local dpos = vector.round(vector.new(pos)) -- digital pos
|
||||
--local node = minetest.get_node(dpos)
|
||||
|
||||
if self._stuck then
|
||||
return stuck_arrow_on_step(self, dtime)
|
||||
|
@ -407,6 +332,7 @@ vl_projectile.register("mcl_bows:arrow_entity", {
|
|||
end
|
||||
|
||||
-- TODO: change to use vl_physics
|
||||
-- TODO: move to vl_projectile
|
||||
local def = minetest.registered_nodes[minetest.get_node(pos).name]
|
||||
if def and def.liquidtype ~= "none" then
|
||||
-- Slow down arrow in liquids
|
||||
|
|
|
@ -2,6 +2,7 @@ local mod = {}
|
|||
vl_projectile = mod
|
||||
|
||||
local GRAVITY = tonumber(minetest.settings:get("movement_gravity"))
|
||||
local enable_pvp = minetest.settings:get_bool("enable_pvp")
|
||||
|
||||
function mod.update_projectile(self, dtime)
|
||||
local entity_name = self.name
|
||||
|
@ -23,8 +24,38 @@ end
|
|||
|
||||
local function no_op()
|
||||
end
|
||||
|
||||
local enable_pvp = minetest.settings:get_bool("enable_pvp")
|
||||
local function damage_particles(pos, is_critical)
|
||||
if is_critical then
|
||||
minetest.add_particlespawner({
|
||||
amount = 15,
|
||||
time = 0.1,
|
||||
minpos = vector.offset(pos, -0.5, -0.5, -0.5),
|
||||
maxpos = vector.offset(pos, 0.5, 0.5, 0.5),
|
||||
minvel = vector.new(-0.1, -0.1, -0.1),
|
||||
maxvel = vector.new(0.1, 0.1, 0.1),
|
||||
minexptime = 1,
|
||||
maxexptime = 2,
|
||||
minsize = 1.5,
|
||||
maxsize = 1.5,
|
||||
collisiondetection = false,
|
||||
vertical = false,
|
||||
texture = "mcl_particles_crit.png^[colorize:#bc7a57:127",
|
||||
})
|
||||
end
|
||||
end
|
||||
local function random_hit_positions(positions, placement)
|
||||
if positions == "x" then
|
||||
return math.random(-4, 4)
|
||||
elseif positions == "y" then
|
||||
return math.random(0, 10)
|
||||
end
|
||||
if placement == "front" and positions == "z" then
|
||||
return 3
|
||||
elseif placement == "back" and positions == "z" then
|
||||
return -3
|
||||
end
|
||||
return 0
|
||||
end
|
||||
local function check_hitpoint(hitpoint)
|
||||
if hitpoint.type ~= "object" then return false end
|
||||
|
||||
|
@ -41,6 +72,59 @@ local function check_hitpoint(hitpoint)
|
|||
|
||||
return false
|
||||
end
|
||||
local function handle_player_sticking(self, entity_def, projectile_def, entity)
|
||||
if self._in_player or self._blocked then return end
|
||||
if not projectile_def.sticks_in_players then return end
|
||||
|
||||
minetest.after(150, function()
|
||||
self.object:remove()
|
||||
end)
|
||||
|
||||
-- Handle blocking projectiles
|
||||
if mcl_shields.is_blocking(obj) then
|
||||
self._blocked = true
|
||||
self.object:set_velocity(vector.multiply(self.object:get_velocity(), -0.25))
|
||||
return
|
||||
end
|
||||
|
||||
-- Handle when the projectile hits the player
|
||||
local placement
|
||||
self._placement = math.random(1, 2)
|
||||
if self._placement == 1 then
|
||||
placement = "front"
|
||||
else
|
||||
placement = "back"
|
||||
end
|
||||
self._in_player = true
|
||||
if self._placement == 2 then
|
||||
self._rotation_station = 90
|
||||
else
|
||||
self._rotation_station = -90
|
||||
end
|
||||
self._y_position = random_arrow_positions("y", placement)
|
||||
self._x_position = random_arrow_positions("x", placement)
|
||||
if self._y_position > 6 and self._x_position < 2 and self._x_position > -2 then
|
||||
self._attach_parent = "Head"
|
||||
self._y_position = self._y_position - 6
|
||||
elseif self._x_position > 2 then
|
||||
self._attach_parent = "Arm_Right"
|
||||
self._y_position = self._y_position - 3
|
||||
self._x_position = self._x_position - 2
|
||||
elseif self._x_position < -2 then
|
||||
self._attach_parent = "Arm_Left"
|
||||
self._y_position = self._y_position - 3
|
||||
self._x_position = self._x_position + 2
|
||||
else
|
||||
self._attach_parent = "Body"
|
||||
end
|
||||
self._z_rotation = math.random(-30, 30)
|
||||
self._y_rotation = math.random( -30, 30)
|
||||
self.object:set_attach(
|
||||
obj, self._attach_parent,
|
||||
vector.new(self._x_position, self._y_position, random_arrow_positions("z", placement)),
|
||||
vector.new(0, self._rotation_station + self._y_rotation, self._z_rotation)
|
||||
)
|
||||
end
|
||||
|
||||
function mod.collides_with_solids(self, dtime, entity_def, projectile_def)
|
||||
local pos = self.object:get_pos()
|
||||
|
@ -102,16 +186,51 @@ local function handle_entity_collision(self, entity_def, projectile_def, entity)
|
|||
local dir = vector.normalize(self.object:get_velocity())
|
||||
local self_vl_projectile = self._vl_projectile
|
||||
|
||||
-- Allow punching
|
||||
local allow_punching = projectile_def.allow_punching or true
|
||||
if type(allow_punching) == "function" then
|
||||
allow_punching = allow_punching(self, entity_def, projectile_def, entity)
|
||||
end
|
||||
print("allow_punching="..tostring(allow_punching))
|
||||
|
||||
if allow_punching then
|
||||
-- Get damage
|
||||
local dmg = projectile_def.damage_groups or 0
|
||||
if type(dmg) == "function" then
|
||||
dmg = dmg(self, entity_def, projectile_def, entity)
|
||||
end
|
||||
|
||||
local entity_lua = entity:get_luaentity()
|
||||
|
||||
-- Apply damage
|
||||
-- Note: Damage blocking for shields is handled in mcl_shields with an mcl_damage modifier
|
||||
local do_damage = false
|
||||
if entity:is_player() and projectile_def.hits_players and self_vl_projectile.owner ~= hit:get_player_name() then
|
||||
entity:punch(self.object, 1.0, projectile_def.tool or { full_punch_interval = 1.0, damage_groups = dmg }, dir )
|
||||
elseif (entity.is_mob == true or entity._hittable_by_projectile) and (self_vl_projectile.owner ~= entity) then
|
||||
do_damage = true
|
||||
|
||||
handle_player_sticking(self, entity_def, projectile_def, entity)
|
||||
elseif entity_lua and (entity_lua.is_mob == true or entity_lua._hittable_by_projectile) and (self_vl_projectile.owner ~= entity) then
|
||||
do_damage = true
|
||||
entity:punch(self.object, 1.0, projectile_def.tool or { full_punch_interval = 1.0, damage_groups = dmg }, dir )
|
||||
end
|
||||
|
||||
-- Call entity collied hook
|
||||
if do_damage then
|
||||
entity:punch(self.object, 1.0, projectile_def.tool or { full_punch_interval = 1.0, damage_groups = dmg }, dir )
|
||||
|
||||
-- Indicate damage
|
||||
damage_particles(vector.add(pos, vector.multiply(self.object:get_velocity(), 0.1)), self._is_critical)
|
||||
|
||||
-- Light things on fire
|
||||
if mcl_burning.is_burning(self.object) then
|
||||
mcl_burning.set_on_fire(obj, 5)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Call entity collision hook
|
||||
(projectile_def.on_collide_with_entity or no_op)(self, pos, entity)
|
||||
|
||||
-- Call entity reverse hook
|
||||
-- Call reverse entity collision hook
|
||||
local other_entity_def = minetest.registered_entities[entity.name] or {}
|
||||
local other_entity_vl_projectile = other_entity_def._vl_projectile or {}
|
||||
local hook = (other_entity_vl_projectile or {}).on_collide or no_op
|
||||
|
@ -120,7 +239,8 @@ local function handle_entity_collision(self, entity_def, projectile_def, entity)
|
|||
-- Play sounds
|
||||
local sounds = (projectile_def.sounds or {})
|
||||
local sound = sounds.on_entity_collide or sounds.on_collision
|
||||
if on_collide_sound then
|
||||
if type(sound) == "function" then sound = sound(self, entity_def, projectile_def, entity)
|
||||
if sound then
|
||||
local arg2 = table.copy(sound[2])
|
||||
arg2.pos = pos
|
||||
minetest.sound_play(sound[1], arg2, sound[3])
|
||||
|
@ -136,7 +256,6 @@ end
|
|||
|
||||
function mod.collides_with_entities(self, dtime, entity_def, projectile_def)
|
||||
local pos = self.object:get_pos()
|
||||
local dmg = projectile_def.damage_groups or 0
|
||||
|
||||
local hit = nil
|
||||
local owner = self._vl_projectile.owner
|
||||
|
|
Loading…
Reference in a new issue