throwing/init.lua

341 lines
10 KiB
Lua
Raw Normal View History

2017-01-04 18:53:37 +01:00
throwing = {}
throwing.arrows = {}
throwing.target_object = 1
throwing.target_node = 2
throwing.target_both = 3
throwing.modname = minetest.get_current_modname()
2017-01-04 18:53:37 +01:00
--------- Arrows functions ---------
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
local function shoot_arrow(itemstack, player, throw_itself)
2017-01-17 19:03:53 +01:00
local inventory = player:get_inventory()
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
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")
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
else
obj = minetest.add_entity(pos, "__builtin:item", arrow)
end
local luaentity = obj:get_luaentity()
luaentity.player = player:get_player_name()
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
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
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)
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
if not minetest.setting_getbool("creative_mode") then
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
if obj:get_player_name() == self.player then -- Avoid hitting the hitter
return false
2017-01-04 18:53:37 +01:00
end
end
end
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()
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
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()
return
end
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")
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-06-20 16:42:35 +02:00
hit_failed()
return
end
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
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
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
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
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.
Should return false or false, reason on failure.
on_throw(pos, hitter)
Unlike on_hit, it is optional.
2017-01-04 18:53:37 +01:00
]]
function throwing.register_arrow(name, def)
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
minetest.log("action", "Player "..playername.." placed arrow "..throwing.modname..":"..name.." at ("..pointed_thing.above.x..","..pointed_thing.above.y..","..pointed_thing.above.z..")")
minetest.set_node(pointed_thing.above, {name = throwing.modname..":"..name})
itemstack:take_item()
return itemstack
2017-01-12 19:17:51 +01:00
else
2017-06-20 17:07:46 +02: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..")")
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
minetest.register_entity(name.."_entity", {
2017-01-04 18:53:37 +01:00
physical = false,
timer = 0,
visual = "wielditem",
visual_size = {x = 0.125, y = 0.125},
textures = {throwing.modname..":"..name},
2017-01-04 18:53:37 +01:00
collisionbox = {0, 0, 0, 0, 0, 0},
on_hit = def.on_hit,
on_hit_sound = def.on_hit_sound,
on_throw_sound = def.on_throw_sound,
on_throw = def.on_throw,
allow_protected = def.allow_protected,
target = def.target,
2017-01-17 19:03:53 +01:00
on_hit_fails = def.on_hit_fails,
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
})
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 = {
{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 = {
{ "default:stick", "default:stick", def.itemcraft}
2017-01-04 18:53:37 +01:00
}
})
end
end
---------- Bows -----------
function throwing.register_bow(name, def)
if not string.find(name, ":") then
name = throwing.modname..":"..name
end
if not def.allow_shot then
def.allow_shot = function(player, itemstack)
return throwing.is_arrow(itemstack)
end
end
2017-06-20 17:07:46 +02:00
def.inventory_image = def.texture
def.textre = nil
def.on_use = function(itemstack, user, pointed_thing)
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
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
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)
end
return itemstack
end
minetest.register_tool(name, def)
2017-01-04 18:53:37 +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 = {
{"farming:cotton", def.itemcraft, ""},
{"farming:cotton", "", def.itemcraft},
{"farming:cotton", def.itemcraft, ""},
2017-01-04 18:53:37 +01:00
}
})
end
end
dofile(minetest.get_modpath(throwing.modname).."/registration.lua")