Add quiver

This commit is contained in:
Juraj Vajda 2022-10-17 00:02:58 +00:00
commit 6eff91575d
44 changed files with 2426 additions and 959 deletions

View File

@ -13,7 +13,7 @@
"license": "LGPL-2.1-or-later", "license": "LGPL-2.1-or-later",
"media_license": "CC-BY-SA-4.0", "media_license": "CC-BY-SA-4.0",
"repo": "https://bitbucket.org/minetest_gamers/x_bows/src/master/", "repo": "https://bitbucket.org/minetest_gamers/x_bows/src/master/",
"issue_tracker": "https://bitbucket.org/minetest_gamers/x_bows/issues", "issue_tracker": "https://bitbucket.org/minetest_gamers/x_bows/issues?status=new&status=open",
"forums": 26466, "forums": 26466,
"video_url": "https://youtu.be/pItpltmUoa8" "video_url": "https://youtu.be/pItpltmUoa8"
} }

View File

@ -23,6 +23,7 @@ read_globals = {
"PcgRandom", "PcgRandom",
"ItemStack", "ItemStack",
"AreaStore", "AreaStore",
"unpack",
"vector", "vector",

View File

@ -2,7 +2,7 @@
Adds bow and arrows to Minetest. Adds bow and arrows to Minetest.
![screenshot](screenshot.png) ![screenshot](screenshot.1.png)
Video: https://youtu.be/pItpltmUoa8 Video: https://youtu.be/pItpltmUoa8
@ -11,6 +11,7 @@ Video: https://youtu.be/pItpltmUoa8
* bow will force you sneak when loaded (optional dep. playerphysics) * bow will force you sneak when loaded (optional dep. playerphysics)
* loaded bow will slightly adjust the player FOV * loaded bow will slightly adjust the player FOV
* bow uses minetest tool capabilities - if the bow is not loaded for long enough (time from last puch) the arrow will fly shorter range * bow uses minetest tool capabilities - if the bow is not loaded for long enough (time from last puch) the arrow will fly shorter range
* charged bow in inventory will discharge and give back the arrow when not selected
* arrow uses raycast * arrow uses raycast
* arrow has chance of critical shots/hits (only on full punch interval) * arrow has chance of critical shots/hits (only on full punch interval)
* arrow uses minetest damage calculation (including 3d_armor) for making damage (no hardcoded values) * arrow uses minetest damage calculation (including 3d_armor) for making damage (no hardcoded values)
@ -24,9 +25,15 @@ Video: https://youtu.be/pItpltmUoa8
* registers only one entity reused for all arrows * registers only one entity reused for all arrows
* (experimental) poison arrow - dealing damage for 5s but will not kill the target * (experimental) poison arrow - dealing damage for 5s but will not kill the target
* target block reduces fall damage by -30 * target block reduces fall damage by -30
* quiver for more arrow storage (can hold only arrows)
* quiver perks when in inventory (faster arrows, more arrow damage...)
* quiver shows temporarily its inventory in HUD overlay when loading or shooting (quickview)
* quiver item shows its content in infotext (hover over the item)
## How To ## How To
### Bow
With the bow selected in hotbar and in your hand, press right click on mouse (PC) or the same action as when placing blocks, to load the bow. With the bow selected in hotbar and in your hand, press right click on mouse (PC) or the same action as when placing blocks, to load the bow.
For bow to be loaded you have to have arrows in the main invetory. Charging bow will have slight sound effect and can be fired at any time with left click (PC) For bow to be loaded you have to have arrows in the main invetory. Charging bow will have slight sound effect and can be fired at any time with left click (PC)
or the same action as when you are digging a block. Waiting for full charge of the bow is recommended or the same action as when you are digging a block. Waiting for full charge of the bow is recommended
@ -47,6 +54,17 @@ If you shoot the arrow before the bow is fully charged the speed/distance will b
Changing the selection in hotbar will unload the bow and give you back arrow from the unloaded bow - this applies also when login in to the game (bow will be discharged and arrow will be returned to inventory) and also when you drop the charged arrow (discharged bow will be dropped with arrow item). Changing the selection in hotbar will unload the bow and give you back arrow from the unloaded bow - this applies also when login in to the game (bow will be discharged and arrow will be returned to inventory) and also when you drop the charged arrow (discharged bow will be dropped with arrow item).
If you have `playerphysics` or `player_monoids` mod installed, charged bow will slow you down until you release the arrow. If you have `playerphysics` or `player_monoids` mod installed, charged bow will slow you down until you release the arrow.
### Quiver
Quiver item can hold inventory of arrows. When player has quiver in his/hers inventory, bow can take arrows from quiver, otherwise arrows outside of the quiver are used to load the bow.
Though, if arrow from quivers are used to laod the bow, the arrows have additional speed and damage.
If we are loading/shooting arrows from quiver, there is temporary quickview HUD overlay shown, peeking in to the quivers inventory from which the arrow was taken. Arrows used from quiver will be faster only when the bow is fully charged - see "How To - Bow" for more information on how to know when bow is fully charged.
There are few indications on how to know when the bow shot arrow from quiver:
* there is temporary HUD overview shown peeking in to the quiver inventory
* after shooting, arrow will have blue/purple particle trail (if bow was fully charged)
## Dependencies ## Dependencies
- none - none
@ -73,11 +91,6 @@ GNU Lesser General Public License v2.1 or later (see included LICENSE file)
- x_bows_bow_wood.png - x_bows_bow_wood.png
- x_bows_bow_wood_charged.png - x_bows_bow_wood_charged.png
- x_bows_arrow_wood.png - x_bows_arrow_wood.png
- x_bows_arrow_tile_point_top.png
- x_bows_arrow_tile_point_right.png
- x_bows_arrow_tile_point_bottom.png
- x_bows_arrow_tile_point_left.png
- x_bows_arrow_tile_tail.png
- x_bows_arrow_particle.png - x_bows_arrow_particle.png
- x_bows_arrow_tipped_particle.png - x_bows_arrow_tipped_particle.png
- x_bows_bubble.png - x_bows_bubble.png
@ -92,6 +105,18 @@ Modified by SaKeL:
- x_bows_arrow_diamond.png - x_bows_arrow_diamond.png
- x_bows_arrow_diamond_poison.png - x_bows_arrow_diamond_poison.png
**CC-BY-SA-3.0, by paramat**
- x_bows_quiver_hotbar_selected.png
- x_bows_quiver_hotbar.png
**LGPL-2.1-or-later, by SaKeL**
- x_bows_quiver.png
- x_bows_quiver_open.png
- x_bows_arrow_slot.png
- x_bows_arrow_mesh.png
### Sounds ### Sounds
**Creative Commons License, EminYILDIRIM**, https://freesound.org **Creative Commons License, EminYILDIRIM**, https://freesound.org
@ -122,6 +147,24 @@ Modified by SaKeL:
- x_bows_arrow_successful_hit.ogg - x_bows_arrow_successful_hit.ogg
**Creative Commons License, Shamewap**, https://freesound.org
- x_bows_quiver.1.ogg
- x_bows_quiver.2.ogg
- x_bows_quiver.3.ogg
- x_bows_quiver.4.ogg
- x_bows_quiver.5.ogg
- x_bows_quiver.6.ogg
- x_bows_quiver.7.ogg
- x_bows_quiver.8.ogg
- x_bows_quiver.9.ogg
### Models
****LGPL-2.1-or-later, by SaKeL**
- x_bows_arrow.obj
## Installation ## Installation
see: http://wiki.minetest.com/wiki/Installing_Mods see: http://wiki.minetest.com/wiki/Installing_Mods

View File

@ -1,4 +1,6 @@
-- Gets total armor level from 3d armor ---Gets total armor level from 3d armor
---@param player ObjectRef
---@return integer
local function get_3d_armor_armor(player) local function get_3d_armor_armor(player)
local armor_total = 0 local armor_total = 0
@ -11,12 +13,18 @@ local function get_3d_armor_armor(player)
return armor_total return armor_total
end end
-- Limits number `x` between `min` and `max` values ---Limits number `x` between `min` and `max` values
---@param x integer
---@param min integer
---@param max integer
---@return integer
local function limit(x, min, max) local function limit(x, min, max)
return math.min(math.max(x, min), max) return math.min(math.max(x, min), max)
end end
-- Gets `ObjectRef` collision box ---Gets collision box
---@param obj ObjectRef
---@return number[]
local function get_obj_box(obj) local function get_obj_box(obj)
local box local box
@ -29,7 +37,14 @@ local function get_obj_box(obj)
return box return box
end end
-- Poison Arrow Effects ---Poison Arrow Effects
---@param tick integer|number
---@param time integer|number
---@param time_left integer|number
---@param arrow_obj ObjectRef
---@param target_obj ObjectRef
---@param old_damage_texture_modifier string
---@param punch_def table
function x_bows.poison_effect(tick, time, time_left, arrow_obj, target_obj, old_damage_texture_modifier, punch_def) function x_bows.poison_effect(tick, time, time_left, arrow_obj, target_obj, old_damage_texture_modifier, punch_def)
if not arrow_obj or target_obj:get_hp() <= 0 then if not arrow_obj or target_obj:get_hp() <= 0 then
return return
@ -98,7 +113,6 @@ end
minetest.register_entity('x_bows:arrow_entity', { minetest.register_entity('x_bows:arrow_entity', {
initial_properties = { initial_properties = {
visual = 'wielditem', visual = 'wielditem',
visual_size = {x = 0.2, y = 0.2, z = 0.3},
collisionbox = {0, 0, 0, 0, 0, 0}, collisionbox = {0, 0, 0, 0, 0, 0},
selectionbox = {0, 0, 0, 0, 0, 0}, selectionbox = {0, 0, 0, 0, 0, 0},
physical = false, physical = false,
@ -106,6 +120,8 @@ minetest.register_entity('x_bows:arrow_entity', {
hp_max = 1 hp_max = 1
}, },
---@param self table
---@param staticdata string
on_activate = function(self, staticdata) on_activate = function(self, staticdata)
if not self or not staticdata or staticdata == '' then if not self or not staticdata or staticdata == '' then
self.object:remove() self.object:remove()
@ -134,6 +150,8 @@ minetest.register_entity('x_bows:arrow_entity', {
self._tflp = _staticdata._tflp self._tflp = _staticdata._tflp
self._tool_capabilities = _staticdata._tool_capabilities self._tool_capabilities = _staticdata._tool_capabilities
self._is_critical_hit = _staticdata.is_critical_hit self._is_critical_hit = _staticdata.is_critical_hit
self._faster_arrows_multiplier = _staticdata.faster_arrows_multiplier
self._add_damage = _staticdata.add_damage
if self.arrow == 'x_bows:arrow_diamond_tipped_poison' then if self.arrow == 'x_bows:arrow_diamond_tipped_poison' then
self._poison_arrow = true self._poison_arrow = true
@ -145,6 +163,8 @@ minetest.register_entity('x_bows:arrow_entity', {
}) })
end, end,
---@param self table
---@param killer ObjectRef|nil
on_death = function(self, killer) on_death = function(self, killer)
if not self._old_pos then if not self._old_pos then
self.object:remove() self.object:remove()
@ -154,6 +174,8 @@ minetest.register_entity('x_bows:arrow_entity', {
minetest.item_drop(ItemStack(self.arrow), nil, vector.round(self._old_pos)) minetest.item_drop(ItemStack(self.arrow), nil, vector.round(self._old_pos))
end, end,
---@param self table
---@param dtime integer|number
on_step = function(self, dtime) on_step = function(self, dtime)
local pos = self.object:get_pos() local pos = self.object:get_pos()
self._old_pos = self._old_pos or pos self._old_pos = self._old_pos or pos
@ -189,6 +211,8 @@ minetest.register_entity('x_bows:arrow_entity', {
if self._tflp >= self._tool_capabilities.full_punch_interval then if self._tflp >= self._tool_capabilities.full_punch_interval then
if self._is_critical_hit then if self._is_critical_hit then
x_bows.particle_effect(self._old_pos, 'arrow_crit') x_bows.particle_effect(self._old_pos, 'arrow_crit')
elseif self._faster_arrows_multiplier then
x_bows.particle_effect(self._old_pos, 'arrow_fast')
else else
x_bows.particle_effect(self._old_pos, 'arrow') x_bows.particle_effect(self._old_pos, 'arrow')
end end
@ -268,6 +292,11 @@ minetest.register_entity('x_bows:arrow_entity', {
-- calculate damage -- calculate damage
local target_armor_groups = pointed_thing.ref:get_armor_groups() local target_armor_groups = pointed_thing.ref:get_armor_groups()
local _damage = 0 local _damage = 0
if self._add_damage then
_damage = _damage + self._add_damage
end
for group, base_damage in pairs(self._tool_capabilities.damage_groups) do for group, base_damage in pairs(self._tool_capabilities.damage_groups) do
_damage = _damage _damage = _damage
+ base_damage + base_damage
@ -548,4 +577,21 @@ minetest.register_entity('x_bows:arrow_entity', {
self._old_pos = pos self._old_pos = pos
end, end,
---@param self table
---@param puncher ObjectRef|nil
---@param time_from_last_punch number|integer|nil
---@param tool_capabilities ToolCapabilitiesDef|nil
---@param dir Vector
---@param damage number|integer
on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir, damage)
local wood_sound_def = default.node_sound_wood_defaults()
minetest.sound_play(wood_sound_def.dig.name, {
pos = self.object:get_pos(),
gain = wood_sound_def.dig.gain
})
return false
end,
}) })

286
init.lua
View File

@ -6,10 +6,13 @@ ItemStack = ItemStack--[[@as ItemStack]]
vector = vector--[[@as Vector]] vector = vector--[[@as Vector]]
default = default--[[@as MtgDefault]] 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 mod_start_time = minetest.get_us_time()
local bow_charged_timer = 0 local bow_charged_timer = 0
-- main class ---x_bows main class
---@class XBows
x_bows = { x_bows = {
pvp = minetest.settings:get_bool('enable_pvp') or false, pvp = minetest.settings:get_bool('enable_pvp') or false,
creative = minetest.settings:get_bool('creative_mode') or false, creative = minetest.settings:get_bool('creative_mode') or false,
@ -19,26 +22,34 @@ x_bows = {
player_monoids = minetest.get_modpath('player_monoids'), player_monoids = minetest.get_modpath('player_monoids'),
registered_arrows = {}, registered_arrows = {},
registered_bows = {}, registered_bows = {},
registered_quivers = {},
player_bow_sneak = {}, player_bow_sneak = {},
settings = { settings = {
x_bows_attach_arrows_to_entities = minetest.settings:get_bool('x_bows_attach_arrows_to_entities', false) 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) function x_bows.is_creative(name)
return x_bows.creative or minetest.check_player_privs(name, {creative = true}) return x_bows.creative or minetest.check_player_privs(name, {creative = true})
end end
---Reset charged bow to uncharged bow, this will return the arrow item to the inventory also ---Reset charged bow to uncharged bow, this will return the arrow item to the inventory also
---@param player ObjectRef Player Ref ---@param player ObjectRef Player Ref
---@param includeWielded? boolean Will include reset for wielded bow also ---@param includeWielded? boolean Will include reset for wielded bow also. default: `false`
---@return nil ---@return nil
local function reset_charged_bow(player, includeWielded) local function reset_charged_bow(player, includeWielded)
local _includeWielded = includeWielded or false local _includeWielded = includeWielded or false
local inv = player:get_inventory() local inv = player:get_inventory()
---@cast inv InvRef if inv and inv:contains_item('main', 'x_bows:bow_wood_charged') then
if inv:contains_item('main', 'x_bows:bow_wood_charged') then
local inv_list = inv:get_list('main') local inv_list = inv:get_list('main')
for i, st in ipairs(inv_list) do for i, st in ipairs(inv_list) do
@ -46,14 +57,14 @@ local function reset_charged_bow(player, includeWielded)
if not st:is_empty() and x_bows.registered_bows[st:get_name()] and reset then if not st:is_empty() and x_bows.registered_bows[st:get_name()] and reset then
local item_meta = st:get_meta() local item_meta = st:get_meta()
local arrow = item_meta:get_string('arrow') local arrow_itemstack = ItemStack(minetest.deserialize(item_meta:get_string('arrow_itemstack_string')))
-- return arrow -- return arrow
if arrow ~= '' and not x_bows.is_creative(player:get_player_name()) then if arrow_itemstack and not x_bows.is_creative(player:get_player_name()) then
if inv:room_for_item('main', {name=arrow}) then if inv:room_for_item('main', {name=arrow_itemstack:get_name()}) then
inv:add_item('main', arrow) inv:add_item('main', arrow_itemstack:get_name())
else else
minetest.item_drop(ItemStack({name=arrow, count=1}), player, player:get_pos()) minetest.item_drop(ItemStack({name=arrow_itemstack:get_name(), count=1}), player, player:get_pos())
end end
end end
@ -70,8 +81,13 @@ end
minetest.register_on_joinplayer(function(player) minetest.register_on_joinplayer(function(player)
reset_charged_bow(player, true) reset_charged_bow(player, true)
x_bows.quiver.close_quiver(player)
end) end)
---Register bows
---@param name string
---@param def table
---@return boolean|nil
function x_bows.register_bow(name, def) function x_bows.register_bow(name, def)
if name == nil or name == '' then if name == nil or name == '' then
return false return false
@ -89,12 +105,9 @@ function x_bows.register_bow(name, def)
description = def.description .. '\n' .. minetest.colorize('#00FF00', 'Critical Arrow Chance: ' description = def.description .. '\n' .. minetest.colorize('#00FF00', 'Critical Arrow Chance: '
.. (1 / def.crit_chance) * 100 .. '%'), .. (1 / def.crit_chance) * 100 .. '%'),
inventory_image = def.inventory_image or 'x_bows_bow_wood.png', inventory_image = def.inventory_image or 'x_bows_bow_wood.png',
-- on_use = function(itemstack, user, pointed_thing)
-- end,
on_place = x_bows.load, on_place = x_bows.load,
on_secondary_use = x_bows.load, on_secondary_use = x_bows.load,
groups = {bow = 1, flammable = 1}, groups = {bow = 1, flammable = 1}
-- range = 0
}) })
-- charged bow -- charged bow
@ -106,11 +119,11 @@ function x_bows.register_bow(name, def)
groups = {bow = 1, flammable = 1, not_in_creative_inventory = 1}, groups = {bow = 1, flammable = 1, not_in_creative_inventory = 1},
on_drop = function(itemstack, dropper, pos) on_drop = function(itemstack, dropper, pos)
local item_meta = itemstack:get_meta() local item_meta = itemstack:get_meta()
local arrow = item_meta:get_string('arrow') local arrow_itemstack = ItemStack(minetest.deserialize(item_meta:get_string('arrow_itemstack_string')))
-- return arrow -- return arrow
if arrow ~= '' and not x_bows.is_creative(dropper:get_player_name()) then if arrow_itemstack and not x_bows.is_creative(dropper:get_player_name()) then
minetest.item_drop(ItemStack({name=arrow, count=1}), dropper, {x=pos.x + 0.5, y=pos.y + 0.5, z=pos.z + 0.5}) 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 end
itemstack:set_name(def.name) itemstack:set_name(def.name)
@ -128,6 +141,10 @@ function x_bows.register_bow(name, def)
end end
end end
---Register arrows
---@param name string
---@param def table
---@return boolean|nil
function x_bows.register_arrow(name, def) function x_bows.register_arrow(name, def)
if name == nil or name == '' then if name == nil or name == '' then
return false return false
@ -142,6 +159,7 @@ function x_bows.register_arrow(name, def)
description = def.description .. '\n' .. minetest.colorize('#00FF00', 'Damage: ' description = def.description .. '\n' .. minetest.colorize('#00FF00', 'Damage: '
.. def.tool_capabilities.damage_groups.fleshy) .. '\n' .. minetest.colorize('#00BFFF', 'Charge Time: ' .. def.tool_capabilities.damage_groups.fleshy) .. '\n' .. minetest.colorize('#00BFFF', 'Charge Time: '
.. def.tool_capabilities.full_punch_interval .. 's'), .. def.tool_capabilities.full_punch_interval .. 's'),
short_description = def.description,
inventory_image = def.inventory_image, inventory_image = def.inventory_image,
groups = {arrow = 1, flammable = 1} groups = {arrow = 1, flammable = 1}
}) })
@ -155,14 +173,100 @@ function x_bows.register_arrow(name, def)
end end
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) function x_bows.load(itemstack, user, pointed_thing)
local time_load = minetest.get_us_time() local player_name = user:get_player_name()
local inv = user:get_inventory() local inv = user:get_inventory()--[[@as InvRef]]
local inv_list = inv:get_list('main') local inv_list = inv:get_list('main')
local bow_name = itemstack:get_name() local bow_name = itemstack:get_name()
local bow_def = x_bows.registered_bows[bow_name .. '_charged'] local bow_def = x_bows.registered_bows[bow_name .. '_charged']
---@alias ItemStackArrows {["stack"]: ItemStack, ["idx"]: number|integer}[]
---@type ItemStackArrows
local itemstack_arrows = {} local itemstack_arrows = {}
---trigger right click event if pointed item has one
if pointed_thing.under then if pointed_thing.under then
local node = minetest.get_node(pointed_thing.under) local node = minetest.get_node(pointed_thing.under)
local node_def = minetest.registered_nodes[node.name] local node_def = minetest.registered_nodes[node.name]
@ -172,67 +276,122 @@ function x_bows.load(itemstack, user, pointed_thing)
end end
end end
for _, st in ipairs(inv_list) do ---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 if not st:is_empty() and x_bows.registered_arrows[st:get_name()] then
table.insert(itemstack_arrows, st) table.insert(itemstack_arrows, {stack = st, idx = i})
end end
end end
-- take 1st found arrow in the list -- take 1st found arrow in the list
local itemstack_arrow = itemstack_arrows[1] itemstack_arrow = #itemstack_arrows > 0 and itemstack_arrows[1].stack or nil
end
if itemstack_arrow and bow_def then if itemstack_arrow and bow_def then
local _tool_capabilities = x_bows.registered_arrows[itemstack_arrow:get_name()].tool_capabilities local _tool_capabilities = x_bows.registered_arrows[itemstack_arrow:get_name()].tool_capabilities
minetest.after(0, function(v_user, v_bow_name, v_time_load) ---@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() local wielded_item = v_user:get_wielded_item()
local wielded_item_name = wielded_item:get_name()
if wielded_item_name == v_bow_name then if wielded_item:get_name() == v_bow_name then
local meta = wielded_item:get_meta() 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()))
meta:set_string('arrow', itemstack_arrow:get_name())
meta:set_string('time_load', tostring(v_time_load))
wielded_item:set_name(v_bow_name .. '_charged') wielded_item:set_name(v_bow_name .. '_charged')
v_user:set_wielded_item(wielded_item) v_user:set_wielded_item(wielded_item)
if not x_bows.is_creative(user:get_player_name()) then if not x_bows.is_creative(v_user:get_player_name()) and v_itemstack_arrow_meta:get_int('is_arrow_from_quiver') ~= 1 then
inv:remove_item('main', itemstack_arrow:get_name()) v_itemstack_arrow:take_item()
v_inv:set_stack('main', v_itemstack_arrows[1].idx, v_itemstack_arrow)
end end
end end
end, user, bow_name, time_load) end, user, bow_name, itemstack_arrow, inv, itemstack_arrows)
-- sound plays when charge time reaches full punch interval time ---stop previous charged sound after job
-- @TODO: find a way to prevent this from playing when not fully charged if x_bows.charge_sound_after_job[player_name] then
minetest.after(_tool_capabilities.full_punch_interval, function(v_user, v_bow_name) 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 = v_user:get_wielded_item()
local wielded_item_name = wielded_item:get_name() local wielded_item_name = wielded_item:get_name()
if wielded_item_name == v_bow_name .. '_charged' then if wielded_item_name == v_bow_name .. '_charged' then
minetest.sound_play('x_bows_bow_loaded', { minetest.sound_play('x_bows_bow_loaded', {
to_player = user:get_player_name(), to_player = v_user:get_player_name(),
gain = 0.6 gain = 0.6
}) })
end end
end, user, bow_name) end, user, bow_name))
minetest.sound_play('x_bows_bow_load', { minetest.sound_play('x_bows_bow_load', {
to_player = user:get_player_name(), to_player = player_name,
gain = 0.6 gain = 0.6
}) })
return itemstack return itemstack
end end
return itemstack
end end
---Shoots the bow
---@param itemstack ItemStack
---@param user ObjectRef
---@param pointed_thing? PointedThingDef
---@return ItemStack
function x_bows.shoot(itemstack, user, pointed_thing) function x_bows.shoot(itemstack, user, pointed_thing)
local time_shoot = minetest.get_us_time(); local time_shoot = minetest.get_us_time();
local meta = itemstack:get_meta() local meta = itemstack:get_meta()
local meta_arrow = meta:get_string('arrow')
local time_load = tonumber(meta:get_string('time_load')) local time_load = tonumber(meta:get_string('time_load'))
local tflp = (time_shoot - time_load) / 1000000 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 not x_bows.registered_arrows[meta_arrow] then 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 return itemstack
end end
@ -240,29 +399,41 @@ function x_bows.shoot(itemstack, user, pointed_thing)
local bow_name = x_bows.registered_bows[bow_name_charged].name local bow_name = x_bows.registered_bows[bow_name_charged].name
local uses = x_bows.registered_bows[bow_name_charged].uses local uses = x_bows.registered_bows[bow_name_charged].uses
local crit_chance = x_bows.registered_bows[bow_name_charged].crit_chance local crit_chance = x_bows.registered_bows[bow_name_charged].crit_chance
local _tool_capabilities = x_bows.registered_arrows[meta_arrow].tool_capabilities local _tool_capabilities = x_bows.registered_arrows[arrow_name].tool_capabilities
local quiver_xbows_def = x_bows.registered_quivers[quiver_name]
local staticdata = { local staticdata = {
arrow = meta_arrow, arrow = arrow_name,
user_name = user:get_player_name(), user_name = user:get_player_name(),
is_critical_hit = false, is_critical_hit = false,
_tool_capabilities = _tool_capabilities, _tool_capabilities = _tool_capabilities,
_tflp = tflp, _tflp = tflp,
} }
-- crits, only on full punch interval ---crits, only on full punch interval
if crit_chance and crit_chance > 1 and tflp >= _tool_capabilities.full_punch_interval then if crit_chance and crit_chance > 1 and tflp >= _tool_capabilities.full_punch_interval then
if math.random(1, crit_chance) == 1 then if math.random(1, crit_chance) == 1 then
staticdata.is_critical_hit = true staticdata.is_critical_hit = true
end end
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' local sound_name = 'x_bows_bow_shoot'
if staticdata.is_critical_hit then if staticdata.is_critical_hit then
sound_name = 'x_bows_bow_shoot_crit' sound_name = 'x_bows_bow_shoot_crit'
end end
meta:set_string('arrow', '') meta:set_string('arrow_itemstack_string', '')
itemstack:set_name(bow_name) itemstack:set_name(bow_name)
local pos = user:get_pos() local pos = user:get_pos()
@ -285,6 +456,11 @@ function x_bows.shoot(itemstack, user, pointed_thing)
if strength_multiplier > _tool_capabilities.full_punch_interval then if strength_multiplier > _tool_capabilities.full_punch_interval then
strength_multiplier = 1 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 end
local strength = 30 * strength_multiplier local strength = 30 * strength_multiplier
@ -306,6 +482,10 @@ function x_bows.shoot(itemstack, user, pointed_thing)
return itemstack return itemstack
end end
---Arrow particle
---@param pos Vector
---@param type 'arrow' | 'arrow_crit' | 'bubble' | 'arrow_tipped'
---@return number|nil
function x_bows.particle_effect(pos, type) function x_bows.particle_effect(pos, type)
if type == 'arrow' then if type == 'arrow' then
return minetest.add_particlespawner({ return minetest.add_particlespawner({
@ -345,6 +525,25 @@ function x_bows.particle_effect(pos, type)
}, },
glow = 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 elseif type == 'bubble' then
return minetest.add_particlespawner({ return minetest.add_particlespawner({
amount = 1, amount = 1,
@ -437,6 +636,7 @@ local path = minetest.get_modpath('x_bows')
dofile(path .. '/nodes.lua') dofile(path .. '/nodes.lua')
dofile(path .. '/arrow.lua') dofile(path .. '/arrow.lua')
dofile(path .. '/items.lua') dofile(path .. '/items.lua')
dofile(path .. '/quiver.lua')
local mod_end_time = (minetest.get_us_time() - mod_start_time) / 1000000 local mod_end_time = (minetest.get_us_time() - mod_start_time) / 1000000

View File

@ -117,6 +117,19 @@ x_bows.register_arrow('arrow_diamond_tipped_poison', {
craft_count = 1 craft_count = 1
}) })
x_bows.register_quiver('quiver', {
description = 'Quiver \n\n Empty\n',
short_description = 'Quiver',
recipe = {
{'group:arrow', 'group:arrow', 'group:arrow'},
{'group:arrow', 'wool:brown', 'group:arrow'},
{'group:arrow', 'group:arrow', 'group:arrow'}
},
craft_count = 1,
faster_arrows = 5,
add_damage = 2
})
minetest.register_craft({ minetest.register_craft({
type = 'fuel', type = 'fuel',
recipe = 'x_bows:bow_wood', recipe = 'x_bows:bow_wood',
@ -128,3 +141,9 @@ minetest.register_craft({
recipe = 'x_bows:arrow_wood', recipe = 'x_bows:arrow_wood',
burntime = 1, burntime = 1,
}) })
minetest.register_craft({
type = 'fuel',
recipe = 'x_bows:quiver',
burntime = 3,
})

View File

@ -1,6 +1,6 @@
name = x_bows name = x_bows
description = Adds bow and arrows to Minetest. description = Adds bow and arrows to Minetest.
depends = depends =
optional_depends = default, farming, 3d_armor, hbhunger, mesecons, playerphysics, player_monoids optional_depends = default, farming, 3d_armor, hbhunger, mesecons, playerphysics, player_monoids, wool
supported_games = minetest_game supported_games = minetest_game
min_minetest_version = 5.4 min_minetest_version = 5.4

582
models/x_bows_arrow.obj Normal file
View File

@ -0,0 +1,582 @@
# Blender v3.3.0 OBJ File: 'arrow2.blend'
# www.blender.org
mtllib arrow2.mtl
o arrow2
v -0.007500 -0.030000 -0.112500
v -0.007500 -0.015000 -0.180000
v -0.007500 -0.015000 -0.157500
v -0.007500 0.015000 0.180000
v -0.007500 0.015000 0.090000
v -0.022500 -0.000000 -0.112500
v -0.037500 -0.000000 -0.112500
v -0.037500 0.015000 -0.180000
v -0.037500 -0.000000 -0.180000
v 0.022500 0.015000 -0.157500
v -0.007500 0.045000 -0.112500
v 0.007500 -0.015000 -0.157500
v 0.007500 -0.015000 0.090000
v 0.007500 -0.015000 -0.112500
v 0.007500 -0.000000 0.180000
v 0.007500 -0.000000 0.157500
v 0.022500 -0.000000 0.157500
v 0.022500 -0.000000 0.090000
v 0.022500 -0.000000 -0.090000
v 0.022500 0.015000 -0.090000
v 0.037500 -0.000000 -0.112500
v 0.037500 0.015000 -0.112500
v -0.022500 0.015000 -0.157500
v 0.007500 -0.000000 -0.180000
v 0.007500 0.015000 -0.180000
v 0.037500 0.015000 -0.180000
v 0.007500 0.030000 0.157500
v 0.007500 0.030000 -0.157500
v 0.007500 -0.030000 -0.112500
v -0.007500 -0.015000 0.157500
v 0.007500 -0.015000 0.157500
v -0.007500 -0.015000 -0.090000
v 0.007500 -0.015000 -0.090000
v -0.022500 0.015000 0.157500
v 0.022500 0.015000 0.157500
v -0.022500 -0.000000 -0.090000
v -0.007500 -0.000000 -0.090000
v 0.007500 -0.000000 -0.090000
v -0.037500 0.015000 -0.112500
v 0.022500 0.015000 -0.112500
v -0.007500 0.015000 0.157500
v -0.007500 0.030000 0.157500
v -0.007500 0.030000 -0.090000
v -0.007500 0.030000 -0.112500
v 0.007500 0.030000 -0.112500
v 0.007500 -0.015000 -0.180000
v -0.007500 -0.000000 -0.157500
v 0.007500 -0.000000 -0.157500
v 0.007500 -0.000000 0.090000
v 0.022500 0.015000 0.090000
v -0.022500 -0.000000 -0.180000
v 0.022500 -0.000000 -0.180000
v 0.022500 0.015000 -0.180000
v 0.007500 0.030000 0.090000
v 0.007500 0.015000 0.090000
v -0.007500 0.015000 -0.157500
v 0.007500 0.045000 -0.180000
v -0.007500 -0.030000 -0.180000
v 0.007500 -0.030000 -0.180000
v -0.007500 -0.015000 0.090000
v -0.007500 -0.015000 -0.112500
v -0.007500 -0.000000 0.180000
v -0.007500 -0.000000 0.157500
v -0.022500 -0.000000 0.157500
v -0.022500 -0.000000 0.090000
v -0.007500 -0.000000 0.090000
v 0.022500 -0.000000 -0.112500
v -0.022500 -0.000000 -0.157500
v -0.007500 -0.000000 -0.180000
v 0.022500 -0.000000 -0.157500
v 0.037500 -0.000000 -0.180000
v -0.007500 0.030000 -0.157500
v -0.007500 0.030000 -0.180000
v 0.007500 0.030000 -0.180000
v 0.007500 0.015000 0.180000
v 0.007500 0.015000 0.157500
v -0.022500 0.015000 0.090000
v 0.007500 0.015000 -0.090000
v -0.007500 0.015000 -0.090000
v -0.022500 0.015000 -0.090000
v -0.022500 0.015000 -0.112500
v -0.022500 0.015000 -0.180000
v 0.007500 0.015000 -0.157500
v -0.007500 0.015000 -0.180000
v -0.007500 0.030000 0.090000
v 0.007500 0.030000 -0.090000
v 0.007500 0.045000 -0.112500
v -0.007500 0.045000 -0.180000
vt 0.533333 0.533333
vt 0.000000 0.600000
vt 0.533333 0.600000
vt 0.466667 0.866667
vt 0.533333 0.800000
vt 0.466667 0.800000
vt 0.533333 0.866667
vt 0.600000 0.800000
vt 0.533333 0.800000
vt 0.733333 0.400000
vt 0.800000 0.200000
vt 0.733333 0.200000
vt 0.400000 0.733333
vt 0.200000 0.800000
vt 0.400000 0.800000
vt 0.466667 0.666667
vt 0.400000 0.733333
vt 0.400000 0.666667
vt 0.733333 0.866667
vt 0.800000 0.800000
vt 0.733333 0.800000
vt 0.866667 0.333333
vt 0.933333 0.266667
vt 0.866667 0.266667
vt 0.066667 0.533333
vt 0.133333 0.000000
vt 0.066667 0.000000
vt 0.200000 0.466667
vt 0.200000 0.533333
vt 0.133333 0.533333
vt 0.666667 0.866667
vt 0.600000 0.933333
vt 0.600000 0.866667
vt 0.800000 0.333333
vt 0.866667 0.266667
vt 0.800000 0.266667
vt 0.200000 0.866667
vt 0.000000 0.800000
vt 0.000000 0.866667
vt 0.866667 0.800000
vt 0.800000 0.866667
vt 0.800000 0.800000
vt 0.200000 0.733333
vt 0.000000 0.800000
vt 0.200000 0.800000
vt 0.600000 0.733333
vt 0.400000 0.800000
vt 0.400000 0.733333
vt 0.666667 0.866667
vt 0.733333 0.800000
vt 0.666667 0.800000
vt 0.733333 0.200000
vt 0.666667 0.266667
vt 0.666667 0.200000
vt 0.733333 0.200000
vt 0.666667 0.000000
vt 0.733333 0.000000
vt 0.733333 0.666667
vt 0.666667 0.733333
vt 0.666667 0.666667
vt 0.466667 0.333333
vt 0.466667 0.400000
vt 0.333333 0.333333
vt 0.733333 0.200000
vt 0.800000 0.000000
vt 0.800000 0.200000
vt 0.533333 0.733333
vt 0.600000 0.533333
vt 0.533333 0.533333
vt 0.466667 0.866667
vt 0.400000 0.933333
vt 0.400000 0.866667
vt 0.666667 0.266667
vt 0.600000 0.266667
vt 0.600000 0.200000
vt 0.333333 0.066667
vt 0.400000 0.066667
vt 0.400000 0.133333
vt 0.200000 0.866667
vt 0.133333 0.933333
vt 0.133333 0.866667
vt 0.000000 0.533333
vt 0.066667 0.000000
vt 0.066667 0.533333
vt 0.400000 0.866667
vt 0.466667 0.800000
vt 0.400000 0.800000
vt 0.466667 0.200000
vt 0.533333 0.200000
vt 0.466667 0.066667
vt 0.266667 0.866667
vt 0.200000 0.933333
vt 0.200000 0.866667
vt 0.866667 0.400000
vt 0.933333 0.333333
vt 0.866667 0.333333
vt 0.400000 0.866667
vt 0.200000 0.800000
vt 0.400000 0.800000
vt 0.333333 0.933333
vt 0.400000 0.866667
vt 0.333333 0.866667
vt 0.066667 0.866667
vt 0.000000 0.933333
vt 0.000000 0.866667
vt 0.800000 0.400000
vt 0.733333 0.466667
vt 0.733333 0.400000
vt 0.733333 0.466667
vt 0.666667 0.266667
vt 0.733333 0.266667
vt 0.200000 0.733333
vt 0.000000 0.666667
vt 0.200000 0.666667
vt 0.866667 0.333333
vt 0.800000 0.400000
vt 0.800000 0.333333
vt 0.333333 0.866667
vt 0.266667 0.933333
vt 0.266667 0.866667
vt 0.533333 0.600000
vt 0.000000 0.666667
vt 0.000000 0.600000
vt 0.866667 0.533333
vt 0.800000 0.600000
vt 0.800000 0.533333
vt 0.600000 0.466667
vt 0.600000 0.533333
vt 0.666667 0.533333
vt 0.466667 0.933333
vt 0.533333 0.866667
vt 0.466667 0.866667
vt 0.666667 0.666667
vt 0.733333 0.466667
vt 0.666667 0.466667
vt 0.866667 0.200000
vt 0.933333 0.133333
vt 0.866667 0.133333
vt 0.800000 0.666667
vt 0.733333 0.733333
vt 0.733333 0.666667
vt 0.733333 0.666667
vt 0.800000 0.466667
vt 0.800000 0.666667
vt 0.866667 0.000000
vt 0.800000 0.066667
vt 0.800000 0.000000
vt 0.800000 0.733333
vt 0.600000 0.800000
vt 0.800000 0.800000
vt 0.933333 0.000000
vt 0.866667 0.066667
vt 0.866667 0.000000
vt 0.866667 0.200000
vt 0.800000 0.200000
vt 0.400000 0.733333
vt 0.200000 0.666667
vt 0.400000 0.666667
vt 0.800000 0.200000
vt 0.866667 0.133333
vt 0.800000 0.133333
vt 0.133333 0.866667
vt 0.066667 0.933333
vt 0.066667 0.866667
vt 0.800000 0.133333
vt 0.866667 0.066667
vt 0.800000 0.066667
vt 0.800000 0.466667
vt 0.866667 0.400000
vt 0.800000 0.400000
vt 0.133333 0.133333
vt 0.133333 0.200000
vt 0.200000 0.200000
vt 0.466667 0.466667
vt 0.533333 0.466667
vt 0.533333 0.400000
vt 0.666667 0.733333
vt 0.600000 0.533333
vt 0.600000 0.733333
vt 0.800000 0.533333
vt 0.866667 0.466667
vt 0.800000 0.466667
vt 0.866667 0.733333
vt 0.800000 0.800000
vt 0.800000 0.733333
vt 0.933333 0.066667
vt 0.866667 0.133333
vt 0.866667 0.066667
vt 0.533333 0.933333
vt 0.600000 0.866667
vt 0.533333 0.866667
vt 0.800000 0.733333
vt 0.866667 0.666667
vt 0.800000 0.666667
vt 0.800000 0.666667
vt 0.866667 0.600000
vt 0.800000 0.600000
vt 0.600000 0.866667
vt 0.666667 0.800000
vt 0.600000 0.800000
vt 0.866667 0.266667
vt 0.933333 0.200000
vt 0.866667 0.200000
vt 0.933333 0.400000
vt 0.866667 0.466667
vt 0.866667 0.400000
vt 0.533333 0.666667
vt 0.466667 0.733333
vt 0.466667 0.666667
vt 0.000000 0.533333
vt 0.533333 0.866667
vt 0.600000 0.866667
vt 0.800000 0.400000
vt 0.200000 0.733333
vt 0.466667 0.733333
vt 0.800000 0.866667
vt 0.933333 0.333333
vt 0.133333 0.533333
vt 0.133333 0.333333
vt 0.200000 0.333333
vt 0.200000 0.266667
vt 0.266667 0.266667
vt 0.266667 0.466667
vt 0.666667 0.933333
vt 0.866667 0.333333
vt 0.200000 0.800000
vt 0.866667 0.866667
vt 0.000000 0.733333
vt 0.600000 0.800000
vt 0.733333 0.866667
vt 0.733333 0.266667
vt 0.666667 0.200000
vt 0.733333 0.733333
vt 0.266667 0.400000
vt 0.266667 0.333333
vt 0.333333 0.266667
vt 0.533333 0.266667
vt 0.533333 0.333333
vt 0.733333 0.000000
vt 0.600000 0.733333
vt 0.466667 0.933333
vt 0.533333 0.200000
vt 0.600000 0.066667
vt 0.533333 0.000000
vt 0.600000 0.000000
vt 0.666667 0.066667
vt 0.200000 0.133333
vt 0.200000 0.066667
vt 0.133333 0.066667
vt 0.133333 0.000000
vt 0.333333 0.000000
vt 0.200000 0.933333
vt 0.000000 0.000000
vt 0.466667 0.866667
vt 0.533333 0.000000
vt 0.466667 0.000000
vt 0.400000 0.066667
vt 0.400000 0.266667
vt 0.466667 0.266667
vt 0.266667 0.933333
vt 0.933333 0.400000
vt 0.200000 0.866667
vt 0.400000 0.933333
vt 0.066667 0.933333
vt 0.800000 0.466667
vt 0.666667 0.466667
vt 0.000000 0.733333
vt 0.866667 0.400000
vt 0.333333 0.933333
vt 0.533333 0.666667
vt 0.866667 0.600000
vt 0.666667 0.333333
vt 0.600000 0.333333
vt 0.600000 0.266667
vt 0.533333 0.266667
vt 0.533333 0.466667
vt 0.533333 0.933333
vt 0.733333 0.666667
vt 0.933333 0.200000
vt 0.800000 0.733333
vt 0.733333 0.466667
vt 0.866667 0.066667
vt 0.600000 0.733333
vt 0.933333 0.066667
vt 0.200000 0.733333
vt 0.866667 0.200000
vt 0.133333 0.933333
vt 0.866667 0.133333
vt 0.866667 0.466667
vt 0.200000 0.266667
vt 0.333333 0.200000
vt 0.400000 0.266667
vt 0.400000 0.200000
vt 0.333333 0.133333
vt 0.333333 0.400000
vt 0.333333 0.466667
vt 0.266667 0.466667
vt 0.266667 0.533333
vt 0.466667 0.533333
vt 0.666667 0.533333
vt 0.866667 0.533333
vt 0.866667 0.800000
vt 0.933333 0.133333
vt 0.600000 0.933333
vt 0.866667 0.733333
vt 0.866667 0.666667
vt 0.666667 0.866667
vt 0.933333 0.266667
vt 0.933333 0.466667
vt 0.533333 0.733333
vn -1.0000 0.0000 0.0000
vn 0.0000 -1.0000 0.0000
vn 0.0000 0.0000 -1.0000
vn 0.0000 1.0000 0.0000
vn 0.0000 0.0000 1.0000
vn 1.0000 0.0000 0.0000
usemtl arrow2_Material
s off
f 37/1/1 5/2/1 79/3/1
f 63/4/2 15/5/2 62/6/2
f 85/7/3 55/8/3 5/9/3
f 49/10/2 17/11/2 16/12/2
f 60/13/1 63/14/1 66/15/1
f 86/16/4 44/17/4 43/18/4
f 56/19/1 69/20/1 47/21/1
f 53/22/3 71/23/3 52/24/3
f 37/25/2 49/26/2 66/27/2
f 23/28/4 82/29/4 8/30/4
f 17/31/5 76/32/5 16/33/5
f 69/34/2 48/35/2 47/36/2
f 8/37/1 7/38/1 39/39/1
f 67/40/6 20/41/6 19/42/6
f 5/43/1 42/44/1 85/45/1
f 18/46/6 35/47/6 17/48/6
f 80/49/1 6/50/1 36/51/1
f 33/52/5 37/53/5 32/54/5
f 54/55/4 42/56/4 27/57/4
f 12/58/4 2/59/4 3/60/4
f 12/61/6 48/62/6 14/63/6
f 88/64/4 87/65/4 57/66/4
f 60/67/2 31/68/2 30/69/2
f 6/70/5 39/71/5 7/72/5
f 26/73/4 53/74/4 10/75/4
f 28/76/6 74/77/6 57/78/6
f 29/79/5 61/80/5 1/81/5
f 79/82/4 55/83/4 78/84/4
f 72/85/3 83/86/3 56/87/3
f 68/88/2 47/89/2 6/90/2
f 63/91/5 34/92/5 64/93/5
f 8/94/3 51/95/3 9/96/3
f 26/97/6 21/98/6 71/99/6
f 23/100/3 47/101/3 68/102/3
f 51/103/6 23/104/6 68/105/6
f 76/106/5 42/107/5 41/108/5
f 5/109/4 34/110/4 41/111/4
f 49/112/6 31/113/6 13/114/6
f 83/115/4 84/116/4 56/117/4
f 21/118/5 40/119/5 67/120/5
f 38/121/6 55/122/6 49/123/6
f 31/124/5 63/125/5 30/126/5
f 70/127/2 52/128/2 71/129/2
f 83/130/3 70/131/3 48/132/3
f 65/133/2 63/134/2 64/135/2
f 2/136/3 59/137/3 58/138/3
f 78/139/5 43/140/5 79/141/5
f 55/142/4 35/143/4 50/144/4
f 45/145/5 11/146/5 44/147/5
f 65/148/1 34/149/1 77/150/1
f 15/151/5 4/152/5 62/153/5
f 47/36/3 12/154/3 3/155/3
f 54/156/6 76/157/6 55/158/6
f 66/159/3 13/160/3 60/161/3
f 24/162/6 83/163/6 48/164/6
f 73/165/2 28/166/2 72/167/2
f 61/168/2 33/169/2 32/170/2
f 79/171/1 43/172/1 44/173/1
f 3/174/1 2/175/1 58/176/1
f 59/177/2 1/178/2 58/179/2
f 88/180/3 74/181/3 73/182/3
f 16/183/6 75/184/6 15/185/6
f 37/186/5 80/187/5 36/188/5
f 55/189/3 18/190/3 49/191/3
f 10/192/1 52/193/1 70/194/1
f 4/195/1 63/196/1 62/197/1
f 84/198/3 24/199/3 69/200/3
f 77/201/3 66/202/3 65/203/3
f 19/204/5 78/205/5 38/206/5
f 75/207/4 41/208/4 4/209/4
f 37/1/1 66/210/1 5/2/1
f 63/4/2 16/211/2 15/5/2
f 85/7/3 54/212/3 55/8/3
f 49/10/2 18/213/2 17/11/2
f 60/13/1 30/214/1 63/14/1
f 86/16/4 45/215/4 44/17/4
f 56/19/1 84/216/1 69/20/1
f 53/22/3 26/217/3 71/23/3
f 37/25/2 38/218/2 49/26/2
f 8/30/4 39/219/4 23/28/4
f 39/219/4 81/220/4 23/28/4
f 81/220/4 80/221/4 79/222/4
f 79/222/4 56/223/4 81/220/4
f 56/223/4 23/28/4 81/220/4
f 17/31/5 35/224/5 76/32/5
f 69/34/2 24/225/2 48/35/2
f 8/37/1 9/226/1 7/38/1
f 67/40/6 40/227/6 20/41/6
f 5/43/1 41/228/1 42/44/1
f 18/46/6 50/229/6 35/47/6
f 80/49/1 81/230/1 6/50/1
f 33/52/5 38/231/5 37/53/5
f 54/55/4 85/232/4 42/56/4
f 12/58/4 46/233/4 2/59/4
f 48/62/6 38/234/6 14/63/6
f 38/234/6 33/235/6 14/63/6
f 14/63/6 29/236/6 12/61/6
f 29/236/6 59/237/6 12/61/6
f 59/237/6 46/238/6 12/61/6
f 88/64/4 11/239/4 87/65/4
f 60/67/2 13/240/2 31/68/2
f 6/70/5 81/241/5 39/71/5
f 10/75/4 83/242/4 40/243/4
f 83/242/4 78/244/4 40/243/4
f 78/244/4 20/245/4 40/243/4
f 40/243/4 22/246/4 10/75/4
f 22/246/4 26/73/4 10/75/4
f 57/78/6 87/247/6 28/76/6
f 87/247/6 45/248/6 28/76/6
f 45/248/6 86/249/6 78/250/6
f 78/250/6 83/251/6 45/248/6
f 83/251/6 28/76/6 45/248/6
f 29/79/5 14/252/5 61/80/5
f 79/82/4 5/253/4 55/83/4
f 72/85/3 28/254/3 83/86/3
f 47/89/2 37/255/2 6/90/2
f 37/255/2 36/256/2 6/90/2
f 6/90/2 7/257/2 68/88/2
f 7/257/2 9/258/2 68/88/2
f 9/258/2 51/259/2 68/88/2
f 63/91/5 41/260/5 34/92/5
f 8/94/3 82/261/3 51/95/3
f 26/97/6 22/262/6 21/98/6
f 23/100/3 56/263/3 47/101/3
f 51/103/6 82/264/6 23/104/6
f 76/106/5 27/265/5 42/107/5
f 5/109/4 77/266/4 34/110/4
f 49/112/6 16/267/6 31/113/6
f 83/115/4 25/268/4 84/116/4
f 21/118/5 22/269/5 40/119/5
f 38/121/6 78/270/6 55/122/6
f 31/124/5 16/271/5 63/125/5
f 71/129/2 21/272/2 70/127/2
f 21/272/2 67/273/2 70/127/2
f 67/273/2 19/274/2 38/275/2
f 38/275/2 48/276/2 67/273/2
f 48/276/2 70/127/2 67/273/2
f 83/130/3 10/277/3 70/131/3
f 65/133/2 66/278/2 63/134/2
f 2/136/3 46/279/3 59/137/3
f 78/139/5 86/280/5 43/140/5
f 55/142/4 76/281/4 35/143/4
f 45/145/5 87/282/5 11/146/5
f 65/148/1 64/283/1 34/149/1
f 15/151/5 75/284/5 4/152/5
f 47/36/3 48/35/3 12/154/3
f 54/156/6 27/285/6 76/157/6
f 66/159/3 49/286/3 13/160/3
f 24/162/6 25/287/6 83/163/6
f 73/165/2 74/288/2 28/166/2
f 61/168/2 14/289/2 33/169/2
f 44/173/1 11/290/1 72/291/1
f 11/290/1 88/292/1 72/291/1
f 88/292/1 73/293/1 72/291/1
f 72/291/1 56/294/1 44/173/1
f 56/294/1 79/171/1 44/173/1
f 58/176/1 1/295/1 3/174/1
f 1/295/1 61/296/1 3/174/1
f 61/296/1 32/297/1 37/298/1
f 37/298/1 47/299/1 61/296/1
f 47/299/1 3/174/1 61/296/1
f 59/177/2 29/300/2 1/178/2
f 88/180/3 57/301/3 74/181/3
f 16/183/6 76/302/6 75/184/6
f 37/186/5 79/303/5 80/187/5
f 55/189/3 50/304/3 18/190/3
f 10/192/1 53/305/1 52/193/1
f 4/195/1 41/306/1 63/196/1
f 84/198/3 25/307/3 24/199/3
f 77/201/3 5/308/3 66/202/3
f 19/204/5 20/309/5 78/205/5
f 75/207/4 76/310/4 41/208/4

View File

@ -1,23 +1,7 @@
minetest.register_node('x_bows:arrow_node', { minetest.register_node('x_bows:arrow_node', {
drawtype = 'nodebox', drawtype = 'mesh',
node_box = { mesh = 'x_bows_arrow.obj',
type = 'fixed', tiles = {'x_bows_arrow_mesh.png'},
fixed = {
{-0.1875, 0, -0.5, 0.1875, 0, 0.5},
{0, -0.1875, -0.5, 0, 0.1875, 0.5},
{-0.5, -0.5, -0.5, 0.5, 0.5, -0.5},
}
},
-- Textures of node; +Y, -Y, +X, -X, +Z, -Z
-- Textures of node; top, bottom, right, left, front, back
tiles = {
'x_bows_arrow_tile_point_top.png',
'x_bows_arrow_tile_point_bottom.png',
'x_bows_arrow_tile_point_right.png',
'x_bows_arrow_tile_point_left.png',
'x_bows_arrow_tile_tail.png',
'x_bows_arrow_tile_tail.png'
},
groups = {not_in_creative_inventory=1}, groups = {not_in_creative_inventory=1},
sunlight_propagates = true, sunlight_propagates = true,
paramtype = 'light', paramtype = 'light',

515
quiver.lua Normal file
View File

@ -0,0 +1,515 @@
---Close one or all open quivers in players inventory
---@param player ObjectRef
---@param quiver_id? string If `nil` then all open quivers will be closed
---@returns nil
function x_bows.quiver.close_quiver(player, quiver_id)
local player_inv = player:get_inventory()
---find matching quiver item in players inventory with the open formspec name
if player_inv and player_inv:contains_item('main', 'x_bows:quiver_open') then
local inv_list = player_inv:get_list('main')
for i, st in ipairs(inv_list) do
local st_meta = st:get_meta()
if not st:is_empty() and st:get_name() == 'x_bows:quiver_open' then
if quiver_id and st_meta:get_string('quiver_id') == quiver_id then
local replace_item = x_bows.quiver.get_replacement_item(st, 'x_bows:quiver')
player_inv:set_stack('main', i, replace_item)
break
else
local replace_item = x_bows.quiver.get_replacement_item(st, 'x_bows:quiver')
player_inv:set_stack('main', i, replace_item)
end
end
end
end
end
---Swap item in player inventory indicating open quiver. Preserve all ItemStack definition and meta.
---@param from_stack ItemStack transfer data from this item
---@param to_item_name string transfer data to this item
---@return ItemStack ItemStack replacement item
function x_bows.quiver.get_replacement_item(from_stack, to_item_name)
---@type ItemStack
local replace_item = ItemStack({
name = to_item_name,
count = from_stack:get_count(),
wear = from_stack:get_wear()
})
local replace_item_meta = replace_item:get_meta()
local from_stack_meta = from_stack:get_meta()
replace_item_meta:set_string('quiver_items', from_stack_meta:get_string('quiver_items'))
replace_item_meta:set_string('quiver_id', from_stack_meta:get_string('quiver_id'))
replace_item_meta:set_string('description', from_stack_meta:get_string('description'))
return replace_item
end
---Gets arrow from quiver
---@param player ObjectRef
---@return {["found_arrow_stack"]: ItemStack|nil, ["quiver_id"]: string|nil, ["quiver_name"]: string|nil}
function x_bows.quiver.get_itemstack_arrow_from_quiver(player)
local player_inv = player:get_inventory()
---@type ItemStack|nil
local found_arrow_stack
local prev_detached_inv_list = {}
local quiver_id
local quiver_name
---find matching quiver item in players inventory with the open formspec name
if player_inv and player_inv:contains_item('main', 'x_bows:quiver') then
local inv_list = player_inv:get_list('main')
for i, st in ipairs(inv_list) do
if not st:is_empty() and st:get_name() == 'x_bows:quiver' then
local st_meta = st:get_meta()
local player_name = player:get_player_name()
quiver_id = st_meta:get_string('quiver_id')
local detached_inv = x_bows.quiver.get_or_create_detached_inv(
quiver_id,
player_name,
st_meta:get_string('quiver_items')
)
if not detached_inv:is_empty('main') then
local detached_inv_list = detached_inv:get_list('main')
---find arrows inside quiver inventory
for j, qst in ipairs(detached_inv_list) do
---save copy of inv list before we take the item
table.insert(prev_detached_inv_list, detached_inv:get_stack('main', j))
if not qst:is_empty() and not found_arrow_stack then
quiver_name = st:get_name()
found_arrow_stack = qst:take_item()
if not x_bows.is_creative(player_name) then
detached_inv:set_list('main', detached_inv_list)
x_bows.quiver.save(detached_inv, player, true)
end
end
end
end
end
if found_arrow_stack then
---show HUD - quiver inventory
x_bows.quiver.udate_or_create_hud(player, prev_detached_inv_list)
break
end
end
end
return {
found_arrow_stack = found_arrow_stack,
quiver_id = quiver_id,
quiver_name = quiver_name
}
end
---Remove all added HUDs
---@param player ObjectRef
function x_bows.quiver.remove_hud(player)
local player_name = player:get_player_name()
if x_bows.quiver.hud_item_ids[player_name] then
for _, v in pairs(x_bows.quiver.hud_item_ids[player_name]) do
if type(v) == 'table' then
for _, v2 in pairs(v) do
player:hud_remove(v2)
end
else
player:hud_remove(v)
end
end
x_bows.quiver.hud_item_ids[player_name] = {
arrow_inv_img = {},
stack_count = {}
}
else
x_bows.quiver.hud_item_ids[player_name] = {
arrow_inv_img = {},
stack_count = {}
}
end
end
---Update or create HUD
---@todo implement hud_change?
---@param player ObjectRef
---@param inv_list ItemStack[]
---@return nil
function x_bows.quiver.udate_or_create_hud(player, inv_list)
local player_name = player:get_player_name()
local selected_bg_added = false
if x_bows.quiver.after_job[player_name] then
for _, v in pairs(x_bows.quiver.after_job[player_name]) do
v:cancel()
end
x_bows.quiver.after_job[player_name] = {}
else
x_bows.quiver.after_job[player_name] = {}
end
x_bows.quiver.remove_hud(player)
---title image
x_bows.quiver.hud_item_ids[player_name].title_image = player:hud_add({
hud_elem_type = 'image',
position = {x = 1, y = 0.5},
offset = {x = -120, y = -140},
text = 'x_bows_quiver.png',
scale = {x = 4, y = 4},
alignment = 0,
})
---title copy
local quiver_def = minetest.registered_items['x_bows:quiver']
x_bows.quiver.hud_item_ids[player_name].title_copy = player:hud_add({
hud_elem_type = 'text',
position = {x = 1, y = 0.5},
offset = {x = -120, y = -75},
text = quiver_def.short_description,
alignment = 0,
scale = {x = 100, y = 30},
number = 0xFFFFFF,
})
---hotbar bg
x_bows.quiver.hud_item_ids[player_name].hotbar_bg = player:hud_add({
hud_elem_type = 'image',
position = {x = 1, y = 0.5},
offset = {x = -238, y = 0},
text = 'x_bows_quiver_hotbar.png',
scale = {x = 1, y = 1},
alignment = {x = 1, y = 0 },
})
for j, qst in ipairs(inv_list) do
if not qst:is_empty() then
local found_arrow_stack_def = minetest.registered_items[qst:get_name()]
if not selected_bg_added then
selected_bg_added = true
---ui selected bg
x_bows.quiver.hud_item_ids[player_name].hotbar_selected = player:hud_add({
hud_elem_type = 'image',
position = {x = 1, y = 0.5},
offset = {x = -308 + (j * 74), y = 2},
text = 'x_bows_quiver_hotbar_selected.png',
scale = {x = 1, y = 1},
alignment = {x = 1, y = 0 },
})
end
if found_arrow_stack_def then
---arrow inventory image
table.insert(x_bows.quiver.hud_item_ids[player_name].arrow_inv_img, player:hud_add({
hud_elem_type = 'image',
position = {x = 1, y = 0.5},
offset = {x = -300 + (j * 74), y = 0},
text = found_arrow_stack_def.inventory_image,
scale = {x = 4, y = 4},
alignment = {x = 1, y = 0 },
}))
---stack count
table.insert(x_bows.quiver.hud_item_ids[player_name].stack_count, player:hud_add({
hud_elem_type = 'text',
position = {x = 1, y = 0.5},
offset = {x = -244 + (j * 74), y = 23},
text = qst:get_count(),
alignment = -1,
scale = {x = 50, y = 10},
number = 0xFFFFFF,
}))
end
end
end
---@param v_player ObjectRef
table.insert(x_bows.quiver.after_job[player_name], minetest.after(10, function(v_player)
x_bows.quiver.remove_hud(v_player)
end, player))
end
---Get existing detached inventory or create new one
---@param quiver_id string
---@param player_name string
---@param quiver_items? string
---@return InvRef
function x_bows.quiver.get_or_create_detached_inv(quiver_id, player_name, quiver_items)
local detached_inv
if quiver_id ~= '' then
detached_inv = minetest.get_inventory({type='detached', name=quiver_id})
end
if not detached_inv then
detached_inv = minetest.create_detached_inventory(quiver_id, {
---@param inv InvRef detached inventory
---@param from_list string
---@param from_index number
---@param to_list string
---@param to_index number
---@param count number
---@param player ObjectRef
allow_move = function(inv, from_list, from_index, to_list, to_index, count, player)
if x_bows.quiver.quiver_can_allow(inv, player) then
return count
else
return 0
end
end,
---@param inv InvRef detached inventory
---@param listname string listname of the inventory, e.g. `'main'`
---@param index number
---@param stack ItemStack
---@param player ObjectRef
allow_put = function(inv, listname, index, stack, player)
if minetest.get_item_group(stack:get_name(), 'arrow') ~= 0 and x_bows.quiver.quiver_can_allow(inv, player) then
return stack:get_count()
else
return 0
end
end,
---@param inv InvRef detached inventory
---@param listname string listname of the inventory, e.g. `'main'`
---@param index number
---@param stack ItemStack
---@param player ObjectRef
allow_take = function(inv, listname, index, stack, player)
if minetest.get_item_group(stack:get_name(), 'arrow') ~= 0 and x_bows.quiver.quiver_can_allow(inv, player) then
return stack:get_count()
else
return 0
end
end,
---@param inv InvRef detached inventory
---@param from_list string
---@param from_index number
---@param to_list string
---@param to_index number
---@param count number
---@param player ObjectRef
on_move = function(inv, from_list, from_index, to_list, to_index, count, player)
x_bows.quiver.save(inv, player)
end,
---@param inv InvRef detached inventory
---@param listname string listname of the inventory, e.g. `'main'`
---@param index number index where was item put
---@param stack ItemStack stack of item what was put
---@param player ObjectRef
on_put = function(inv, listname, index, stack, player)
x_bows.quiver.save(inv, player)
end,
---@param inv InvRef detached inventory
---@param listname string listname of the inventory, e.g. `'main'`
---@param index number
---@param stack ItemStack
---@param player ObjectRef
on_take = function(inv, listname, index, stack, player)
x_bows.quiver.save(inv, player)
end,
}, player_name)
detached_inv:set_size('main', 3 * 1)
end
---populate items in inventory
if quiver_items and quiver_items ~= '' then
x_bows.quiver.set_string_to_inv(detached_inv, quiver_items)
end
return detached_inv
end
---create UUID
---@return string
local function uuid()
local template ='xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'
---@diagnostic disable-next-line: redundant-return-value
return string.gsub(template, '[xy]', function (c)
local v = (c == 'x') and math.random(0, 0xf) or math.random(8, 0xb)
return string.format('%x', v)
end)
end
---create formspec
---@param name string name of the form
---@return string
local function get_formspec(name)
local width = 3
local height = 1
local list_w = 8
local list_pos_x = (list_w - width) / 2
local formspec =
'size['..list_w..',6]' ..
'list[detached:'..name..';main;'..list_pos_x..',0.3;'..width..',1;]'..
'list[current_player;main;0,'..(height + 0.85)..';'..list_w..',1;]'..
'list[current_player;main;0,'..(height + 2.08)..';'..list_w..',3;8]'..
'listring[detached:'..name..';main]'..
'listring[current_player;main]'..
default.get_hotbar_bg(0, height + 0.85)
--update formspec
local inv = minetest.get_inventory({type='detached', name=name})
local invlist = inv:get_list(name)
---inventory slots overlay
local px, py = list_pos_x, 0.3
for i = 1, 3 do
if not invlist or invlist[i]:is_empty() then
formspec = formspec ..
'image[' .. px .. ',' .. py .. ';1,1;x_bows_arrow_slot.png]'
end
px = px + 1
end
return formspec
end
---convert inventory of itemstacks to serialized string
---@param inv InvRef
---@return {['inv_string']: string, ['content_description']: string}
local function get_string_from_inv(inv)
local inv_list = inv:get_list('main')
local t = {}
local content_description = ''
for i, st in ipairs(inv_list) do
if not st:is_empty() then
table.insert(t, st:to_table())
content_description = content_description .. '\n' ..st:get_short_description()..' '..st:get_count()
else
table.insert(t, {is_empty = true})
end
end
return {
inv_string = minetest.serialize(t),
content_description = content_description == '' and '\nEmpty' or content_description
}
end
---set items from serialized string to inventory
---@param inv InvRef inventory to add items to
---@param str string previously stringified inventory of itemstacks
function x_bows.quiver.set_string_to_inv(inv, str)
local t = minetest.deserialize(str)
for i, item in ipairs(t) do
if not item.is_empty then
inv:set_stack('main', i, ItemStack(item))
end
end
end
---save quiver inventory to itemstack meta
---@param inv InvRef
---@param player ObjectRef
---@param quiver_is_closed? boolean default `false`
function x_bows.quiver.save(inv, player, quiver_is_closed)
local player_inv = player:get_inventory()
local inv_loc = inv:get_location()
local quiver_item_name = quiver_is_closed and 'x_bows:quiver' or 'x_bows:quiver_open'
---find matching quiver item in players inventory with the open formspec name
if player_inv and player_inv:contains_item('main', quiver_item_name) then
local inv_list = player_inv:get_list('main')
for i, st in ipairs(inv_list) do
local st_meta = st:get_meta()
if not st:is_empty() and st:get_name() == quiver_item_name and st_meta:get_string('quiver_id') == inv_loc.name then
---save inventory items in quiver item meta
local string_from_inventory_result = get_string_from_inv(inv)
st_meta:set_string('quiver_items', string_from_inventory_result.inv_string)
---update description
local new_description = st:get_short_description()..'\n'..string_from_inventory_result.content_description..'\n'
st_meta:set_string('description', new_description)
player_inv:set_stack('main', i, st)
break
end
end
end
end
---check if we are allowing actions in the correct quiver inventory
---@param inv InvRef
---@param player ObjectRef
---@return boolean
function x_bows.quiver.quiver_can_allow(inv, player)
local player_inv = player:get_inventory()
local inv_loc = inv:get_location()
---find matching quiver item in players inventory with the open formspec name
if player_inv and player_inv:contains_item('main', 'x_bows:quiver_open') then
local inv_list = player_inv:get_list('main')
for i, st in ipairs(inv_list) do
local st_meta = st:get_meta()
if not st:is_empty() and st:get_name() == 'x_bows:quiver_open' and st_meta:get_string('quiver_id') == inv_loc.name then
return true
end
end
end
return false
end
---Open quiver
---@param itemstack ItemStack
---@param user ObjectRef
---@return ItemStack
function x_bows.open_quiver(itemstack, user)
local itemstack_meta = itemstack:get_meta()
local pname = user:get_player_name()
local quiver_id = itemstack_meta:get_string('quiver_id')
---create inventory id and save it
if quiver_id == '' then
quiver_id = itemstack:get_name()..'_'..uuid()
itemstack_meta:set_string('quiver_id', quiver_id)
end
local quiver_items = itemstack_meta:get_string('quiver_items')
x_bows.quiver.get_or_create_detached_inv(quiver_id, pname, quiver_items)
---show open variation of quiver
local replace_item = x_bows.quiver.get_replacement_item(itemstack, 'x_bows:quiver_open')
itemstack:replace(replace_item)
minetest.sound_play('x_bows_quiver', {
to_player = user:get_player_name(),
gain = 0.1
})
minetest.show_formspec(pname, quiver_id, get_formspec(quiver_id))
return itemstack
end
minetest.register_on_player_receive_fields(function(player, formname, fields)
if player and fields.quit then
x_bows.quiver.close_quiver(player, formname)
end
end)

BIN
screenshot.1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 634 KiB

BIN
screenshot.2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 632 KiB

BIN
screenshot.3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 364 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 537 KiB

BIN
sounds/x_bows_quiver.1.ogg Normal file

Binary file not shown.

BIN
sounds/x_bows_quiver.2.ogg Normal file

Binary file not shown.

BIN
sounds/x_bows_quiver.3.ogg Normal file

Binary file not shown.

BIN
sounds/x_bows_quiver.4.ogg Normal file

Binary file not shown.

BIN
sounds/x_bows_quiver.5.ogg Normal file

Binary file not shown.

BIN
sounds/x_bows_quiver.6.ogg Normal file

Binary file not shown.

BIN
sounds/x_bows_quiver.7.ogg Normal file

Binary file not shown.

BIN
sounds/x_bows_quiver.8.ogg Normal file

Binary file not shown.

BIN
sounds/x_bows_quiver.9.ogg Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 180 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 165 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 161 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 180 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 135 B

BIN
textures/x_bows_quiver.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 223 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 317 B

View File

@ -13,6 +13,3 @@
---@field on_detach_child fun(self: table, child: ObjectRef): nil Function receive a "luaentity" table as `self`. `child`: an `ObjectRef` of the child that detaches ---@field on_detach_child fun(self: table, child: ObjectRef): nil Function receive a "luaentity" table as `self`. `child`: an `ObjectRef` of the child that detaches
---@field on_detach fun(self: table, parent: ObjectRef|nil) Function receive a "luaentity" table as `self`. `parent`: an `ObjectRef` (can be `nil`) from where it got detached. This happens before the parent object is removed from the world. ---@field on_detach fun(self: table, parent: ObjectRef|nil) Function receive a "luaentity" table as `self`. `parent`: an `ObjectRef` (can be `nil`) from where it got detached. This happens before the parent object is removed from the world.
---@field get_staticdata fun(self: table) Function receive a "luaentity" table as `self`. Should return a string that will be passed to `on_activate` when the object is instantiated the next time. ---@field get_staticdata fun(self: table) Function receive a "luaentity" table as `self`. Should return a string that will be passed to `on_activate` when the object is instantiated the next time.
---Entity definition
---@class ObjectProperties

View File

@ -1,7 +1,3 @@
---https://github.com/sumneko/lua-language-server/wiki ---https://github.com/sumneko/lua-language-server/wiki
---Undefined location
---@class LocationUndefined
---@field type string|'undefined'
---@alias Dump fun(obj: any, dumped?: any): string returns a string which makes `obj` human-readable, `obj`: arbitrary variable, `dumped`: table, default: `{}` ---@alias Dump fun(obj: any, dumped?: any): string returns a string which makes `obj` human-readable, `obj`: arbitrary variable, `dumped`: table, default: `{}`

View File

@ -17,4 +17,4 @@
---@field get_lists fun(): table Returns table that maps listnames to inventory lists ---@field get_lists fun(): table Returns table that maps listnames to inventory lists
---@field set_lists fun(self: InvRef, lists: table): nil Sets inventory lists, size will not change ---@field set_lists fun(self: InvRef, lists: table): nil Sets inventory lists, size will not change
---@field remove_item fun(self: InvRef, listname: string, stack: string|ItemStack): nil Take as many items as specified from the list, returns the items that were actually removed, as an `ItemStack`, note that any item metadata is ignored, so attempting to remove a specific unique item this way will likely remove the wrong one, to do that use `set_stack` with an empty `ItemStack`. ---@field remove_item fun(self: InvRef, listname: string, stack: string|ItemStack): nil Take as many items as specified from the list, returns the items that were actually removed, as an `ItemStack`, note that any item metadata is ignored, so attempting to remove a specific unique item this way will likely remove the wrong one, to do that use `set_stack` with an empty `ItemStack`.
---@field get_location fun(): InvRef|LocationUndefined returns a location compatible to `minetest.get_inventory(location)`. returns `{type="undefined"}` in case location is not known ---@field get_location fun(self: InvRef): {['type']: 'player'|'node'|'detached'|'undefined', ['name']: string|nil, ['pos']: Vector|nil} returns a location compatible to `minetest.get_inventory(location)`. returns `{type="undefined"}` in case location is not known

View File

@ -27,7 +27,7 @@
---@field add_wear_by_uses fun(self: ItemStack, max_uses: integer|number): nil Increases wear in such a way that, if only this function is called, the item breaks after `max_uses` times. Valid `max_uses` range is [0,65536] Does nothing if item is not a tool or if `max_uses` is 0 ---@field add_wear_by_uses fun(self: ItemStack, max_uses: integer|number): nil Increases wear in such a way that, if only this function is called, the item breaks after `max_uses` times. Valid `max_uses` range is [0,65536] Does nothing if item is not a tool or if `max_uses` is 0
---@field add_item fun(self: ItemStack, item: string|table): ItemStack Returns leftover `ItemStack` Put some item or stack onto this stack ---@field add_item fun(self: ItemStack, item: string|table): ItemStack Returns leftover `ItemStack` Put some item or stack onto this stack
---@field item_fits fun(self: ItemStack, item: string|table): boolean Returns `true` if item or stack can be fully added to this one. ---@field item_fits fun(self: ItemStack, item: string|table): boolean Returns `true` if item or stack can be fully added to this one.
---@field take_item fun(self: ItemStack, n: integer|number): ItemStack Returns taken `ItemStack` Take (and remove) up to `n` items from this stack `n`: number, default: `1` ---@field take_item fun(self: ItemStack, n?: integer|number): ItemStack Returns taken `ItemStack` Take (and remove) up to `n` items from this stack `n`: number, default: `1`
---@field peek_item fun(self: ItemStack, n: integer|number): ItemStack Returns taken `ItemStack` Copy (don't remove) up to `n` items from this stack `n`: number, default: `1` ---@field peek_item fun(self: ItemStack, n: integer|number): ItemStack Returns taken `ItemStack` Copy (don't remove) up to `n` items from this stack `n`: number, default: `1`
---@field name string ---@field name string
---@field count integer ---@field count integer

View File

@ -12,7 +12,7 @@
---@field set_string fun(self: MetaDataRef, key: string, value: string): string Value of `""` will delete the key. ---@field set_string fun(self: MetaDataRef, key: string, value: string): string Value of `""` will delete the key.
---@field get_string fun(self: MetaDataRef, key: string): string Returns `""` if key not present. ---@field get_string fun(self: MetaDataRef, key: string): string Returns `""` if key not present.
---@field set_int fun(self: MetaDataRef, key: string, value: integer): nil ---@field set_int fun(self: MetaDataRef, key: string, value: integer): nil
---@field get_int fun(self: MetaDataRef, key: string): string|integer Returns `0` if key not present. ---@field get_int fun(self: MetaDataRef, key: string): integer|number Returns `0` if key not present.
---@field set_float fun(self: MetaDataRef, key: string, value: number): nil ---@field set_float fun(self: MetaDataRef, key: string, value: number): nil
---@field get_float fun(self: MetaDataRef, key): integer|number Returns `0` if key not present. ---@field get_float fun(self: MetaDataRef, key): integer|number Returns `0` if key not present.
---@field to_table fun(): nil Returns `nil` or a table with keys: `fields`: key-value storage `inventory`: `{list1 = {}, ...}}` (NodeMetaRef only) ---@field to_table fun(): nil Returns `nil` or a table with keys: `fields`: key-value storage `inventory`: `{list1 = {}, ...}}` (NodeMetaRef only)

View File

@ -62,6 +62,11 @@
---@field add_node fun(pos: Vector, node: SetNodeTable): nil alias to `minetest.set_node`, Set node at position `pos`, `node`: table `{name=string, param1=number, param2=number}`, If param1 or param2 is omitted, it's set to `0`. e.g. `minetest.set_node({x=0, y=10, z=0}, {name="default:wood"})` ---@field add_node fun(pos: Vector, node: SetNodeTable): nil alias to `minetest.set_node`, Set node at position `pos`, `node`: table `{name=string, param1=number, param2=number}`, If param1 or param2 is omitted, it's set to `0`. e.g. `minetest.set_node({x=0, y=10, z=0}, {name="default:wood"})`
---@field string_to_pos fun(string: string): Vector|nil If the string can't be parsed to a position, nothing is returned. ---@field string_to_pos fun(string: string): Vector|nil If the string can't be parsed to a position, nothing is returned.
---@field chat_send_player fun(name: string, text: string): nil ---@field chat_send_player fun(name: string, text: string): nil
---@field create_detached_inventory fun(name: string, callbacks: DetachedInventoryCallbacks, player_name?: string): InvRef Creates a detached inventory. If it already exists, it is cleared. `callbacks`: See [Detached inventory callbacks], `player_name`: Make detached inventory available to one player exclusively, by default they will be sent to every player (even if not used). Note that this parameter is mostly just a workaround and will be removed in future releases.
---@field get_mod_storage fun(): StorageRef Mod metadata: per mod metadata, saved automatically. Can be obtained via `minetest.get_mod_storage()` during load time.
---@field show_formspec fun(playername: string, formname: string, formspec: string): nil `playername`: name of player to show formspec, `formname`: name passed to `on_player_receive_fields` callbacks. It should follow the `"modname:<whatever>"` naming convention. `formspec`: formspec to display
---@field register_on_player_receive_fields fun(func: fun(player: ObjectRef, formname: string, fields: table)): nil Called when the server received input from `player` in a formspec with the given `formname`. Specifically, this is called on any of the following events: a button was pressed, Enter was pressed while the focus was on a text field, a checkbox was toggled, something was selected in a dropdown list, a different tab was selected, selection was changed in a textlist or table, an entry was double-clicked in a textlist or table, a scrollbar was moved, or the form was actively closed by the player.
---@field get_inventory fun(location: {['"type"']: 'player'|'node'|'detached', ['"name"']: string|nil, ['"pos"']: Vector|nil}): InvRef
---Minetest settings ---Minetest settings
---@class MinetestSettings ---@class MinetestSettings
@ -83,6 +88,15 @@
---@field param1 number ---@field param1 number
---@field param2 number ---@field param2 number
--- Detached inventory callbacks
---@class DetachedInventoryCallbacks
---@field allow_move fun(inv: InvRef, from_list: string, from_index: number, to_list: string, to_index: number, count: number, player: ObjectRef): number Called when a player wants to move items inside the inventory. Return value: number of items allowed to move.
---@field allow_put fun(inv: InvRef, listname: string, index: number, stack: ItemStack, player: ObjectRef): number Called when a player wants to put something into the inventory. Return value: number of items allowed to put. Return value -1: Allow and don't modify item count in inventory.
---@field allow_take fun(inv: InvRef, listname: string, index: number, stack: ItemStack, player: ObjectRef): number Called when a player wants to take something out of the inventory. Return value: number of items allowed to take. Return value -1: Allow and don't modify item count in inventory.
---@field on_move fun(inv: InvRef, from_list: string, from_index: number, to_list: string, to_index: number, count: number, player: ObjectRef): nil
---@field on_put fun(inv: InvRef, listname: string, index: number, stack: ItemStack, player: ObjectRef): nil
---@field on_take fun(inv: InvRef, listname: string, index: number, stack: ItemStack, player: ObjectRef): nil Called after the actual action has happened, according to what was allowed. No return value.
--- Job table --- Job table
---@class JobTable ---@class JobTable
---@field cancel fun(self: JobTable) Cancels the job function from being called ---@field cancel fun(self: JobTable) Cancels the job function from being called

View File

@ -29,6 +29,7 @@
---@field node_sound_glass_defaults fun(table?: NodeSoundDef): NodeSoundDef ---@field node_sound_glass_defaults fun(table?: NodeSoundDef): NodeSoundDef
---@field node_sound_metal_defaults fun(table?: NodeSoundDef): NodeSoundDef ---@field node_sound_metal_defaults fun(table?: NodeSoundDef): NodeSoundDef
---@field node_sound_ice_defaults fun(table?: NodeSoundDef): NodeSoundDef ---@field node_sound_ice_defaults fun(table?: NodeSoundDef): NodeSoundDef
---@field get_hotbar_bg fun(x: number, y: number): nil Get the hotbar background as string, containing the formspec elements. x: Horizontal position in the formspec, y: Vertical position in the formspec.
--- Leaf decay definition --- Leaf decay definition
---@class RegisterLeafdecayDef ---@class RegisterLeafdecayDef

View File

@ -13,6 +13,10 @@
---@field buildable_to boolean If true, placed nodes can replace this node. default: `false` ---@field buildable_to boolean If true, placed nodes can replace this node. default: `false`
---@field tiles string|NodeTilesDef Textures of node; +Y, -Y, +X, -X, +Z, -Z. List can be shortened to needed length. ---@field tiles string|NodeTilesDef Textures of node; +Y, -Y, +X, -X, +Z, -Z. List can be shortened to needed length.
---@field sound NodeSoundDef Definition of node sounds to be played at various events. ---@field sound NodeSoundDef Definition of node sounds to be played at various events.
---@field drawtype NodeDrawTypes
---@field liquid_viscosity number|integer Controls speed at which the liquid spreads/flows (max. 7).
-- 0 is fastest, 7 is slowest. By default, this also slows down movement of players inside the node (can be overridden using `move_resistance`)
---@field walkable boolean If true, objects collide with node.
---Textures of node; +Y, -Y, +X, -X, +Z, -Z. List can be shortened to needed length. ---Textures of node; +Y, -Y, +X, -X, +Z, -Z. List can be shortened to needed length.
---@class NodeTilesDef ---@class NodeTilesDef
@ -22,3 +26,24 @@
---@field align_style 'node'|'world'|'user' align style determines whether the texture will be rotated with the node or kept aligned with its surroundings. "user" means that client setting will be used, similar to `glasslike_framed_optional`. Note: supported by solid nodes and nodeboxes only. ---@field align_style 'node'|'world'|'user' align style determines whether the texture will be rotated with the node or kept aligned with its surroundings. "user" means that client setting will be used, similar to `glasslike_framed_optional`. Note: supported by solid nodes and nodeboxes only.
---@field scale number|integer scale is used to make texture span several (exactly `scale`) nodes, instead of just one, in each direction. Works for world-aligned textures only. Note that as the effect is applied on per-mapblock basis, `16` should be equally divisible by `scale` or you may get wrong results. ---@field scale number|integer scale is used to make texture span several (exactly `scale`) nodes, instead of just one, in each direction. Works for world-aligned textures only. Note that as the effect is applied on per-mapblock basis, `16` should be equally divisible by `scale` or you may get wrong results.
---@field color ColorSpec the texture's color will be multiplied with this color. the tile's color overrides the owning node's color in all cases. ---@field color ColorSpec the texture's color will be multiplied with this color. the tile's color overrides the owning node's color in all cases.
---There are a bunch of different looking node types. `*_optional` drawtypes need less rendering time if deactivated (always client-side).
---@alias NodeDrawTypes
---| '"normal"' # A node-sized cube.
---| '"airlike"' # Invisible, uses no texture.
---| '"liquid"' # The cubic source node for a liquid. Faces bordering to the same node are never rendered. Connects to node specified in `liquid_alternative_flowing`. Use `backface_culling = false` for the tiles you want to make visible when inside the node.
---| '"flowingliquid"' # The flowing version of a liquid, appears with various heights and slopes. Faces bordering to the same node are never rendered. Connects to node specified in `liquid_alternative_source`. Node textures are defined with `special_tiles` where the first tile is for the top and bottom faces and the second tile is for the side faces. `tiles` is used for the item/inventory/wield image rendering. Use `backface_culling = false` for the special tiles you want to make visible when inside the node
---| '"glasslike"' # Often used for partially-transparent nodes. Only external sides of textures are visible.
---| '"glasslike_framed"' # All face-connected nodes are drawn as one volume within a surrounding frame. The frame appearance is generated from the edges of the first texture specified in `tiles`. The width of the edges used are 1/16th of texture size: 1 pixel for 16x16, 2 pixels for 32x32 etc. The glass 'shine' (or other desired detail) on each node face is supplied by the second texture specified in `tiles`.
---| '"glasslike_framed_optional"' # This switches between the above 2 drawtypes according to the menu setting 'Connected Glass'.
---| '"allfaces"' # Often used for partially-transparent nodes. External and internal sides of textures are visible.
---| '"allfaces_optional"' # Often used for leaves nodes. This switches between `normal`, `glasslike` and `allfaces` according to the menu setting: Opaque Leaves / Simple Leaves / Fancy Leaves. With 'Simple Leaves' selected, the texture specified in `special_tiles` is used instead, if present. This allows a visually thicker texture to be used to compensate for how `glasslike` reduces visual thickness.
---| '"torchlike"' # A single vertical texture. If `paramtype2="[color]wallmounted"`: If placed on top of a node, uses the first texture specified in `tiles`. If placed against the underside of a node, uses the second texture specified in `tiles`. If placed on the side of a node, uses the third texture specified in `tiles` and is perpendicular to that node. If `paramtype2="none"`: Will be rendered as if placed on top of a node (see above) and only the first texture is used.
---| '"signlike"' # A single texture parallel to, and mounted against, the top, underside or side of a node. If `paramtype2="[color]wallmounted"`, it rotates according to `param2` If `par
---| '"plantlike"' # Two vertical and diagonal textures at right-angles to each other. See `paramtype2 = "meshoptions"` above for other options.
---| '"firelike"' # When above a flat surface, appears as 6 textures, the central 2 as `plantlike` plus 4 more surrounding those. If not above a surface the central 2 do not appear, but the texture appears against the faces of surrounding nodes if they are present.
---| '"fencelike"' # A 3D model suitable for a wooden fence. One placed node appears as a single vertical post. Adjacently-placed nodes cause horizontal bars to appear between them.
---| '"raillike"' # Often used for tracks for mining carts. Requires 4 textures to be specified in `tiles`, in order: Straight, curved, t-junction, crossing. Each placed node automatically switches to a suitable rotated texture determined by the adjacent `raillike` nodes, in order to create a continuous track network. Becomes a sloping node if placed against stepped nodes.
---| '"nodebox"' # Often used for stairs and slabs. Allows defining nodes consisting of an arbitrary number of boxes. See [Node boxes] below for more information.
---| '"mesh"' # Uses models for nodes. Tiles should hold model materials textures. Only static meshes are implemented. For supported model formats see Irrlicht engine documentation.
---| '"plantlike_rooted"' # Enables underwater `plantlike` without air bubbles around the nodes. Consists of a base cube at the co-ordinates of the node plus a `plantlike` extension above If `paramtype2="leveled", the `plantlike` extension has a height of `param2 / 16` nodes, otherwise it's the height of 1 node If `paramtype2="wallmounted"`, the `plantlike` extension will be at one of the corresponding 6 sides of the base cube. Also, the base cube rotates like a `normal` cube would The `plantlike` extension visually passes through any nodes above the base cube without affecting them. The base cube texture tiles are defined as normal, the `plantlike` extension uses the defined special tile, for example: `special_tiles = {{name = "default_papyrus.png"}},`

View File

@ -21,6 +21,13 @@
---@field add_velocity fun(self: ObjectRef, vel: Vector): nil `vel` is a vector, e.g. `{x=0.0, y=2.3, z=1.0}`. In comparison to using get_velocity, adding the velocity and then using set_velocity, add_velocity is supposed to avoid synchronization problems. Additionally, players also do not support set_velocity. If a player: Does not apply during free_move. Note that since the player speed is normalized at each move step, increasing e.g. Y velocity beyond what would usually be achieved (see: physics overrides) will cause existing X/Z velocity to be reduced. Example: `add_velocity({x=0, y=6.5, z=0})` is equivalent to pressing the jump key (assuming default settings) ---@field add_velocity fun(self: ObjectRef, vel: Vector): nil `vel` is a vector, e.g. `{x=0.0, y=2.3, z=1.0}`. In comparison to using get_velocity, adding the velocity and then using set_velocity, add_velocity is supposed to avoid synchronization problems. Additionally, players also do not support set_velocity. If a player: Does not apply during free_move. Note that since the player speed is normalized at each move step, increasing e.g. Y velocity beyond what would usually be achieved (see: physics overrides) will cause existing X/Z velocity to be reduced. Example: `add_velocity({x=0, y=6.5, z=0})` is equivalent to pressing the jump key (assuming default settings)
---@field get_properties fun(self: ObjectRef): table Returns object property table ---@field get_properties fun(self: ObjectRef): table Returns object property table
---@field get_children fun(self: ObjectRef): ObjectRef[] Returns a list of ObjectRefs that are attached to the object. ---@field get_children fun(self: ObjectRef): ObjectRef[] Returns a list of ObjectRefs that are attached to the object.
---@field set_properties fun(self: ObjectRef, object_properties: ObjectProperties): nil For entities; disables the regular damage mechanism for players punching it by hand or a non-tool item, so that it can do something else than take damage.
---@field get_look_dir fun(self: ObjectRef): Vector get camera direction as a unit vector
---@field get_meta fun(self: ObjectRef): MetaDataRef returns a PlayerMetaRef.
---@field hud_add fun(self: ObjectRef, hud_definition: table): number|integer|nil add a HUD element described by HUD def, returns ID number on success
---@field hud_remove fun(self: ObjectRef, id: number|integer): nil remove the HUD element of the specified id
---@field hud_change fun(self: ObjectRef, id: number|integer, stat: string, value: any): nil change a value of a previously added HUD element. `stat` supports the same keys as in the hud definition table except for `"hud_elem_type"`.
---@field set_wielded_item fun(self: ObjectRef, item: ItemStack): boolean replaces the wielded item, returns `true` if successful.
---Moving things in the game are generally these. ---Moving things in the game are generally these.
---This is basically a reference to a C++ `ServerActiveObject`. ---This is basically a reference to a C++ `ServerActiveObject`.
@ -33,3 +40,39 @@
---@field immortal number|integer Skips all damage and breath handling for an object. This group will also hide the integrated HUD status bars for players. It is automatically set to all players when damage is disabled on the server and cannot be reset (subject to change). ---@field immortal number|integer Skips all damage and breath handling for an object. This group will also hide the integrated HUD status bars for players. It is automatically set to all players when damage is disabled on the server and cannot be reset (subject to change).
---@field fall_damage_add_percent number|integer Modifies the fall damage suffered by players when they hit the ground. It is analog to the node group with the same name. See the node group above for the exact calculation. ---@field fall_damage_add_percent number|integer Modifies the fall damage suffered by players when they hit the ground. It is analog to the node group with the same name. See the node group above for the exact calculation.
---@field punch_operable number|integer For entities; disables the regular damage mechanism for players punching it by hand or a non-tool item, so that it can do something else than take damage. ---@field punch_operable number|integer For entities; disables the regular damage mechanism for players punching it by hand or a non-tool item, so that it can do something else than take damage.
---Used by `ObjectRef` methods. Part of an Entity definition. These properties are not persistent, but are applied automatically to the corresponding Lua entity using the given registration fields. Player properties need to be saved manually.
---@class ObjectProperties
---@field hp_max integer Defines the maximum and default HP of the entity. For Lua entities the maximum is not enforced. For players this defaults to `minetest.PLAYER_MAX_HP_DEFAULT`.
---@field breath_max integer For players only. Defaults to `minetest.PLAYER_MAX_BREATH_DEFAULT`.
---@field zoom_fov number For players only. Zoom FOV in degrees. Note that zoom loads and/or generates world beyond the server's maximum send and generate distances, so acts like a telescope. Smaller zoom_fov values increase the distance loaded/generated. Defaults to 15 in creative mode, 0 in survival mode. zoom_fov = 0 disables zooming for the player.
---@field eye_height number For players only. Camera height above feet position in nodes.
---@field physical boolean Collide with `walkable` nodes.
---@field collide_with_objects boolean Collide with other objects if physical = true
---@field collisionbox number[]|integer[]
---@field selectionbox number[]|integer[] Selection box uses collision box dimensions when not set. For both boxes: {xmin, ymin, zmin, xmax, ymax, zmax} in nodes from object position.
---@field pointable boolean Whether the object can be pointed at
---@field visual 'cube'|'sprite'|'upright_sprite'|'mesh'|'wielditem'|'item' "cube" is a node-sized cube. "sprite" is a flat texture always facing the player. "upright_sprite" is a vertical flat texture. "mesh" uses the defined mesh model. "wielditem" is used for dropped items. (see builtin/game/item_entity.lua). For this use 'wield_item = itemname' (Deprecated: 'textures = {itemname}'). If the item has a 'wield_image' the object will be an extrusion of that, otherwise: If 'itemname' is a cubic node or nodebox the object will appear identical to 'itemname'. If 'itemname' is a plantlike node the object will be an extrusion of its texture. Otherwise for non-node items, the object will be an extrusion of 'inventory_image'. If 'itemname' contains a ColorString or palette index (e.g. from `minetest.itemstring_with_palette()`), the entity will inherit the color. "item" is similar to "wielditem" but ignores the 'wield_image' parameter.
---@field visual_size {['x']: integer|number, ['y']: integer|number, ['z']: integer|number} Multipliers for the visual size. If `z` is not specified, `x` will be used to scale the entity along both horizontal axes.
---@field mesh string File name of mesh when using "mesh" visual
---@field textures table Number of required textures depends on visual. "cube" uses 6 textures just like a node, but all 6 must be defined. "sprite" uses 1 texture. "upright_sprite" uses 2 textures: {front, back}. "wielditem" expects 'textures = {itemname}'. "mesh" requires one texture for each mesh buffer/material (in order)
---@field colors table Number of required colors depends on visual
---@field use_texture_alpha boolean Use texture's alpha channel. Excludes "upright_sprite" and "wielditem". Note: currently causes visual issues when viewed through other semi-transparent materials such as water.
---@field spritediv {['x']: integer|number, ['y']: integer|number} Used with spritesheet textures for animation and/or frame selection according to position relative to player. Defines the number of columns and rows in the spritesheet: {columns, rows}.
---@field initial_sprite_basepos {['x']: integer|number, ['y']: integer|number} Used with spritesheet textures. Defines the {column, row} position of the initially used frame in the spritesheet.
---@field is_visible boolean If false, object is invisible and can't be pointed.
---@field makes_footstep_sound boolean If true, is able to make footstep sounds of nodes
---@field automatic_rotate number|integer Set constant rotation in radians per second, positive or negative. Object rotates along the local Y-axis, and works with set_rotation. Set to 0 to disable constant rotation.
---@field stepheight number|integer If positive number, object will climb upwards when it moves horizontally against a `walkable` node, if the height difference is within `stepheight`.
---@field automatic_face_movement_dir number|integer Automatically set yaw to movement direction, offset in degrees. 'false' to disable.
---@field automatic_face_movement_max_rotation_per_sec number|integer Limit automatic rotation to this value in degrees per second. No limit if value <= 0.
---@field backface_culling boolean Set to false to disable backface_culling for model
---@field glow number|integer Add this much extra lighting when calculating texture color. Value < 0 disables light's effect on texture color. For faking self-lighting, UI style entities, or programmatic coloring in mods.
---@field nametag string The name to display on the head of the object. By default empty. If the object is a player, a nil or empty nametag is replaced by the player's name. For all other objects, a nil or empty string removes the nametag. To hide a nametag, set its color alpha to zero. That will disable it entirely.
---@field nametag_color ColorSpec Sets text color of nametag
---@field nametag_bgcolor ColorSpec Sets background color of nametag `false` will cause the background to be set automatically based on user settings. Default: false
---@field infotext string Same as infotext for nodes. Empty by default
---@field static_save boolean If false, never save this object statically. It will simply be deleted when the block gets unloaded. The get_staticdata() callback is never called then. Defaults to 'true'.
---@field damage_texture_modifier string Texture modifier to be applied for a short duration when object is hit
---@field shaded boolean Setting this to 'false' disables diffuse lighting of entity
---@field show_on_minimap boolean Defaults to true for players, false for other entities. If set to true the entity will show as a marker on the minimap.

View File

@ -8,6 +8,7 @@
--- Definition of node sounds to be played at various events. --- Definition of node sounds to be played at various events.
---@class NodeSoundDef ---@class NodeSoundDef
---@field name string Sound name.
---@field footstep SimpleSoundSpec If walkable, played when object walks on it. If node is climbable or a liquid, played when object moves through it ---@field footstep SimpleSoundSpec If walkable, played when object walks on it. If node is climbable or a liquid, played when object moves through it
---@field dig SimpleSoundSpec|'__group' While digging node. If `"__group"`, then the sound will be `default_dig_<groupname>`, where `<groupname>` is the name of the item's digging group with the fastest digging time. In case of a tie, one of the sounds will be played (but we cannot predict which one) Default value: `"__group"` ---@field dig SimpleSoundSpec|'__group' While digging node. If `"__group"`, then the sound will be `default_dig_<groupname>`, where `<groupname>` is the name of the item's digging group with the fastest digging time. In case of a tie, one of the sounds will be played (but we cannot predict which one) Default value: `"__group"`
---@field dug SimpleSoundSpec Node was dug ---@field dug SimpleSoundSpec Node was dug