2019-09-19 14:00:26 +02:00
|
|
|
local S = minetest.get_translator("unified_inventory")
|
2018-04-02 13:33:36 +02:00
|
|
|
local F = minetest.formspec_escape
|
2021-03-07 14:18:17 +01:00
|
|
|
local ui = unified_inventory
|
2012-12-02 10:08:32 +01:00
|
|
|
|
2022-10-05 21:07:11 +02:00
|
|
|
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
|
|
|
|
|
2012-12-03 23:36:15 +01:00
|
|
|
-- Create detached creative inventory after loading all mods
|
|
|
|
minetest.after(0.01, function()
|
2014-04-22 21:12:49 +02:00
|
|
|
local rev_aliases = {}
|
2022-10-05 21:07:11 +02:00
|
|
|
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)
|
2014-04-22 21:12:49 +02:00
|
|
|
end
|
2022-10-05 21:07:11 +02:00
|
|
|
|
|
|
|
-- Filtered item list
|
2021-03-07 14:18:17 +01:00
|
|
|
ui.items_list = {}
|
2013-09-29 00:15:37 +02:00
|
|
|
for name, def in pairs(minetest.registered_items) do
|
2022-10-05 21:07:11 +02:00
|
|
|
if ui.is_itemdef_listable(def) then
|
2021-03-07 14:18:17 +01:00
|
|
|
table.insert(ui.items_list, name)
|
2022-10-05 21:07:11 +02:00
|
|
|
|
|
|
|
-- Alias processing: Find recipes that belong to the current item name
|
2014-04-22 21:12:49 +02:00
|
|
|
local all_names = rev_aliases[name] or {}
|
|
|
|
table.insert(all_names, name)
|
2022-10-05 21:07:11 +02:00
|
|
|
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)
|
2014-04-22 21:12:49 +02:00
|
|
|
end
|
|
|
|
end
|
2013-10-07 04:16:31 +02:00
|
|
|
end
|
2012-12-03 23:36:15 +01:00
|
|
|
end
|
2012-12-02 10:08:32 +01:00
|
|
|
end
|
2021-03-07 14:18:17 +01:00
|
|
|
table.sort(ui.items_list)
|
|
|
|
ui.items_list_size = #ui.items_list
|
2022-02-23 21:48:26 +01:00
|
|
|
print("Unified Inventory. Inventory size: "..ui.items_list_size)
|
2022-10-05 21:07:11 +02:00
|
|
|
|
|
|
|
-- Analyse dropped items -> custom "digging" recipes
|
2021-03-07 14:18:17 +01:00
|
|
|
for _, name in ipairs(ui.items_list) do
|
2014-04-29 23:14:08 +02:00
|
|
|
local def = minetest.registered_items[name]
|
2016-08-07 04:21:02 +02:00
|
|
|
-- Simple drops
|
2014-04-29 23:14:08 +02:00
|
|
|
if type(def.drop) == "string" then
|
|
|
|
local dstack = ItemStack(def.drop)
|
|
|
|
if not dstack:is_empty() and dstack:get_name() ~= name then
|
2021-03-07 14:18:17 +01:00
|
|
|
ui.register_craft({
|
2014-04-29 23:14:08 +02:00
|
|
|
type = "digging",
|
|
|
|
items = {name},
|
|
|
|
output = def.drop,
|
|
|
|
width = 0,
|
|
|
|
})
|
|
|
|
|
|
|
|
end
|
2016-08-07 05:44:36 +02:00
|
|
|
-- Complex drops. Yes, it's really complex!
|
2016-08-07 03:49:16 +02:00
|
|
|
elseif type(def.drop) == "table" then
|
2016-08-07 04:47:15 +02:00
|
|
|
--[[ Extract single items from the table and save them into dedicated tables
|
2016-08-07 05:44:36 +02:00
|
|
|
to register them later, in order to avoid duplicates. These tables counts
|
|
|
|
the total number of guaranteed drops and drops by chance (“maybes”) for each item.
|
|
|
|
For “maybes”, the final count is the theoretical maximum number of items, not
|
|
|
|
neccessarily the actual drop count. ]]
|
2016-08-07 04:47:15 +02:00
|
|
|
local drop_guaranteed = {}
|
|
|
|
local drop_maybe = {}
|
2016-08-07 05:44:36 +02:00
|
|
|
-- This is for catching an obscure corner case: If the top items table has
|
|
|
|
-- only items with rarity = 1, but max_items is set, then only the first
|
|
|
|
-- max_items will be part of the drop, any later entries are logically
|
|
|
|
-- impossible, so this variable is for keeping track of this
|
|
|
|
local max_items_left = def.drop.max_items
|
|
|
|
-- For checking whether we still encountered only guaranteed only so far;
|
|
|
|
-- for the first “maybe” item it will become false which will cause ALL
|
|
|
|
-- later items to be considered “maybes”.
|
|
|
|
-- A common idiom is:
|
|
|
|
-- { max_items 1, { items = {
|
|
|
|
-- { items={"example:1"}, rarity = 5 },
|
|
|
|
-- { items={"example:2"}, rarity = 1 }, }}}
|
|
|
|
-- example:2 must be considered a “maybe” because max_items is set and it
|
|
|
|
-- appears after a “maybe”
|
|
|
|
local max_start = true
|
|
|
|
-- Let's iterate through the items madness!
|
2016-10-08 05:54:05 +02:00
|
|
|
-- Handle invalid drop entries gracefully.
|
|
|
|
local drop_items = def.drop.items or { }
|
|
|
|
for i=1,#drop_items do
|
2016-08-07 05:44:36 +02:00
|
|
|
if max_items_left ~= nil and max_items_left <= 0 then break end
|
2016-10-08 05:54:05 +02:00
|
|
|
local itit = drop_items[i]
|
2016-08-07 03:49:16 +02:00
|
|
|
for j=1,#itit.items do
|
|
|
|
local dstack = ItemStack(itit.items[j])
|
|
|
|
if not dstack:is_empty() and dstack:get_name() ~= name then
|
2016-08-07 04:21:02 +02:00
|
|
|
local dname = dstack:get_name()
|
2016-08-07 04:38:08 +02:00
|
|
|
local dcount = dstack:get_count()
|
2016-08-07 05:44:36 +02:00
|
|
|
-- Guaranteed drops AND we are not yet in “maybe mode”
|
|
|
|
if #itit.items == 1 and itit.rarity == 1 and max_start then
|
2016-08-07 04:47:15 +02:00
|
|
|
if drop_guaranteed[dname] == nil then
|
|
|
|
drop_guaranteed[dname] = 0
|
|
|
|
end
|
|
|
|
drop_guaranteed[dname] = drop_guaranteed[dname] + dcount
|
2016-08-07 05:44:36 +02:00
|
|
|
|
|
|
|
if max_items_left ~= nil then
|
|
|
|
max_items_left = max_items_left - 1
|
|
|
|
if max_items_left <= 0 then break end
|
|
|
|
end
|
|
|
|
-- Drop was a “maybe”
|
2016-08-07 04:47:15 +02:00
|
|
|
else
|
2016-08-07 05:44:36 +02:00
|
|
|
if max_items_left ~= nil then max_start = false end
|
2016-08-07 04:47:15 +02:00
|
|
|
if drop_maybe[dname] == nil then
|
|
|
|
drop_maybe[dname] = 0
|
2016-08-07 04:38:08 +02:00
|
|
|
end
|
2016-08-07 04:47:15 +02:00
|
|
|
drop_maybe[dname] = drop_maybe[dname] + dcount
|
2016-08-07 04:21:02 +02:00
|
|
|
end
|
2016-08-07 03:49:16 +02:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2016-08-07 04:47:15 +02:00
|
|
|
for itemstring, count in pairs(drop_guaranteed) do
|
2021-03-07 14:18:17 +01:00
|
|
|
ui.register_craft({
|
2016-08-07 04:47:15 +02:00
|
|
|
type = "digging",
|
|
|
|
items = {name},
|
|
|
|
output = itemstring .. " " .. count,
|
|
|
|
width = 0,
|
|
|
|
})
|
|
|
|
end
|
|
|
|
for itemstring, count in pairs(drop_maybe) do
|
2021-03-07 14:18:17 +01:00
|
|
|
ui.register_craft({
|
2016-08-07 04:47:15 +02:00
|
|
|
type = "digging_chance",
|
2016-08-07 04:21:02 +02:00
|
|
|
items = {name},
|
2016-08-07 04:47:15 +02:00
|
|
|
output = itemstring .. " " .. count,
|
2016-08-07 04:21:02 +02:00
|
|
|
width = 0,
|
|
|
|
})
|
|
|
|
end
|
2014-04-29 23:14:08 +02:00
|
|
|
end
|
|
|
|
end
|
2022-02-23 21:48:26 +01:00
|
|
|
|
|
|
|
-- Step 1: group-indexed lookup table for items
|
|
|
|
local spec_matcher = {}
|
|
|
|
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
|
|
|
|
|
|
|
|
-- Step 2: Find all matching items for the given spec (groups)
|
|
|
|
local function get_matching_spec_items(specname)
|
|
|
|
if specname:sub(1,6) ~= "group:" then
|
2022-04-22 22:01:15 +02:00
|
|
|
return { [specname] = true }
|
2022-02-23 21:48:26 +01:00
|
|
|
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
|
|
|
|
|
2021-03-07 14:18:17 +01:00
|
|
|
for _, recipes in pairs(ui.crafts_for.recipe) do
|
2022-04-22 22:01:15 +02:00
|
|
|
-- List of crafts that return this item string (variable "_")
|
2014-06-13 16:04:20 +02:00
|
|
|
for _, recipe in ipairs(recipes) do
|
|
|
|
local ingredient_items = {}
|
2016-11-19 03:34:40 +01:00
|
|
|
for _, spec in pairs(recipe.items) do
|
2022-02-23 21:48:26 +01:00
|
|
|
-- 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
|
2014-06-13 16:04:20 +02:00
|
|
|
end
|
|
|
|
end
|
|
|
|
for name, _ in pairs(ingredient_items) do
|
2022-02-23 21:48:26 +01:00
|
|
|
if not ui.crafts_for.usage[name] then
|
2021-03-07 14:18:17 +01:00
|
|
|
ui.crafts_for.usage[name] = {}
|
2014-06-13 16:04:20 +02:00
|
|
|
end
|
2021-03-07 14:18:17 +01:00
|
|
|
table.insert(ui.crafts_for.usage[name], recipe)
|
2014-06-13 16:04:20 +02:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2021-10-14 18:38:07 +02:00
|
|
|
|
|
|
|
for _, callback in ipairs(ui.initialized_callbacks) do
|
|
|
|
callback()
|
|
|
|
end
|
2012-12-03 23:36:15 +01:00
|
|
|
end)
|
2012-12-02 10:08:32 +01:00
|
|
|
|
2022-10-05 21:07:11 +02:00
|
|
|
---------------- Home API ----------------
|
2013-03-03 16:28:24 +01:00
|
|
|
|
2013-09-29 00:15:37 +02:00
|
|
|
local function load_home()
|
2021-03-07 14:18:17 +01:00
|
|
|
local input = io.open(ui.home_filename, "r")
|
2015-04-18 21:23:05 +02:00
|
|
|
if not input then
|
2021-03-07 14:18:17 +01:00
|
|
|
ui.home_pos = {}
|
2015-04-18 21:23:05 +02:00
|
|
|
return
|
2012-12-02 10:08:32 +01:00
|
|
|
end
|
2015-04-18 21:23:05 +02:00
|
|
|
while true do
|
|
|
|
local x = input:read("*n")
|
|
|
|
if not x then break end
|
|
|
|
local y = input:read("*n")
|
|
|
|
local z = input:read("*n")
|
|
|
|
local name = input:read("*l")
|
2021-03-07 14:18:17 +01:00
|
|
|
ui.home_pos[name:sub(2)] = {x = x, y = y, z = z}
|
2015-04-18 21:23:05 +02:00
|
|
|
end
|
|
|
|
io.close(input)
|
2012-12-02 10:08:32 +01:00
|
|
|
end
|
2022-10-05 21:07:11 +02:00
|
|
|
|
2013-09-29 00:15:37 +02:00
|
|
|
load_home()
|
2012-12-02 10:08:32 +01:00
|
|
|
|
2021-03-07 14:18:17 +01:00
|
|
|
function ui.set_home(player, pos)
|
2012-12-02 10:08:32 +01:00
|
|
|
local player_name = player:get_player_name()
|
2021-03-07 14:18:17 +01:00
|
|
|
ui.home_pos[player_name] = vector.round(pos)
|
2021-05-13 15:19:46 +02:00
|
|
|
|
2012-12-02 10:08:32 +01:00
|
|
|
-- save the home data from the table to the file
|
2021-03-07 14:18:17 +01:00
|
|
|
local output = io.open(ui.home_filename, "w")
|
2021-05-13 15:19:46 +02:00
|
|
|
if not output then
|
|
|
|
minetest.log("warning", "[unified_inventory] Failed to save file: "
|
|
|
|
.. ui.home_filename)
|
|
|
|
return
|
|
|
|
end
|
2021-03-07 14:18:17 +01:00
|
|
|
for k, v in pairs(ui.home_pos) do
|
2015-04-18 21:23:05 +02:00
|
|
|
output:write(v.x.." "..v.y.." "..v.z.." "..k.."\n")
|
2012-12-02 10:08:32 +01:00
|
|
|
end
|
|
|
|
io.close(output)
|
|
|
|
end
|
|
|
|
|
2021-03-07 14:18:17 +01:00
|
|
|
function ui.go_home(player)
|
|
|
|
local pos = ui.home_pos[player:get_player_name()]
|
2015-04-18 21:23:05 +02:00
|
|
|
if pos then
|
2019-06-16 10:26:40 +02:00
|
|
|
player:set_pos(pos)
|
2020-07-10 21:58:45 +02:00
|
|
|
return true
|
2012-12-02 10:08:32 +01:00
|
|
|
end
|
2020-07-10 21:58:45 +02:00
|
|
|
return false
|
2012-12-02 10:08:32 +01:00
|
|
|
end
|
|
|
|
|
2022-10-05 21:07:11 +02:00
|
|
|
---------------- Crafting API ----------------
|
|
|
|
|
2021-03-07 14:18:17 +01:00
|
|
|
function ui.register_craft(options)
|
2015-08-16 16:40:49 +02:00
|
|
|
if not options.output then
|
2013-09-29 00:15:37 +02:00
|
|
|
return
|
2012-12-11 00:48:23 +01:00
|
|
|
end
|
2013-09-29 00:15:37 +02:00
|
|
|
local itemstack = ItemStack(options.output)
|
|
|
|
if itemstack:is_empty() then
|
|
|
|
return
|
2012-12-11 00:48:23 +01:00
|
|
|
end
|
2014-04-30 01:11:10 +02:00
|
|
|
if options.type == "normal" and options.width == 0 then
|
|
|
|
options = { type = "shapeless", items = options.items, output = options.output, width = 0 }
|
|
|
|
end
|
2021-10-14 18:38:07 +02:00
|
|
|
local item_name = itemstack:get_name()
|
|
|
|
if not ui.crafts_for.recipe[item_name] then
|
|
|
|
ui.crafts_for.recipe[item_name] = {}
|
|
|
|
end
|
|
|
|
table.insert(ui.crafts_for.recipe[item_name],options)
|
|
|
|
|
|
|
|
for _, callback in ipairs(ui.craft_registered_callbacks) do
|
|
|
|
callback(item_name, options)
|
2013-10-07 04:16:31 +02:00
|
|
|
end
|
2012-12-11 00:48:23 +01:00
|
|
|
end
|
|
|
|
|
2014-04-30 01:11:10 +02:00
|
|
|
local craft_type_defaults = {
|
|
|
|
width = 3,
|
|
|
|
height = 3,
|
|
|
|
uses_crafting_grid = false,
|
|
|
|
}
|
2014-05-12 04:00:32 +02:00
|
|
|
|
2021-03-07 14:18:17 +01:00
|
|
|
function ui.craft_type_defaults(name, options)
|
2014-05-12 04:00:32 +02:00
|
|
|
if not options.description then
|
|
|
|
options.description = name
|
|
|
|
end
|
2014-04-30 01:11:10 +02:00
|
|
|
setmetatable(options, {__index = craft_type_defaults})
|
|
|
|
return options
|
|
|
|
end
|
2014-05-12 04:00:32 +02:00
|
|
|
|
|
|
|
|
2021-03-07 14:18:17 +01:00
|
|
|
function ui.register_craft_type(name, options)
|
2022-10-05 21:07:11 +02:00
|
|
|
ui.registered_craft_types[name] = ui.craft_type_defaults(name, options)
|
2014-04-30 01:11:10 +02:00
|
|
|
end
|
|
|
|
|
2014-05-12 04:00:32 +02:00
|
|
|
|
2021-03-07 14:18:17 +01:00
|
|
|
ui.register_craft_type("normal", {
|
2018-04-02 13:33:36 +02:00
|
|
|
description = F(S("Crafting")),
|
2015-02-07 18:10:40 +01:00
|
|
|
icon = "ui_craftgrid_icon.png",
|
2014-04-30 01:11:10 +02:00
|
|
|
width = 3,
|
|
|
|
height = 3,
|
2014-06-13 13:15:50 +02:00
|
|
|
get_shaped_craft_width = function (craft) return craft.width end,
|
|
|
|
dynamic_display_size = function (craft)
|
|
|
|
local w = craft.width
|
|
|
|
local h = math.ceil(table.maxn(craft.items) / craft.width)
|
|
|
|
local g = w < h and h or w
|
|
|
|
return { width = g, height = g }
|
|
|
|
end,
|
2014-04-30 01:11:10 +02:00
|
|
|
uses_crafting_grid = true,
|
|
|
|
})
|
|
|
|
|
2014-05-12 04:00:32 +02:00
|
|
|
|
2021-03-07 14:18:17 +01:00
|
|
|
ui.register_craft_type("shapeless", {
|
2018-04-02 13:33:36 +02:00
|
|
|
description = F(S("Mixing")),
|
2015-02-07 18:10:40 +01:00
|
|
|
icon = "ui_craftgrid_icon.png",
|
2014-04-30 01:11:10 +02:00
|
|
|
width = 3,
|
|
|
|
height = 3,
|
2014-06-13 13:15:50 +02:00
|
|
|
dynamic_display_size = function (craft)
|
|
|
|
local maxn = table.maxn(craft.items)
|
|
|
|
local g = 1
|
|
|
|
while g*g < maxn do g = g + 1 end
|
|
|
|
return { width = g, height = g }
|
|
|
|
end,
|
2014-04-30 01:11:10 +02:00
|
|
|
uses_crafting_grid = true,
|
|
|
|
})
|
|
|
|
|
2014-05-12 04:00:32 +02:00
|
|
|
|
2021-03-07 14:18:17 +01:00
|
|
|
ui.register_craft_type("cooking", {
|
2018-04-02 13:33:36 +02:00
|
|
|
description = F(S("Cooking")),
|
2015-02-07 18:10:40 +01:00
|
|
|
icon = "default_furnace_front.png",
|
2014-04-30 01:11:10 +02:00
|
|
|
width = 1,
|
|
|
|
height = 1,
|
|
|
|
})
|
|
|
|
|
2014-05-12 04:00:32 +02:00
|
|
|
|
2021-03-07 14:18:17 +01:00
|
|
|
ui.register_craft_type("digging", {
|
2018-04-02 13:33:36 +02:00
|
|
|
description = F(S("Digging")),
|
2015-02-07 18:10:40 +01:00
|
|
|
icon = "default_tool_steelpick.png",
|
2014-04-30 01:11:10 +02:00
|
|
|
width = 1,
|
|
|
|
height = 1,
|
|
|
|
})
|
|
|
|
|
2021-03-07 14:18:17 +01:00
|
|
|
ui.register_craft_type("digging_chance", {
|
2016-08-07 03:49:16 +02:00
|
|
|
description = "Digging (by chance)",
|
|
|
|
icon = "default_tool_steelpick.png^[transformFY.png",
|
|
|
|
width = 1,
|
|
|
|
height = 1,
|
|
|
|
})
|
2014-05-01 17:08:24 +02:00
|
|
|
|
2022-10-05 21:07:11 +02:00
|
|
|
---------------- GUI registrations ----------------
|
|
|
|
|
2021-03-07 14:18:17 +01:00
|
|
|
function ui.register_page(name, def)
|
|
|
|
ui.pages[name] = def
|
2012-12-11 00:48:23 +01:00
|
|
|
end
|
2013-09-21 21:40:20 +02:00
|
|
|
|
2014-05-12 04:00:32 +02:00
|
|
|
|
2021-03-07 14:18:17 +01:00
|
|
|
function ui.register_button(name, def)
|
2013-09-29 00:15:37 +02:00
|
|
|
if not def.action then
|
|
|
|
def.action = function(player)
|
2021-03-07 14:18:17 +01:00
|
|
|
ui.set_inventory_formspec(player, name)
|
2013-09-21 21:40:20 +02:00
|
|
|
end
|
|
|
|
end
|
2013-09-29 00:15:37 +02:00
|
|
|
def.name = name
|
2021-03-07 14:18:17 +01:00
|
|
|
table.insert(ui.buttons, def)
|
2013-09-21 21:40:20 +02:00
|
|
|
end
|
|
|
|
|
2022-10-05 21:07:11 +02:00
|
|
|
---------------- Callback registrations ----------------
|
|
|
|
|
2021-10-14 18:38:07 +02:00
|
|
|
function ui.register_on_initialized(callback)
|
|
|
|
if type(callback) ~= "function" then
|
|
|
|
error(("Initialized callback must be a function, %s given."):format(type(callback)))
|
|
|
|
end
|
|
|
|
table.insert(ui.initialized_callbacks, callback)
|
|
|
|
end
|
|
|
|
|
|
|
|
function ui.register_on_craft_registered(callback)
|
|
|
|
if type(callback) ~= "function" then
|
|
|
|
error(("Craft registered callback must be a function, %s given."):format(type(callback)))
|
|
|
|
end
|
|
|
|
table.insert(ui.craft_registered_callbacks, callback)
|
|
|
|
end
|
|
|
|
|
2022-10-05 21:07:11 +02:00
|
|
|
---------------- List getters ----------------
|
|
|
|
|
2021-10-14 18:38:07 +02:00
|
|
|
function ui.get_recipe_list(output)
|
|
|
|
return ui.crafts_for.recipe[output]
|
|
|
|
end
|
|
|
|
|
|
|
|
function ui.get_registered_outputs()
|
|
|
|
local outputs = {}
|
|
|
|
for item_name, _ in pairs(ui.crafts_for.recipe) do
|
|
|
|
table.insert(outputs, item_name)
|
|
|
|
end
|
|
|
|
return outputs
|
|
|
|
end
|
|
|
|
|
2022-10-05 21:07:11 +02:00
|
|
|
---------------- Player utilities ----------------
|
|
|
|
|
2021-03-07 14:18:17 +01:00
|
|
|
function ui.is_creative(playername)
|
2015-08-16 16:40:49 +02:00
|
|
|
return minetest.check_player_privs(playername, {creative=true})
|
2018-06-23 14:56:27 +02:00
|
|
|
or minetest.settings:get_bool("creative_mode")
|
2013-09-21 21:40:20 +02:00
|
|
|
end
|
Use 9-slicing to build inventory-type backgrounds
This way the slots are all nice and crisp regardless of GUI scale or
image size, and we only need the single slot and its bright version.
This also makes the standard crafting grid into a style table entry that
can be referenced to insert the crafting grid at its proper
style-specific position in any formspec.
And it also makes the craft grid arrow, its X position, and the crafting
grid's result slot X position into style table entries.
Includes a few public helper functions to do most of the work:
`ui.single_slot(xpos, ypos, bright)`
Does just what it sounds like: it returns a single slot image.
`xpos` and `ypos` are normal coordinates in slots, as you'd use in
`image[]` element. `bright` is a flag that switches to the brighter
version of the slot image.
`ui.make_trash_slot(xpos, ypos)`
Creates a single slot, with a one-item `list[]` and a trash can icon
overlay.
`ui.make_inv_img_grid(xpos, ypos, width, height, bright)`
Generates a `width` by `height` grid of slot images, using the
single_slot function above, starting at (`xpos`,`ypos`) for the
top-left. Position is as in any `image[]` element, and dimensions
are in integer numbers of slots (so 8,4 would be a standard inventory).
`bright` is as above.
All three return a string that can be directly inserted into a formspec.
2021-03-08 18:14:31 +01:00
|
|
|
|
2022-10-05 21:07:11 +02:00
|
|
|
---------------- Formspec helpers ----------------
|
|
|
|
|
Use 9-slicing to build inventory-type backgrounds
This way the slots are all nice and crisp regardless of GUI scale or
image size, and we only need the single slot and its bright version.
This also makes the standard crafting grid into a style table entry that
can be referenced to insert the crafting grid at its proper
style-specific position in any formspec.
And it also makes the craft grid arrow, its X position, and the crafting
grid's result slot X position into style table entries.
Includes a few public helper functions to do most of the work:
`ui.single_slot(xpos, ypos, bright)`
Does just what it sounds like: it returns a single slot image.
`xpos` and `ypos` are normal coordinates in slots, as you'd use in
`image[]` element. `bright` is a flag that switches to the brighter
version of the slot image.
`ui.make_trash_slot(xpos, ypos)`
Creates a single slot, with a one-item `list[]` and a trash can icon
overlay.
`ui.make_inv_img_grid(xpos, ypos, width, height, bright)`
Generates a `width` by `height` grid of slot images, using the
single_slot function above, starting at (`xpos`,`ypos`) for the
top-left. Position is as in any `image[]` element, and dimensions
are in integer numbers of slots (so 8,4 would be a standard inventory).
`bright` is as above.
All three return a string that can be directly inserted into a formspec.
2021-03-08 18:14:31 +01:00
|
|
|
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 "") )
|
|
|
|
end
|
|
|
|
|
|
|
|
function ui.make_trash_slot(xpos, ypos)
|
|
|
|
return
|
|
|
|
ui.single_slot(xpos, ypos)..
|
2021-03-11 03:20:31 +01:00
|
|
|
"image["..xpos..","..ypos..";1.25,1.25;ui_trash_slot_icon.png]"..
|
2021-03-10 18:26:28 +01:00
|
|
|
"list[detached:trash;main;"..(xpos + ui.list_img_offset)..","..(ypos + ui.list_img_offset)..";1,1;]"
|
Use 9-slicing to build inventory-type backgrounds
This way the slots are all nice and crisp regardless of GUI scale or
image size, and we only need the single slot and its bright version.
This also makes the standard crafting grid into a style table entry that
can be referenced to insert the crafting grid at its proper
style-specific position in any formspec.
And it also makes the craft grid arrow, its X position, and the crafting
grid's result slot X position into style table entries.
Includes a few public helper functions to do most of the work:
`ui.single_slot(xpos, ypos, bright)`
Does just what it sounds like: it returns a single slot image.
`xpos` and `ypos` are normal coordinates in slots, as you'd use in
`image[]` element. `bright` is a flag that switches to the brighter
version of the slot image.
`ui.make_trash_slot(xpos, ypos)`
Creates a single slot, with a one-item `list[]` and a trash can icon
overlay.
`ui.make_inv_img_grid(xpos, ypos, width, height, bright)`
Generates a `width` by `height` grid of slot images, using the
single_slot function above, starting at (`xpos`,`ypos`) for the
top-left. Position is as in any `image[]` element, and dimensions
are in integer numbers of slots (so 8,4 would be a standard inventory).
`bright` is as above.
All three return a string that can be directly inserted into a formspec.
2021-03-08 18:14:31 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
function ui.make_inv_img_grid(xpos, ypos, width, height, bright)
|
|
|
|
local tiled = {}
|
|
|
|
local n=1
|
|
|
|
for y = 0, (height - 1) do
|
|
|
|
for x = 0, (width -1) do
|
|
|
|
tiled[n] = ui.single_slot(xpos + (ui.imgscale * x), ypos + (ui.imgscale * y), bright)
|
|
|
|
n = n + 1
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return table.concat(tiled)
|
|
|
|
end
|