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-01-04 18:53:37 +01:00
local function shoot_arrow ( itemstack , player )
2017-01-17 19:03:53 +01:00
local inventory = player : get_inventory ( )
2017-06-20 16:23:04 +02:00
local arrow = inventory : get_stack ( " main " , player : get_wield_index ( ) + 1 ) : get_name ( )
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 " )
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
player : get_inventory ( ) : remove_item ( " main " , arrow )
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 )
minetest.log ( level or " action " , " [throwing] Arrow " .. self.node .. " throwed by player " .. self.player .. " " .. tostring ( self.timer ) .. " s ago " .. message )
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-01-15 12:03:54 +01:00
if not minetest.setting_getbool ( " creative_mode " ) then
player : get_inventory ( ) : add_item ( " main " , self.node )
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-01-17 19:03:53 +01:00
local ret , reason = self.on_hit ( pos , self.last_pos , node , obj , player , self.data , self )
2017-01-12 20:49:42 +01:00
if ret == false then
if reason then
2017-01-15 12:12:36 +01:00
logging ( " : on_hit function failed for reason: " .. reason )
2017-01-12 20:49:42 +01:00
else
2017-01-15 12:12:36 +01:00
logging ( " : on_hit function failed " )
2017-01-12 20:49:42 +01:00
end
2017-01-17 19:03:53 +01:00
hit_failed ( )
2017-01-15 12:03:54 +01:00
return
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
--[[
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-01-12 20:49:42 +01:00
table.insert ( throwing.arrows , throwing.modname .. " : " .. name )
2017-01-04 18:53:37 +01:00
2017-01-17 17:43:10 +01:00
local groups = { dig_immediate = 3 }
if def.groups then
for k , v in pairs ( def.groups ) do
groups [ k ] = v
2017-01-12 19:17:51 +01:00
end
end
2017-01-12 20:49:42 +01:00
minetest.register_node ( throwing.modname .. " : " .. name , {
2017-01-04 18:53:37 +01:00
drawtype = " nodebox " ,
2017-01-12 19:17:51 +01:00
paramtype = " light " ,
2017-01-04 18:53:37 +01:00
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 } ,
}
} ,
2017-01-17 17:43:10 +01:00
tiles = def.tiles ,
inventory_image = def.tiles [ 1 ] ,
description = def.description ,
groups = groups ,
2017-01-12 19:17:51 +01:00
on_place = function ( itemstack , placer , pointed_thing )
if minetest.setting_getbool ( " throwing.allow_arrow_placing " ) and pointed_thing.above then
2017-01-13 18:04:41 +01:00
local playername = placer : get_player_name ( )
if not minetest.is_protected ( pointed_thing.above , playername ) then
2017-01-15 13:05:05 +01:00
minetest.log ( " action " , " Player " .. playername .. " placed arrow " .. throwing.modname .. " : " .. name .. " at ( " .. pointed_thing.above . x .. " , " .. pointed_thing.above . y .. " , " .. pointed_thing.above . z .. " ) " )
2017-01-12 20:49:42 +01:00
minetest.set_node ( pointed_thing.above , { name = throwing.modname .. " : " .. name } )
2017-01-12 19:17:51 +01:00
itemstack : take_item ( )
return itemstack
else
2017-01-13 18:04:41 +01:00
minetest.log ( " warning " , " Player " .. playername .. " tried to place arrow " .. throwing.modname .. " : " .. name .. " into a protected area at ( " .. pointed_thing.above . x .. " , " .. pointed_thing.above . y .. " , " .. pointed_thing.above . z .. " ) " )
2017-01-15 13:05:05 +01:00
minetest.record_protection_violation ( pointed_thing.above , playername )
2017-01-12 19:17:51 +01:00
return itemstack
end
else
return itemstack
end
end
2017-01-04 18:53:37 +01:00
} )
2017-01-12 20:49:42 +01:00
minetest.register_entity ( throwing.modname .. " : " .. name .. " _entity " , {
2017-01-04 18:53:37 +01:00
physical = false ,
timer = 0 ,
visual = " wielditem " ,
visual_size = { x = 0.125 , y = 0.125 } ,
2017-01-12 20:49:42 +01:00
textures = { throwing.modname .. " : " .. 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-01-12 20:49:42 +01:00
node = throwing.modname .. " : " .. name ,
2017-01-04 18:53:37 +01:00
player = " " ,
2017-01-17 19:03:53 +01:00
on_step = arrow_step ,
data = { }
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-01-17 17:43:10 +01:00
output = throwing.modname .. " : " .. 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-01-17 17:43:10 +01:00
output = throwing.modname .. " : " .. 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-01-17 17:43:10 +01:00
function throwing . register_bow ( name , def )
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-01-12 20:49:42 +01:00
minetest.register_tool ( throwing.modname .. " : " .. name , {
2017-01-17 17:43:10 +01:00
description = def.description ,
inventory_image = def.texture ,
2017-01-04 18:53:37 +01:00
on_use = function ( itemstack , user , pointed_thing )
2017-06-20 16:23:04 +02:00
if not def.allow_shot ( user , user : get_inventory ( ) : get_stack ( " main " , user : get_wield_index ( ) + 1 ) ) then
return itemstack
end
2017-01-04 18:53:37 +01:00
if shoot_arrow ( itemstack , user , pointed_thing ) then
if not minetest.setting_getbool ( " creative_mode " ) then
itemstack : add_wear ( 65535 / 30 )
end
end
return itemstack
end ,
2017-01-17 17:43:10 +01:00
groups = def.groups
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-01-12 20:49:42 +01:00
output = throwing.modname .. " : " .. 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 " )