diff --git a/api.lua b/api.lua index 7d41d4f..63868ba 100644 --- a/api.lua +++ b/api.lua @@ -1,3 +1,5 @@ +sfinv = sfinv--[[@as Sfinv]] + ---Check if table contains value ---@param table table ---@param value string|number @@ -286,11 +288,15 @@ function XBows.register_arrow(self, name, def) local mod_name = def.custom.mod_name or 'x_bows' def.custom.name = mod_name .. ':' .. name def.description = def.description or name + def.short_description = def.short_description or name def.custom.tool_capabilities = def.custom.tool_capabilities or { full_punch_interval = 1, max_drop_level = 0, damage_groups = {fleshy=2} } + def.custom.description_abilities = minetest.colorize('#00FF00', 'Damage: ' + .. def.custom.tool_capabilities.damage_groups.fleshy) .. '\n' .. minetest.colorize('#00BFFF', 'Charge Time: ' + .. def.custom.tool_capabilities.full_punch_interval .. 's') def.groups = mergeTables({arrow = 1, flammable = 1}, def.groups or {}) def.custom.particle_effect = def.custom.particle_effect or 'arrow' def.custom.particle_effect_crit = def.custom.particle_effect_crit or 'arrow_crit' @@ -304,10 +310,8 @@ function XBows.register_arrow(self, name, def) self.registered_arrows[def.custom.name] = def minetest.register_craftitem(def.custom.name, { - description = def.description .. '\n' .. minetest.colorize('#00FF00', 'Damage: ' - .. def.custom.tool_capabilities.damage_groups.fleshy) .. '\n' .. minetest.colorize('#00BFFF', 'Charge Time: ' - .. def.custom.tool_capabilities.full_punch_interval .. 's'), - short_description = def.description, + description = def.description .. '\n' .. def.custom.description_abilities, + short_description = def.short_description, inventory_image = def.inventory_image, groups = def.groups }) @@ -1814,12 +1818,25 @@ end ---@param quiver_is_closed? boolean ---@return nil function XBowsQuiver.save(self, inv, player, quiver_is_closed) - local player_inv = player:get_inventory() + local player_inv = player:get_inventory()--[[@as InvRef]] local inv_loc = inv:get_location() local quiver_item_name = quiver_is_closed and 'x_bows:quiver' or 'x_bows:quiver_open' + local player_quiver_inv_stack = player_inv:get_stack('x_bows:quiver_inv', 1) - ---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 + if not player_quiver_inv_stack:is_empty() and player_quiver_inv_stack:get_meta():get_string('quiver_id') == inv_loc.name then + local st_meta = player_quiver_inv_stack:get_meta() + ---save inventory items in quiver item meta + local string_from_inventory_result = self:get_string_from_inv(inv) + + st_meta:set_string('quiver_items', string_from_inventory_result.inv_string) + + ---update description + local new_description = player_quiver_inv_stack:get_short_description()..'\n'..string_from_inventory_result.content_description..'\n' + + st_meta:set_string('description', new_description) + player_inv:set_stack('x_bows:quiver_inv', 1, player_quiver_inv_stack) + elseif player_inv and player_inv:contains_item('main', quiver_item_name) then + ---find matching quiver item in players inventory with the open formspec name local inv_list = player_inv:get_list('main') for i, st in ipairs(inv_list) do @@ -1849,11 +1866,16 @@ end ---@param player ObjectRef ---@return boolean function XBowsQuiver.quiver_can_allow(self, inv, player) - local player_inv = player:get_inventory() + local player_inv = player:get_inventory()--[[@as InvRef]] local inv_loc = inv:get_location() + local player_quiver_inv_stack = player_inv:get_stack('x_bows:quiver_inv', 1) - ---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 + if not player_quiver_inv_stack:is_empty() and player_quiver_inv_stack:get_meta():get_string('quiver_id') == inv_loc.name then + ---find quiver in player `quiver_inv` inv list + return true + elseif player_inv and player_inv:contains_item('main', 'x_bows:quiver_open') then + ---find quiver in player `main` inv list + ---matching quiver item in players inventory with the open formspec name local inv_list = player_inv:get_list('main') for i, st in ipairs(inv_list) do @@ -1901,3 +1923,211 @@ function XBows.open_quiver(self, itemstack, user) minetest.show_formspec(pname, quiver_id, XBowsQuiver:get_formspec(quiver_id)) return itemstack end + +---Register sfinv page +---@param self XBowsQuiver +function XBowsQuiver.sfinv_register_page(self) + sfinv.register_page('x_bows:quiver_page', { + title = 'X Bows', + get = function(this, player, context) + local formspec = { + ---arrow + 'label[0,0;Arrows you are wearing:]', + 'list[current_player;x_bows:arrow_inv;0,0.5;1,1;]', + 'image[0,0.5;1,1;x_bows_arrow_slot.png;]', + 'listring[current_player;x_bows:arrow_inv]', + 'listring[current_player;main]', + ---quiver + 'label[0,1.5;Quiver you are wearing:]', + 'list[current_player;x_bows:quiver_inv;0,2;1,1;]', + 'image[0,2;1,1;x_bows_quiver_slot.png]', + 'listring[current_player;x_bows:quiver_inv]', + 'listring[current_player;main]', + } + + if not context._itemstack_arrow then + context._itemstack_arrow = player:get_inventory():get_stack('x_bows:arrow_inv', 1) + end + + if not context._itemstack_quiver then + context._itemstack_quiver = player:get_inventory():get_stack('x_bows:quiver_inv', 1) + end + + if context._itemstack_arrow and not context._itemstack_arrow:is_empty() then + local x_bows_registered_arrow_def = self.registered_arrows[context._itemstack_arrow:get_name()] + + if x_bows_registered_arrow_def then + formspec[#formspec + 1] = 'label[2.5,0;' .. minetest.formspec_escape(context._itemstack_arrow:get_short_description()) .. '\n'.. minetest.formspec_escape(x_bows_registered_arrow_def.custom.description_abilities) ..']' + end + end + + + if context._itemstack_quiver and not context._itemstack_quiver:is_empty() then + local st_meta = context._itemstack_quiver:get_meta() + local quiver_id = st_meta:get_string('quiver_id') + + ---description + formspec[#formspec + 1] = 'label[2.5,1.5;' .. minetest.formspec_escape(context._itemstack_quiver:get_short_description()) .. ']' + formspec[#formspec + 1] = 'list[detached:'..quiver_id..';main;0,3.2;3,1;]' + formspec[#formspec + 1] = 'listring[detached:'..quiver_id..';main]' + formspec[#formspec + 1] = 'listring[current_player;main]' + end + + return sfinv.make_formspec(player, context, table.concat(formspec, ''), true) + end, + on_enter = function(this, player, context) + if not context._itemstack_quiver then + context._itemstack_quiver = player:get_inventory():get_stack('x_bows:quiver_inv', 1) + end + + if context._itemstack_quiver and not context._itemstack_quiver:is_empty() then + local st_meta = context._itemstack_quiver:get_meta() + local quiver_id = st_meta:get_string('quiver_id') + + self:get_or_create_detached_inv( + quiver_id, + player:get_player_name(), + st_meta:get_string('quiver_items') + ) + end + end + }) + + minetest.register_allow_player_inventory_action(function(player, action, inventory, inventory_info) + ---arrow inventory + if action == 'move' and inventory_info.to_list == 'x_bows:arrow_inv' then + local stack = inventory:get_stack(inventory_info.from_list, inventory_info.from_index) + + if minetest.get_item_group(stack:get_name(), 'arrow') ~= 0 then + return inventory_info.count + else + return 0 + end + elseif action == 'move' and inventory_info.from_list == 'x_bows:arrow_inv' then + local stack = inventory:get_stack(inventory_info.from_list, inventory_info.from_index) + + if minetest.get_item_group(stack:get_name(), 'arrow') ~= 0 then + return inventory_info.count + else + return 0 + end + elseif action == 'put' and inventory_info.listname == 'x_bows:arrow_inv' then + if minetest.get_item_group(inventory_info.stack:get_name(), 'arrow') ~= 0 then + return inventory_info.stack:get_count() + else + return 0 + end + elseif action == 'take' and inventory_info.listname == 'x_bows:arrow_inv' then + if minetest.get_item_group(inventory_info.stack:get_name(), 'arrow') ~= 0 then + return inventory_info.stack:get_count() + else + return 0 + end + end + + ---quiver inventory + if action == 'move' and inventory_info.to_list == 'x_bows:quiver_inv' then + local stack = inventory:get_stack(inventory_info.from_list, inventory_info.from_index) + if minetest.get_item_group(stack:get_name(), 'quiver') ~= 0 then + return inventory_info.count + else + return 0 + end + elseif action == 'move' and inventory_info.from_list == 'x_bows:quiver_inv' then + local stack = inventory:get_stack(inventory_info.from_list, inventory_info.from_index) + if minetest.get_item_group(stack:get_name(), 'quiver') ~= 0 then + return inventory_info.count + else + return 0 + end + elseif action == 'put' and inventory_info.listname == 'x_bows:quiver_inv' then + if minetest.get_item_group(inventory_info.stack:get_name(), 'quiver') ~= 0 then + return inventory_info.stack:get_count() + else + return 0 + end + elseif action == 'take' and inventory_info.listname == 'x_bows:quiver_inv' then + if minetest.get_item_group(inventory_info.stack:get_name(), 'quiver') ~= 0 then + return inventory_info.stack:get_count() + else + return 0 + end + end + + return inventory_info.count or inventory_info.stack:get_count() + end) + + minetest.register_on_player_inventory_action(function(player, action, inventory, inventory_info) + ---arrow + if action == 'move' and inventory_info.to_list == 'x_bows:arrow_inv' then + local stack = inventory:get_stack(inventory_info.to_list, inventory_info.to_index) + local context = sfinv.get_or_create_context(player) + context._itemstack_arrow = stack + sfinv.set_context(player, context) + sfinv.set_player_inventory_formspec(player) + elseif action == 'move' and inventory_info.from_list == 'x_bows:arrow_inv' then + local stack = inventory:get_stack(inventory_info.from_list, inventory_info.from_index) + local context = sfinv.get_or_create_context(player) + + context._itemstack_arrow = stack + sfinv.set_context(player, context) + sfinv.set_player_inventory_formspec(player) + elseif action == 'put' and inventory_info.listname == 'x_bows:arrow_inv' then + local context = sfinv.get_or_create_context(player) + + context._itemstack_arrow = inventory_info.stack + sfinv.set_context(player, context) + sfinv.set_player_inventory_formspec(player) + elseif action == 'take' and inventory_info.listname == 'x_bows:arrow_inv' then + local context = sfinv.get_or_create_context(player) + context._itemstack_arrow = nil + sfinv.set_context(player, context) + sfinv.set_player_inventory_formspec(player) + end + + ---quiver + if action == 'move' and inventory_info.to_list == 'x_bows:quiver_inv' then + local stack = inventory:get_stack(inventory_info.to_list, inventory_info.to_index) + local context = sfinv.get_or_create_context(player) + local st_meta = stack:get_meta() + local player_name = player:get_player_name() + local quiver_id = st_meta:get_string('quiver_id') + + if quiver_id == '' then + quiver_id = stack:get_name()..'_'..uuid() + st_meta:set_string('quiver_id', quiver_id) + inventory:set_stack(inventory_info.to_list, inventory_info.to_index, stack) + end + + self:get_or_create_detached_inv( + quiver_id, + player_name, + st_meta:get_string('quiver_items') + ) + + context._itemstack_quiver = stack + sfinv.set_context(player, context) + sfinv.set_player_inventory_formspec(player) + elseif action == 'move' and inventory_info.from_list == 'x_bows:quiver_inv' then + local stack = inventory:get_stack(inventory_info.from_list, inventory_info.from_index) + local context = sfinv.get_or_create_context(player) + + context._itemstack_quiver = stack + sfinv.set_context(player, context) + sfinv.set_player_inventory_formspec(player) + elseif action == 'put' and inventory_info.listname == 'x_bows:quiver_inv' then + local context = sfinv.get_or_create_context(player) + + if minetest.get_item_group(inventory_info.stack:get_name(), 'quiver') ~= 0 then + context._itemstack_quiver = inventory_info.stack + sfinv.set_context(player, context) + sfinv.set_player_inventory_formspec(player) + end + elseif action == 'take' and inventory_info.listname == 'x_bows:quiver_inv' then + local context = sfinv.get_or_create_context(player) + context._itemstack_quiver = nil + sfinv.set_context(player, context) + sfinv.set_player_inventory_formspec(player) + end + end) +end diff --git a/init.lua b/init.lua index e8f2335..67f1d0f 100644 --- a/init.lua +++ b/init.lua @@ -5,6 +5,7 @@ minetest = minetest--[[@as Minetest]] ItemStack = ItemStack--[[@as ItemStack]] vector = vector--[[@as Vector]] default = default--[[@as MtgDefault]] +sfinv = sfinv--[[@as Sfinv]] math.randomseed(tonumber(tostring(os.time()):reverse():sub(1, 9))--[[@as number]]) @@ -19,6 +20,16 @@ dofile(path .. '/arrow.lua') dofile(path .. '/items.lua') dofile(path .. '/quiver.lua') +XBowsQuiver:sfinv_register_page() + +minetest.register_on_joinplayer(function(player) + local inv_quiver = player:get_inventory()--[[@as InvRef]] + local inv_arrow = player:get_inventory()--[[@as InvRef]] + + inv_quiver:set_size('x_bows:quiver_inv', 1 * 1) + inv_arrow:set_size('x_bows:arrow_inv', 1 * 1) +end) + ---backwards compatibility minetest.register_alias('x_bows:arrow_diamond_tipped_poison', 'x_bows:arrow_diamond') diff --git a/textures/x_bows_quiver_slot.png b/textures/x_bows_quiver_slot.png new file mode 100644 index 0000000..1acc67a Binary files /dev/null and b/textures/x_bows_quiver_slot.png differ diff --git a/types/minetest.type.lua b/types/minetest.type.lua index ba78ff0..7ce93f2 100644 --- a/types/minetest.type.lua +++ b/types/minetest.type.lua @@ -70,6 +70,9 @@ ---@field dir_to_wallmounted fun(dir: Vector): number Convert a vector to a wallmounted value, used for `paramtype2="wallmounted"` ---@field item_place_node fun(itemstack: ItemStack, placer: ObjectRef, pointed_thing: PointedThingDef, param2?: , prevent_after_place?: boolean): Vector|nil Place item as a node, `param2` overrides `facedir` and wallmounted `param2`, `prevent_after_place`: if set to `true`, `after_place_node` is not called or the newly placed node to prevent a callback and placement loop. returns `itemstack, position`, `position`: the location the node was placed to. `nil` if nothing was placed. ---@field unregister_item fun(name: string): nil Unregisters the item from the engine, and deletes the entry with key `name` from `minetest.registered_items` and from the associated item table according to its nature: `minetest.registered_nodes`, etc. +---@field register_allow_player_inventory_action fun(func: fun(player: ObjectRef, action: string, inventory: InvRef, inventory_info: {["from_list"]: string, ["to_list"]: string, ["from_index"]: number, ["to_index"]: number, ["count"]: number} | {["listname"]: string, ["index"]: number, ["stack"]: ItemStack}): number): nil Determines how much of a stack may be taken, put or moved to a player inventory. `player` (type `ObjectRef`) is the player who modified the inventory, `inventory` (type `InvRef`). List of possible `action` (string) values and their `inventory_info` (table) contents: `move`: `{from_list=string, to_list=string, from_index=number, to_index=number, count=number}`, `put`: `{listname=string, index=number, stack=ItemStack}`, `take`: Same as `put`. Return a numeric value to limit the amount of items to be taken, put or moved. A value of `-1` for `take` will make the source stack infinite. +---@field register_on_player_inventory_action fun(func: fun(player: ObjectRef, action: string, inventory: InvRef, inventory_info: {["from_list"]: string, ["to_list"]: string, ["from_index"]: number, ["to_index"]: number, ["count"]: number} | {["listname"]: string, ["index"]: number, ["stack"]: ItemStack}): nil): nil Called after a take, put or move event from/to/in a player inventory. Function arguments: see `minetest.register_allow_player_inventory_action`. Does not accept or handle any return value. +---@field formspec_escape fun(str: string): string returns a string, escapes the characters "[", "]", "\", "," and ";", which can not be used in formspecs. ---Minetest settings ---@class MinetestSettings diff --git a/types/mtg-sfinv.lua b/types/mtg-sfinv.lua new file mode 100644 index 0000000..7d0070b --- /dev/null +++ b/types/mtg-sfinv.lua @@ -0,0 +1,25 @@ +---Sfinv API +---@class Sfinv +---@field register_page fun(name: string, def: SfinvDef): nil Register a page +---@field make_formspec fun(player: ObjectRef, contex: SfinvContext, content: string, show_inv?: boolean, size?: string): nil Adds a theme to a formspec show_inv, defaults to false. Whether to show the player's main inventory size, defaults to `size[8,8.6]` if not specified +---@field get_or_create_context fun(player: ObjectRef): SfinvContext Gets the player's context +---@field set_context fun(player: ObjectRef, context: SfinvContext): nil +---@field get_formspec fun(player: ObjectRef, context: SfinvContext): string Builds current page's formspec +---@field set_player_inventory_formspec fun(player: ObjectRef): string (re)builds page formspec and calls set_inventory_formspec(). + + +---Sfinv Definition +---@class SfinvDef +---@field title string Human readable page name (required) +---@field get fun(self: Sfinv, player: ObjectRef, context: SfinvContext): string Returns a formspec string. See formspec variables. (required) +---@field is_in_nav fun(self: Sfinv, player: ObjectRef, context: SfinvContext): boolean Return true to show in the navigation (the tab header, by default) +---@field on_player_receive_fields fun(self: Sfinv, player: ObjectRef, context: SfinvContext, fields: table): nil On formspec submit. +---@field on_enter fun(self: Sfinv, player: ObjectRef, context: SfinvContext): nil Called when the player changes pages, usually using the tabs. +---@field on_leave fun(self: Sfinv, player: ObjectRef, context: SfinvContext): nil When leaving this page to go to another, called before other's on_enter + +---Sfinv Context, including: any thing you want to store, sfinv will clear the stored data on log out / log in +---@class SfinvContext +---@field page string Current page name +---@field nav string[] A list of page names +---@field nav_titles string[] A list of page titles +---@field nav_idx number Current nav index (in nav and nav_titles) diff --git a/types/xbows.type.lua b/types/xbows.type.lua index 8cf36b7..f951395 100644 --- a/types/xbows.type.lua +++ b/types/xbows.type.lua @@ -107,6 +107,7 @@ ---@field on_hit_entity fun(self: table, pointed_thing_ref: table) ---@field on_hit_player fun(self: table, pointed_thing_ref: table) ---@field on_after_activate fun(self: table) +---@field description_abilities string ---Custom field in ItemDef