forked from minetest-mods/unified_inventory
Compare commits
23 Commits
4633a276a2
...
match_copy
Author | SHA1 | Date | |
---|---|---|---|
88b3033477 | |||
19e14aa21e | |||
82cdf24045 | |||
31c35dcd59 | |||
826d5f4683 | |||
db1c3c10b8 | |||
9533200e25 | |||
177debd13c | |||
8e9ea34ae8 | |||
574de91971 | |||
fc562ecaa0 | |||
182ab493c3 | |||
14da1a3dd0 | |||
fa079c31b6 | |||
c1fef26c87 | |||
477acd2f89 | |||
19efce45ed | |||
dbe06be68b | |||
3074d625e2 | |||
5ac2558da4 | |||
25c40fea6c | |||
23a45b8131 | |||
d6688872c8 |
116
api.lua
116
api.lua
@ -2,40 +2,49 @@ local S = minetest.get_translator("unified_inventory")
|
||||
local F = minetest.formspec_escape
|
||||
local ui = unified_inventory
|
||||
|
||||
local function is_recipe_craftable(recipe)
|
||||
-- Ensure the ingedients exist
|
||||
for _, itemname in pairs(recipe.items) do
|
||||
local groups = string.find(itemname, "group:")
|
||||
if groups then
|
||||
if not ui.get_group_item(string.sub(groups, 8)).item then
|
||||
return false
|
||||
end
|
||||
else
|
||||
-- Possibly an item
|
||||
if not minetest.registered_items[itemname]
|
||||
or minetest.get_item_group(itemname, "not_in_craft_guide") ~= 0 then
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
-- Create detached creative inventory after loading all mods
|
||||
minetest.after(0.01, function()
|
||||
local rev_aliases = {}
|
||||
for source, target in pairs(minetest.registered_aliases) do
|
||||
if not rev_aliases[target] then rev_aliases[target] = {} end
|
||||
table.insert(rev_aliases[target], source)
|
||||
for original, newname in pairs(minetest.registered_aliases) do
|
||||
if not rev_aliases[newname] then
|
||||
rev_aliases[newname] = {}
|
||||
end
|
||||
table.insert(rev_aliases[newname], original)
|
||||
end
|
||||
|
||||
-- Filtered item list
|
||||
ui.items_list = {}
|
||||
for name, def in pairs(minetest.registered_items) do
|
||||
if (not def.groups.not_in_creative_inventory or
|
||||
def.groups.not_in_creative_inventory == 0) and
|
||||
def.description and def.description ~= "" then
|
||||
if ui.is_itemdef_listable(def) then
|
||||
table.insert(ui.items_list, name)
|
||||
|
||||
-- Alias processing: Find recipes that belong to the current item name
|
||||
local all_names = rev_aliases[name] or {}
|
||||
table.insert(all_names, name)
|
||||
for _, player_name in ipairs(all_names) do
|
||||
local recipes = minetest.get_all_craft_recipes(player_name)
|
||||
if recipes then
|
||||
for _, recipe in ipairs(recipes) do
|
||||
|
||||
local unknowns
|
||||
|
||||
for _,chk in pairs(recipe.items) do
|
||||
local groupchk = string.find(chk, "group:")
|
||||
if (not groupchk and not minetest.registered_items[chk])
|
||||
or (groupchk and not ui.get_group_item(string.gsub(chk, "group:", "")).item)
|
||||
or minetest.get_item_group(chk, "not_in_craft_guide") ~= 0 then
|
||||
unknowns = true
|
||||
end
|
||||
end
|
||||
|
||||
if not unknowns then
|
||||
ui.register_craft(recipe)
|
||||
end
|
||||
for _, itemname in ipairs(all_names) do
|
||||
local recipes = minetest.get_all_craft_recipes(itemname)
|
||||
for _, recipe in ipairs(recipes or {}) do
|
||||
if is_recipe_craftable(recipe) then
|
||||
ui.register_craft(recipe)
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -43,7 +52,9 @@ minetest.after(0.01, function()
|
||||
end
|
||||
table.sort(ui.items_list)
|
||||
ui.items_list_size = #ui.items_list
|
||||
print("Unified Inventory. inventory size: "..ui.items_list_size)
|
||||
print("Unified Inventory. Inventory size: "..ui.items_list_size)
|
||||
|
||||
-- Analyse dropped items -> custom "digging" recipes
|
||||
for _, name in ipairs(ui.items_list) do
|
||||
local def = minetest.registered_items[name]
|
||||
-- Simple drops
|
||||
@ -133,24 +144,42 @@ minetest.after(0.01, function()
|
||||
end
|
||||
end
|
||||
end
|
||||
for _, recipes in pairs(ui.crafts_for.recipe) do
|
||||
|
||||
-- Step 1: Initialize cache for looking up groups
|
||||
unified_inventory.init_matching_cache()
|
||||
|
||||
-- Step 2: Find all matching items for the given spec (groups)
|
||||
local get_matching_spec_items = unified_inventory.get_matching_items
|
||||
|
||||
for outputitemname, recipes in pairs(ui.crafts_for.recipe) do
|
||||
-- List of crafts that return this item string (variable "_")
|
||||
|
||||
-- Problem: The group cache must be initialized after all mods finished loading
|
||||
-- thus, invalid recipes might be indexed. Hence perform filtering with `new_recipe_list`
|
||||
local new_recipe_list = {}
|
||||
for _, recipe in ipairs(recipes) do
|
||||
local ingredient_items = {}
|
||||
for _, spec in pairs(recipe.items) do
|
||||
local matches_spec = ui.canonical_item_spec_matcher(spec)
|
||||
for _, name in ipairs(ui.items_list) do
|
||||
if matches_spec(name) then
|
||||
ingredient_items[name] = true
|
||||
end
|
||||
-- Get items that fit into this spec (group or item name)
|
||||
local specname = ItemStack(spec):get_name()
|
||||
for item_name, _ in pairs(get_matching_spec_items(specname)) do
|
||||
ingredient_items[item_name] = true
|
||||
end
|
||||
end
|
||||
for name, _ in pairs(ingredient_items) do
|
||||
if ui.crafts_for.usage[name] == nil then
|
||||
if not ui.crafts_for.usage[name] then
|
||||
ui.crafts_for.usage[name] = {}
|
||||
end
|
||||
table.insert(ui.crafts_for.usage[name], recipe)
|
||||
end
|
||||
|
||||
if next(ingredient_items) then
|
||||
-- There's at least one known ingredient: mark as good recipe
|
||||
-- PS: What whatll be done about partially incomplete recipes?
|
||||
table.insert(new_recipe_list, recipe)
|
||||
end
|
||||
end
|
||||
ui.crafts_for.recipe[outputitemname] = new_recipe_list
|
||||
end
|
||||
|
||||
for _, callback in ipairs(ui.initialized_callbacks) do
|
||||
@ -158,8 +187,8 @@ minetest.after(0.01, function()
|
||||
end
|
||||
end)
|
||||
|
||||
---------------- Home API ----------------
|
||||
|
||||
-- load_home
|
||||
local function load_home()
|
||||
local input = io.open(ui.home_filename, "r")
|
||||
if not input then
|
||||
@ -176,6 +205,7 @@ local function load_home()
|
||||
end
|
||||
io.close(input)
|
||||
end
|
||||
|
||||
load_home()
|
||||
|
||||
function ui.set_home(player, pos)
|
||||
@ -204,7 +234,8 @@ function ui.go_home(player)
|
||||
return false
|
||||
end
|
||||
|
||||
-- register_craft
|
||||
---------------- Crafting API ----------------
|
||||
|
||||
function ui.register_craft(options)
|
||||
if not options.output then
|
||||
return
|
||||
@ -227,14 +258,12 @@ function ui.register_craft(options)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local craft_type_defaults = {
|
||||
width = 3,
|
||||
height = 3,
|
||||
uses_crafting_grid = false,
|
||||
}
|
||||
|
||||
|
||||
function ui.craft_type_defaults(name, options)
|
||||
if not options.description then
|
||||
options.description = name
|
||||
@ -245,8 +274,7 @@ end
|
||||
|
||||
|
||||
function ui.register_craft_type(name, options)
|
||||
ui.registered_craft_types[name] =
|
||||
ui.craft_type_defaults(name, options)
|
||||
ui.registered_craft_types[name] = ui.craft_type_defaults(name, options)
|
||||
end
|
||||
|
||||
|
||||
@ -303,6 +331,8 @@ ui.register_craft_type("digging_chance", {
|
||||
height = 1,
|
||||
})
|
||||
|
||||
---------------- GUI registrations ----------------
|
||||
|
||||
function ui.register_page(name, def)
|
||||
ui.pages[name] = def
|
||||
end
|
||||
@ -318,6 +348,8 @@ function ui.register_button(name, def)
|
||||
table.insert(ui.buttons, def)
|
||||
end
|
||||
|
||||
---------------- Callback registrations ----------------
|
||||
|
||||
function ui.register_on_initialized(callback)
|
||||
if type(callback) ~= "function" then
|
||||
error(("Initialized callback must be a function, %s given."):format(type(callback)))
|
||||
@ -332,6 +364,8 @@ function ui.register_on_craft_registered(callback)
|
||||
table.insert(ui.craft_registered_callbacks, callback)
|
||||
end
|
||||
|
||||
---------------- List getters ----------------
|
||||
|
||||
function ui.get_recipe_list(output)
|
||||
return ui.crafts_for.recipe[output]
|
||||
end
|
||||
@ -344,11 +378,15 @@ function ui.get_registered_outputs()
|
||||
return outputs
|
||||
end
|
||||
|
||||
---------------- Player utilities ----------------
|
||||
|
||||
function ui.is_creative(playername)
|
||||
return minetest.check_player_privs(playername, {creative=true})
|
||||
or minetest.settings:get_bool("creative_mode")
|
||||
end
|
||||
|
||||
---------------- Formspec helpers ----------------
|
||||
|
||||
function ui.single_slot(xpos, ypos, bright)
|
||||
return string.format("background9[%f,%f;%f,%f;ui_single_slot%s.png;false;16]",
|
||||
xpos, ypos, ui.imgscale, ui.imgscale, (bright and "_bright" or "") )
|
||||
|
71
bags.lua
71
bags.lua
@ -114,6 +114,8 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
end
|
||||
end)
|
||||
|
||||
-- Player slots are preserved when unified_inventory is disabled. Do not allow modification.
|
||||
-- Fix: use a detached inventory and store the data separately.
|
||||
local function save_bags_metadata(player, bags_inv)
|
||||
local is_empty = true
|
||||
local bags = {}
|
||||
@ -163,7 +165,7 @@ local function load_bags_metadata(player, bags_inv)
|
||||
save_bags_metadata(player, bags_inv)
|
||||
end
|
||||
|
||||
-- Clean up deprecated garbage after saving
|
||||
-- Legacy: Clean up old player lists
|
||||
for i = 1, 4 do
|
||||
local bag = "bag" .. i
|
||||
player_inv:set_size(bag, 0)
|
||||
@ -172,46 +174,29 @@ end
|
||||
|
||||
minetest.register_on_joinplayer(function(player)
|
||||
local player_name = player:get_player_name()
|
||||
local bags_inv = minetest.create_detached_inventory(player_name .. "_bags",{
|
||||
local bags_inv = minetest.create_detached_inventory(player_name .. "_bags", {
|
||||
allow_put = function(inv, listname, index, stack, player)
|
||||
local new_slots = stack:get_definition().groups.bagslots
|
||||
if not new_slots then
|
||||
return 0 -- ItemStack is not a bag.
|
||||
end
|
||||
|
||||
-- The execution order of `allow_put`/`allow_take` is not defined.
|
||||
-- We do not know the replacement ItemStack if the items are swapped.
|
||||
-- Hence, bag slot upgrades and downgrades are not possible with the
|
||||
-- current API.
|
||||
|
||||
if not player:get_inventory():is_empty(listname .. "contents") then
|
||||
-- Legacy: in case `allow_take` is not executed on old Minetest versions.
|
||||
return 0
|
||||
end
|
||||
return 1
|
||||
end,
|
||||
on_put = function(inv, listname, index, stack, player)
|
||||
player:get_inventory():set_size(listname .. "contents",
|
||||
stack:get_definition().groups.bagslots)
|
||||
save_bags_metadata(player, inv)
|
||||
end,
|
||||
allow_put = function(inv, listname, index, stack, player)
|
||||
local new_slots = stack:get_definition().groups.bagslots
|
||||
if not new_slots then
|
||||
return 0
|
||||
end
|
||||
local player_inv = player:get_inventory()
|
||||
local old_slots = player_inv:get_size(listname .. "contents")
|
||||
|
||||
if new_slots >= old_slots then
|
||||
return 1
|
||||
end
|
||||
|
||||
-- using a smaller bag, make sure it fits
|
||||
local old_list = player_inv:get_list(listname .. "contents")
|
||||
local new_list = {}
|
||||
local slots_used = 0
|
||||
local use_new_list = false
|
||||
|
||||
for i, v in ipairs(old_list) do
|
||||
if v and not v:is_empty() then
|
||||
slots_used = slots_used + 1
|
||||
use_new_list = i > new_slots
|
||||
new_list[slots_used] = v
|
||||
end
|
||||
end
|
||||
if new_slots >= slots_used then
|
||||
if use_new_list then
|
||||
player_inv:set_list(listname .. "contents", new_list)
|
||||
end
|
||||
return 1
|
||||
end
|
||||
-- New bag is smaller: Disallow inserting
|
||||
return 0
|
||||
end,
|
||||
allow_take = function(inv, listname, index, stack, player)
|
||||
if player:get_inventory():is_empty(listname .. "contents") then
|
||||
return stack:get_count()
|
||||
@ -230,6 +215,20 @@ minetest.register_on_joinplayer(function(player)
|
||||
load_bags_metadata(player, bags_inv)
|
||||
end)
|
||||
|
||||
|
||||
minetest.register_allow_player_inventory_action(function(player, action, inventory, info)
|
||||
-- From detached inventory -> player inventory: put & take callbacks
|
||||
if action ~= "put" or not info.listname:find("bag%dcontents") then
|
||||
return
|
||||
end
|
||||
if info.stack:get_definition().groups.bagslots then
|
||||
-- Problem 1: empty bags could be moved into their own slots
|
||||
-- Problem 2: cannot reliably keep track of ItemStack ownership due to
|
||||
--> Disallow all external bag movements into this list
|
||||
return 0
|
||||
end
|
||||
end)
|
||||
|
||||
-- register bag tools
|
||||
minetest.register_tool("unified_inventory:bag_small", {
|
||||
description = S("Small Bag"),
|
||||
|
@ -1,3 +1,5 @@
|
||||
local ui = unified_inventory
|
||||
|
||||
local function default_refill(stack)
|
||||
stack:set_count(stack:get_stack_max())
|
||||
local itemdef = minetest.registered_items[stack:get_name()]
|
||||
@ -12,19 +14,17 @@ end
|
||||
minetest.register_on_joinplayer(function(player)
|
||||
local player_name = player:get_player_name()
|
||||
unified_inventory.players[player_name] = {}
|
||||
unified_inventory.current_index[player_name] = 1
|
||||
unified_inventory.current_index[player_name] = 1 -- Item (~page) index
|
||||
unified_inventory.filtered_items_list[player_name] =
|
||||
unified_inventory.items_list
|
||||
unified_inventory.items_list
|
||||
unified_inventory.activefilter[player_name] = ""
|
||||
unified_inventory.active_search_direction[player_name] = "nochange"
|
||||
unified_inventory.apply_filter(player, "", "nochange")
|
||||
unified_inventory.current_searchbox[player_name] = ""
|
||||
unified_inventory.current_category[player_name] = "all"
|
||||
unified_inventory.current_category_scroll[player_name] = 0
|
||||
unified_inventory.alternate[player_name] = 1
|
||||
unified_inventory.current_item[player_name] = nil
|
||||
unified_inventory.current_craft_direction[player_name] = "recipe"
|
||||
unified_inventory.set_inventory_formspec(player, unified_inventory.default)
|
||||
|
||||
-- Refill slot
|
||||
local refill = minetest.create_detached_inventory(player_name.."refill", {
|
||||
@ -46,13 +46,21 @@ minetest.register_on_joinplayer(function(player)
|
||||
refill:set_size("main", 1)
|
||||
end)
|
||||
|
||||
minetest.register_on_mods_loaded(function()
|
||||
minetest.register_on_joinplayer(function(player)
|
||||
-- After everything is initialized, set up the formspec
|
||||
ui.apply_filter(player, "", "nochange")
|
||||
ui.set_inventory_formspec(player, unified_inventory.default)
|
||||
end)
|
||||
end)
|
||||
|
||||
local function apply_new_filter(player, search_text, new_dir)
|
||||
local player_name = player:get_player_name()
|
||||
|
||||
minetest.sound_play("click", {to_player=player_name, gain = 0.1})
|
||||
unified_inventory.apply_filter(player, search_text, new_dir)
|
||||
unified_inventory.current_searchbox[player_name] = search_text
|
||||
unified_inventory.set_inventory_formspec(player,
|
||||
unified_inventory.current_page[player_name])
|
||||
ui.apply_filter(player, search_text, new_dir)
|
||||
ui.current_searchbox[player_name] = search_text
|
||||
ui.set_inventory_formspec(player, ui.current_page[player_name])
|
||||
end
|
||||
|
||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
@ -65,9 +73,12 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
end
|
||||
|
||||
-- always take new search text, even if not searching on it yet
|
||||
local dirty_search_filter = false
|
||||
|
||||
if fields.searchbox
|
||||
and fields.searchbox ~= unified_inventory.current_searchbox[player_name] then
|
||||
unified_inventory.current_searchbox[player_name] = fields.searchbox
|
||||
dirty_search_filter = true
|
||||
end
|
||||
|
||||
|
||||
@ -88,19 +99,14 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
unified_inventory.current_page[player_name])
|
||||
end
|
||||
|
||||
if fields.next_category then
|
||||
local scroll = math.min(#unified_inventory.category_list-ui_peruser.pagecols, unified_inventory.current_category_scroll[player_name] + 1)
|
||||
if scroll ~= unified_inventory.current_category_scroll[player_name] then
|
||||
unified_inventory.current_category_scroll[player_name] = scroll
|
||||
unified_inventory.set_inventory_formspec(player,
|
||||
unified_inventory.current_page[player_name])
|
||||
end
|
||||
end
|
||||
if fields.prev_category then
|
||||
local scroll = math.max(0, unified_inventory.current_category_scroll[player_name] - 1)
|
||||
if scroll ~= unified_inventory.current_category_scroll[player_name] then
|
||||
unified_inventory.current_category_scroll[player_name] = scroll
|
||||
unified_inventory.set_inventory_formspec(player,
|
||||
if fields.next_category or fields.prev_category then
|
||||
local step = fields.next_category and 1 or -1
|
||||
local scroll_old = ui.current_category_scroll[player_name]
|
||||
local scroll_new = math.max(0, math.min(#ui.category_list - ui_peruser.pagecols, scroll_old + step))
|
||||
|
||||
if scroll_old ~= scroll_new then
|
||||
ui.current_category_scroll[player_name] = scroll_new
|
||||
ui.set_inventory_formspec(player,
|
||||
unified_inventory.current_page[player_name])
|
||||
end
|
||||
end
|
||||
@ -197,13 +203,16 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
|
||||
if fields.searchbutton
|
||||
or fields.key_enter_field == "searchbox" then
|
||||
unified_inventory.apply_filter(player, unified_inventory.current_searchbox[player_name], "nochange")
|
||||
unified_inventory.set_inventory_formspec(player,
|
||||
unified_inventory.current_page[player_name])
|
||||
minetest.sound_play("paperflip2",
|
||||
{to_player=player_name, gain = 1.0})
|
||||
if dirty_search_filter then
|
||||
ui.apply_filter(player, ui.current_searchbox[player_name], "nochange")
|
||||
ui.set_inventory_formspec(player, ui.current_page[player_name])
|
||||
minetest.sound_play("paperflip2",
|
||||
{to_player=player_name, gain = 1.0})
|
||||
end
|
||||
elseif fields.searchresetbutton then
|
||||
apply_new_filter(player, "", "nochange")
|
||||
if ui.activefilter[player_name] ~= "" then
|
||||
apply_new_filter(player, "", "nochange")
|
||||
end
|
||||
end
|
||||
|
||||
-- alternate buttons
|
||||
@ -241,11 +250,8 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
unified_inventory.current_page[player_name])
|
||||
end)
|
||||
|
||||
if minetest.delete_detached_inventory then
|
||||
minetest.register_on_leaveplayer(function(player)
|
||||
local player_name = player:get_player_name()
|
||||
minetest.delete_detached_inventory(player_name.."_bags")
|
||||
minetest.delete_detached_inventory(player_name.."craftrecipe")
|
||||
minetest.delete_detached_inventory(player_name.."refill")
|
||||
end)
|
||||
end
|
||||
minetest.register_on_leaveplayer(function(player)
|
||||
local player_name = player:get_player_name()
|
||||
minetest.remove_detached_inventory(player_name.."_bags")
|
||||
minetest.remove_detached_inventory(player_name.."refill")
|
||||
end)
|
||||
|
@ -96,6 +96,9 @@ function unified_inventory.register_category(category_name, config)
|
||||
end
|
||||
update_category_list()
|
||||
end
|
||||
|
||||
-- TODO: Mark these for removal. They are pretty much useless
|
||||
|
||||
function unified_inventory.set_category_symbol(category_name, symbol)
|
||||
ensure_category_exists(category_name)
|
||||
unified_inventory.registered_categories[category_name].symbol = symbol
|
||||
|
@ -1,7 +1,8 @@
|
||||
unified_inventory API
|
||||
=====================
|
||||
|
||||
This file provides information about the API of unified_inventory.
|
||||
This file provides information about the API of unified_inventory
|
||||
and can be viewed in Markdown readers.
|
||||
|
||||
API revisions within unified_inventory can be checked using:
|
||||
|
||||
@ -23,7 +24,9 @@ Grouped by use-case, afterwards sorted alphabetically.
|
||||
Callbacks
|
||||
---------
|
||||
|
||||
Register a callback that will be run whenever a craft is registered via unified_inventory.register_craft:
|
||||
Register a callback that will be run whenever a craft is registered via unified_inventory.register_craft.
|
||||
This callback is run before any recipe ingredients checks, hence it is also executed on recipes that are
|
||||
purged after all mods finished loading.
|
||||
|
||||
unified_inventory.register_on_craft_registered(
|
||||
function (item_name, options)
|
||||
@ -163,68 +166,57 @@ Register a non-standard craft recipe:
|
||||
Categories
|
||||
----------
|
||||
|
||||
Register a new category:
|
||||
The config table (second argument) is optional, and all its members are optional
|
||||
See the unified_inventory.set_category_* functions for more details on the members of the config table
|
||||
* `unified_inventory.register_category(name, def)`
|
||||
* Registers a new category
|
||||
* `name` (string): internal category name
|
||||
* `def` (optional, table): also its fields are optional
|
||||
|
||||
unified_inventory.register_category("category_name", {
|
||||
symbol = "mod_name:item_name" or "texture.png",
|
||||
symbol = source,
|
||||
-- ^ Can be in the format "mod_name:item_name" or "texture.png",
|
||||
label = "Human Readable Label",
|
||||
index = 5,
|
||||
-- ^ Categories are sorted by index. Lower numbers appear before higher ones.
|
||||
-- By default, the name is translated to a number: AA -> 0.0101, ZZ -> 0.2626
|
||||
--- Predefined category indices: "all" = -2, "uncategorized" = -1
|
||||
items = {
|
||||
"mod_name:item_name",
|
||||
"another_mod:different_item"
|
||||
}
|
||||
-- ^ List of items within this category
|
||||
})
|
||||
* `unified_inventory.remove_category(name)`
|
||||
* Removes an entire category
|
||||
|
||||
Add / override the symbol for a category:
|
||||
The category does not need to exist first
|
||||
The symbol can be an item name or a texture image
|
||||
If unset this will default to "default:stick"
|
||||
Modifier functions (to be removed)
|
||||
|
||||
unified_inventory.set_category_symbol("category_name", "mod_name:item_name" or "texture.png")
|
||||
* `unified_inventory.set_category_symbol(name, source)`
|
||||
* Changes the symbol of the category. The category does not need to exist yet.
|
||||
* `name` (string): internal category name
|
||||
* `source` (string, optional): `"mod_name:item_name"` or `"texture.png"`.
|
||||
Defaults to `"default:stick"` if not specified.
|
||||
* `unified_inventory.set_category_label(name, label)`
|
||||
* Changes the human readable label of the category.
|
||||
* `name` (string): internal category name
|
||||
* `label` (string): human readable label. Defaults to the category name.
|
||||
* `unified_inventory.set_category_index(name, index)`
|
||||
* Changes the sorting index of the category.
|
||||
* `name` (string): internal category name
|
||||
* `index` (numeric): any real number
|
||||
|
||||
Add / override the human readable label for a category:
|
||||
If unset this will default to the category name
|
||||
Item management
|
||||
|
||||
unified_inventory.set_category_label("category_name", "Human Readable Label")
|
||||
* ` unified_inventory.add_category_item(name, itemname)`
|
||||
* Adds a single item to the category
|
||||
* `itemname` (string): self-explanatory
|
||||
* `unified_inventory.add_category_items(name, { itemname1, itemname2, ... }`
|
||||
* Same as above but with multiple items
|
||||
* `unified_inventory.remove_category_item(name, itemname)`
|
||||
* Removes an item from the category
|
||||
* `unified_inventory.find_category(itemname)`
|
||||
* Looks up the first category containing this item
|
||||
* Returns: category name (string) or nil
|
||||
* `unified_inventory.find_categories(itemname)`
|
||||
* Looks up the item name within all registered categories
|
||||
* Returns: array of category names (table)
|
||||
|
||||
Add / override the sorting index of the category:
|
||||
Must be a number, can also be negative (-5) or fractional (2.345)
|
||||
This determines the position the category appears in the list of categories
|
||||
The "all" meta-category has index -2, the "misc"/"uncategorized" meta-category has index -1, use a negative number smaller than these to make a category appear before these in the list
|
||||
By default categories are sorted alphabetically with an index between 0.0101(AA) and 0.2626(ZZ)
|
||||
|
||||
unified_inventory.set_category_index("category_name", 5)
|
||||
|
||||
Add a single item to a category:
|
||||
|
||||
unified_inventory.add_category_item("category_name", "mod_name:item_name")
|
||||
|
||||
Add multiple items to a category:
|
||||
|
||||
unified_inventory.add_category_items("category_name", {
|
||||
"mod_name:item_name",
|
||||
"another_mod:different_item"
|
||||
})
|
||||
|
||||
Remove an item from a category:
|
||||
|
||||
unified_inventory.remove_category_item("category_name", "mod_name:item_name")
|
||||
|
||||
Remove a category entirely:
|
||||
|
||||
unified_inventory.remove_category("category_name")
|
||||
|
||||
Finding existing items in categories:
|
||||
This will find the first category an item exists in
|
||||
It should be used for checking if an item is catgorised
|
||||
Returns "category_name" or nil
|
||||
|
||||
unified_inventory.find_category("mod_name:item_name")
|
||||
|
||||
|
||||
This will find all the categories an item exists in
|
||||
Returns a number indexed table (list) of category names
|
||||
|
||||
unified_inventory.find_categories("mod_name:item_name")
|
||||
|
101
group.lua
101
group.lua
@ -1,29 +1,5 @@
|
||||
local S = minetest.get_translator("unified_inventory")
|
||||
|
||||
function unified_inventory.canonical_item_spec_matcher(spec)
|
||||
local specname = ItemStack(spec):get_name()
|
||||
if specname:sub(1, 6) ~= "group:" then
|
||||
return function (itemname)
|
||||
return itemname == specname
|
||||
end
|
||||
end
|
||||
|
||||
local group_names = specname:sub(7):split(",")
|
||||
return function (itemname)
|
||||
local itemdef = minetest.registered_items[itemname]
|
||||
for _, group_name in ipairs(group_names) do
|
||||
if (itemdef.groups[group_name] or 0) == 0 then
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
function unified_inventory.item_matches_spec(item, spec)
|
||||
local itemname = ItemStack(item):get_name()
|
||||
return unified_inventory.canonical_item_spec_matcher(spec)(itemname)
|
||||
end
|
||||
local ui = unified_inventory
|
||||
|
||||
function unified_inventory.extract_groupnames(groupname)
|
||||
local specname = ItemStack(groupname):get_name()
|
||||
@ -34,22 +10,6 @@ function unified_inventory.extract_groupnames(groupname)
|
||||
return table.concat(group_names, S(" and ")), #group_names
|
||||
end
|
||||
|
||||
unified_inventory.registered_group_items = {
|
||||
mesecon_conductor_craftable = "mesecons:wire_00000000_off",
|
||||
stone = "default:cobble",
|
||||
wood = "default:wood",
|
||||
book = "default:book",
|
||||
sand = "default:sand",
|
||||
leaves = "default:leaves",
|
||||
tree = "default:tree",
|
||||
vessel = "vessels:glass_bottle",
|
||||
wool = "wool:white",
|
||||
}
|
||||
|
||||
function unified_inventory.register_group_item(groupname, itemname)
|
||||
unified_inventory.registered_group_items[groupname] = itemname
|
||||
end
|
||||
|
||||
|
||||
-- This is used when displaying craft recipes, where an ingredient is
|
||||
-- specified by group rather than as a specific item. A single-item group
|
||||
@ -67,6 +27,7 @@ end
|
||||
-- It may be a comma-separated list of group names. This is really a
|
||||
-- "group:..." ingredient specification, minus the "group:" prefix.
|
||||
|
||||
-- TODO Replace this with the more efficient spec matcher (below)
|
||||
local function compute_group_item(group_name_list)
|
||||
local group_names = group_name_list:split(",")
|
||||
local candidate_items = {}
|
||||
@ -125,3 +86,61 @@ function unified_inventory.get_group_item(group_name)
|
||||
return group_item_cache[group_name]
|
||||
end
|
||||
|
||||
|
||||
--[[
|
||||
This is for filtering known items by groups
|
||||
e.g. find all items that match "group:flower,yellow" (flower AND yellow groups)
|
||||
]]
|
||||
local spec_matcher = {}
|
||||
function unified_inventory.init_matching_cache()
|
||||
for _, name in ipairs(ui.items_list) do
|
||||
-- we only need to care about groups, exact items are handled separately
|
||||
for group, value in pairs(minetest.registered_items[name].groups) do
|
||||
if value and value ~= 0 then
|
||||
if not spec_matcher[group] then
|
||||
spec_matcher[group] = {}
|
||||
end
|
||||
spec_matcher[group][name] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
Retrieves all matching items
|
||||
|
||||
Arguments:
|
||||
specname (string): Item name or group(s) to filter
|
||||
|
||||
Output:
|
||||
{
|
||||
matchingitem1 = true,
|
||||
...
|
||||
}
|
||||
]]
|
||||
function unified_inventory.get_matching_items(specname)
|
||||
if specname:sub(1,6) ~= "group:" then
|
||||
return { [specname] = true }
|
||||
end
|
||||
|
||||
local accepted = {}
|
||||
for i, group in ipairs(specname:sub(7):split(",")) do
|
||||
if i == 1 then
|
||||
-- First step: Copy all possible item names in this group
|
||||
for name, _ in pairs(spec_matcher[group] or {}) do
|
||||
accepted[name] = true
|
||||
end
|
||||
else
|
||||
-- Perform filtering
|
||||
if spec_matcher[group] then
|
||||
for name, _ in pairs(accepted) do
|
||||
accepted[name] = spec_matcher[group][name]
|
||||
end
|
||||
else
|
||||
-- No matching items
|
||||
return {}
|
||||
end
|
||||
end
|
||||
end
|
||||
return accepted
|
||||
end
|
||||
|
41
init.lua
41
init.lua
@ -1,5 +1,11 @@
|
||||
-- Unified Inventory
|
||||
|
||||
if not minetest.features.formspec_version_element then
|
||||
-- At least formspec_version[] is the minimal feature requirement
|
||||
error("Unified Inventory requires Minetest version 5.4.0 or newer.\n" ..
|
||||
" Please update Minetest or use an older version of Unified Inventory.")
|
||||
end
|
||||
|
||||
local modpath = minetest.get_modpath(minetest.get_current_modname())
|
||||
local worldpath = minetest.get_worldpath()
|
||||
|
||||
@ -44,9 +50,11 @@ unified_inventory = {
|
||||
trash_enabled = (minetest.settings:get_bool("unified_inventory_trash") ~= false),
|
||||
imgscale = 1.25,
|
||||
list_img_offset = 0.13,
|
||||
standard_background = "background9[0,0;1,1;ui_formbg_9_sliced.png;true;16]",
|
||||
standard_background = "bgcolor[#0000]background9[0,0;1,1;ui_formbg_9_sliced.png;true;16]",
|
||||
|
||||
version = 3
|
||||
hide_disabled_buttons = minetest.settings:get_bool("unified_inventory_hide_disabled_buttons", false),
|
||||
|
||||
version = 4
|
||||
}
|
||||
|
||||
local ui = unified_inventory
|
||||
@ -59,10 +67,16 @@ ui.style_full = {
|
||||
formspec_y = 1,
|
||||
formw = 17.75,
|
||||
formh = 12.25,
|
||||
-- Item browser size, pos
|
||||
pagecols = 8,
|
||||
pagerows = 9,
|
||||
page_x = 10.75,
|
||||
page_y = 2.30,
|
||||
-- Item browser controls
|
||||
page_buttons_x = 11.60,
|
||||
page_buttons_y = 10.15,
|
||||
searchwidth = 3.4,
|
||||
-- Crafting grid positions
|
||||
craft_x = 2.8,
|
||||
craft_y = 1.15,
|
||||
craftresult_x = 7.8,
|
||||
@ -74,13 +88,15 @@ ui.style_full = {
|
||||
craft_guide_resultstr_x = 0.3,
|
||||
craft_guide_resultstr_y = 0.6,
|
||||
give_btn_x = 0.25,
|
||||
-- Tab switching buttons
|
||||
main_button_x = 0.4,
|
||||
main_button_y = 11.0,
|
||||
page_buttons_x = 11.60,
|
||||
page_buttons_y = 10.15,
|
||||
searchwidth = 3.4,
|
||||
main_button_cols = 12,
|
||||
main_button_rows = 1,
|
||||
-- Tab title position
|
||||
form_header_x = 0.4,
|
||||
form_header_y = 0.4,
|
||||
-- Generic sizes
|
||||
btn_spc = 0.85,
|
||||
btn_size = 0.75,
|
||||
std_inv_x = 0.3,
|
||||
@ -92,10 +108,16 @@ ui.style_lite = {
|
||||
formspec_y = 0.6,
|
||||
formw = 14,
|
||||
formh = 9.75,
|
||||
-- Item browser size, pos
|
||||
pagecols = 4,
|
||||
pagerows = 5,
|
||||
page_x = 10.5,
|
||||
page_y = 2.15,
|
||||
-- Item browser controls
|
||||
page_buttons_x = 10.5,
|
||||
page_buttons_y = 6.15,
|
||||
searchwidth = 1.6,
|
||||
-- Crafting grid positions
|
||||
craft_x = 2.6,
|
||||
craft_y = 0.75,
|
||||
craftresult_x = 5.75,
|
||||
@ -107,13 +129,15 @@ ui.style_lite = {
|
||||
craft_guide_resultstr_x = 0.15,
|
||||
craft_guide_resultstr_y = 0.35,
|
||||
give_btn_x = 0.15,
|
||||
-- Tab switching buttons
|
||||
main_button_x = 10.5,
|
||||
main_button_y = 8.15,
|
||||
page_buttons_x = 10.5,
|
||||
page_buttons_y = 6.15,
|
||||
searchwidth = 1.6,
|
||||
main_button_cols = 4,
|
||||
main_button_rows = 2,
|
||||
-- Tab title position
|
||||
form_header_x = 0.2,
|
||||
form_header_y = 0.2,
|
||||
-- Generic sizes
|
||||
btn_spc = 0.8,
|
||||
btn_size = 0.7,
|
||||
std_inv_x = 0.1,
|
||||
@ -170,3 +194,4 @@ end
|
||||
|
||||
dofile(modpath.."/item_names.lua")
|
||||
dofile(modpath.."/waypoints.lua")
|
||||
dofile(modpath.."/legacy.lua") -- mod compatibility
|
||||
|
115
internal.lua
115
internal.lua
@ -18,7 +18,7 @@ function ui.demangle_for_formspec(str)
|
||||
return string.gsub(str, "_([0-9]+)_", function (v) return string.char(v) end)
|
||||
end
|
||||
|
||||
|
||||
-- Get the player-specific unified_inventory style
|
||||
function ui.get_per_player_formspec(player_name)
|
||||
local draw_lite_mode = ui.lite_mode and not minetest.check_player_privs(player_name, {ui_full=true})
|
||||
|
||||
@ -27,6 +27,7 @@ function ui.get_per_player_formspec(player_name)
|
||||
return style
|
||||
end
|
||||
|
||||
-- Creates an item image or regular image button with a tooltip
|
||||
local function formspec_button(ui_peruser, name, image, offset, pos, scale, label)
|
||||
local element = 'image_button'
|
||||
if minetest.registered_items[image] then
|
||||
@ -43,47 +44,68 @@ local function formspec_button(ui_peruser, name, image, offset, pos, scale, labe
|
||||
string.format("tooltip[%s;%s]", name, F(label or name))
|
||||
end
|
||||
|
||||
local function formspec_add_filters(player, formspec, style)
|
||||
local button_row = 0
|
||||
local button_col = 0
|
||||
-- Add registered buttons (tabs)
|
||||
local function formspec_tab_buttons(player, formspec, style)
|
||||
local n = #formspec + 1
|
||||
|
||||
-- Main buttons
|
||||
|
||||
local filtered_inv_buttons = {}
|
||||
|
||||
for i, def in pairs(ui.buttons) do
|
||||
for _, def in pairs(ui.buttons) do
|
||||
if not (style.is_lite_mode and def.hide_lite) then
|
||||
table.insert(filtered_inv_buttons, def)
|
||||
if def.condition == nil or def.condition(player) or not ui.hide_disabled_buttons then
|
||||
table.insert(filtered_inv_buttons, def)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local needs_scrollbar = #filtered_inv_buttons > style.main_button_cols * style.main_button_rows
|
||||
|
||||
formspec[n] = ("scroll_container[%g,%g;%g,%g;tabbtnscroll;vertical]"):format(
|
||||
style.main_button_x, style.main_button_y, -- position
|
||||
style.main_button_cols * style.btn_spc, style.main_button_rows -- size
|
||||
)
|
||||
n = n + 1
|
||||
|
||||
for i, def in pairs(filtered_inv_buttons) do
|
||||
if style.is_lite_mode and i > 4 then
|
||||
button_row = 1
|
||||
button_col = 1
|
||||
end
|
||||
local pos_x = ((i - 1) % style.main_button_cols) * style.btn_spc
|
||||
local pos_y = math.floor((i - 1) / style.main_button_cols) * style.btn_spc
|
||||
|
||||
if def.type == "image" then
|
||||
local pos_x = style.main_button_x + style.btn_spc * (i - 1) - button_col * style.btn_spc * 4
|
||||
local pos_y = style.main_button_y + button_row * style.btn_spc
|
||||
if (def.condition == nil or def.condition(player) == true) then
|
||||
formspec[n] = string.format("image_button[%f,%f;%f,%f;%s;%s;]",
|
||||
if (def.condition == nil or def.condition(player)) then
|
||||
formspec[n] = string.format("image_button[%g,%g;%g,%g;%s;%s;]",
|
||||
pos_x, pos_y, style.btn_size, style.btn_size,
|
||||
F(def.image),
|
||||
F(def.name))
|
||||
formspec[n+1] = "tooltip["..F(def.name)..";"..(def.tooltip or "").."]"
|
||||
n = n+2
|
||||
|
||||
else
|
||||
formspec[n] = string.format("image[%f,%f;%f,%f;%s^[colorize:#808080:alpha]",
|
||||
formspec[n] = string.format("image[%g,%g;%g,%g;%s^[colorize:#808080:alpha]",
|
||||
pos_x, pos_y, style.btn_size, style.btn_size,
|
||||
def.image)
|
||||
n = n+1
|
||||
end
|
||||
end
|
||||
end
|
||||
formspec[n] = "scroll_container_end[]"
|
||||
if needs_scrollbar then
|
||||
local total_rows = math.ceil(#filtered_inv_buttons / style.main_button_cols)
|
||||
formspec[n+1] = ("scrollbaroptions[max=%i;arrows=hide]"):format(
|
||||
-- This calculation is not 100% accurate but "good enough"
|
||||
(total_rows - style.main_button_rows) * style.btn_spc * 10
|
||||
)
|
||||
formspec[n+2] = ("scrollbar[%g,%g;0.4,%g;vertical;tabbtnscroll;0]"):format(
|
||||
style.main_button_x + style.main_button_cols * style.btn_spc - 0.1, -- x pos
|
||||
style.main_button_y, -- y pos
|
||||
style.main_button_rows * style.btn_spc -- height
|
||||
)
|
||||
formspec[n+3] = "scrollbaroptions[max=1000;arrows=default]"
|
||||
end
|
||||
end
|
||||
|
||||
-- Add category GUI elements (top right)
|
||||
local function formspec_add_categories(player, formspec, ui_peruser)
|
||||
local player_name = player:get_player_name()
|
||||
local n = #formspec + 1
|
||||
@ -97,9 +119,9 @@ local function formspec_add_categories(player, formspec, ui_peruser)
|
||||
ui_peruser.form_header_y - (ui_peruser.is_lite_mode and 0 or 0.2)
|
||||
}
|
||||
|
||||
formspec[n] = string.format("background9[%f,%f;%f,%f;%s;false;3]",
|
||||
ui_peruser.page_x-0.1, categories_scroll_pos[2],
|
||||
(ui_peruser.btn_spc * ui_peruser.pagecols) + 0.13, 1.4 + (ui_peruser.is_lite_mode and 0 or 0.2),
|
||||
formspec[n] = string.format("background9[%f,%f;%f,%f;%s;false;16]",
|
||||
ui_peruser.page_x-0.15, categories_scroll_pos[2],
|
||||
(ui_peruser.btn_spc * ui_peruser.pagecols) + 0.2, 1.4 + (ui_peruser.is_lite_mode and 0 or 0.2),
|
||||
"ui_smallbg_9_sliced.png")
|
||||
n = n + 1
|
||||
|
||||
@ -238,8 +260,14 @@ local function formspec_add_item_browser(player, formspec, ui_peruser)
|
||||
ui_peruser.btn_size, ui_peruser.btn_size,
|
||||
name, button_name
|
||||
)
|
||||
local tooltip = item.description
|
||||
if item.mod_origin then
|
||||
-- "mod_origin" may not be specified for items that were
|
||||
-- registered in a callback (during or before ServerEnv init)
|
||||
tooltip = tooltip .. " [" .. item.mod_origin .. "]"
|
||||
end
|
||||
formspec[n + 1] = ("tooltip[%s;%s]"):format(
|
||||
button_name, minetest.formspec_escape(item.description)
|
||||
button_name, minetest.formspec_escape(tooltip)
|
||||
)
|
||||
n = n + 2
|
||||
list_index = list_index + 1
|
||||
@ -280,7 +308,7 @@ function ui.get_formspec(player, page)
|
||||
|
||||
fs[#fs + 1] = fsdata.formspec
|
||||
|
||||
formspec_add_filters(player, fs, ui_peruser)
|
||||
formspec_tab_buttons(player, fs, ui_peruser)
|
||||
|
||||
if fsdata.draw_inventory ~= false then
|
||||
-- Player inventory
|
||||
@ -305,7 +333,7 @@ function ui.set_inventory_formspec(player, page)
|
||||
end
|
||||
end
|
||||
|
||||
local function valid_def(def)
|
||||
function ui.is_itemdef_listable(def)
|
||||
return (not def.groups.not_in_creative_inventory
|
||||
or def.groups.not_in_creative_inventory == 0)
|
||||
and def.description
|
||||
@ -318,9 +346,11 @@ function ui.apply_filter(player, filter, search_dir)
|
||||
return false
|
||||
end
|
||||
local player_name = player:get_player_name()
|
||||
|
||||
local lfilter = string.lower(filter)
|
||||
local ffilter
|
||||
if lfilter:sub(1, 6) == "group:" then
|
||||
-- Group filter: all groups of the item must match
|
||||
local groups = lfilter:sub(7):split(",")
|
||||
ffilter = function(name, def)
|
||||
for _, group in ipairs(groups) do
|
||||
@ -332,7 +362,10 @@ function ui.apply_filter(player, filter, search_dir)
|
||||
return true
|
||||
end
|
||||
else
|
||||
local lang = minetest.get_player_information(player_name).lang_code
|
||||
-- Name filter: fuzzy match item names and descriptions
|
||||
local player_info = minetest.get_player_information(player_name)
|
||||
local lang = player_info and player_info.lang_code or ""
|
||||
|
||||
ffilter = function(name, def)
|
||||
local lname = string.lower(name)
|
||||
local ldesc = string.lower(def.description)
|
||||
@ -342,37 +375,53 @@ function ui.apply_filter(player, filter, search_dir)
|
||||
or llocaldesc and string.find(llocaldesc, lfilter, 1, true)
|
||||
end
|
||||
end
|
||||
ui.filtered_items_list[player_name]={}
|
||||
|
||||
local is_itemdef_listable = ui.is_itemdef_listable
|
||||
local filtered_items = {}
|
||||
|
||||
local category = ui.current_category[player_name] or 'all'
|
||||
if category == 'all' then
|
||||
for name, def in pairs(minetest.registered_items) do
|
||||
if valid_def(def)
|
||||
if is_itemdef_listable(def)
|
||||
and ffilter(name, def) then
|
||||
table.insert(ui.filtered_items_list[player_name], name)
|
||||
table.insert(filtered_items, name)
|
||||
end
|
||||
end
|
||||
elseif category == 'uncategorized' then
|
||||
for name, def in pairs(minetest.registered_items) do
|
||||
if (not ui.find_category(name))
|
||||
and valid_def(def)
|
||||
if is_itemdef_listable(def)
|
||||
and not ui.find_category(name)
|
||||
and ffilter(name, def) then
|
||||
table.insert(ui.filtered_items_list[player_name], name)
|
||||
table.insert(filtered_items, name)
|
||||
end
|
||||
end
|
||||
else
|
||||
for name,exists in pairs(ui.registered_category_items[category]) do
|
||||
-- Any other category is selected
|
||||
for name, exists in pairs(ui.registered_category_items[category]) do
|
||||
local def = minetest.registered_items[name]
|
||||
if exists and def
|
||||
and valid_def(def)
|
||||
and is_itemdef_listable(def)
|
||||
and ffilter(name, def) then
|
||||
table.insert(ui.filtered_items_list[player_name], name)
|
||||
table.insert(filtered_items, name)
|
||||
end
|
||||
end
|
||||
end
|
||||
table.sort(ui.filtered_items_list[player_name])
|
||||
ui.filtered_items_list_size[player_name] = #ui.filtered_items_list[player_name]
|
||||
table.sort(filtered_items)
|
||||
|
||||
ui.filtered_items_list_size[player_name] = #filtered_items
|
||||
ui.filtered_items_list[player_name] = filtered_items
|
||||
ui.current_index[player_name] = 1
|
||||
ui.activefilter[player_name] = filter
|
||||
ui.active_search_direction[player_name] = search_dir
|
||||
ui.set_inventory_formspec(player, ui.current_page[player_name])
|
||||
end
|
||||
|
||||
-- Inform players about potential visual issues
|
||||
minetest.register_on_joinplayer(function(player)
|
||||
local player_name = player:get_player_name()
|
||||
local info = minetest.get_player_information(player_name)
|
||||
if info and (info.formspec_version or 0) < 4 then
|
||||
minetest.chat_send_player(player_name, S("Unified Inventory: Your game version is too old"
|
||||
.. " and does not support the GUI requirements. You might experience visual issues."))
|
||||
end
|
||||
end)
|
||||
|
55
legacy.lua
Normal file
55
legacy.lua
Normal file
@ -0,0 +1,55 @@
|
||||
-- Inefficient pattern matching
|
||||
|
||||
local warned_funcs = {}
|
||||
local function LOG_ONCE(funcname)
|
||||
if warned_funcs[funcname] then return end
|
||||
warned_funcs[funcname] = true
|
||||
minetest.log("error", "Call to undocumented, deprecated API '" .. funcname .. "'."
|
||||
.. " In a future version of Unified Inventory this will result in a real error.")
|
||||
end
|
||||
|
||||
function unified_inventory.canonical_item_spec_matcher(spec)
|
||||
LOG_ONCE("canonical_item_spec_matcher")
|
||||
local specname = ItemStack(spec):get_name()
|
||||
if specname:sub(1, 6) ~= "group:" then
|
||||
return function (itemname)
|
||||
return itemname == specname
|
||||
end
|
||||
end
|
||||
|
||||
local group_names = specname:sub(7):split(",")
|
||||
return function (itemname)
|
||||
local itemdef = minetest.registered_items[itemname]
|
||||
for _, group_name in ipairs(group_names) do
|
||||
if (itemdef.groups[group_name] or 0) == 0 then
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
function unified_inventory.item_matches_spec(item, spec)
|
||||
LOG_ONCE("item_matches_spec")
|
||||
local itemname = ItemStack(item):get_name()
|
||||
return unified_inventory.canonical_item_spec_matcher(spec)(itemname)
|
||||
end
|
||||
|
||||
|
||||
unified_inventory.registered_group_items = {
|
||||
mesecon_conductor_craftable = "mesecons:wire_00000000_off",
|
||||
stone = "default:cobble",
|
||||
wood = "default:wood",
|
||||
book = "default:book",
|
||||
sand = "default:sand",
|
||||
leaves = "default:leaves",
|
||||
tree = "default:tree",
|
||||
vessel = "vessels:glass_bottle",
|
||||
wool = "wool:white",
|
||||
}
|
||||
|
||||
function unified_inventory.register_group_item(groupname, itemname)
|
||||
LOG_ONCE("register_group_item")
|
||||
unified_inventory.registered_group_items[groupname] = itemname
|
||||
end
|
||||
|
@ -7,17 +7,17 @@ Bag @1=Tasche @1
|
||||
Small Bag=Kleine Tasche
|
||||
Medium Bag=Mittelgroße Tasche
|
||||
Large Bag=Große Tasche
|
||||
All Items=
|
||||
Misc. Items=
|
||||
Plant Life=
|
||||
Building Materials=
|
||||
Tools=
|
||||
Minerals and Metals=
|
||||
Environment and Worldgen=
|
||||
Lighting=
|
||||
All Items=Alle Gegenstände
|
||||
Misc. Items=Sonstige Gegenstände
|
||||
Plant Life=Pfanzenwelt
|
||||
Building Materials=Baumaterialien
|
||||
Tools=Werkzeuge
|
||||
Minerals and Metals=Minerale und Metalle
|
||||
Environment and Worldgen=Umwelt und Welterstellung
|
||||
Lighting=Beleuchtung
|
||||
and = und
|
||||
Scroll categories left=
|
||||
Scroll categories right=
|
||||
Scroll categories left=Kategorien nach links blättern
|
||||
Scroll categories right=Kategorien nach rechts blättern
|
||||
Search=Suchen
|
||||
Reset search and display everything=Suche zurücksetzen und alles anzeigen
|
||||
First page=Erste Seite
|
||||
@ -76,10 +76,10 @@ Waypoints=Wegpunkte
|
||||
Select Waypoint #@1=Wegpunkt Nr. @1 auswählen
|
||||
Waypoint @1=Wegpunkt Nr. @1
|
||||
Set waypoint to current location=Setze Wegpunkt zur derzeitigen Position
|
||||
Hide waypoint=
|
||||
Show waypoint=
|
||||
Hide coordinates=
|
||||
Show coordinates=
|
||||
Hide waypoint=Wegpunkt verstecken
|
||||
Show waypoint=Wegpunkt zeigen
|
||||
Hide coordinates=Koordinaten verstecken
|
||||
Show coordinates=Koordinaten zeigen
|
||||
Change color of waypoint display=Farbe der Darstellung der Wegpunkte ändern
|
||||
Edit waypoint name=Name des Wegpunkts ändern
|
||||
Waypoint active=Wegpunkt aktiv
|
||||
|
@ -7,17 +7,17 @@ Bag @1=背包@1
|
||||
Small Bag=小背包
|
||||
Medium Bag=中背包
|
||||
Large Bag=大背包
|
||||
All Items=
|
||||
Misc. Items=
|
||||
Plant Life=
|
||||
Building Materials=
|
||||
Tools=
|
||||
Minerals and Metals=
|
||||
Environment and Worldgen=
|
||||
Lighting=
|
||||
All Items=所有物品
|
||||
Misc. Items=杂项
|
||||
Plant Life=植物
|
||||
Building Materials=建材
|
||||
Tools=工具
|
||||
Minerals and Metals=矿物与金属
|
||||
Environment and Worldgen=自然环境
|
||||
Lighting=光源
|
||||
and = 和
|
||||
Scroll categories left=
|
||||
Scroll categories right=
|
||||
Scroll categories left=向左滚动分类栏
|
||||
Scroll categories right=向右滚动分类栏
|
||||
Search=搜索
|
||||
Reset search and display everything=重置搜索并显示所有物品
|
||||
First page=第一页
|
||||
@ -32,7 +32,7 @@ Page=页面
|
||||
@1 of @2=第@1页,共@2页
|
||||
Filter=过滤器
|
||||
Can use the creative inventory=可以使用创造背包
|
||||
Forces Unified Inventory to be displayed in Full mode if Lite mode is configured globally=
|
||||
Forces Unified Inventory to be displayed in Full mode if Lite mode is configured globally=如果轻量模式被全局配置,强迫Unified Inventory以完全模式展现。
|
||||
Crafting Grid=合成表
|
||||
Crafting Guide=合成指南
|
||||
Set home position=设置家的位置
|
||||
@ -45,7 +45,7 @@ You don't have the settime privilege!=你没有“settime”权限!
|
||||
Set time to night=设置时间到晚上
|
||||
Time of day set to 9pm=时间设置到晚上9点
|
||||
Clear inventory=清空背包
|
||||
This button has been disabled outside of creative mode to prevent accidental inventory trashing.@nUse the trash slot instead.=
|
||||
This button has been disabled outside of creative mode to prevent accidental inventory trashing.@nUse the trash slot instead.=此按钮已在非创造模式中禁用以防止意外的背包清空。@n请使用垃圾桶栏。
|
||||
Inventory cleared!=清空背包
|
||||
Trash:=丢弃:
|
||||
Refill:=填满:
|
||||
@ -57,13 +57,13 @@ No recipes=没有配方
|
||||
No usages=没有用法
|
||||
Result=结果
|
||||
Ingredient=原料
|
||||
Show next recipe=
|
||||
Show next usage=
|
||||
Show previous recipe=
|
||||
Show previous usage=
|
||||
@1 (@2)=
|
||||
Show next recipe=显示下一个配方
|
||||
Show next usage=显示下一个用法
|
||||
Show previous recipe=显示前一个配方
|
||||
Show previous usage=显示前一个用法
|
||||
@1 (@2)=@1 (@2)
|
||||
Give me:=给予:
|
||||
This recipe is too@@large to be displayed.=
|
||||
This recipe is too@@large to be displayed.=该配方太@@大,不能显示。
|
||||
To craft grid:=填充物品到合成表
|
||||
All=全部
|
||||
Crafting=合成
|
||||
@ -76,10 +76,10 @@ Waypoints=航路点
|
||||
Select Waypoint #@1=查询航路点 #@1
|
||||
Waypoint @1=航路点 @1
|
||||
Set waypoint to current location=将航路点设置到当前位置
|
||||
Hide waypoint=
|
||||
Show waypoint=
|
||||
Hide coordinates=
|
||||
Show coordinates=
|
||||
Hide waypoint=隐藏航路点
|
||||
Show waypoint=显示航路点
|
||||
Hide coordinates=隐藏坐标
|
||||
Show coordinates=显示坐标
|
||||
Change color of waypoint display=改变航路点显示的颜色
|
||||
Edit waypoint name=编辑航路点名称
|
||||
Waypoint active=航路点已激活
|
||||
|
@ -7,17 +7,17 @@ Bag @1=揹包@1
|
||||
Small Bag=小揹包
|
||||
Medium Bag=中揹包
|
||||
Large Bag=大揹包
|
||||
All Items=
|
||||
Misc. Items=
|
||||
Plant Life=
|
||||
Building Materials=
|
||||
Tools=
|
||||
Minerals and Metals=
|
||||
Environment and Worldgen=
|
||||
Lighting=
|
||||
All Items=所有物品
|
||||
Misc. Items=雜項
|
||||
Plant Life=植物
|
||||
Building Materials=建材
|
||||
Tools=工具
|
||||
Minerals and Metals=礦物與金屬
|
||||
Environment and Worldgen=自然環境
|
||||
Lighting=光源
|
||||
and = 和
|
||||
Scroll categories left=
|
||||
Scroll categories right=
|
||||
Scroll categories left=向左滾動分類欄
|
||||
Scroll categories right=向右滾動分類欄
|
||||
Search=搜索
|
||||
Reset search and display everything=重置搜索並顯示所有物品
|
||||
First page=第一頁
|
||||
@ -32,7 +32,7 @@ Page=頁面
|
||||
@1 of @2=第@1頁,共@2頁
|
||||
Filter=過濾器
|
||||
Can use the creative inventory=可以使用創造揹包
|
||||
Forces Unified Inventory to be displayed in Full mode if Lite mode is configured globally=
|
||||
Forces Unified Inventory to be displayed in Full mode if Lite mode is configured globally=如果輕量模式被全局配置,強迫Unified Inventory以完全模式展現。
|
||||
Crafting Grid=合成表
|
||||
Crafting Guide=合成指南
|
||||
Set home position=設置家的位置
|
||||
@ -45,7 +45,7 @@ You don't have the settime privilege!=你沒有“settime”權限!
|
||||
Set time to night=設置時間到晚上
|
||||
Time of day set to 9pm=時間設置到晚上9點
|
||||
Clear inventory=清空揹包
|
||||
This button has been disabled outside of creative mode to prevent accidental inventory trashing.@nUse the trash slot instead.=
|
||||
This button has been disabled outside of creative mode to prevent accidental inventory trashing.@nUse the trash slot instead.=此按鈕已在非創造模式中禁用以防止意外的背包清空。@n請使用垃圾桶欄。
|
||||
Inventory cleared!=清空揹包
|
||||
Trash:=丟棄:
|
||||
Refill:=填滿:
|
||||
@ -57,13 +57,13 @@ No recipes=沒有配方
|
||||
No usages=沒有用法
|
||||
Result=結果
|
||||
Ingredient=原料
|
||||
Show next recipe=
|
||||
Show next usage=
|
||||
Show previous recipe=
|
||||
Show previous usage=
|
||||
@1 (@2)=
|
||||
Show next recipe=顯示下一個配方
|
||||
Show next usage=顯示下一個用法
|
||||
Show previous recipe=顯示上一個配方
|
||||
Show previous usage=顯示上一個用法
|
||||
@1 (@2)=@1 (@2)
|
||||
Give me:=給予:
|
||||
This recipe is too@@large to be displayed.=
|
||||
This recipe is too@@large to be displayed.=該配方太@@大,不能顯示。
|
||||
To craft grid:=填充物品到合成表
|
||||
All=全部
|
||||
Crafting=合成
|
||||
@ -76,10 +76,10 @@ Waypoints=航路點
|
||||
Select Waypoint #@1=查詢航路點 #@1
|
||||
Waypoint @1=航路點 @1
|
||||
Set waypoint to current location=將航路點設置到當前位置
|
||||
Hide waypoint=
|
||||
Show waypoint=
|
||||
Hide coordinates=
|
||||
Show coordinates=
|
||||
Hide waypoint=隱藏航路點
|
||||
Show waypoint=顯示航路點
|
||||
Hide coordinates=隱藏坐標
|
||||
Show coordinates=顯示坐標
|
||||
Change color of waypoint display=改變航路點顯示的顏色
|
||||
Edit waypoint name=編輯航路點名稱
|
||||
Waypoint active=航路點已激活
|
||||
|
@ -126,25 +126,18 @@ Example output:
|
||||
}
|
||||
--]]
|
||||
function unified_inventory.find_usable_items(inv_items, craft_items)
|
||||
local get_group = minetest.get_item_group
|
||||
local result = {}
|
||||
|
||||
for craft_item in pairs(craft_items) do
|
||||
local group = craft_item:match("^group:(.+)")
|
||||
local found = {}
|
||||
-- may specify group:type1,type2
|
||||
local items = unified_inventory.get_matching_items(craft_item)
|
||||
|
||||
if group ~= nil then
|
||||
for inv_item in pairs(inv_items) do
|
||||
if get_group(inv_item, group) > 0 then
|
||||
found[inv_item] = true
|
||||
end
|
||||
end
|
||||
else
|
||||
if inv_items[craft_item] ~= nil then
|
||||
found[craft_item] = true
|
||||
local found = {}
|
||||
for itemname, _ in pairs(items) do
|
||||
if inv_items[itemname] then
|
||||
found[itemname] = true
|
||||
end
|
||||
end
|
||||
|
||||
result[craft_item] = found
|
||||
end
|
||||
|
||||
|
2
mod.conf
2
mod.conf
@ -1,6 +1,6 @@
|
||||
name = unified_inventory
|
||||
|
||||
optional_depends = default, creative, sfinv, datastorage, farming
|
||||
optional_depends = default, creative, sfinv, datastorage
|
||||
description = """
|
||||
Unified Inventory replaces the default survival and creative inventory.
|
||||
It adds a nicer interface and a number of features, such as a crafting guide.
|
||||
|
15
register.lua
15
register.lua
@ -207,11 +207,9 @@ ui.register_page("craft", {
|
||||
|
||||
local function stack_image_button(x, y, w, h, buttonname_prefix, item)
|
||||
local name = item:get_name()
|
||||
local count = item:get_count()
|
||||
local wear = item:get_wear()
|
||||
local description = item:get_meta():get_string("description")
|
||||
local show_is_group = false
|
||||
local displayitem = name.." "..count.." "..wear
|
||||
local displayitem = item:to_string()
|
||||
local selectitem = name
|
||||
if name:sub(1, 6) == "group:" then
|
||||
local group_name = name:sub(7)
|
||||
@ -245,6 +243,9 @@ local function stack_image_button(x, y, w, h, buttonname_prefix, item)
|
||||
return button
|
||||
end
|
||||
|
||||
-- The recipe text contains parameters, hence they can yet not be translated.
|
||||
-- Instead, use a dummy translation call so that it can be picked up by the
|
||||
-- static parsing of the translation string update script
|
||||
local recipe_text = {
|
||||
recipe = NS("Recipe @1 of @2"),
|
||||
usage = NS("Usage @1 of @2"),
|
||||
@ -499,6 +500,14 @@ local function craftguide_craft(player, formname, fields)
|
||||
local alternate = ui.alternate[player_name]
|
||||
|
||||
local craft = crafts[alternate]
|
||||
if not craft.width then
|
||||
if not craft.output then
|
||||
minetest.log("warning", "[unified_inventory] Craft has no output.")
|
||||
else
|
||||
minetest.log("warning", ("[unified_inventory] Craft for '%s' has no width."):format(craft.output))
|
||||
end
|
||||
return
|
||||
end
|
||||
if craft.width > 3 then return end
|
||||
|
||||
ui.craftguide_match_craft(player, "main", "craft", craft, amount)
|
||||
|
BIN
screenshot.png
BIN
screenshot.png
Binary file not shown.
Before Width: | Height: | Size: 134 KiB After Width: | Height: | Size: 220 KiB |
@ -10,5 +10,8 @@ unified_inventory_bags (Enable bags) bool true
|
||||
#and the give privilege.
|
||||
unified_inventory_trash (Enable trash) bool true
|
||||
|
||||
#If enabled, disabled buttons will be hidden instead of grayed out.
|
||||
unified_inventory_hide_disabled_buttons (Hide disabled buttons) bool false
|
||||
|
||||
unified_inventory_automatic_categorization (Items automatically added to categories) bool true
|
||||
|
||||
unified_inventory_automatic_categorization (Items automatically added to categories) bool true
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 240 B After Width: | Height: | Size: 510 B |
Binary file not shown.
Before Width: | Height: | Size: 139 B After Width: | Height: | Size: 551 B |
Reference in New Issue
Block a user