![stujones11](/assets/img/avatar_default.png)
Update turret mod Fix undeclared global in blast function Update rockets and grenades Update grapple hook and flares Unlink model textures
303 lines
8.8 KiB
Lua
303 lines
8.8 KiB
Lua
local config = {
|
|
crossbow_uses = 50,
|
|
arrow_lifetime = 180,
|
|
arrow_object_attach = false,
|
|
}
|
|
|
|
-- Legacy Config Support
|
|
|
|
for name, _ in pairs(config) do
|
|
local global = "SHOOTER_"..name:upper()
|
|
if minetest.global_exists(global) then
|
|
config[name] = _G[global]
|
|
end
|
|
local setting = minetest.settings:get("shooter_"..name)
|
|
if type(setting) == "string" then
|
|
config[name] = tonumber(setting)
|
|
end
|
|
end
|
|
|
|
local arrow_tool_caps = {damage_groups={fleshy=2}}
|
|
if minetest.global_exists("SHOOTER_ARROW_TOOL_CAPS") then
|
|
arrow_tool_caps = table.copy(SHOOTER_ARROW_TOOL_CAPS)
|
|
end
|
|
|
|
local dye_basecolors = (dye and dye.basecolors) or
|
|
{"white", "grey", "black", "red", "yellow", "green", "cyan", "blue", "magenta"}
|
|
|
|
-- name is the overlay texture name, colour is used to select the wool texture
|
|
local function get_texture(name, colour)
|
|
return "wool_"..colour..".png^shooter_"..name..".png^[makealpha:255,126,126"
|
|
end
|
|
|
|
local function get_animation_frame(dir)
|
|
local angle = math.atan(dir.y)
|
|
local frame = 90 - math.floor(angle * 360 / math.pi)
|
|
if frame < 1 then
|
|
frame = 1
|
|
elseif frame > 180 then
|
|
frame = 180
|
|
end
|
|
return frame
|
|
end
|
|
|
|
local function get_pointed_thing(pos, dir, dist)
|
|
local p1 = vector.add(pos, dir)
|
|
local p2 = vector.add(pos, vector.multiply(dir, dist))
|
|
local ray = minetest.raycast(p1, p2, true, true)
|
|
return ray:next()
|
|
end
|
|
|
|
local function strike(arrow, pointed_thing, name)
|
|
local puncher = minetest.get_player_by_name(name)
|
|
if not puncher then
|
|
return
|
|
end
|
|
local object = arrow.object
|
|
local hit_pos = pointed_thing.intersection_point or object:get_pos()
|
|
local dir = vector.normalize(object:get_velocity())
|
|
if pointed_thing.type == "object" then
|
|
local target = pointed_thing.ref
|
|
if shooter:is_valid_object(target) then
|
|
local puncher = minetest.get_player_by_name(name)
|
|
if puncher and puncher ~= target then
|
|
local groups = target:get_armor_groups() or {}
|
|
if groups.fleshy then
|
|
shooter:spawn_particles(hit_pos,
|
|
shooter.config.explosion_texture)
|
|
end
|
|
target:punch(object, nil, arrow_tool_caps, dir)
|
|
if config.arrow_object_attach then
|
|
local pos = vector.multiply(vector.subtract(target:get_pos(),
|
|
hit_pos), -10)
|
|
local rot = vector.new()
|
|
rot.y = (target:get_yaw() - object:get_yaw()) * 57.2958
|
|
object:set_attach(target, "", pos, rot)
|
|
arrow.state = "stuck"
|
|
else
|
|
arrow.state = "dropped"
|
|
end
|
|
end
|
|
end
|
|
elseif pointed_thing.type == "node" then
|
|
local pos = minetest.get_pointed_thing_position(pointed_thing, false)
|
|
local node = minetest.get_node(pos)
|
|
hit_pos = vector.subtract(hit_pos, vector.multiply(dir, 0.25))
|
|
arrow.node_pos = pos
|
|
arrow.state = "stuck"
|
|
shooter:play_node_sound(node, pos)
|
|
else
|
|
return
|
|
end
|
|
arrow:stop(hit_pos)
|
|
end
|
|
|
|
minetest.register_entity("shooter_crossbow:arrow_entity", {
|
|
physical = false,
|
|
visual = "mesh",
|
|
mesh = "shooter_arrow.b3d",
|
|
visual_size = {x=1, y=1},
|
|
textures = {
|
|
get_texture("arrow_uv", "white"),
|
|
},
|
|
color = "white",
|
|
timer = 0,
|
|
lifetime = config.arrow_lifetime,
|
|
user = nil,
|
|
state = "init",
|
|
node_pos = nil,
|
|
collisionbox = {0,0,0, 0,0,0},
|
|
stop = function(self, pos)
|
|
local acceleration = {x=0, y=-10, z=0}
|
|
if self.state == "stuck" then
|
|
pos = pos or self.object:get_pos()
|
|
acceleration = {x=0, y=0, z=0}
|
|
end
|
|
if pos then
|
|
self.object:moveto(pos)
|
|
end
|
|
self.object:set_properties({
|
|
physical = true,
|
|
collisionbox = {-1/8,-1/8,-1/8, 1/8,1/8,1/8},
|
|
})
|
|
self.object:set_velocity({x=0, y=0, z=0})
|
|
self.object:set_acceleration(acceleration)
|
|
end,
|
|
on_activate = function(self, staticdata)
|
|
self.object:set_armor_groups({immortal=1})
|
|
if staticdata == "expired" then
|
|
self.object:remove()
|
|
end
|
|
end,
|
|
on_punch = function(self, puncher)
|
|
if puncher then
|
|
if puncher:is_player() then
|
|
local stack = "shooter_crossbow:arrow_"..self.color
|
|
local inv = puncher:get_inventory()
|
|
if inv:room_for_item("main", stack) then
|
|
inv:add_item("main", stack)
|
|
self.object:remove()
|
|
end
|
|
end
|
|
end
|
|
end,
|
|
on_step = function(self, dtime)
|
|
if self.state == "init" then
|
|
return
|
|
end
|
|
self.timer = self.timer + dtime
|
|
self.lifetime = self.lifetime - dtime
|
|
if self.lifetime < 0 then
|
|
self.object:remove()
|
|
return
|
|
elseif self.state == "dropped" then
|
|
return
|
|
elseif self.state == "stuck" then
|
|
if self.timer > 1 then
|
|
if self.node_pos then
|
|
local node = minetest.get_node(self.node_pos)
|
|
if node.name then
|
|
local item = minetest.registered_items[node.name]
|
|
if item then
|
|
if not item.walkable then
|
|
self.state = "dropped"
|
|
self:stop()
|
|
return
|
|
end
|
|
end
|
|
end
|
|
end
|
|
self.timer = 0
|
|
end
|
|
return
|
|
end
|
|
if self.timer > 0.2 then
|
|
local dir = vector.normalize(self.object:get_velocity())
|
|
local frame = get_animation_frame(dir)
|
|
local pos = self.object:get_pos()
|
|
local pointed_thing = get_pointed_thing(pos, dir, 5)
|
|
if pointed_thing then
|
|
strike(self, pointed_thing, self.user)
|
|
end
|
|
self.object:set_animation({x=frame, y=frame}, 0)
|
|
self.timer = 0
|
|
end
|
|
end,
|
|
get_staticdata = function(self)
|
|
return "expired"
|
|
end,
|
|
})
|
|
|
|
for _, color in pairs(dye_basecolors) do
|
|
minetest.register_craftitem("shooter_crossbow:arrow_"..color, {
|
|
description = color:gsub("%a", string.upper, 1).." Arrow",
|
|
inventory_image = get_texture("arrow_inv", color),
|
|
})
|
|
minetest.register_tool("shooter_crossbow:crossbow_loaded_"..color, {
|
|
description = "Crossbow",
|
|
inventory_image = get_texture("crossbow_loaded", color),
|
|
groups = {not_in_creative_inventory=1},
|
|
on_use = function(itemstack, user, pointed_thing)
|
|
minetest.sound_play("shooter_click", {object=user})
|
|
if not minetest.setting_getbool("creative_mode") then
|
|
itemstack:add_wear(65535 / config.crossbow_uses)
|
|
end
|
|
itemstack = "shooter_crossbow:crossbow 1 "..itemstack:get_wear()
|
|
local pos = user:get_pos()
|
|
local dir = user:get_look_dir()
|
|
local yaw = user:get_look_horizontal()
|
|
if pos and dir and yaw then
|
|
pos.y = pos.y + shooter.config.camera_height
|
|
local obj = minetest.add_entity(pos,
|
|
"shooter_crossbow:arrow_entity")
|
|
local ent = nil
|
|
if obj then
|
|
ent = obj:get_luaentity()
|
|
end
|
|
if ent then
|
|
ent.user = user:get_player_name()
|
|
ent.state = "flight"
|
|
ent.color = color
|
|
obj:set_properties({
|
|
textures = {get_texture("arrow_uv", color)}
|
|
})
|
|
minetest.sound_play("shooter_throw", {object=obj})
|
|
local frame = get_animation_frame(dir)
|
|
obj:set_yaw(yaw - math.pi / 2)
|
|
obj:set_animation({x=frame, y=frame}, 0)
|
|
obj:set_velocity({x=dir.x * 14, y=dir.y * 14, z=dir.z * 14})
|
|
obj:set_acceleration({x=dir.x * -3, y=-5, z=dir.z * -3})
|
|
pointed_thing = get_pointed_thing(pos, dir, 5)
|
|
if pointed_thing then
|
|
strike(ent, pointed_thing, ent.user)
|
|
end
|
|
end
|
|
end
|
|
return itemstack
|
|
end,
|
|
})
|
|
end
|
|
|
|
minetest.register_tool("shooter_crossbow:crossbow", {
|
|
description = "Crossbow",
|
|
inventory_image = "shooter_crossbow.png",
|
|
on_use = function(itemstack, user, pointed_thing)
|
|
local inv = user:get_inventory()
|
|
local stack = inv:get_stack("main", user:get_wield_index() + 1)
|
|
local color = string.match(stack:get_name(), "shooter_crossbow:arrow_(%a+)")
|
|
if color then
|
|
minetest.sound_play("shooter_reload", {object=user})
|
|
if not minetest.setting_getbool("creative_mode") then
|
|
inv:remove_item("main", "shooter_crossbow:arrow_"..color.." 1")
|
|
end
|
|
return "shooter_crossbow:crossbow_loaded_"..color.." 1 "..itemstack:get_wear()
|
|
end
|
|
for _, color in pairs(dye_basecolors) do
|
|
if inv:contains_item("main", "shooter_crossbow:arrow_"..color) then
|
|
minetest.sound_play("shooter_reload", {object=user})
|
|
if not minetest.setting_getbool("creative_mode") then
|
|
inv:remove_item("main", "shooter_crossbow:arrow_"..color.." 1")
|
|
end
|
|
return "shooter_crossbow:crossbow_loaded_"..color.." 1 "..itemstack:get_wear()
|
|
end
|
|
end
|
|
minetest.sound_play("shooter_click", {object=user})
|
|
end,
|
|
})
|
|
|
|
if shooter.config.enable_crafting == true then
|
|
minetest.register_craft({
|
|
output = "shooter_crossbow:crossbow",
|
|
recipe = {
|
|
{"default:stick", "default:stick", "default:stick"},
|
|
{"default:stick", "default:stick", ""},
|
|
{"default:stick", "", "default:bronze_ingot"},
|
|
},
|
|
})
|
|
minetest.register_craft({
|
|
output = "shooter_crossbow:arrow_white",
|
|
recipe = {
|
|
{"default:steel_ingot", "", ""},
|
|
{"", "default:stick", "default:paper"},
|
|
{"", "default:paper", "default:stick"},
|
|
},
|
|
})
|
|
for _, color in pairs(dye_basecolors) do
|
|
if color ~= "white" then
|
|
minetest.register_craft({
|
|
output = "shooter_crossbow:arrow_"..color,
|
|
recipe = {
|
|
{"", "dye:"..color, "shooter_crossbow:arrow_white"},
|
|
},
|
|
})
|
|
end
|
|
end
|
|
end
|
|
|
|
--Backwards compatibility
|
|
minetest.register_alias("shooter:crossbow", "shooter_crossbow:crossbow")
|
|
for _, color in pairs(dye_basecolors) do
|
|
minetest.register_alias("shooter:arrow_"..color, "shooter_crossbow:arrow_"..color)
|
|
minetest.register_alias("shooter:crossbow_loaded_"..color, "shooter_crossbow:crossbow_loaded_"..color)
|
|
end
|