2017-01-04 18:53:37 +01:00
throwing = { }
throwing.arrows = { }
2017-01-17 17:43:10 +01:00
throwing.target_object = 1
throwing.target_node = 2
throwing.target_both = 3
2017-01-12 20:49:42 +01:00
throwing.modname = minetest.get_current_modname ( )
2017-01-04 18:53:37 +01:00
--------- Arrows functions ---------
2017-06-20 16:23:04 +02:00
function throwing . is_arrow ( itemstack )
for _ , arrow in ipairs ( throwing.arrows ) do
if ( type ( itemstack ) == " string " and itemstack or itemstack : get_name ( ) ) == arrow then
return true
end
end
return false
end
2017-06-20 17:03:14 +02:00
local function shoot_arrow ( itemstack , player , throw_itself )
2017-01-17 19:03:53 +01:00
local inventory = player : get_inventory ( )
2017-06-20 17:03:14 +02:00
local arrow
if throw_itself then
arrow = player : get_wielded_item ( ) : get_name ( )
else
arrow = inventory : get_stack ( " main " , player : get_wield_index ( ) + 1 ) : get_name ( )
end
2017-06-20 16:23:04 +02:00
local playerpos = player : getpos ( )
local pos = { x = playerpos.x , y = playerpos.y + 1.5 , z = playerpos.z }
local obj
if throwing.is_arrow ( arrow ) then
obj = minetest.add_entity ( pos , arrow .. " _entity " )
2017-06-20 16:39:03 +02:00
elseif minetest.registered_items [ arrow ] . throwing_entity then
if type ( minetest.registered_items [ arrow ] . throwing_entity ) == " string " then
obj = minetest.add_entity ( pos , minetest.registered_items [ arrow ] . throwing_entity )
else -- Type is a function
obj = minetest.registered_items [ arrow ] . throwing_entity ( pos , player )
end
2017-06-20 16:23:04 +02:00
else
obj = minetest.add_entity ( pos , " __builtin:item " , arrow )
end
2017-01-08 15:29:25 +01:00
2017-06-20 16:23:04 +02:00
local luaentity = obj : get_luaentity ( )
luaentity.player = player : get_player_name ( )
2017-01-08 15:29:25 +01:00
2017-06-20 16:23:04 +02:00
if luaentity.on_throw then
if luaentity.on_throw ( pos , player , ( ( player : get_wield_index ( ) + 1 ) % inventory : get_size ( " main " ) ) + 1 , luaentity.data , luaentity ) == false then
obj : remove ( )
return false
end
end
2017-01-17 17:43:10 +01:00
2017-06-20 16:23:04 +02:00
local dir = player : get_look_dir ( )
local velocity_factor = tonumber ( minetest.setting_get ( " throwing.velocity_factor " ) ) or 19
local horizontal_acceleration_factor = tonumber ( minetest.setting_get ( " throwing.horizontal_acceleration_factor " ) ) or - 3
local vertical_acceleration = tonumber ( minetest.setting_get ( " throwing.vertical_acceleration " ) ) or - 10
2017-01-17 16:32:30 +01:00
2017-06-20 16:23:04 +02:00
obj : setvelocity ( { x = dir.x * velocity_factor , y = dir.y * velocity_factor , z = dir.z * velocity_factor } )
obj : setacceleration ( { x = dir.x * horizontal_acceleration_factor , y = vertical_acceleration , z = dir.z * horizontal_acceleration_factor } )
obj : setyaw ( player : get_look_horizontal ( ) - math.pi / 2 )
2017-01-17 16:32:30 +01:00
2017-06-20 16:23:04 +02:00
if luaentity.on_throw_sound ~= " " then
minetest.sound_play ( luaentity.on_throw_sound or " throwing_sound " , { pos = playerpos , gain = 0.5 } )
2017-01-04 18:53:37 +01:00
end
2017-06-20 16:23:04 +02:00
if not minetest.setting_getbool ( " creative_mode " ) then
2017-06-20 17:03:14 +02:00
inventory : remove_item ( " main " , arrow )
2017-06-20 16:23:04 +02:00
end
return true
2017-01-04 18:53:37 +01:00
end
local function arrow_step ( self , dtime )
self.timer = self.timer + dtime
local pos = self.object : getpos ( )
local node = minetest.get_node ( pos )
local logging = function ( message , level )
2017-06-20 21:23:56 +02:00
minetest.log ( level or " action " , " [throwing] Arrow " .. self.item or self.name .. " throwed by player " .. self.player .. " " .. tostring ( self.timer ) .. " s ago " .. message )
2017-01-04 18:53:37 +01:00
end
local hit = function ( pos , node , obj )
if obj then
if obj : is_player ( ) then
2017-01-08 10:35:53 +01:00
if obj : get_player_name ( ) == self.player then -- Avoid hitting the hitter
2017-01-17 17:43:10 +01:00
return false
2017-01-04 18:53:37 +01:00
end
end
end
2017-01-15 12:03:54 +01:00
local player = minetest.get_player_by_name ( self.player )
if not player then -- Possible if the player disconnected
2017-01-04 18:53:37 +01:00
return
end
2017-01-17 19:03:53 +01:00
local function hit_failed ( )
2017-06-20 21:23:56 +02:00
if not minetest.setting_getbool ( " creative_mode " ) and self.item then
player : get_inventory ( ) : add_item ( " main " , self.item )
2017-01-15 12:03:54 +01:00
end
2017-01-17 19:03:53 +01:00
if self.on_hit_fails then
self.on_hit_fails ( pos , player , self.data , self )
end
2017-01-15 12:03:54 +01:00
end
if not self.last_pos then
2017-01-15 12:12:36 +01:00
logging ( " hitted a node during its first call to the step function " )
2017-01-17 19:03:53 +01:00
hit_failed ( )
2017-01-05 20:39:41 +01:00
return
end
2017-01-15 12:03:54 +01:00
2017-03-05 15:30:02 +01:00
if node and minetest.is_protected ( pos , self.player ) and not self.allow_protected then -- Forbid hitting nodes in protected areas
2017-01-15 13:05:05 +01:00
minetest.record_protection_violation ( pos , self.player )
2017-01-15 12:12:36 +01:00
logging ( " hitted a node into a protected area " )
2017-01-15 12:03:54 +01:00
return
end
2017-06-20 16:42:35 +02:00
if self.on_hit then
local ret , reason = self.on_hit ( pos , self.last_pos , node , obj , player , self.data , self )
if ret == false then
if reason then
logging ( " : on_hit function failed for reason: " .. reason )
else
logging ( " : on_hit function failed " )
end
2017-01-12 20:49:42 +01:00
2017-06-20 16:42:35 +02:00
hit_failed ( )
return
end
2017-01-12 20:49:42 +01:00
end
2017-01-04 18:53:37 +01:00
if self.on_hit_sound then
minetest.sound_play ( self.on_hit_sound , { pos = pos , gain = 0.8 } )
end
if node then
logging ( " collided with node " .. node.name .. " at ( " .. pos.x .. " , " .. pos.y .. " , " .. pos.z .. " ) " )
elseif obj then
if obj : get_luaentity ( ) then
logging ( " collided with luaentity " .. obj : get_luaentity ( ) . name .. " at ( " .. pos.x .. " , " .. pos.y .. " , " .. pos.z .. " ) " )
elseif obj : is_player ( ) then
2017-01-07 18:36:06 +01:00
logging ( " collided with player " .. obj : get_player_name ( ) .. " at ( " .. pos.x .. " , " .. pos.y .. " , " .. pos.z .. " ) " )
2017-01-04 18:53:37 +01:00
else
logging ( " collided with object at ( " .. pos.x .. " , " .. pos.y .. " , " .. pos.z .. " ) " )
end
end
end
-- Collision with a node
if node.name == " ignore " then
self.object : remove ( )
logging ( " reached ignore. Removing. " )
return
elseif node.name ~= " air " then
2017-01-17 17:43:10 +01:00
if self.target ~= throwing.target_object then -- throwing.target_both, nil, throwing.target_node, or any invalid value
if hit ( pos , node , nil ) ~= false then
self.object : remove ( )
end
else
self.object : remove ( )
end
2017-01-04 18:53:37 +01:00
return
end
-- Collision with an object
local objs = minetest.get_objects_inside_radius ( pos , 1 )
for k , obj in pairs ( objs ) do
if obj : get_luaentity ( ) then
if obj : get_luaentity ( ) . name ~= self.name and obj : get_luaentity ( ) . name ~= " __builtin:item " then
2017-01-17 17:43:10 +01:00
if self.target ~= throwing.target_node then -- throwing.target_both, nil, throwing.target_object, or any invalid value
if hit ( pos , nil , obj ) ~= false then
self.object : remove ( )
end
else
self.object : remove ( )
end
2017-01-04 18:53:37 +01:00
end
else
2017-01-17 17:43:10 +01:00
if self.target ~= throwing.target_node then -- throwing.target_both, nil, throwing.target_object, or any invalid value
if hit ( pos , nil , obj ) ~= false then
self.object : remove ( )
end
else
self.object : remove ( )
end
2017-01-04 18:53:37 +01:00
end
end
self.last_pos = pos -- Used by the build arrow
end
2017-06-20 21:23:56 +02:00
function throwing . make_arrow_def ( def )
def.timer = 0
def.player = " "
def.on_step = arrow_step
def.data = { }
return def
end
2017-01-04 18:53:37 +01:00
--[[
on_hit ( pos , last_pos , node , object , hitter )
Either node or object is nil , depending whether the arrow collided with an object ( luaentity or player ) or with a node .
No log message is needed in this function ( a generic log message is automatically emitted ) , except on error or warning .
2017-01-12 20:49:42 +01:00
Should return false or false , reason on failure .
2017-01-17 16:32:30 +01:00
on_throw ( pos , hitter )
Unlike on_hit , it is optional .
2017-01-04 18:53:37 +01:00
] ]
2017-01-17 17:43:10 +01:00
function throwing . register_arrow ( name , def )
2017-06-20 16:29:36 +02:00
if not string.find ( name , " : " ) then
name = throwing.modname .. " : " .. name
end
table.insert ( throwing.arrows , name )
2017-01-04 18:53:37 +01:00
2017-06-20 17:07:46 +02:00
if not def.groups then
def.groups = { }
2017-01-12 19:17:51 +01:00
end
2017-06-20 17:07:46 +02:00
if not def.groups . dig_immediate then
def.groups . dig_immediate = 3
end
def.inventory_image = def.tiles [ 1 ]
def.on_place = function ( itemstack , placer , pointed_thing )
if minetest.setting_getbool ( " throwing.allow_arrow_placing " ) and pointed_thing.above then
local playername = placer : get_player_name ( )
if not minetest.is_protected ( pointed_thing.above , playername ) then
2017-06-20 21:37:04 +02:00
minetest.log ( " action " , " Player " .. playername .. " placed arrow " .. name .. " at ( " .. pointed_thing.above . x .. " , " .. pointed_thing.above . y .. " , " .. pointed_thing.above . z .. " ) " )
minetest.set_node ( pointed_thing.above , { name = name } )
2017-06-20 17:07:46 +02:00
itemstack : take_item ( )
return itemstack
2017-01-12 19:17:51 +01:00
else
2017-06-20 21:37:04 +02:00
minetest.log ( " warning " , " Player " .. playername .. " tried to place arrow " .. name .. " into a protected area at ( " .. pointed_thing.above . x .. " , " .. pointed_thing.above . y .. " , " .. pointed_thing.above . z .. " ) " )
2017-06-20 17:07:46 +02:00
minetest.record_protection_violation ( pointed_thing.above , playername )
2017-01-12 19:17:51 +01:00
return itemstack
end
2017-06-20 17:07:46 +02:00
else
return itemstack
2017-01-12 19:17:51 +01:00
end
2017-06-20 17:07:46 +02:00
end
def.drawtype = " nodebox "
def.paramtype = " light "
def.node_box = {
type = " fixed " ,
fixed = {
-- Shaft
{ - 6.5 / 17 , - 1.5 / 17 , - 1.5 / 17 , 6.5 / 17 , 1.5 / 17 , 1.5 / 17 } ,
-- Spitze
{ - 4.5 / 17 , 2.5 / 17 , 2.5 / 17 , - 3.5 / 17 , - 2.5 / 17 , - 2.5 / 17 } ,
{ - 8.5 / 17 , 0.5 / 17 , 0.5 / 17 , - 6.5 / 17 , - 0.5 / 17 , - 0.5 / 17 } ,
-- Federn
{ 6.5 / 17 , 1.5 / 17 , 1.5 / 17 , 7.5 / 17 , 2.5 / 17 , 2.5 / 17 } ,
{ 7.5 / 17 , - 2.5 / 17 , 2.5 / 17 , 6.5 / 17 , - 1.5 / 17 , 1.5 / 17 } ,
{ 7.5 / 17 , 2.5 / 17 , - 2.5 / 17 , 6.5 / 17 , 1.5 / 17 , - 1.5 / 17 } ,
{ 6.5 / 17 , - 1.5 / 17 , - 1.5 / 17 , 7.5 / 17 , - 2.5 / 17 , - 2.5 / 17 } ,
{ 7.5 / 17 , 2.5 / 17 , 2.5 / 17 , 8.5 / 17 , 3.5 / 17 , 3.5 / 17 } ,
{ 8.5 / 17 , - 3.5 / 17 , 3.5 / 17 , 7.5 / 17 , - 2.5 / 17 , 2.5 / 17 } ,
{ 8.5 / 17 , 3.5 / 17 , - 3.5 / 17 , 7.5 / 17 , 2.5 / 17 , - 2.5 / 17 } ,
{ 7.5 / 17 , - 2.5 / 17 , - 2.5 / 17 , 8.5 / 17 , - 3.5 / 17 , - 3.5 / 17 } ,
}
}
minetest.register_node ( name , def )
2017-01-04 18:53:37 +01:00
2017-06-20 21:23:56 +02:00
minetest.register_entity ( name .. " _entity " , throwing.make_arrow_def {
2017-01-04 18:53:37 +01:00
physical = false ,
visual = " wielditem " ,
visual_size = { x = 0.125 , y = 0.125 } ,
2017-06-20 21:37:04 +02:00
textures = { name } ,
2017-01-04 18:53:37 +01:00
collisionbox = { 0 , 0 , 0 , 0 , 0 , 0 } ,
2017-01-17 17:43:10 +01:00
on_hit = def.on_hit ,
on_hit_sound = def.on_hit_sound ,
on_throw_sound = def.on_throw_sound ,
on_throw = def.on_throw ,
2017-03-05 15:30:02 +01:00
allow_protected = def.allow_protected ,
2017-01-17 17:43:10 +01:00
target = def.target ,
2017-01-17 19:03:53 +01:00
on_hit_fails = def.on_hit_fails ,
2017-06-20 21:23:56 +02:00
item = name ,
2017-01-04 18:53:37 +01:00
} )
2017-01-17 17:43:10 +01:00
if def.itemcraft then
2017-01-04 18:53:37 +01:00
minetest.register_craft ( {
2017-06-20 17:56:43 +02:00
output = name .. " " .. tostring ( def.craft_quantity or 1 ) ,
2017-01-04 18:53:37 +01:00
recipe = {
2017-01-17 17:43:10 +01:00
{ def.itemcraft , " default:stick " , " default:stick " }
2017-01-04 18:53:37 +01:00
}
} )
minetest.register_craft ( {
2017-06-20 17:56:43 +02:00
output = name .. " " .. tostring ( def.craft_quantity or 1 ) ,
2017-01-04 18:53:37 +01:00
recipe = {
2017-01-17 17:43:10 +01:00
{ " default:stick " , " default:stick " , def.itemcraft }
2017-01-04 18:53:37 +01:00
}
} )
end
end
---------- Bows -----------
2017-06-20 20:19:27 +02:00
local bow_cooldown = { }
2017-01-17 17:43:10 +01:00
function throwing . register_bow ( name , def )
2017-06-20 16:29:36 +02:00
if not string.find ( name , " : " ) then
name = throwing.modname .. " : " .. name
end
2017-06-20 16:23:04 +02:00
if not def.allow_shot then
def.allow_shot = function ( player , itemstack )
return throwing.is_arrow ( itemstack )
end
end
2017-06-20 20:19:27 +02:00
if not def.inventory_image then
def.inventory_image = def.texture
end
2017-06-20 17:07:46 +02:00
def.on_use = function ( itemstack , user , pointed_thing )
2017-06-20 20:19:27 +02:00
-- Cooldown
local meta = itemstack : get_meta ( )
local cooldown = def.cooldown or tonumber ( minetest.settings : get ( " throwing.bow_cooldown " ) ) or 0.2
if cooldown > 0 and meta : get_int ( " cooldown " ) > os.time ( ) then
return
end
-- Throw itself?
2017-06-20 17:07:46 +02:00
if not def.throw_itself and not def.allow_shot ( user , user : get_inventory ( ) : get_stack ( " main " , user : get_wield_index ( ) + 1 ) ) then
2017-01-04 18:53:37 +01:00
return itemstack
2017-06-20 17:07:46 +02:00
end
2017-06-20 20:19:27 +02:00
-- Shoot arrow
2017-06-20 17:07:46 +02:00
if shoot_arrow ( itemstack , user , def.throw_itself ) then
if not minetest.setting_getbool ( " creative_mode " ) then
2017-06-20 17:32:00 +02:00
itemstack : add_wear ( 65535 / ( def.uses or 50 ) )
2017-06-20 17:07:46 +02:00
end
end
2017-06-20 20:19:27 +02:00
2017-06-20 17:07:46 +02:00
if def.throw_itself then
-- This is a bug. If we return ItemStack(nil), the player punches the entity,
-- and if the entity if a __builtin:item, it gets back to his inventory.
minetest.after ( 0.1 , function ( )
user : get_inventory ( ) : remove_item ( " main " , itemstack )
end )
2017-06-20 20:19:27 +02:00
elseif cooldown > 0 then
meta : set_int ( " cooldown " , os.time ( ) + cooldown )
2017-06-20 17:07:46 +02:00
end
return itemstack
end
minetest.register_tool ( name , def )
2017-01-04 18:53:37 +01:00
2017-01-17 17:43:10 +01:00
if def.itemcraft then
2017-01-04 18:53:37 +01:00
minetest.register_craft ( {
2017-06-20 17:32:00 +02:00
output = name ,
2017-01-04 18:53:37 +01:00
recipe = {
2017-01-17 17:43:10 +01:00
{ " farming:cotton " , def.itemcraft , " " } ,
{ " farming:cotton " , " " , def.itemcraft } ,
{ " farming:cotton " , def.itemcraft , " " } ,
2017-01-04 18:53:37 +01:00
}
} )
end
end
2017-01-12 20:49:42 +01:00
dofile ( minetest.get_modpath ( throwing.modname ) .. " /registration.lua " )