mirror of
https://git.minetest.land/VoxeLibre/VoxeLibre.git
synced 2024-11-17 00:21:07 +01:00
More refactoring to generalize arrow code into projectile
This commit is contained in:
parent
99c92fa5e5
commit
027b5cda00
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,
|
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
|
-- Destroy arrow entity self at pos and drops it as an item
|
||||||
local function spawn_item(self, pos)
|
local function spawn_item(self, pos)
|
||||||
if not minetest.is_creative_enabled("") then
|
if not minetest.is_creative_enabled("") then
|
||||||
|
@ -165,9 +145,28 @@ vl_projectile.register("mcl_bows:arrow_entity", {
|
||||||
|
|
||||||
_vl_projectile = {
|
_vl_projectile = {
|
||||||
survive_collision = true,
|
survive_collision = true,
|
||||||
|
sticks_in_players = true,
|
||||||
|
damage_groups = function(self)
|
||||||
|
return { fleshy = self._damage }
|
||||||
|
end,
|
||||||
behaviors = {
|
behaviors = {
|
||||||
vl_projectile.raycast_collides_with_entities,
|
|
||||||
vl_projectile.collides_with_solids,
|
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)
|
on_collide_with_solid = function(self, pos, node, node_def)
|
||||||
local def = node_def
|
local def = node_def
|
||||||
|
@ -252,6 +251,7 @@ vl_projectile.register("mcl_bows:arrow_entity", {
|
||||||
|
|
||||||
if obj:get_hp() > 0 then
|
if obj:get_hp() > 0 then
|
||||||
-- Check if there is no solid node between arrow and object
|
-- 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)
|
local ray = minetest.raycast(self.object:get_pos(), obj:get_pos(), true)
|
||||||
for pointed_thing in ray do
|
for pointed_thing in ray do
|
||||||
if pointed_thing.type == "object" and pointed_thing.ref == obj then
|
if pointed_thing.type == "object" and pointed_thing.ref == obj then
|
||||||
|
@ -269,78 +269,6 @@ vl_projectile.register("mcl_bows:arrow_entity", {
|
||||||
end
|
end
|
||||||
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
|
if lua then
|
||||||
local entity_name = lua.name
|
local entity_name = lua.name
|
||||||
-- Achievement for hitting skeleton, wither skeleton or stray (TODO) with an arrow at least 50 meters away
|
-- 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
|
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
|
end
|
||||||
|
|
||||||
if not obj:is_player() then
|
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
|
self._time_in_air = self._time_in_air + dtime
|
||||||
|
|
||||||
local pos = self.object:get_pos()
|
local pos = self.object:get_pos()
|
||||||
local dpos = vector.round(vector.new(pos)) -- digital pos
|
--local dpos = vector.round(vector.new(pos)) -- digital pos
|
||||||
local node = minetest.get_node(dpos)
|
--local node = minetest.get_node(dpos)
|
||||||
|
|
||||||
if self._stuck then
|
if self._stuck then
|
||||||
return stuck_arrow_on_step(self, dtime)
|
return stuck_arrow_on_step(self, dtime)
|
||||||
|
@ -407,6 +332,7 @@ vl_projectile.register("mcl_bows:arrow_entity", {
|
||||||
end
|
end
|
||||||
|
|
||||||
-- TODO: change to use vl_physics
|
-- TODO: change to use vl_physics
|
||||||
|
-- TODO: move to vl_projectile
|
||||||
local def = minetest.registered_nodes[minetest.get_node(pos).name]
|
local def = minetest.registered_nodes[minetest.get_node(pos).name]
|
||||||
if def and def.liquidtype ~= "none" then
|
if def and def.liquidtype ~= "none" then
|
||||||
-- Slow down arrow in liquids
|
-- Slow down arrow in liquids
|
||||||
|
|
|
@ -2,6 +2,7 @@ local mod = {}
|
||||||
vl_projectile = mod
|
vl_projectile = mod
|
||||||
|
|
||||||
local GRAVITY = tonumber(minetest.settings:get("movement_gravity"))
|
local GRAVITY = tonumber(minetest.settings:get("movement_gravity"))
|
||||||
|
local enable_pvp = minetest.settings:get_bool("enable_pvp")
|
||||||
|
|
||||||
function mod.update_projectile(self, dtime)
|
function mod.update_projectile(self, dtime)
|
||||||
local entity_name = self.name
|
local entity_name = self.name
|
||||||
|
@ -23,8 +24,38 @@ end
|
||||||
|
|
||||||
local function no_op()
|
local function no_op()
|
||||||
end
|
end
|
||||||
|
local function damage_particles(pos, is_critical)
|
||||||
local enable_pvp = minetest.settings:get_bool("enable_pvp")
|
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)
|
local function check_hitpoint(hitpoint)
|
||||||
if hitpoint.type ~= "object" then return false end
|
if hitpoint.type ~= "object" then return false end
|
||||||
|
|
||||||
|
@ -41,6 +72,59 @@ local function check_hitpoint(hitpoint)
|
||||||
|
|
||||||
return false
|
return false
|
||||||
end
|
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)
|
function mod.collides_with_solids(self, dtime, entity_def, projectile_def)
|
||||||
local pos = self.object:get_pos()
|
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 dir = vector.normalize(self.object:get_velocity())
|
||||||
local self_vl_projectile = self._vl_projectile
|
local self_vl_projectile = self._vl_projectile
|
||||||
|
|
||||||
if entity:is_player() and projectile_def.hits_players and self_vl_projectile.owner ~= hit:get_player_name() then
|
-- Allow punching
|
||||||
entity:punch(self.object, 1.0, projectile_def.tool or { full_punch_interval = 1.0, damage_groups = dmg }, dir )
|
local allow_punching = projectile_def.allow_punching or true
|
||||||
elseif (entity.is_mob == true or entity._hittable_by_projectile) and (self_vl_projectile.owner ~= entity) then
|
if type(allow_punching) == "function" then
|
||||||
entity:punch(self.object, 1.0, projectile_def.tool or { full_punch_interval = 1.0, damage_groups = dmg }, dir )
|
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
|
||||||
|
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
|
||||||
|
|
||||||
|
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
|
end
|
||||||
|
|
||||||
-- Call entity collied hook
|
-- Call entity collision hook
|
||||||
(projectile_def.on_collide_with_entity or no_op)(self, pos, entity)
|
(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_def = minetest.registered_entities[entity.name] or {}
|
||||||
local other_entity_vl_projectile = other_entity_def._vl_projectile or {}
|
local other_entity_vl_projectile = other_entity_def._vl_projectile or {}
|
||||||
local hook = (other_entity_vl_projectile or {}).on_collide or no_op
|
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
|
-- Play sounds
|
||||||
local sounds = (projectile_def.sounds or {})
|
local sounds = (projectile_def.sounds or {})
|
||||||
local sound = sounds.on_entity_collide or sounds.on_collision
|
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])
|
local arg2 = table.copy(sound[2])
|
||||||
arg2.pos = pos
|
arg2.pos = pos
|
||||||
minetest.sound_play(sound[1], arg2, sound[3])
|
minetest.sound_play(sound[1], arg2, sound[3])
|
||||||
|
@ -136,7 +256,6 @@ end
|
||||||
|
|
||||||
function mod.collides_with_entities(self, dtime, entity_def, projectile_def)
|
function mod.collides_with_entities(self, dtime, entity_def, projectile_def)
|
||||||
local pos = self.object:get_pos()
|
local pos = self.object:get_pos()
|
||||||
local dmg = projectile_def.damage_groups or 0
|
|
||||||
|
|
||||||
local hit = nil
|
local hit = nil
|
||||||
local owner = self._vl_projectile.owner
|
local owner = self._vl_projectile.owner
|
||||||
|
|
Loading…
Reference in a new issue