6 Commits

Author SHA1 Message Date
4c4fb3ba88 Merge remote-tracking branch 'github/master' 2021-11-01 10:43:50 +01:00
8c84751ff8 Add u_i initialization and craft registration callbacks (#184)
Main purpose: custom recipe sorting provided by cuszom mods.
2021-10-14 18:38:07 +02:00
9d66a21271 Merge remote-tracking branch 'upstream/master' 2021-08-31 20:12:45 +02:00
b1c85a2cb8 Fix waypoint initialization order 2021-08-29 07:47:32 +02:00
3fffa41eaf Remove unused internal functions 2021-08-24 19:44:36 +02:00
077bed90eb Move default inventory draw calls to separate functions 2021-08-14 15:25:55 +02:00
6 changed files with 248 additions and 187 deletions

41
api.lua
View File

@ -152,6 +152,10 @@ minetest.after(0.01, function()
end end
end end
end end
for _, callback in ipairs(ui.initialized_callbacks) do
callback()
end
end) end)
@ -212,10 +216,15 @@ function ui.register_craft(options)
if options.type == "normal" and options.width == 0 then if options.type == "normal" and options.width == 0 then
options = { type = "shapeless", items = options.items, output = options.output, width = 0 } options = { type = "shapeless", items = options.items, output = options.output, width = 0 }
end end
if not ui.crafts_for.recipe[itemstack:get_name()] then local item_name = itemstack:get_name()
ui.crafts_for.recipe[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)
end end
table.insert(ui.crafts_for.recipe[itemstack:get_name()],options)
end end
@ -309,6 +318,32 @@ function ui.register_button(name, def)
table.insert(ui.buttons, def) table.insert(ui.buttons, def)
end end
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
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
function ui.is_creative(playername) function ui.is_creative(playername)
return minetest.check_player_privs(playername, {creative=true}) return minetest.check_player_privs(playername, {creative=true})
or minetest.settings:get_bool("creative_mode") or minetest.settings:get_bool("creative_mode")

View File

@ -24,8 +24,7 @@ minetest.register_on_joinplayer(function(player)
unified_inventory.alternate[player_name] = 1 unified_inventory.alternate[player_name] = 1
unified_inventory.current_item[player_name] = nil unified_inventory.current_item[player_name] = nil
unified_inventory.current_craft_direction[player_name] = "recipe" unified_inventory.current_craft_direction[player_name] = "recipe"
unified_inventory.set_inventory_formspec(player, unified_inventory.set_inventory_formspec(player, unified_inventory.default)
unified_inventory.default)
-- Refill slot -- Refill slot
local refill = minetest.create_detached_inventory(player_name.."refill", { local refill = minetest.create_detached_inventory(player_name.."refill", {

View File

@ -20,6 +20,64 @@ Grouped by use-case, afterwards sorted alphabetically.
* Checks whether creative is enabled or the player has `creative` * Checks whether creative is enabled or the player has `creative`
Callbacks
---------
Register a callback that will be run whenever a craft is registered via unified_inventory.register_craft:
unified_inventory.register_on_craft_registered(
function (item_name, options)
-- item_name (string): name of the output item, equivalent to `ItemStack:get_name()`
-- options (table): definition table of crafts registered by `unified_inventory.register_craft`
end
)
Register a callback that will be run after all mods have loaded and after the unified_inventory mod has initialised all its internal structures:
unified_inventory.register_on_initialized(callback)
-- The callback is passed no arguments
Accessing Data
--------------
These methods should be used instead of accessing the unified_inventory data structures directly - this will ensure your code survives any potential restructuring of the mod.
Get a list of recipes for a particular output item:
unified_inventory.get_recipe_list(output_item)
Returns a list of tables, each holding a recipe definition, like:
{
{
type = "normal",
items = { "default:stick", "default:stick", "default:stick", "default:stick" },
output = "default:wood",
width = 2
},
{
type = "shapeless",
items = { "default:tree" },
output = "default:wood 4",
width = 0
},
...
}
Get a list of all the output items crafts have been registered for:
unified_inventory.get_registered_outputs()
Returns a list of item names, like:
{
"default:stone",
"default:chest",
"default:brick",
"doors:door_wood",
...
}
Pages Pages
----- -----

View File

@ -24,6 +24,8 @@ unified_inventory = {
filtered_items_list = {}, filtered_items_list = {},
pages = {}, pages = {},
buttons = {}, buttons = {},
initialized_callbacks = {},
craft_registered_callbacks = {},
-- Homepos stuff -- Homepos stuff
home_pos = {}, home_pos = {},
@ -43,7 +45,8 @@ unified_inventory = {
imgscale = 1.25, imgscale = 1.25,
list_img_offset = 0.13, list_img_offset = 0.13,
standard_background = "background9[0,0;1,1;ui_formbg_9_sliced.png;true;16]", standard_background = "background9[0,0;1,1;ui_formbg_9_sliced.png;true;16]",
version = 2
version = 3
} }
local ui = unified_inventory local ui = unified_inventory

View File

@ -22,7 +22,9 @@ end
function ui.get_per_player_formspec(player_name) 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}) local draw_lite_mode = ui.lite_mode and not minetest.check_player_privs(player_name, {ui_full=true})
return table.copy(draw_lite_mode and ui.style_lite or ui.style_full), draw_lite_mode local style = table.copy(draw_lite_mode and ui.style_lite or ui.style_full)
style.is_lite_mode = draw_lite_mode
return style
end end
local function formspec_button(ui_peruser, name, image, offset, pos, scale, label) local function formspec_button(ui_peruser, name, image, offset, pos, scale, label)
@ -41,54 +43,24 @@ local function formspec_button(ui_peruser, name, image, offset, pos, scale, labe
string.format("tooltip[%s;%s]", name, F(label or name)) string.format("tooltip[%s;%s]", name, F(label or name))
end end
function ui.get_formspec(player, page) local function formspec_add_filters(player, formspec, style)
if not player then
return ""
end
local player_name = player:get_player_name()
local ui_peruser,draw_lite_mode = ui.get_per_player_formspec(player_name)
ui.current_page[player_name] = page
local pagedef = ui.pages[page]
if not pagedef then
return "" -- Invalid page name
end
local formspec = {
"formspec_version[4]",
"size["..ui_peruser.formw..","..ui_peruser.formh.."]",
pagedef.formspec_prepend and "" or "no_prepend[]",
ui.standard_background
}
local n = 5
local perplayer_formspec = ui.get_per_player_formspec(player_name)
local fsdata = pagedef.get_formspec(player, perplayer_formspec)
formspec[n] = fsdata.formspec
n = n+1
local button_row = 0 local button_row = 0
local button_col = 0 local button_col = 0
local n = #formspec + 1
-- Main buttons -- Main buttons
local filtered_inv_buttons = {} local filtered_inv_buttons = {}
for i, def in pairs(ui.buttons) do for i, def in pairs(ui.buttons) do
if not (draw_lite_mode and def.hide_lite) then if not (style.is_lite_mode and def.hide_lite) then
table.insert(filtered_inv_buttons, def) table.insert(filtered_inv_buttons, def)
end end
end end
local j = 1 --Modif NALC (sys4 20/11/2018) 12 buttons max by row local j = 1 --Modif NALC (sys4 20/11/2018) 12 buttons max by row
for i, def in pairs(filtered_inv_buttons) do for i, def in pairs(filtered_inv_buttons) do
if style.is_lite_mode and i > 4 then
if draw_lite_mode and i > 4 then
button_row = 1 button_row = 1
button_col = 1 button_col = 1
elseif not draw_lite_mode and j > 12 then elseif not draw_lite_mode and j > 12 then
@ -97,62 +69,61 @@ function ui.get_formspec(player, page)
end end
if def.type == "image" then if def.type == "image" then
local pos_x = style.main_button_x + style.btn_spc * (j - 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 if (def.condition == nil or def.condition(player) == true) then
formspec[n] = string.format("image_button[%f,%f;%f,%f;%s;%s;]", formspec[n] = string.format("image_button[%f,%f;%f,%f;%s;%s;]",
ui_peruser.main_button_x + ui_peruser.btn_spc * (j - 1) - button_col * ui_peruser.btn_spc * 4, -- Modif NALC pos_x, pos_y, style.btn_size, style.btn_size,
ui_peruser.main_button_y + button_row * ui_peruser.btn_spc,
ui_peruser.btn_size,ui_peruser.btn_size,
F(def.image), F(def.image),
F(def.name)) F(def.name))
formspec[n+1] = "tooltip["..F(def.name)..";"..(def.tooltip or "").."]" formspec[n+1] = "tooltip["..F(def.name)..";"..(def.tooltip or "").."]"
n = n+2 n = n+2
else else
formspec[n] = string.format("image[%f,%f;%f,%f;%s^[colorize:#808080:alpha]", formspec[n] = string.format("image[%f,%f;%f,%f;%s^[colorize:#808080:alpha]",
ui_peruser.main_button_x + ui_peruser.btn_spc * (j - 1) - button_col * ui_peruser.btn_spc * 4, -- Modif NALC pos_x, pos_y, style.btn_size, style.btn_size,
ui_peruser.main_button_y + button_row * ui_peruser.btn_spc, def.image)
ui_peruser.btn_size,ui_peruser.btn_size,def.image)
n = n+1 n = n+1
end end
end end
j = j + 1 -- Modif NALC j = j + 1 -- Modif NALC
end end
end
if fsdata.draw_inventory ~= false then local function formspec_add_categories(player, formspec, ui_peruser)
-- Player inventory local player_name = player:get_player_name()
formspec[n] = "listcolors[#00000000;#00000000]" local n = #formspec + 1
formspec[n+1] = ui_peruser.standard_inv
n = n+2
end
if fsdata.draw_item_list == false then local categories_pos = {
return table.concat(formspec, "") ui_peruser.page_x,
end ui_peruser.page_y-ui_peruser.btn_spc-0.5
}
-- Category filters local categories_scroll_pos = {
ui_peruser.page_x,
local categories_pos = { ui_peruser.page_x, ui_peruser.page_y-ui_peruser.btn_spc-0.5 } ui_peruser.form_header_y - (ui_peruser.is_lite_mode and 0 or 0.2)
local categories_scroll_pos = { ui_peruser.page_x, ui_peruser.form_header_y-(draw_lite_mode and 0 or 0.2) } }
formspec[n] = string.format("background9[%f,%f;%f,%f;%s;false;3]", formspec[n] = string.format("background9[%f,%f;%f,%f;%s;false;3]",
ui_peruser.page_x-0.1, categories_scroll_pos[2], ui_peruser.page_x-0.1, categories_scroll_pos[2],
(ui_peruser.btn_spc * ui_peruser.pagecols) + 0.13, 1.4+(draw_lite_mode and 0 or 0.2), (ui_peruser.btn_spc * ui_peruser.pagecols) + 0.13, 1.4 + (ui_peruser.is_lite_mode and 0 or 0.2),
"ui_smallbg_9_sliced.png") "ui_smallbg_9_sliced.png")
n = n + 1 n = n + 1
formspec[n] = string.format("label[%f,%f;%s]", ui_peruser.page_x, ui_peruser.form_header_y+(draw_lite_mode and 0.3 or 0.2), "Category:") formspec[n] = string.format("label[%f,%f;%s]",
ui_peruser.page_x,
ui_peruser.form_header_y + (ui_peruser.is_lite_mode and 0.3 or 0.2), F(S("Category:")))
n = n + 1 n = n + 1
local scroll_offset = 0 local scroll_offset = 0
local category_count = #unified_inventory.category_list local category_count = #ui.category_list
if category_count > ui_peruser.pagecols then if category_count > ui_peruser.pagecols then
scroll_offset = unified_inventory.current_category_scroll[player_name] scroll_offset = ui.current_category_scroll[player_name]
end end
for index, category in ipairs(unified_inventory.category_list) do for index, category in ipairs(ui.category_list) do
local column = index - scroll_offset local column = index - scroll_offset
if column > 0 and column <= ui_peruser.pagecols then if column > 0 and column <= ui_peruser.pagecols then
local scale = 0.8 local scale = 0.8
if unified_inventory.current_category[player_name] == category.name then if ui.current_category[player_name] == category.name then
scale = 1 scale = 1
end end
formspec[n] = formspec_button(ui_peruser, "category_"..category.name, category.symbol, categories_pos, {column-1, 0}, scale, category.label) formspec[n] = formspec_button(ui_peruser, "category_"..category.name, category.symbol, categories_pos, {column-1, 0}, scale, category.label)
@ -167,10 +138,13 @@ function ui.get_formspec(player, page)
if category_count > ui_peruser.pagecols and category_count - scroll_offset > ui_peruser.pagecols then if category_count > ui_peruser.pagecols and category_count - scroll_offset > ui_peruser.pagecols then
-- next -- next
formspec[n] = formspec_button(ui_peruser, "next_category", "ui_right_icon.png", categories_scroll_pos, {ui_peruser.pagecols - 1, 0}, 0.8, S("Scroll categories right")) formspec[n] = formspec_button(ui_peruser, "next_category", "ui_right_icon.png", categories_scroll_pos, {ui_peruser.pagecols - 1, 0}, 0.8, S("Scroll categories right"))
n = n + 1
end end
end
local function formspec_add_search_box(player, formspec, ui_peruser)
local player_name = player:get_player_name()
local n = #formspec + 1
-- Search box
formspec[n] = "field_close_on_enter[searchbox;false]" formspec[n] = "field_close_on_enter[searchbox;false]"
formspec[n+1] = string.format("field[%f,%f;%f,%f;searchbox;;%s]", formspec[n+1] = string.format("field[%f,%f;%f,%f;searchbox;;%s]",
@ -187,7 +161,16 @@ function ui.get_formspec(player, page)
ui_peruser.btn_size, ui_peruser.btn_size) ui_peruser.btn_size, ui_peruser.btn_size)
formspec[n+5] = "tooltip[searchresetbutton;"..F(S("Reset search and display everything")).."]" formspec[n+5] = "tooltip[searchresetbutton;"..F(S("Reset search and display everything")).."]"
n = n + 6 if ui.activefilter[player_name] ~= "" then
formspec[n+6] = string.format("label[%f,%f;%s: %s]",
ui_peruser.page_x, ui_peruser.page_y - 0.25,
F(S("Filter")), F(ui.activefilter[player_name]))
end
end
local function formspec_add_item_browser(player, formspec, ui_peruser)
local player_name = player:get_player_name()
local n = #formspec + 1
-- Controls to flip items pages -- Controls to flip items pages
@ -200,9 +183,9 @@ function ui.get_formspec(player, page)
{ "ui_skip_forward_icon.png", "end_list", S("Last page") }, { "ui_skip_forward_icon.png", "end_list", S("Last page") },
} }
if draw_lite_mode then if ui_peruser.is_lite_mode then
btnlist[5] = nil
btnlist[2] = nil btnlist[2] = nil
btnlist[5] = nil
end end
local bn = 0 local bn = 0
@ -217,69 +200,109 @@ function ui.get_formspec(player, page)
n = n + 2 n = n + 2
end end
local no_matches = S("No matching items")
if draw_lite_mode then
no_matches = S("No matches.")
end
-- Items list -- Items list
if #ui.filtered_items_list[player_name] == 0 then if #ui.filtered_items_list[player_name] == 0 then
formspec[n] = "label["..ui_peruser.page_x..","..(ui_peruser.page_y+0.15)..";" .. F(no_matches) .. "]" local no_matches = S("No matching items")
else if ui_peruser.is_lite_mode then
local dir = ui.active_search_direction[player_name] no_matches = S("No matches.")
local list_index = ui.current_index[player_name] end
local page2 = math.floor(list_index / (ui_peruser.items_per_page) + 1)
local pagemax = math.floor(
(#ui.filtered_items_list[player_name] - 1)
/ (ui_peruser.items_per_page) + 1)
for y = 0, ui_peruser.pagerows - 1 do
for x = 0, ui_peruser.pagecols - 1 do
local name = ui.filtered_items_list[player_name][list_index]
local item = minetest.registered_items[name]
if item then
-- Clicked on current item: Flip crafting direction
if name == ui.current_item[player_name] then
local cdir = ui.current_craft_direction[player_name]
if cdir == "recipe" then
dir = "usage"
elseif cdir == "usage" then
dir = "recipe"
end
else
-- Default: use active search direction by default
dir = ui.active_search_direction[player_name]
end
local button_name = "item_button_" .. dir .. "_" formspec[n] = "label["..ui_peruser.page_x..","..(ui_peruser.page_y+0.15)..";" .. F(no_matches) .. "]"
.. ui.mangle_for_formspec(name) return
formspec[n] = ("item_image_button[%f,%f;%f,%f;%s;%s;]"):format( end
ui_peruser.page_x + x * ui_peruser.btn_spc,
ui_peruser.page_y + y * ui_peruser.btn_spc, local dir = ui.active_search_direction[player_name]
ui_peruser.btn_size, ui_peruser.btn_size, local list_index = ui.current_index[player_name]
name, button_name local page2 = math.floor(list_index / (ui_peruser.items_per_page) + 1)
) local pagemax = math.floor(
formspec[n + 1] = ("tooltip[%s;%s \\[%s\\]]"):format( (#ui.filtered_items_list[player_name] - 1)
button_name, minetest.formspec_escape(item.description), / (ui_peruser.items_per_page) + 1)
item.mod_origin or "??" for y = 0, ui_peruser.pagerows - 1 do
) for x = 0, ui_peruser.pagecols - 1 do
n = n + 2 local name = ui.filtered_items_list[player_name][list_index]
list_index = list_index + 1 local item = minetest.registered_items[name]
if item then
-- Clicked on current item: Flip crafting direction
if name == ui.current_item[player_name] then
local cdir = ui.current_craft_direction[player_name]
if cdir == "recipe" then
dir = "usage"
elseif cdir == "usage" then
dir = "recipe"
end
else
-- Default: use active search direction by default
dir = ui.active_search_direction[player_name]
end end
local button_name = "item_button_" .. dir .. "_"
.. ui.mangle_for_formspec(name)
formspec[n] = ("item_image_button[%f,%f;%f,%f;%s;%s;]"):format(
ui_peruser.page_x + x * ui_peruser.btn_spc,
ui_peruser.page_y + y * ui_peruser.btn_spc,
ui_peruser.btn_size, ui_peruser.btn_size,
name, button_name
)
formspec[n + 1] = ("tooltip[%s;%s \\[%s\\]]"):format(
button_name, minetest.formspec_escape(item.description),
item.mod_origin or "??"
)
n = n + 2
list_index = list_index + 1
end end
end end
formspec[n] = string.format("label[%f,%f;%s: %s]",
ui_peruser.page_buttons_x + ui_peruser.btn_spc * (draw_lite_mode and 1 or 2),
ui_peruser.page_buttons_y + 0.1 + ui_peruser.btn_spc * 2,
F(S("Page")), S("@1 of @2",page2,pagemax))
end end
n= n+1 formspec[n] = string.format("label[%f,%f;%s: %s]",
ui_peruser.page_buttons_x + ui_peruser.btn_spc * (ui_peruser.is_lite_mode and 1 or 2),
ui_peruser.page_buttons_y + 0.1 + ui_peruser.btn_spc * 2,
F(S("Page")), S("@1 of @2",page2,pagemax))
end
if ui.activefilter[player_name] ~= "" then function ui.get_formspec(player, page)
formspec[n] = string.format("label[%f,%f;%s: %s]",
ui_peruser.page_x, ui_peruser.page_y - 0.25, if not player then
F(S("Filter")), F(ui.activefilter[player_name])) return ""
end end
return table.concat(formspec, "")
local player_name = player:get_player_name()
local ui_peruser = ui.get_per_player_formspec(player_name)
ui.current_page[player_name] = page
local pagedef = ui.pages[page]
if not pagedef then
return "" -- Invalid page name
end
local fs = {
"formspec_version[4]",
"size["..ui_peruser.formw..","..ui_peruser.formh.."]",
pagedef.formspec_prepend and "" or "no_prepend[]",
ui.standard_background
}
local perplayer_formspec = ui.get_per_player_formspec(player_name)
local fsdata = pagedef.get_formspec(player, perplayer_formspec)
fs[#fs + 1] = fsdata.formspec
formspec_add_filters(player, fs, ui_peruser)
if fsdata.draw_inventory ~= false then
-- Player inventory
fs[#fs + 1] = "listcolors[#00000000;#00000000]"
fs[#fs + 1] = ui_peruser.standard_inv
end
if fsdata.draw_item_list == false then
return table.concat(fs, "")
end
formspec_add_categories(player, fs, ui_peruser)
formspec_add_search_box(player, fs, ui_peruser)
formspec_add_item_browser(player, fs, ui_peruser)
return table.concat(fs)
end end
function ui.set_inventory_formspec(player, page) function ui.set_inventory_formspec(player, page)
@ -357,63 +380,5 @@ function ui.apply_filter(player, filter, search_dir)
ui.current_index[player_name] = 1 ui.current_index[player_name] = 1
ui.activefilter[player_name] = filter ui.activefilter[player_name] = filter
ui.active_search_direction[player_name] = search_dir ui.active_search_direction[player_name] = search_dir
ui.set_inventory_formspec(player, ui.set_inventory_formspec(player, ui.current_page[player_name])
ui.current_page[player_name])
end
function ui.items_in_group(groups)
local items = {}
for name, item in pairs(minetest.registered_items) do
for _, group in pairs(groups:split(',')) do
if item.groups[group] then
table.insert(items, name)
end
end
end
return items
end
function ui.sort_inventory(inv)
local inlist = inv:get_list("main")
local typecnt = {}
local typekeys = {}
for _, st in ipairs(inlist) do
if not st:is_empty() then
local n = st:get_name()
local w = st:get_wear()
local m = st:get_metadata()
local k = string.format("%s %05d %s", n, w, m)
if not typecnt[k] then
typecnt[k] = {
name = n,
wear = w,
metadata = m,
stack_max = st:get_stack_max(),
count = 0,
}
table.insert(typekeys, k)
end
typecnt[k].count = typecnt[k].count + st:get_count()
end
end
table.sort(typekeys)
local outlist = {}
for _, k in ipairs(typekeys) do
local tc = typecnt[k]
while tc.count > 0 do
local c = math.min(tc.count, tc.stack_max)
table.insert(outlist, ItemStack({
name = tc.name,
wear = tc.wear,
metadata = tc.metadata,
count = c,
}))
tc.count = tc.count - c
end
end
if #outlist > #inlist then return end
while #outlist < #inlist do
table.insert(outlist, ItemStack(nil))
end
inv:set_list("main", outlist)
end end

View File

@ -330,8 +330,9 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
end end
end) end)
-- waypoints_temp must be initialized before the general unified_inventory
minetest.register_on_joinplayer(function(player) -- joinplayer callback is run for updating the inventory
table.insert(minetest.registered_on_joinplayers, 1, function(player)
local player_name = player:get_player_name() local player_name = player:get_player_name()
local waypoints = get_waypoint_data(player) local waypoints = get_waypoint_data(player)