2021-10-15 21:00:37 +02:00
local S = minetest.get_translator ( minetest.get_current_modname ( ) )
local math = math
local vector = vector
-- Time in seconds after which a stuck arrow is deleted
2024-08-31 13:41:42 +02:00
local ROCKET_TIMEOUT = 1
2021-10-15 21:00:37 +02:00
-- Time after which stuck arrow is rechecked for being stuck
local STUCK_RECHECK_TIME = 0.1
local YAW_OFFSET = - math.pi / 2
local function damage_explosion ( self , damagemulitplier )
2022-11-18 07:26:44 +01:00
if self._harmless then return end
2024-08-31 13:41:42 +02:00
2022-06-04 16:33:18 +02:00
local p = self.object : get_pos ( )
2022-09-13 02:39:11 +02:00
if not p then return end
2022-06-04 16:33:18 +02:00
mcl_explosions.explode ( p , 3 , { } )
local objects = minetest.get_objects_inside_radius ( p , 8 )
2021-10-15 21:00:37 +02:00
for _ , obj in pairs ( objects ) do
if obj : is_player ( ) then
2022-06-04 16:33:18 +02:00
mcl_util.deal_damage ( obj , damagemulitplier - vector.distance ( p , obj : get_pos ( ) ) , { type = " explosion " } )
2024-02-04 18:42:13 +01:00
elseif obj : get_luaentity ( ) and obj : get_luaentity ( ) . is_mob then
2021-10-15 21:00:37 +02:00
obj : punch ( self.object , 1.0 , {
full_punch_interval = 1.0 ,
2022-06-04 16:33:18 +02:00
damage_groups = { fleshy = damagemulitplier - vector.distance ( p , obj : get_pos ( ) ) } ,
2024-02-04 18:42:13 +01:00
} , self.object : get_velocity ( ) ) -- TODO possibly change the punch dir to be outwards instead of rocket velocity
2021-10-15 21:00:37 +02:00
end
end
end
local function particle_explosion ( self )
local particle_pattern = math.random ( 1 , 3 )
2022-02-26 23:52:03 +01:00
local fpitch
local type = math.random ( 1 , 2 )
local size = math.random ( 1 , 3 )
2021-10-15 21:00:37 +02:00
local colors = { " red " , " yellow " , " blue " , " green " , " white " }
local this_colors = { colors [ math.random ( # colors ) ] , colors [ math.random ( # colors ) ] , colors [ math.random ( # colors ) ] }
if size == 1 then
fpitch = math.random ( 200 , 300 )
elseif size == 2 then
fpitch = math.random ( 100 , 130 )
else
fpitch = math.random ( 60 , 70 )
end
if type == 1 then
minetest.sound_play ( " mcl_bows_firework " , {
pos = self.object : get_pos ( ) ,
max_hear_distance = 100 ,
gain = 3.0 ,
pitch = fpitch / 100
} , true )
else
minetest.sound_play ( " mcl_bows_firework_soft " , {
pos = self.object : get_pos ( ) ,
max_hear_distance = 100 ,
gain = 4.0 ,
pitch = fpitch / 100
} , true )
end
if particle_pattern == 1 then
minetest.add_particlespawner ( {
amount = 400 * size ,
time = 0.0001 ,
minpos = self.object : get_pos ( ) ,
maxpos = self.object : get_pos ( ) ,
minvel = vector.new ( - 7 * size , - 7 * size , - 7 * size ) ,
maxvel = vector.new ( 7 * size , 7 * size , 7 * size ) ,
minexptime = .6 * size / 2 ,
maxexptime = .9 * size / 2 ,
minsize = 2 * size ,
maxsize = 3 * size ,
collisiondetection = false ,
vertical = false ,
texture = " mcl_bows_firework_ " .. this_colors [ 1 ] .. " .png " ,
glow = 14 ,
} )
minetest.add_particlespawner ( {
amount = 400 * size ,
time = 0.0001 ,
minpos = self.object : get_pos ( ) ,
maxpos = self.object : get_pos ( ) ,
minvel = vector.new ( - 2 * size , - 2 * size , - 2 * size ) ,
maxvel = vector.new ( 2 * size , 2 * size , 2 * size ) ,
minexptime = .6 * size / 2 ,
maxexptime = .9 * size / 2 ,
minsize = 2 * size ,
maxsize = 3 * size ,
collisiondetection = false ,
vertical = false ,
texture = " mcl_bows_firework_ " .. this_colors [ 2 ] .. " .png " ,
glow = 14 ,
} )
minetest.add_particlespawner ( {
amount = 100 * size ,
time = 0.0001 ,
minpos = self.object : get_pos ( ) ,
maxpos = self.object : get_pos ( ) ,
minvel = vector.new ( - 14 * size , - 14 * size , - 14 * size ) ,
maxvel = vector.new ( 14 * size , 14 * size , 14 * size ) ,
minexptime = .6 * size / 2 ,
maxexptime = .9 * size / 2 ,
minsize = 2 * size ,
maxsize = 3 * size ,
collisiondetection = false ,
vertical = false ,
texture = " mcl_bows_firework_ " .. this_colors [ 3 ] .. " .png " ,
glow = 14 ,
} )
elseif particle_pattern == 2 then
minetest.add_particlespawner ( {
amount = 240 * size ,
time = 0.0001 ,
minpos = self.object : get_pos ( ) ,
maxpos = self.object : get_pos ( ) ,
minvel = vector.new ( - 5 * size , - 5 * size , - 5 * size ) ,
maxvel = vector.new ( 5 * size , 5 * size , 5 * size ) ,
minexptime = .6 * size / 2 ,
maxexptime = .9 * size / 2 ,
minsize = 2 * size ,
maxsize = 3 * size ,
collisiondetection = false ,
vertical = false ,
texture = " mcl_bows_firework_ " .. this_colors [ 1 ] .. " .png " ,
glow = 14 ,
} )
minetest.add_particlespawner ( {
amount = 500 * size ,
time = 0.0001 ,
minpos = self.object : get_pos ( ) ,
maxpos = self.object : get_pos ( ) ,
minvel = vector.new ( - 2 * size , - 2 * size , - 2 * size ) ,
maxvel = vector.new ( 2 * size , 2 * size , 2 * size ) ,
minexptime = .6 * size / 2 ,
maxexptime = .9 * size / 2 ,
minsize = 2 * size ,
maxsize = 3 * size ,
collisiondetection = false ,
vertical = false ,
texture = " mcl_bows_firework_ " .. this_colors [ 2 ] .. " .png " ,
glow = 14 ,
} )
minetest.add_particlespawner ( {
amount = 350 * size ,
time = 0.0001 ,
minpos = self.object : get_pos ( ) ,
maxpos = self.object : get_pos ( ) ,
minvel = vector.new ( - 3 * size , - 3 * size , - 3 * size ) ,
maxvel = vector.new ( 3 * size , 3 * size , 3 * size ) ,
minexptime = .6 * size / 2 ,
maxexptime = .9 * size / 2 ,
minsize = 2 * size ,
maxsize = 3 * size ,
collisiondetection = false ,
vertical = false ,
texture = " mcl_bows_firework_ " .. this_colors [ 3 ] .. " .png " ,
glow = 14 ,
} )
elseif particle_pattern == 3 then
minetest.add_particlespawner ( {
amount = 400 * size ,
time = 0.0001 ,
minpos = self.object : get_pos ( ) ,
maxpos = self.object : get_pos ( ) ,
minvel = vector.new ( - 6 * size , - 4 * size , - 6 * size ) ,
maxvel = vector.new ( 6 * size , 4 * size , 6 * size ) ,
minexptime = .6 * size ,
maxexptime = .9 * size ,
minsize = 2 * size ,
maxsize = 3 * size ,
collisiondetection = false ,
vertical = false ,
texture = " mcl_bows_firework_ " .. this_colors [ 1 ] .. " .png " ,
glow = 14 ,
} )
minetest.add_particlespawner ( {
amount = 120 * size ,
time = 0.0001 ,
minpos = self.object : get_pos ( ) ,
maxpos = self.object : get_pos ( ) ,
minvel = vector.new ( - 8 * size , 6 * size , - 8 * size ) ,
maxvel = vector.new ( 8 * size , 6 * size , 8 * size ) ,
minexptime = .6 * size ,
maxexptime = .9 * size ,
minsize = 2 * size ,
maxsize = 3 * size ,
collisiondetection = false ,
vertical = false ,
texture = " mcl_bows_firework_ " .. this_colors [ 2 ] .. " .png " ,
glow = 14 ,
} )
minetest.add_particlespawner ( {
amount = 130 * size ,
time = 0.0001 ,
minpos = self.object : get_pos ( ) ,
maxpos = self.object : get_pos ( ) ,
minvel = vector.new ( - 3 * size , 3 * size , - 3 * size ) ,
maxvel = vector.new ( 3 * size , 3 * size , 3 * size ) ,
minexptime = .6 * size ,
maxexptime = .9 * size ,
minsize = 2 * size ,
maxsize = 3 * size ,
collisiondetection = false ,
vertical = false ,
texture = " mcl_bows_firework_ " .. this_colors [ 3 ] .. " .png " ,
glow = 14 ,
} )
end
return size
end
minetest.register_craftitem ( " mcl_bows:rocket " , {
description = S ( " Arrow " ) ,
_tt_help = S ( " Ammunition " ) .. " \n " .. S ( " Damage from bow: 1-10 " ) .. " \n " .. S ( " Damage from dispenser: 3 " ) ,
_doc_items_longdesc = S ( " Arrows are ammunition for bows and dispensers. " ) .. " \n " ..
2024-08-31 13:41:42 +02:00
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. " ) ,
2021-10-15 21:00:37 +02:00
_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. " ) ,
inventory_image = " mcl_bows_rocket.png " ,
groups = { ammo = 1 , ammo_crossbow = 1 , ammo_bow_regular = 1 } ,
_on_dispense = function ( itemstack , dispenserpos , droppos , dropnode , dropdir )
-- Shoot arrow
local shootpos = vector.add ( dispenserpos , vector.multiply ( dropdir , 0.51 ) )
local yaw = math.atan2 ( dropdir.z , dropdir.x ) + YAW_OFFSET
mcl_bows.shoot_arrow ( itemstack : get_name ( ) , shootpos , dropdir , yaw , nil , 19 , 3 )
end ,
2024-08-31 13:41:42 +02:00
_on_collide_with_entity = function ( self , pos , obj )
if self._in_player == false then
obj : punch ( self.object , 1.0 , {
full_punch_interval = 1.0 ,
damage_groups = { fleshy = self._damage } ,
} , self.object : get_velocity ( ) )
2021-10-15 21:00:37 +02:00
2024-08-31 13:41:42 +02:00
if obj : is_player ( ) then
local eploded_particle = particle_explosion ( self )
damage_explosion ( self , eploded_particle * 17 )
mcl_burning.extinguish ( self.object )
self.object : remove ( )
end
end
end ,
} )
2021-10-15 21:00:37 +02:00
-- 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
local item = minetest.add_item ( pos , " mcl_bows:rocket " )
item : set_velocity ( { x = 0 , y = 0 , z = 0 } )
item : set_yaw ( self.object : get_yaw ( ) )
end
mcl_burning.extinguish ( self.object )
self.object : remove ( )
end
local function damage_particles ( pos , is_critical )
if is_critical then
minetest.add_particlespawner ( {
amount = 15 ,
time = 0.1 ,
minpos = { x = pos.x - 0.5 , y = pos.y - 0.5 , z = pos.z - 0.5 } ,
maxpos = { x = pos.x + 0.5 , y = pos.y + 0.5 , z = pos.z + 0.5 } ,
minvel = { x =- 0.1 , y =- 0.1 , z =- 0.1 } ,
maxvel = { x = 0.1 , y = 0.1 , z = 0.1 } ,
minacc = { x = 0 , y = 0 , z = 0 } ,
maxacc = { x = 0 , y = 0 , z = 0 } ,
minexptime = 1 ,
maxexptime = 2 ,
minsize = 1.5 ,
maxsize = 1.5 ,
collisiondetection = false ,
vertical = false ,
texture = " mcl_particles_crit.png^[colorize:#bc7a57:127 " ,
} )
end
end
2024-08-31 13:41:42 +02:00
local arrow_entity = mcl_bows.arrow_entity
local rocket_entity = table.copy ( arrow_entity )
table.update ( rocket_entity , {
mesh = " mcl_bows_rocket.obj " ,
textures = { " mcl_bows_rocket.png " } ,
visual_size = { x = 2.5 , y = 2.5 } ,
save_fields = {
" stuck " , " fuse " , " stuckin " , " lastpos " , " startpos " , " damage " , " is_critical " , " shootername " ,
} ,
_fuse = nil , -- Amount of time (in seconds) the arrow has been stuck so far
_fuserechecktimer = nil , -- An additional timer for periodically re-checking the stuck status of an arrow
} )
rocket_entity.on_step = function ( self , dtime )
self._fuse = ( self._fuse or 0 ) + dtime
2021-10-15 21:00:37 +02:00
2024-08-31 13:41:42 +02:00
if self._fuse > ROCKET_TIMEOUT then
2021-10-15 21:00:37 +02:00
self._stuck = true
end
2024-08-31 13:41:42 +02:00
if self._stuck and self._fuse > ROCKET_TIMEOUT then
local eploded_particle = particle_explosion ( self )
damage_explosion ( self , eploded_particle * 17 )
mcl_burning.extinguish ( self.object )
self.object : remove ( )
return
2021-10-15 21:00:37 +02:00
end
2024-08-31 13:41:42 +02:00
-- Perform normal arrow behaviors
arrow_entity.on_step ( self , dtime )
2021-10-15 21:00:37 +02:00
end
2024-08-31 13:41:42 +02:00
vl_projectile.register ( " mcl_bows:rocket_entity " , rocket_entity )
2021-10-15 21:00:37 +02:00
if minetest.get_modpath ( " mcl_core " ) and minetest.get_modpath ( " mcl_mobitems " ) then
minetest.register_craft ( {
output = " mcl_bows:rocket 1 " ,
recipe = {
{ " mcl_core:paper " } ,
{ " mcl_fireworks:rocket_2 " } ,
{ " mcl_bows:arrow " } ,
}
} )
end
if minetest.get_modpath ( " doc_identifier " ) then
doc.sub . identifier.register_object ( " mcl_bows:rocket_entity " , " craftitems " , " mcl_bows:rocket " )
end