225 lines
6.6 KiB
Lua
225 lines
6.6 KiB
Lua
select_item = {}
|
|
|
|
-- Cache for result of item filters
|
|
local player_filters = {}
|
|
local player_filter_results = {}
|
|
local player_compares = {}
|
|
local player_maxpage = {}
|
|
|
|
local reset_player_info = function(playername)
|
|
player_filters[playername] = nil
|
|
player_filter_results[playername] = nil
|
|
player_compares[playername] = nil
|
|
player_maxpage[playername] = nil
|
|
end
|
|
|
|
-- Predefined filters
|
|
select_item.filters = {}
|
|
|
|
-- Filters out all items not for the Creative Inventory.
|
|
-- This elimininates all items reserved for internal use.
|
|
select_item.filters.creative = function(itemstring)
|
|
local itemdef = minetest.registered_items[itemstring]
|
|
if itemstring == "air" then
|
|
return false
|
|
end
|
|
if itemdef.description == nil or itemdef.description == "" then
|
|
return false
|
|
end
|
|
if minetest.get_item_group(itemstring, "not_in_creative_inventory") == 1 then
|
|
return false
|
|
end
|
|
return true
|
|
end
|
|
|
|
-- No filtering
|
|
select_item.filters.all = function()
|
|
return true
|
|
end
|
|
|
|
local check_item = function(itemstring, filter)
|
|
local itemdef = minetest.registered_items[itemstring]
|
|
if itemstring == "" or itemstring == "unknown" or itemstring == "ignore" or itemdef == nil then
|
|
return
|
|
end
|
|
if type(filter) == "function" then
|
|
if filter(itemstring) ~= true then
|
|
return false
|
|
end
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
local get_items = function(filter, compare)
|
|
local it = {}
|
|
for itemstring, itemdef in pairs(minetest.registered_items) do
|
|
if check_item(itemstring, filter) then
|
|
table.insert(it, {itemstring=itemstring, itemdef=itemdef})
|
|
end
|
|
end
|
|
local internal_compare
|
|
if not compare then
|
|
-- Default sorting: Move description-less items and
|
|
-- items not_in_creative_inventory=1 to the end, then sort by itemstring.
|
|
internal_compare = function(t1, t2)
|
|
local t1d = minetest.registered_items[t1.itemstring].description
|
|
local t2d = minetest.registered_items[t2.itemstring].description
|
|
local t1g = minetest.get_item_group(t1.itemstring, "not_in_creative_inventory")
|
|
local t2g = minetest.get_item_group(t2.itemstring, "not_in_creative_inventory")
|
|
if (t1d == "" and t2d ~= "") then
|
|
return false
|
|
elseif (t1d ~= "" and t2d == "") then
|
|
return true
|
|
end
|
|
if (t1g == 1 and t2g == 0) then
|
|
return false
|
|
elseif (t1g == 0 and t2g == 1) then
|
|
return true
|
|
end
|
|
return t1.itemstring < t2.itemstring
|
|
end
|
|
else
|
|
internal_compare = function(t1, t2)
|
|
return compare(t1.itemstring, t2.itemstring)
|
|
end
|
|
end
|
|
table.sort(it, internal_compare)
|
|
return it
|
|
end
|
|
|
|
local xsize_norm = 12
|
|
local ysize_norm = 9
|
|
|
|
-- Opens the item selection dialog for player with the given filter function at page.
|
|
-- The dialog has unique identifier dialogname.
|
|
-- Returns: Number of items it displays.
|
|
local show_dialog_page = function(playername, dialogname, filter, compare, page)
|
|
local items
|
|
if player_filters[playername] == nil then
|
|
player_filters[playername] = filter
|
|
player_compares[playername] = compare
|
|
items = get_items(filter, compare)
|
|
player_filter_results[playername] = items
|
|
end
|
|
items = player_filter_results[playername]
|
|
local xsize, ysize, total_pages
|
|
if #items < xsize_norm * ysize_norm then
|
|
xsize = xsize_norm
|
|
ysize = math.ceil(#items / xsize)
|
|
total_pages = 1
|
|
else
|
|
xsize = xsize_norm
|
|
ysize = ysize_norm
|
|
total_pages = math.ceil(#items / (xsize * ysize))
|
|
end
|
|
local bg = ""
|
|
-- Legacy default formspec background (MT<=0.4.17)
|
|
if minetest.get_modpath("default") and default.gui_bg then
|
|
bg = default.gui_bg .. default.gui_bg_img .. default.gui_slots
|
|
end
|
|
if #items == 0 then
|
|
local form = "size[6,2]"..
|
|
bg ..
|
|
"label[0,0;There are no items to choose from.]"..
|
|
"button_exit[0,1;2,1;cancel;OK]"
|
|
minetest.show_formspec(playername, "select_item:page1", form)
|
|
return #items
|
|
end
|
|
local form = "size["..xsize..","..(ysize+1).."]" .. bg
|
|
local x = 0
|
|
local y = 0.5
|
|
if page == nil then page = 1 end
|
|
local start = 1 + (page-1) * xsize * ysize
|
|
player_maxpage[playername] = total_pages
|
|
form = form .. "label[0,0;Select an item:]"
|
|
for i=start, #items do
|
|
local itemstring = items[i].itemstring
|
|
local itemdef = items[i].itemdef
|
|
local name = "item_"..itemstring
|
|
form = form .. "item_image_button["..x..","..y..";1,1;"..itemstring..";"..name..";]"
|
|
if itemdef.description == nil or itemdef.description == "" then
|
|
form = form .. "tooltip["..name..";"..itemstring.."]"
|
|
end
|
|
|
|
x = x + 1
|
|
if x >= xsize then
|
|
x = 0
|
|
y = y + 1
|
|
if y >= ysize then
|
|
break
|
|
end
|
|
end
|
|
end
|
|
local ynav = (ysize + 0.5)
|
|
if total_pages > 1 then
|
|
form = form .. "button[0,"..ynav..";1,1;previous;<]"
|
|
form = form .. "button[1,"..ynav..";1,1;next;>]"
|
|
form = form .. "label[2,"..ynav..";"..minetest.formspec_escape(string.format("Page %d/%d", page, total_pages)).."]"
|
|
end
|
|
form = form .. "button_exit["..(xsize-2)..","..ynav..";2,1;cancel;Cancel]"
|
|
minetest.show_formspec(playername, "select_item:page"..page.."%%"..dialogname, form)
|
|
return #items
|
|
end
|
|
|
|
select_item.show_dialog = function(playername, dialogname, filter, compare)
|
|
show_dialog_page(playername, dialogname, filter, compare, 1)
|
|
end
|
|
|
|
local callbacks = {}
|
|
select_item.register_on_select_item = function(callback)
|
|
table.insert(callbacks, callback)
|
|
end
|
|
|
|
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
|
local playername = player:get_player_name()
|
|
if string.sub(formname, 1, 16) == "select_item:page" then
|
|
-- Parse formname
|
|
local rest = string.sub(formname, 17, string.len(formname))
|
|
local split = string.split(rest, "%%", true, 2)
|
|
local page = tonumber(split[1])
|
|
local dialogname = split[2]
|
|
|
|
local item
|
|
for field,_ in pairs(fields) do
|
|
if string.sub(field, 1, 5) == "item_" then
|
|
item = string.sub(field, 6, string.len(field))
|
|
break
|
|
end
|
|
end
|
|
if item or fields.quit then
|
|
local close = true
|
|
for i=1,#callbacks do
|
|
local ret = callbacks[i](playername, dialogname, item)
|
|
if ret == false then
|
|
close = false
|
|
end
|
|
end
|
|
if close then
|
|
minetest.close_formspec(playername, formname)
|
|
end
|
|
reset_player_info(playername)
|
|
end
|
|
if fields.quit or fields.cancel then
|
|
reset_player_info(playername)
|
|
end
|
|
if page ~= nil then
|
|
if fields.previous and page > 1 then
|
|
show_dialog_page(player:get_player_name(), dialogname, player_filters[playername], player_compares[playername], page - 1)
|
|
elseif fields.next then
|
|
local maxpage = player_maxpage[playername]
|
|
if maxpage and (page + 1 <= maxpage) then
|
|
show_dialog_page(playername, dialogname, player_filters[playername], player_compares[playername], page + 1)
|
|
end
|
|
if not maxpage then
|
|
minetest.log("warning", "[select_item] Player "..playername.." managed to navigate select_item menu without maxpage set!")
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end)
|
|
|
|
minetest.register_on_leaveplayer(function(player)
|
|
reset_player_info(player:get_player_name())
|
|
end)
|