644 lines
22 KiB
Lua
644 lines
22 KiB
Lua
-- X Bows
|
|
-- by SaKeL
|
|
|
|
minetest = minetest--[[@as Minetest]]
|
|
ItemStack = ItemStack--[[@as ItemStack]]
|
|
vector = vector--[[@as Vector]]
|
|
default = default--[[@as MtgDefault]]
|
|
|
|
math.randomseed(tonumber(tostring(os.time()):reverse():sub(1, 9))--[[@as number]])
|
|
|
|
local mod_start_time = minetest.get_us_time()
|
|
local bow_charged_timer = 0
|
|
|
|
---x_bows main class
|
|
---@class XBows
|
|
x_bows = {
|
|
pvp = minetest.settings:get_bool('enable_pvp') or false,
|
|
creative = minetest.settings:get_bool('creative_mode') or false,
|
|
mesecons = minetest.get_modpath('mesecons'),
|
|
hbhunger = minetest.get_modpath('hbhunger'),
|
|
playerphysics = minetest.get_modpath('playerphysics'),
|
|
player_monoids = minetest.get_modpath('player_monoids'),
|
|
registered_arrows = {},
|
|
registered_bows = {},
|
|
registered_quivers = {},
|
|
player_bow_sneak = {},
|
|
settings = {
|
|
x_bows_attach_arrows_to_entities = minetest.settings:get_bool('x_bows_attach_arrows_to_entities', false)
|
|
},
|
|
quiver = {
|
|
hud_item_ids = {},
|
|
after_job = {}
|
|
},
|
|
charge_sound_after_job = {}
|
|
}
|
|
|
|
---Shorthand for checking creative priv
|
|
---@param name string
|
|
---@return boolean
|
|
function x_bows.is_creative(name)
|
|
return x_bows.creative or minetest.check_player_privs(name, {creative = true})
|
|
end
|
|
|
|
---Reset charged bow to uncharged bow, this will return the arrow item to the inventory also
|
|
---@param player ObjectRef Player Ref
|
|
---@param includeWielded? boolean Will include reset for wielded bow also. default: `false`
|
|
---@return nil
|
|
local function reset_charged_bow(player, includeWielded)
|
|
local _includeWielded = includeWielded or false
|
|
local inv = player:get_inventory()
|
|
|
|
if inv and inv:contains_item('main', 'x_bows:bow_wood_charged') then
|
|
local inv_list = inv:get_list('main')
|
|
|
|
for i, st in ipairs(inv_list) do
|
|
local reset = _includeWielded or player:get_wield_index() ~= i
|
|
|
|
if not st:is_empty() and x_bows.registered_bows[st:get_name()] and reset then
|
|
local item_meta = st:get_meta()
|
|
local arrow_itemstack = ItemStack(minetest.deserialize(item_meta:get_string('arrow_itemstack_string')))
|
|
|
|
-- return arrow
|
|
if arrow_itemstack and not x_bows.is_creative(player:get_player_name()) then
|
|
if inv:room_for_item('main', {name=arrow_itemstack:get_name()}) then
|
|
inv:add_item('main', arrow_itemstack:get_name())
|
|
else
|
|
minetest.item_drop(ItemStack({name=arrow_itemstack:get_name(), count=1}), player, player:get_pos())
|
|
end
|
|
end
|
|
|
|
-- reset bow to uncharged bow
|
|
inv:set_stack('main', i, ItemStack({
|
|
name=x_bows.registered_bows[st:get_name()].name,
|
|
count=st:get_count(),
|
|
wear=st:get_wear()
|
|
}))
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
minetest.register_on_joinplayer(function(player)
|
|
reset_charged_bow(player, true)
|
|
x_bows.quiver.close_quiver(player)
|
|
end)
|
|
|
|
---Register bows
|
|
---@param name string
|
|
---@param def table
|
|
---@return boolean|nil
|
|
function x_bows.register_bow(name, def)
|
|
if name == nil or name == '' then
|
|
return false
|
|
end
|
|
|
|
def.name = 'x_bows:' .. name
|
|
def.name_charged = 'x_bows:' .. name .. '_charged'
|
|
def.description = def.description or name
|
|
def.uses = def.uses or 150
|
|
|
|
x_bows.registered_bows[def.name_charged] = def
|
|
|
|
-- not charged bow
|
|
minetest.register_tool(def.name, {
|
|
description = def.description .. '\n' .. minetest.colorize('#00FF00', 'Critical Arrow Chance: '
|
|
.. (1 / def.crit_chance) * 100 .. '%'),
|
|
inventory_image = def.inventory_image or 'x_bows_bow_wood.png',
|
|
on_place = x_bows.load,
|
|
on_secondary_use = x_bows.load,
|
|
groups = {bow = 1, flammable = 1}
|
|
})
|
|
|
|
-- charged bow
|
|
minetest.register_tool(def.name_charged, {
|
|
description = def.description .. '\n' .. minetest.colorize('#00FF00', 'Critical Arrow Chance: '
|
|
.. (1 / def.crit_chance) * 100 .. '%'),
|
|
inventory_image = def.inventory_image_charged or 'x_bows_bow_wood_charged.png',
|
|
on_use = x_bows.shoot,
|
|
groups = {bow = 1, flammable = 1, not_in_creative_inventory = 1},
|
|
on_drop = function(itemstack, dropper, pos)
|
|
local item_meta = itemstack:get_meta()
|
|
local arrow_itemstack = ItemStack(minetest.deserialize(item_meta:get_string('arrow_itemstack_string')))
|
|
|
|
-- return arrow
|
|
if arrow_itemstack and not x_bows.is_creative(dropper:get_player_name()) then
|
|
minetest.item_drop(ItemStack({name=arrow_itemstack:get_name(), count=1}), dropper, {x=pos.x + 0.5, y=pos.y + 0.5, z=pos.z + 0.5})
|
|
end
|
|
|
|
itemstack:set_name(def.name)
|
|
-- returns leftover itemstack
|
|
return minetest.item_drop(itemstack, dropper, pos)
|
|
end
|
|
})
|
|
|
|
-- recipes
|
|
if def.recipe then
|
|
minetest.register_craft({
|
|
output = def.name,
|
|
recipe = def.recipe
|
|
})
|
|
end
|
|
end
|
|
|
|
---Register arrows
|
|
---@param name string
|
|
---@param def table
|
|
---@return boolean|nil
|
|
function x_bows.register_arrow(name, def)
|
|
if name == nil or name == '' then
|
|
return false
|
|
end
|
|
|
|
def.name = 'x_bows:' .. name
|
|
def.description = def.description or name
|
|
|
|
x_bows.registered_arrows[def.name] = def
|
|
|
|
minetest.register_craftitem('x_bows:' .. name, {
|
|
description = def.description .. '\n' .. minetest.colorize('#00FF00', 'Damage: '
|
|
.. def.tool_capabilities.damage_groups.fleshy) .. '\n' .. minetest.colorize('#00BFFF', 'Charge Time: '
|
|
.. def.tool_capabilities.full_punch_interval .. 's'),
|
|
short_description = def.description,
|
|
inventory_image = def.inventory_image,
|
|
groups = {arrow = 1, flammable = 1}
|
|
})
|
|
|
|
-- recipes
|
|
if def.craft then
|
|
minetest.register_craft({
|
|
output = def.name ..' ' .. (def.craft_count or 4),
|
|
recipe = def.craft
|
|
})
|
|
end
|
|
end
|
|
|
|
---Register quivers
|
|
---@param name string
|
|
---@param def table
|
|
---@return boolean|nil
|
|
function x_bows.register_quiver(name, def)
|
|
if name == nil or name == '' then
|
|
return false
|
|
end
|
|
|
|
def.name = 'x_bows:' .. name
|
|
def.name_open = 'x_bows:' .. name .. '_open'
|
|
def.description = def.description or name
|
|
def.uses = def.uses or 150
|
|
|
|
x_bows.registered_quivers[def.name] = def
|
|
|
|
---closed quiver
|
|
minetest.register_tool(def.name, {
|
|
description = def.description
|
|
.. '\n' .. minetest.colorize('#00FF00', 'Faster Arrows: ' .. (1 / def.faster_arrows) * 100 .. '%')
|
|
.. '\n' .. minetest.colorize('#FF8080', 'Arrow Damage: +' .. def.add_damage),
|
|
short_description = def.short_description
|
|
.. '\n' .. minetest.colorize('#00FF00', 'Faster Arrows: ' .. (1 / def.faster_arrows) * 100 .. '%')
|
|
.. '\n' .. minetest.colorize('#FF8080', 'Arrow Damage: +' .. def.add_damage),
|
|
inventory_image = def.inventory_image or 'x_bows_quiver.png',
|
|
wield_image = def.wield_image or 'x_bows_quiver.png',
|
|
groups = {quiver = 1, flammable = 1},
|
|
on_secondary_use = function(itemstack, user, pointed_thing)
|
|
return x_bows.open_quiver(itemstack, user)
|
|
end,
|
|
---@param itemstack ItemStack
|
|
---@param placer ObjectRef
|
|
---@param pointed_thing PointedThingDef
|
|
---@return ItemStack|nil
|
|
on_place = function(itemstack, placer, pointed_thing)
|
|
if pointed_thing.under then
|
|
local node = minetest.get_node(pointed_thing.under)
|
|
local node_def = minetest.registered_nodes[node.name]
|
|
|
|
if node_def and node_def.on_rightclick then
|
|
return node_def.on_rightclick(pointed_thing.under, node, placer, itemstack, pointed_thing)
|
|
end
|
|
end
|
|
|
|
return x_bows.open_quiver(itemstack, placer)
|
|
end
|
|
})
|
|
|
|
---open quiver
|
|
minetest.register_tool(def.name_open, {
|
|
description = def.description
|
|
.. '\n' .. minetest.colorize('#00FF00', 'Faster Arrows: ' .. (1 / def.faster_arrows) * 100 .. '%')
|
|
.. '\n' .. minetest.colorize('#FF8080', 'Arrow Damage: +' .. def.add_damage),
|
|
short_description = def.short_description
|
|
.. '\n' .. minetest.colorize('#00FF00', 'Faster Arrows: ' .. (1 / def.faster_arrows) * 100 .. '%')
|
|
.. '\n' .. minetest.colorize('#FF8080', 'Arrow Damage: +' .. def.add_damage),
|
|
inventory_image = def.inventory_image_open or 'x_bows_quiver_open.png',
|
|
wield_image = def.wield_image_open or 'x_bows_quiver_open.png',
|
|
groups = {quiver = 1, flammable = 1, not_in_creative_inventory = 1},
|
|
---@param itemstack ItemStack
|
|
---@param dropper ObjectRef|nil
|
|
---@param pos Vector
|
|
---@return ItemStack
|
|
on_drop = function (itemstack, dropper, pos)
|
|
local replace_item = x_bows.quiver.get_replacement_item(itemstack, 'x_bows:quiver')
|
|
return minetest.item_drop(replace_item, dropper, pos)
|
|
end
|
|
})
|
|
|
|
-- recipes
|
|
if def.recipe then
|
|
minetest.register_craft({
|
|
output = def.name,
|
|
recipe = def.recipe
|
|
})
|
|
end
|
|
end
|
|
|
|
---Loads bow
|
|
---@param itemstack ItemStack
|
|
---@param user ObjectRef
|
|
---@param pointed_thing PointedThingDef
|
|
---@return ItemStack
|
|
function x_bows.load(itemstack, user, pointed_thing)
|
|
local player_name = user:get_player_name()
|
|
local inv = user:get_inventory()--[[@as InvRef]]
|
|
local inv_list = inv:get_list('main')
|
|
local bow_name = itemstack:get_name()
|
|
local bow_def = x_bows.registered_bows[bow_name .. '_charged']
|
|
---@alias ItemStackArrows {["stack"]: ItemStack, ["idx"]: number|integer}[]
|
|
---@type ItemStackArrows
|
|
local itemstack_arrows = {}
|
|
|
|
---trigger right click event if pointed item has one
|
|
if pointed_thing.under then
|
|
local node = minetest.get_node(pointed_thing.under)
|
|
local node_def = minetest.registered_nodes[node.name]
|
|
|
|
if node_def and node_def.on_rightclick then
|
|
return node_def.on_rightclick(pointed_thing.under, node, user, itemstack, pointed_thing)
|
|
end
|
|
end
|
|
|
|
---find itemstack arrow in quiver
|
|
local quiver_result = x_bows.quiver.get_itemstack_arrow_from_quiver(user)
|
|
local itemstack_arrow = quiver_result.found_arrow_stack
|
|
|
|
if itemstack_arrow then
|
|
local itemstack_arrow_meta = itemstack_arrow:get_meta()
|
|
|
|
itemstack_arrow_meta:set_int('is_arrow_from_quiver', 1)
|
|
itemstack_arrow_meta:set_string('quiver_name', quiver_result.quiver_name)
|
|
itemstack_arrow_meta:set_string('quiver_id', quiver_result.quiver_id)
|
|
else
|
|
x_bows.quiver.remove_hud(user)
|
|
|
|
---find itemstack arrow in players inventory
|
|
for i, st in ipairs(inv_list) do
|
|
if not st:is_empty() and x_bows.registered_arrows[st:get_name()] then
|
|
table.insert(itemstack_arrows, {stack = st, idx = i})
|
|
end
|
|
end
|
|
|
|
-- take 1st found arrow in the list
|
|
itemstack_arrow = #itemstack_arrows > 0 and itemstack_arrows[1].stack or nil
|
|
end
|
|
|
|
if itemstack_arrow and bow_def then
|
|
local _tool_capabilities = x_bows.registered_arrows[itemstack_arrow:get_name()].tool_capabilities
|
|
|
|
---@param v_user ObjectRef
|
|
---@param v_bow_name string
|
|
---@param v_itemstack_arrow ItemStack
|
|
---@param v_inv InvRef
|
|
---@param v_itemstack_arrows ItemStackArrows
|
|
minetest.after(0, function(v_user, v_bow_name, v_itemstack_arrow, v_inv, v_itemstack_arrows)
|
|
local wielded_item = v_user:get_wielded_item()
|
|
|
|
if wielded_item:get_name() == v_bow_name then
|
|
local wielded_item_meta = wielded_item:get_meta()
|
|
local v_itemstack_arrow_meta = v_itemstack_arrow:get_meta()
|
|
|
|
wielded_item_meta:set_string('arrow_itemstack_string', minetest.serialize(v_itemstack_arrow:to_table()))
|
|
wielded_item_meta:set_string('time_load', tostring(minetest.get_us_time()))
|
|
|
|
wielded_item:set_name(v_bow_name .. '_charged')
|
|
v_user:set_wielded_item(wielded_item)
|
|
|
|
if not x_bows.is_creative(v_user:get_player_name()) and v_itemstack_arrow_meta:get_int('is_arrow_from_quiver') ~= 1 then
|
|
v_itemstack_arrow:take_item()
|
|
v_inv:set_stack('main', v_itemstack_arrows[1].idx, v_itemstack_arrow)
|
|
end
|
|
end
|
|
end, user, bow_name, itemstack_arrow, inv, itemstack_arrows)
|
|
|
|
---stop previous charged sound after job
|
|
if x_bows.charge_sound_after_job[player_name] then
|
|
for _, v in pairs(x_bows.charge_sound_after_job[player_name]) do
|
|
v:cancel()
|
|
end
|
|
|
|
x_bows.charge_sound_after_job[player_name] = {}
|
|
else
|
|
x_bows.charge_sound_after_job[player_name] = {}
|
|
end
|
|
|
|
---sound plays when charge time reaches full punch interval time
|
|
table.insert(x_bows.charge_sound_after_job[player_name], minetest.after(_tool_capabilities.full_punch_interval, function(v_user, v_bow_name)
|
|
local wielded_item = v_user:get_wielded_item()
|
|
local wielded_item_name = wielded_item:get_name()
|
|
|
|
if wielded_item_name == v_bow_name .. '_charged' then
|
|
minetest.sound_play('x_bows_bow_loaded', {
|
|
to_player = v_user:get_player_name(),
|
|
gain = 0.6
|
|
})
|
|
end
|
|
end, user, bow_name))
|
|
|
|
minetest.sound_play('x_bows_bow_load', {
|
|
to_player = player_name,
|
|
gain = 0.6
|
|
})
|
|
|
|
return itemstack
|
|
end
|
|
|
|
return itemstack
|
|
end
|
|
|
|
---Shoots the bow
|
|
---@param itemstack ItemStack
|
|
---@param user ObjectRef
|
|
---@param pointed_thing? PointedThingDef
|
|
---@return ItemStack
|
|
function x_bows.shoot(itemstack, user, pointed_thing)
|
|
local time_shoot = minetest.get_us_time();
|
|
local meta = itemstack:get_meta()
|
|
local time_load = tonumber(meta:get_string('time_load'))
|
|
local tflp = (time_shoot - time_load) / 1000000
|
|
---@type ItemStack
|
|
local arrow_itemstack = ItemStack(minetest.deserialize(meta:get_string('arrow_itemstack_string')))
|
|
local arrow_itemstack_meta = arrow_itemstack:get_meta()
|
|
local arrow_name = arrow_itemstack:get_name()
|
|
local is_arrow_from_quiver = arrow_itemstack_meta:get_int('is_arrow_from_quiver')
|
|
local quiver_name = arrow_itemstack_meta:get_string('quiver_name')
|
|
local quiver_id = arrow_itemstack_meta:get_string('quiver_id')
|
|
local detached_inv = x_bows.quiver.get_or_create_detached_inv(
|
|
quiver_id,
|
|
user:get_player_name()
|
|
)
|
|
|
|
if is_arrow_from_quiver == 1 then
|
|
x_bows.quiver.udate_or_create_hud(user, detached_inv:get_list('main'))
|
|
else
|
|
x_bows.quiver.remove_hud(user)
|
|
end
|
|
|
|
if not x_bows.registered_arrows[arrow_name] then
|
|
return itemstack
|
|
end
|
|
|
|
local bow_name_charged = itemstack:get_name()
|
|
local bow_name = x_bows.registered_bows[bow_name_charged].name
|
|
local uses = x_bows.registered_bows[bow_name_charged].uses
|
|
local crit_chance = x_bows.registered_bows[bow_name_charged].crit_chance
|
|
local _tool_capabilities = x_bows.registered_arrows[arrow_name].tool_capabilities
|
|
local quiver_xbows_def = x_bows.registered_quivers[quiver_name]
|
|
|
|
local staticdata = {
|
|
arrow = arrow_name,
|
|
user_name = user:get_player_name(),
|
|
is_critical_hit = false,
|
|
_tool_capabilities = _tool_capabilities,
|
|
_tflp = tflp,
|
|
}
|
|
|
|
---crits, only on full punch interval
|
|
if crit_chance and crit_chance > 1 and tflp >= _tool_capabilities.full_punch_interval then
|
|
if math.random(1, crit_chance) == 1 then
|
|
staticdata.is_critical_hit = true
|
|
end
|
|
end
|
|
|
|
---speed multiply
|
|
if quiver_xbows_def and quiver_xbows_def.faster_arrows and quiver_xbows_def.faster_arrows > 1 then
|
|
staticdata.faster_arrows_multiplier = quiver_xbows_def.faster_arrows
|
|
end
|
|
|
|
---add damage
|
|
if quiver_xbows_def and quiver_xbows_def.add_damage and quiver_xbows_def.add_damage > 1 then
|
|
staticdata.add_damage = quiver_xbows_def.add_damage
|
|
end
|
|
|
|
---sound
|
|
local sound_name = 'x_bows_bow_shoot'
|
|
if staticdata.is_critical_hit then
|
|
sound_name = 'x_bows_bow_shoot_crit'
|
|
end
|
|
|
|
meta:set_string('arrow_itemstack_string', '')
|
|
itemstack:set_name(bow_name)
|
|
|
|
local pos = user:get_pos()
|
|
local dir = user:get_look_dir()
|
|
local obj = minetest.add_entity(
|
|
{
|
|
x = pos.x,
|
|
y = pos.y + 1.5,
|
|
z = pos.z
|
|
},
|
|
'x_bows:arrow_entity',
|
|
minetest.serialize(staticdata)
|
|
)
|
|
|
|
if not obj then
|
|
return itemstack
|
|
end
|
|
|
|
local strength_multiplier = tflp
|
|
|
|
if strength_multiplier > _tool_capabilities.full_punch_interval then
|
|
strength_multiplier = 1
|
|
|
|
---faster arrow, only on full punch interval
|
|
if staticdata.faster_arrows_multiplier then
|
|
strength_multiplier = strength_multiplier + (strength_multiplier / staticdata.faster_arrows_multiplier)
|
|
end
|
|
end
|
|
|
|
local strength = 30 * strength_multiplier
|
|
|
|
obj:set_velocity(vector.multiply(dir, strength))
|
|
obj:set_acceleration({x = dir.x * -3, y = -10, z = dir.z * -3})
|
|
obj:set_yaw(minetest.dir_to_yaw(dir))
|
|
|
|
if not x_bows.is_creative(user:get_player_name()) then
|
|
itemstack:add_wear(65535 / uses)
|
|
end
|
|
|
|
minetest.sound_play(sound_name, {
|
|
gain = 0.3,
|
|
pos = user:get_pos(),
|
|
max_hear_distance = 10
|
|
})
|
|
|
|
return itemstack
|
|
end
|
|
|
|
---Arrow particle
|
|
---@param pos Vector
|
|
---@param type 'arrow' | 'arrow_crit' | 'bubble' | 'arrow_tipped'
|
|
---@return number|nil
|
|
function x_bows.particle_effect(pos, type)
|
|
if type == 'arrow' then
|
|
return minetest.add_particlespawner({
|
|
amount = 1,
|
|
time = 0.1,
|
|
minpos = pos,
|
|
maxpos = pos,
|
|
minexptime = 1,
|
|
maxexptime = 1,
|
|
minsize = 2,
|
|
maxsize = 2,
|
|
texture = 'x_bows_arrow_particle.png',
|
|
animation = {
|
|
type = 'vertical_frames',
|
|
aspect_w = 8,
|
|
aspect_h = 8,
|
|
length = 1,
|
|
},
|
|
glow = 1
|
|
})
|
|
elseif type == 'arrow_crit' then
|
|
return minetest.add_particlespawner({
|
|
amount = 3,
|
|
time = 0.1,
|
|
minpos = pos,
|
|
maxpos = pos,
|
|
minexptime = 0.5,
|
|
maxexptime = 0.5,
|
|
minsize = 2,
|
|
maxsize = 2,
|
|
texture = 'x_bows_arrow_particle.png^[colorize:#B22222:127',
|
|
animation = {
|
|
type = 'vertical_frames',
|
|
aspect_w = 8,
|
|
aspect_h = 8,
|
|
length = 1,
|
|
},
|
|
glow = 1
|
|
})
|
|
elseif type == 'arrow_fast' then
|
|
return minetest.add_particlespawner({
|
|
amount = 3,
|
|
time = 0.1,
|
|
minpos = pos,
|
|
maxpos = pos,
|
|
minexptime = 0.5,
|
|
maxexptime = 0.5,
|
|
minsize = 2,
|
|
maxsize = 2,
|
|
texture = 'x_bows_arrow_particle.png^[colorize:#0000FF:64',
|
|
animation = {
|
|
type = 'vertical_frames',
|
|
aspect_w = 8,
|
|
aspect_h = 8,
|
|
length = 1,
|
|
},
|
|
glow = 1
|
|
})
|
|
elseif type == 'bubble' then
|
|
return minetest.add_particlespawner({
|
|
amount = 1,
|
|
time = 1,
|
|
minpos = pos,
|
|
maxpos = pos,
|
|
minvel = {x=1, y=1, z=0},
|
|
maxvel = {x=1, y=1, z=0},
|
|
minacc = {x=1, y=1, z=1},
|
|
maxacc = {x=1, y=1, z=1},
|
|
minexptime = 0.2,
|
|
maxexptime = 0.5,
|
|
minsize = 0.5,
|
|
maxsize = 1,
|
|
texture = 'x_bows_bubble.png'
|
|
})
|
|
elseif type == 'arrow_tipped' then
|
|
return minetest.add_particlespawner({
|
|
amount = 5,
|
|
time = 1,
|
|
minpos = vector.subtract(pos, 0.5),
|
|
maxpos = vector.add(pos, 0.5),
|
|
minexptime = 0.4,
|
|
maxexptime = 0.8,
|
|
minvel = {x=-0.4, y=0.4, z=-0.4},
|
|
maxvel = {x=0.4, y=0.6, z=0.4},
|
|
minacc = {x=0.2, y=0.4, z=0.2},
|
|
maxacc = {x=0.4, y=0.6, z=0.4},
|
|
minsize = 4,
|
|
maxsize = 6,
|
|
texture = 'x_bows_arrow_tipped_particle.png^[colorize:#008000:127',
|
|
animation = {
|
|
type = 'vertical_frames',
|
|
aspect_w = 8,
|
|
aspect_h = 8,
|
|
length = 1,
|
|
},
|
|
glow = 1
|
|
})
|
|
end
|
|
end
|
|
|
|
-- sneak, fov adjustments when bow is charged
|
|
minetest.register_globalstep(function(dtime)
|
|
bow_charged_timer = bow_charged_timer + dtime
|
|
|
|
if bow_charged_timer > 0.5 then
|
|
for _, player in ipairs(minetest.get_connected_players()) do
|
|
local name = player:get_player_name()
|
|
local stack = player:get_wielded_item()
|
|
local item = stack:get_name()
|
|
|
|
if not item then
|
|
return
|
|
end
|
|
|
|
if not x_bows.player_bow_sneak[name] then
|
|
x_bows.player_bow_sneak[name] = {}
|
|
end
|
|
|
|
if item == 'x_bows:bow_wood_charged' and not x_bows.player_bow_sneak[name].sneak then
|
|
if x_bows.playerphysics then
|
|
playerphysics.add_physics_factor(player, 'speed', 'x_bows:bow_wood_charged', 0.25)
|
|
elseif x_bows.player_monoids then
|
|
player_monoids.speed:add_change(player, 0.25, 'x_bows:bow_wood_charged')
|
|
end
|
|
|
|
x_bows.player_bow_sneak[name].sneak = true
|
|
player:set_fov(0.9, true, 0.4)
|
|
elseif item ~= 'x_bows:bow_wood_charged' and x_bows.player_bow_sneak[name].sneak then
|
|
if x_bows.playerphysics then
|
|
playerphysics.remove_physics_factor(player, 'speed', 'x_bows:bow_wood_charged')
|
|
elseif x_bows.player_monoids then
|
|
player_monoids.speed:del_change(player, 'x_bows:bow_wood_charged')
|
|
end
|
|
|
|
x_bows.player_bow_sneak[name].sneak = false
|
|
player:set_fov(0, true, 0.4)
|
|
end
|
|
|
|
reset_charged_bow(player)
|
|
end
|
|
|
|
bow_charged_timer = 0
|
|
end
|
|
end)
|
|
|
|
local path = minetest.get_modpath('x_bows')
|
|
|
|
dofile(path .. '/nodes.lua')
|
|
dofile(path .. '/arrow.lua')
|
|
dofile(path .. '/items.lua')
|
|
dofile(path .. '/quiver.lua')
|
|
|
|
local mod_end_time = (minetest.get_us_time() - mod_start_time) / 1000000
|
|
|
|
print('[Mod] x_bows loaded.. ['.. mod_end_time ..'s]')
|