2021-05-28 01:34:58 +03:00
local S = minetest.get_translator ( minetest.get_current_modname ( ) )
2019-03-07 22:35:02 +02:00
2022-02-27 00:52:03 +02:00
local mod_target = minetest.get_modpath ( " mcl_target " )
2021-05-23 02:06:34 +03:00
local math = math
local vector = vector
2018-05-08 01:38:00 +03:00
-- Time in seconds after which a stuck arrow is deleted
local ARROW_TIMEOUT = 60
2018-05-09 16:10:56 +03:00
-- Time after which stuck arrow is rechecked for being stuck
local STUCK_RECHECK_TIME = 5
2021-05-23 02:06:34 +03:00
--local GRAVITY = 9.81
2018-05-09 16:10:56 +03:00
local YAW_OFFSET = - math.pi / 2
2018-05-08 01:38:00 +03:00
2021-05-23 02:06:34 +03:00
local function dir_to_pitch ( dir )
--local dir2 = vector.normalize(dir)
2019-08-27 23:01:51 +03:00
local xz = math.abs ( dir.x ) + math.abs ( dir.z )
return - math.atan2 ( - dir.y , xz )
end
2021-05-23 02:06:34 +03:00
local function random_arrow_positions ( positions , placement )
if positions == " x " then
2021-03-21 20:47:13 +02:00
return math.random ( - 4 , 4 )
2021-05-23 02:06:34 +03:00
elseif positions == " y " then
2021-03-21 20:47:13 +02:00
return math.random ( 0 , 10 )
2021-03-17 00:22:36 +02:00
end
2021-05-23 02:06:34 +03:00
if placement == " front " and positions == " z " then
2021-03-21 20:47:13 +02:00
return 3
2021-05-23 02:06:34 +03:00
elseif placement == " back " and positions == " z " then
2021-03-21 20:47:13 +02:00
return - 3
2021-03-17 00:22:36 +02:00
end
2021-03-21 20:47:13 +02:00
return 0
2021-03-17 00:22:36 +02:00
end
2017-07-26 20:35:56 +03:00
local mod_awards = minetest.get_modpath ( " awards " ) and minetest.get_modpath ( " mcl_achievements " )
2018-05-09 16:10:56 +03:00
local mod_button = minetest.get_modpath ( " mesecons_button " )
2017-07-26 20:35:56 +03:00
2018-05-08 00:10:49 +03:00
minetest.register_craftitem ( " mcl_bows:arrow " , {
2019-03-07 22:35:02 +02:00
description = S ( " Arrow " ) ,
2020-02-19 05:54:17 +02:00
_tt_help = S ( " Ammunition " ) .. " \n " .. S ( " Damage from bow: 1-10 " ) .. " \n " .. S ( " Damage from dispenser: 3 " ) ,
2019-03-07 22:35:02 +02:00
_doc_items_longdesc = S ( " Arrows are ammunition for bows and dispensers. " ) .. " \n " ..
S ( " An arrow fired from a bow has a regular damage of 1-9. At full charge, there's a 20% chance of a critical hit dealing 10 damage instead. An arrow fired from a dispenser always deals 3 damage. " ) .. " \n " ..
S ( " Arrows might get stuck on solid blocks and can be retrieved again. They are also capable of pushing wooden buttons. " ) ,
_doc_items_usagehelp = S ( " To use arrows as ammunition for a bow, just put them anywhere in your inventory, they will be used up automatically. To use arrows as ammunition for a dispenser, place them in the dispenser's inventory. To retrieve an arrow that sticks in a block, simply walk close to it. " ) ,
2018-05-08 00:10:49 +03:00
inventory_image = " mcl_bows_arrow_inv.png " ,
2021-10-15 22:00:37 +03:00
groups = { ammo = 1 , ammo_bow = 1 , ammo_bow_regular = 1 , ammo_crossbow = 1 } ,
2018-02-01 23:45:19 +02:00
_on_dispense = function ( itemstack , dispenserpos , droppos , dropnode , dropdir )
-- Shoot arrow
local shootpos = vector.add ( dispenserpos , vector.multiply ( dropdir , 0.51 ) )
2018-05-09 16:10:56 +03:00
local yaw = math.atan2 ( dropdir.z , dropdir.x ) + YAW_OFFSET
2018-05-08 00:10:49 +03:00
mcl_bows.shoot_arrow ( itemstack : get_name ( ) , shootpos , dropdir , yaw , nil , 19 , 3 )
2018-02-01 23:45:19 +02:00
end ,
2015-06-29 20:55:56 +03:00
} )
2018-05-08 15:35:15 +03:00
local ARROW_ENTITY = {
physical = true ,
2021-03-14 08:24:34 +02:00
pointable = false ,
2021-03-13 00:45:22 +02:00
visual = " mesh " ,
mesh = " mcl_bows_arrow.obj " ,
2021-03-13 01:56:40 +02:00
visual_size = { x =- 1 , y = 1 } ,
2021-03-13 00:45:22 +02:00
textures = { " mcl_bows_arrow.png " } ,
2018-05-09 17:37:13 +03:00
collisionbox = { - 0.19 , - 0.125 , - 0.19 , 0.19 , 0.125 , 0.19 } ,
2018-05-08 15:35:15 +03:00
collide_with_objects = false ,
2021-01-27 21:11:02 +02:00
_fire_damage_resistant = true ,
2017-02-21 23:51:07 +02:00
_lastpos = { } ,
2017-03-06 02:02:30 +02:00
_startpos = nil ,
2017-02-21 23:51:07 +02:00
_damage = 1 , -- Damage on impact
2020-08-19 15:45:37 +03:00
_is_critical = false , -- Whether this arrow would deal critical damage
2018-05-08 01:38:00 +03:00
_stuck = false , -- Whether arrow is stuck
_stucktimer = nil , -- Amount of time (in seconds) the arrow has been stuck so far
2018-05-09 16:10:56 +03:00
_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.
2017-02-21 23:51:07 +02:00
_shooter = nil , -- ObjectRef of player or mob who shot it
2021-04-14 16:46:52 +03:00
_is_arrow = true ,
2022-01-02 13:06:29 +02:00
_in_player = false ,
2022-03-15 16:58:27 +02:00
_blocked = false ,
2018-05-12 08:00:16 +03:00
_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
2015-06-29 20:55:56 +03:00
}
2018-05-09 17:49:37 +03:00
-- Destroy arrow entity self at pos and drops it as an item
2021-05-28 01:34:58 +03:00
local function spawn_item ( self , pos )
2020-07-10 17:08:40 +03:00
if not minetest.is_creative_enabled ( " " ) then
2018-05-09 16:10:56 +03:00
local item = minetest.add_item ( pos , " mcl_bows:arrow " )
2022-03-15 16:58:27 +02:00
item : set_velocity ( vector.new ( 0 , 0 , 0 ) )
2018-05-09 16:10:56 +03:00
item : set_yaw ( self.object : get_yaw ( ) )
end
2021-01-27 21:11:02 +02:00
mcl_burning.extinguish ( self.object )
2018-05-09 16:10:56 +03:00
self.object : remove ( )
end
2021-05-28 01:34:58 +03:00
local function damage_particles ( pos , is_critical )
2020-08-19 15:45:37 +03:00
if is_critical then
minetest.add_particlespawner ( {
amount = 15 ,
time = 0.1 ,
2022-03-15 16:58:27 +02:00
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 ) ,
2020-08-19 15:45:37 +03:00
minexptime = 1 ,
maxexptime = 2 ,
minsize = 1.5 ,
maxsize = 1.5 ,
collisiondetection = false ,
vertical = false ,
texture = " mcl_particles_crit.png^[colorize:#bc7a57:127 " ,
} )
end
end
2021-05-28 01:34:58 +03:00
function ARROW_ENTITY . on_step ( self , dtime )
2021-04-25 14:09:20 +03:00
mcl_burning.tick ( self.object , dtime , self )
2021-01-27 21:11:02 +02:00
2021-03-17 22:47:02 +02:00
self._time_in_air = self._time_in_air + .001
2019-02-01 07:33:07 +02:00
local pos = self.object : get_pos ( )
2022-02-27 00:52:03 +02:00
local dpos = vector.round ( vector.new ( pos ) ) -- digital pos
2018-05-08 19:30:57 +03:00
local node = minetest.get_node ( dpos )
2015-06-29 20:55:56 +03:00
2018-05-08 01:38:00 +03:00
if self._stuck then
self._stucktimer = self._stucktimer + dtime
2018-05-09 16:10:56 +03:00
self._stuckrechecktimer = self._stuckrechecktimer + dtime
2018-05-08 01:38:00 +03:00
if self._stucktimer > ARROW_TIMEOUT then
2021-01-27 21:11:02 +02:00
mcl_burning.extinguish ( self.object )
2018-05-08 01:38:00 +03:00
self.object : remove ( )
return
end
2018-05-09 16:10:56 +03:00
-- Drop arrow as item when it is no longer stuck
2019-08-27 23:01:51 +03:00
-- FIXME: Arrows are a bit slow to react and continue to float in mid air for a few seconds.
2018-05-09 16:10:56 +03:00
if self._stuckrechecktimer > STUCK_RECHECK_TIME then
local stuckin_def
if self._stuckin then
stuckin_def = minetest.registered_nodes [ minetest.get_node ( self._stuckin ) . name ]
end
-- TODO: In MC, arrow just falls down without turning into an item
if stuckin_def and stuckin_def.walkable == false then
spawn_item ( self , pos )
return
end
self._stuckrechecktimer = 0
end
2018-05-09 19:19:42 +03:00
-- Pickup arrow if player is nearby (not in Creative Mode)
local objects = minetest.get_objects_inside_radius ( pos , 1 )
2018-05-08 01:38:00 +03:00
for _ , obj in ipairs ( objects ) do
if obj : is_player ( ) then
2021-01-27 17:01:04 +02:00
if self._collectable and not minetest.is_creative_enabled ( obj : get_player_name ( ) ) then
2018-05-08 01:38:00 +03:00
if obj : get_inventory ( ) : room_for_item ( " main " , " mcl_bows:arrow " ) then
obj : get_inventory ( ) : add_item ( " main " , " mcl_bows:arrow " )
minetest.sound_play ( " item_drop_pickup " , {
pos = pos ,
max_hear_distance = 16 ,
gain = 1.0 ,
2020-04-07 01:55:45 +03:00
} , true )
2018-05-08 01:38:00 +03:00
end
end
2021-01-27 21:11:02 +02:00
mcl_burning.extinguish ( self.object )
2018-05-09 19:19:42 +03:00
self.object : remove ( )
return
2018-05-08 01:38:00 +03:00
end
end
2018-05-09 18:31:04 +03:00
2018-05-12 20:52:40 +03:00
-- Check for object "collision". Done every tick (hopefully this is not too stressing)
2018-05-08 01:38:00 +03:00
else
2021-03-13 00:45:22 +02:00
2021-03-17 00:22:36 +02:00
if self._damage >= 9 and self._in_player == false then
2021-03-13 00:45:22 +02:00
minetest.add_particlespawner ( {
amount = 1 ,
time = .001 ,
minpos = pos ,
maxpos = pos ,
minvel = vector.new ( - 0.1 , - 0.1 , - 0.1 ) ,
maxvel = vector.new ( 0.1 , 0.1 , 0.1 ) ,
2021-03-13 01:02:53 +02:00
minexptime = 0.5 ,
maxexptime = 0.5 ,
2021-03-13 00:45:22 +02:00
minsize = 2 ,
maxsize = 2 ,
collisiondetection = false ,
vertical = false ,
texture = " mobs_mc_arrow_particle.png " ,
glow = 1 ,
} )
end
2018-05-12 20:52:40 +03:00
-- We just check for any hurtable objects nearby.
-- The radius of 3 is fairly liberal, but anything lower than than will cause
-- arrow to hilariously go through mobs often.
-- TODO: Implement an ACTUAL collision detection (engine support needed).
2020-07-21 02:07:10 +03:00
local objs = minetest.get_objects_inside_radius ( pos , 1.5 )
2017-05-27 00:10:18 +03:00
local closest_object
local closest_distance
2018-05-12 08:00:16 +03:00
if self._deflection_cooloff > 0 then
self._deflection_cooloff = self._deflection_cooloff - dtime
end
2017-05-27 00:10:18 +03:00
-- Iterate through all objects and remember the closest attackable object
2015-06-29 20:55:56 +03:00
for k , obj in pairs ( objs ) do
2018-05-27 11:33:49 +03:00
local ok = false
2017-07-25 02:14:32 +03:00
-- Arrows can only damage players and mobs
2021-03-17 22:47:02 +02:00
if obj : is_player ( ) then
2017-07-25 02:14:32 +03:00
ok = true
2021-05-29 17:12:33 +03:00
elseif obj : get_luaentity ( ) then
2021-03-17 22:47:02 +02:00
if ( obj : get_luaentity ( ) . _cmi_is_mob or obj : get_luaentity ( ) . _hittable_by_projectile ) then
2017-05-27 00:10:18 +03:00
ok = true
end
end
if ok then
2019-02-01 07:33:07 +02:00
local dist = vector.distance ( pos , obj : get_pos ( ) )
2017-05-27 00:10:18 +03:00
if not closest_object or not closest_distance then
closest_object = obj
closest_distance = dist
elseif dist < closest_distance then
closest_object = obj
closest_distance = dist
end
end
end
-- If an attackable object was found, we will damage the closest one only
2021-03-17 22:47:02 +02:00
2021-05-29 17:12:33 +03:00
if closest_object then
2017-05-27 00:10:18 +03:00
local obj = closest_object
2017-07-25 02:14:32 +03:00
local is_player = obj : is_player ( )
local lua = obj : get_luaentity ( )
2021-03-17 22:47:02 +02:00
if obj == self._shooter and self._time_in_air > 1.02 or obj ~= self._shooter and ( is_player or ( lua and ( lua._cmi_is_mob or lua._hittable_by_projectile ) ) ) then
2019-03-08 23:31:26 +02:00
if obj : get_hp ( ) > 0 then
2020-01-20 20:43:29 +02:00
-- Check if there is no solid node between arrow and object
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 == closest_object then
-- Target reached! We can proceed now.
break
elseif pointed_thing.type == " node " then
local nn = minetest.get_node ( minetest.get_pointed_thing_position ( pointed_thing ) ) . name
local def = minetest.registered_nodes [ nn ]
if ( not def ) or def.walkable then
-- There's a node in the way. Delete arrow without damage
2021-01-27 21:11:02 +02:00
mcl_burning.extinguish ( self.object )
2020-01-20 20:43:29 +02:00
self.object : remove ( )
return
end
end
end
2020-06-08 08:51:48 +03:00
-- Punch target object but avoid hurting enderman.
2020-11-06 16:05:31 +02:00
if not lua or lua.name ~= " mobs_mc:enderman " then
2022-03-15 16:58:27 +02:00
if not self._in_player then
2021-03-17 00:22:36 +02:00
damage_particles ( self.object : get_pos ( ) , self._is_critical )
end
2021-01-28 21:16:26 +02:00
if mcl_burning.is_burning ( self.object ) then
2021-02-22 11:55:14 +02:00
mcl_burning.set_on_fire ( obj , 5 )
2021-01-28 21:16:26 +02:00
end
2022-03-15 16:58:27 +02:00
if not self._in_player and not self._blocked then
2021-03-17 00:22:36 +02:00
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
2022-03-15 16:58:27 +02:00
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 )
)
2021-03-17 00:22:36 +02:00
else
2022-03-15 16:58:27 +02:00
self._blocked = true
self.object : set_velocity ( vector.multiply ( self.object : get_velocity ( ) , - 0.25 ) )
2021-03-17 00:22:36 +02:00
end
2021-03-17 01:01:47 +02:00
minetest.after ( 150 , function ( )
2021-03-17 00:22:36 +02:00
self.object : remove ( )
end )
end
end
2020-06-08 08:51:48 +03:00
end
2019-03-08 23:31:26 +02:00
2020-08-19 15:45:37 +03:00
2019-03-08 23:31:26 +02:00
if is_player then
2022-03-15 16:58:27 +02:00
if self._shooter and self._shooter : is_player ( ) and not self._in_player and not self._blocked then
2019-03-08 23:31:26 +02:00
-- “Ding” sound for hitting another player
2020-12-03 13:59:57 +02:00
minetest.sound_play ( { name = " mcl_bows_hit_player " , gain = 0.1 } , { to_player = self._shooter : get_player_name ( ) } , true )
2019-03-08 23:31:26 +02:00
end
2018-05-07 23:23:15 +03:00
end
2017-03-06 02:02:30 +02:00
2019-03-08 23:31:26 +02:00
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
-- NOTE: Range has been reduced because mobs unload much earlier than that ... >_>
-- TODO: This achievement should be given for the kill, not just a hit
if self._shooter and self._shooter : is_player ( ) and vector.distance ( pos , self._startpos ) >= 20 then
if mod_awards and ( entity_name == " mobs_mc:skeleton " or entity_name == " mobs_mc:stray " or entity_name == " mobs_mc:witherskeleton " ) then
awards.unlock ( self._shooter : get_player_name ( ) , " mcl:snipeSkeleton " )
end
2017-03-06 02:02:30 +02:00
end
end
2022-03-15 16:58:27 +02:00
if not self._in_player and not self._blocked then
2021-03-17 00:22:36 +02:00
minetest.sound_play ( { name = " mcl_bows_hit_other " , gain = 0.3 } , { pos = self.object : get_pos ( ) , max_hear_distance = 16 } , true )
end
2017-05-21 06:55:55 +03:00
end
2021-03-17 00:22:36 +02:00
if not obj : is_player ( ) then
2021-03-19 12:37:12 +02:00
mcl_burning.extinguish ( self.object )
2021-10-15 22:00:37 +03:00
if self._piercing == 0 then
self.object : remove ( )
end
2021-03-17 00:22:36 +02:00
end
2018-05-08 15:35:15 +03:00
return
2015-06-29 20:55:56 +03:00
end
end
end
2017-05-27 01:27:56 +03:00
-- Check for node collision
2018-05-08 01:38:00 +03:00
if self._lastpos . x ~= nil and not self._stuck then
2017-01-26 20:05:25 +02:00
local def = minetest.registered_nodes [ node.name ]
2018-05-08 15:35:15 +03:00
local vel = self.object : get_velocity ( )
2018-05-12 08:00:16 +03:00
-- 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. :-(
2018-05-08 15:35:15 +03:00
if ( math.abs ( vel.x ) < 0.0001 ) or ( math.abs ( vel.z ) < 0.0001 ) or ( math.abs ( vel.y ) < 0.00001 ) then
2018-05-09 16:10:56 +03:00
-- Check for the node to which the arrow is pointing
local dir
2018-05-09 16:17:19 +03:00
if math.abs ( vel.y ) < 0.00001 then
if self._lastpos . y < pos.y then
2022-03-15 16:58:27 +02:00
dir = vector.new ( 0 , 1 , 0 )
2018-05-09 16:17:19 +03:00
else
2022-03-15 16:58:27 +02:00
dir = vector.new ( 0 , - 1 , 0 )
2018-05-09 16:17:19 +03:00
end
2018-05-09 16:10:56 +03:00
else
dir = minetest.facedir_to_dir ( minetest.dir_to_facedir ( minetest.yaw_to_dir ( self.object : get_yaw ( ) - YAW_OFFSET ) ) )
end
2018-05-09 19:04:34 +03:00
self._stuckin = vector.add ( dpos , dir )
2018-05-09 16:10:56 +03:00
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
2018-05-12 08:00:16 +03:00
if self._deflection_cooloff <= 0 then
-- Lose 1/3 of velocity on deflection
2018-05-12 16:10:34 +03:00
local newvel = vector.multiply ( vel , 0.6667 )
2018-05-09 16:10:56 +03:00
2018-05-12 16:10:34 +03:00
self.object : set_velocity ( newvel )
2018-05-12 08:00:16 +03:00
-- Reset deflection cooloff timer to prevent many deflections happening in quick succession
2018-05-12 16:10:34 +03:00
self._deflection_cooloff = 1.0
2018-05-12 08:00:16 +03:00
end
else
2018-05-09 16:10:56 +03:00
2018-05-12 08:00:16 +03:00
-- Node was walkable, make arrow stuck
self._stuck = true
self._stucktimer = 0
self._stuckrechecktimer = 0
2018-05-09 16:10:56 +03:00
2022-03-15 16:58:27 +02:00
self.object : set_velocity ( vector.new ( 0 , 0 , 0 ) )
self.object : set_acceleration ( vector.new ( 0 , 0 , 0 ) )
2018-05-12 08:00:16 +03:00
2020-12-08 17:07:49 +02:00
minetest.sound_play ( { name = " mcl_bows_hit_other " , gain = 0.3 } , { pos = self.object : get_pos ( ) , max_hear_distance = 16 } , true )
2021-02-01 09:15:28 +02:00
if mcl_burning.is_burning ( self.object ) and snode.name == " mcl_tnt:tnt " then
tnt.ignite ( self._stuckin )
end
2022-02-27 00:52:03 +02:00
-- Activate target
if mod_target and snode.name == " mcl_target:target_off " then
mcl_target.hit ( self._stuckin , 1 ) --10 redstone ticks
end
2018-05-12 08:00:16 +03:00
-- Push the button! Push, push, push the button!
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
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
2018-05-09 19:04:34 +03:00
end
2018-05-08 15:35:15 +03:00
end
2018-05-08 01:02:07 +03:00
elseif ( def and def.liquidtype ~= " none " ) then
-- Slow down arrow in liquids
local v = def.liquid_viscosity
if not v then
v = 0
end
2021-05-23 02:06:34 +03:00
--local old_v = self._viscosity
2018-05-12 08:00:16 +03:00
self._viscosity = v
2018-05-08 01:02:07 +03:00
local vpenalty = math.max ( 0.1 , 0.98 - 0.1 * v )
if math.abs ( vel.x ) > 0.001 then
vel.x = vel.x * vpenalty
end
if math.abs ( vel.z ) > 0.001 then
vel.z = vel.z * vpenalty
end
self.object : set_velocity ( vel )
2015-06-29 20:55:56 +03:00
end
end
2017-05-27 01:27:56 +03:00
2018-05-09 16:10:56 +03:00
-- Update yaw
if not self._stuck then
local vel = self.object : get_velocity ( )
2019-08-27 23:01:51 +03:00
local yaw = minetest.dir_to_yaw ( vel ) + YAW_OFFSET
local pitch = dir_to_pitch ( vel )
self.object : set_rotation ( { x = 0 , y = yaw , z = pitch } )
2018-05-09 16:10:56 +03:00
end
2017-05-27 01:27:56 +03:00
-- Update internal variable
2022-03-15 16:58:27 +02:00
self._lastpos = pos
2015-06-29 20:55:56 +03:00
end
2018-05-09 17:49:37 +03:00
-- Force recheck of stuck arrows when punched.
-- Otherwise, punching has no effect.
2021-05-28 01:34:58 +03:00
function ARROW_ENTITY . on_punch ( self )
2018-05-09 17:49:37 +03:00
if self._stuck then
self._stuckrechecktimer = STUCK_RECHECK_TIME
end
end
2021-05-28 01:34:58 +03:00
function ARROW_ENTITY . get_staticdata ( self )
2018-05-08 18:16:11 +03:00
local out = {
lastpos = self._lastpos ,
startpos = self._startpos ,
damage = self._damage ,
2020-08-19 15:45:37 +03:00
is_critical = self._is_critical ,
2018-05-08 18:16:11 +03:00
stuck = self._stuck ,
2018-05-09 16:10:56 +03:00
stuckin = self._stuckin ,
2022-01-02 13:06:29 +02:00
stuckin_player = self._in_player ,
2018-05-08 18:16:11 +03:00
}
2018-05-09 18:31:04 +03:00
if self._stuck then
-- If _stucktimer is missing for some reason, assume the maximum
if not self._stucktimer then
self._stucktimer = ARROW_TIMEOUT
end
out.stuckstarttime = minetest.get_gametime ( ) - self._stucktimer
end
2018-05-08 18:16:11 +03:00
if self._shooter and self._shooter : is_player ( ) then
out.shootername = self._shooter : get_player_name ( )
end
return minetest.serialize ( out )
end
2021-05-28 01:34:58 +03:00
function ARROW_ENTITY . on_activate ( self , staticdata , dtime_s )
2021-03-17 22:47:02 +02:00
self._time_in_air = 1.0
2018-05-08 18:16:11 +03:00
local data = minetest.deserialize ( staticdata )
if data then
self._stuck = data.stuck
2018-05-09 18:31:04 +03:00
if data.stuck then
if data.stuckstarttime then
-- First, check if the stuck arrow is aleady past its life timer.
-- If yes, delete it.
self._stucktimer = minetest.get_gametime ( ) - data.stuckstarttime
if self._stucktimer > ARROW_TIMEOUT then
2021-01-27 21:11:02 +02:00
mcl_burning.extinguish ( self.object )
2018-05-09 18:31:04 +03:00
self.object : remove ( )
return
end
end
-- Perform a stuck recheck on the next step.
2018-05-09 16:10:56 +03:00
self._stuckrechecktimer = STUCK_RECHECK_TIME
2018-05-09 18:31:04 +03:00
self._stuckin = data.stuckin
2018-05-09 16:10:56 +03:00
end
2018-05-09 18:31:04 +03:00
-- Get the remaining arrow state
self._lastpos = data.lastpos
self._startpos = data.startpos
self._damage = data.damage
2020-08-19 15:45:37 +03:00
self._is_critical = data.is_critical
2018-05-08 18:16:11 +03:00
if data.shootername then
local shooter = minetest.get_player_by_name ( data.shootername )
if shooter and shooter : is_player ( ) then
self._shooter = shooter
end
end
2022-01-02 13:06:29 +02:00
if data.stuckin_player then
self.object : remove ( )
end
2018-05-08 18:16:11 +03:00
end
2018-05-28 16:49:04 +03:00
self.object : set_armor_groups ( { immortal = 1 } )
2018-05-08 18:16:11 +03:00
end
2022-01-02 13:06:29 +02:00
minetest.register_on_respawnplayer ( function ( player )
for _ , obj in pairs ( player : get_children ( ) ) do
local ent = obj : get_luaentity ( )
if ent and ent.name and string.find ( ent.name , " mcl_bows:arrow_entity " ) then
obj : remove ( )
end
end
end )
2018-05-08 15:35:15 +03:00
minetest.register_entity ( " mcl_bows:arrow_entity " , ARROW_ENTITY )
2015-06-29 20:55:56 +03:00
2017-07-26 20:35:56 +03:00
if minetest.get_modpath ( " mcl_core " ) and minetest.get_modpath ( " mcl_mobitems " ) then
minetest.register_craft ( {
2021-05-29 17:12:33 +03:00
output = " mcl_bows:arrow 4 " ,
2017-07-26 20:35:56 +03:00
recipe = {
2021-05-29 17:12:33 +03:00
{ " mcl_core:flint " } ,
{ " mcl_core:stick " } ,
{ " mcl_mobitems:feather " }
2017-07-26 20:35:56 +03:00
}
} )
end
2018-05-09 18:04:13 +03:00
2021-05-29 17:12:33 +03:00
if minetest.get_modpath ( " doc_identifier " ) then
2018-05-09 18:04:13 +03:00
doc.sub . identifier.register_object ( " mcl_bows:arrow_entity " , " craftitems " , " mcl_bows:arrow " )
end