Add custom compare feature

This commit is contained in:
Wuzzy 2018-05-18 00:37:43 +02:00
parent b25a14188a
commit 87eb35a0ab
2 changed files with 44 additions and 27 deletions

13
API.md
View File

@ -7,7 +7,7 @@ to check for the mod's existence first (`minetest.get_modpath`
returns non-`nil` value). returns non-`nil` value).
## Functions ## Functions
### `select_item.show_dialog(playername, dialogname, filter)` ### `select_item.show_dialog(playername, dialogname, filter, compare)`
Shows an item selection dialog to a player. The player can choose Shows an item selection dialog to a player. The player can choose
one item (which triggers a callback) or abort selection one item (which triggers a callback) or abort selection
(in which case nothing happens). (in which case nothing happens).
@ -21,13 +21,20 @@ to filter out unwanted items.
#### Parameters #### Parameters
* `playername`: Name of player to show dialog to * `playername`: Name of player to show dialog to
* `dialogname`: Identifier of the dialog (must not contain “%%”) * `dialogname`: Identifier of the dialog (must not contain “%%”)
* `filter`: Optional filter function to narrow down the visible * `filter`: (optional) Filter function to narrow down the visible
items (see below) items (see below)
* `compare`: (optional) Custom compare function for sorting,
used in `table.sort`.
Recommended form of `dialogname` is “`<modname>:<name>`”. Almost all Recommended form of `dialogname` is “`<modname>:<name>`”. Almost all
names are allowed, but they must never contain the substring “%%”. names are allowed, but they must never contain the substring “%%”.
Example: `example:select_my_item` Example: `example:select_my_item`
Default sorting sorts items alphabetically by itemstring. It
moves items with empty description to the end, preceded by items
with description, but `not_in_creative_inventory=1`, and then
everything else to the beginning.
#### Filter function #### Filter function
The filter function has the function signature `filter(itemstring)`. The filter function has the function signature `filter(itemstring)`.
This function will be called for each item with the itemstring This function will be called for each item with the itemstring

View File

@ -3,11 +3,13 @@ select_item = {}
-- Cache for result of item filters -- Cache for result of item filters
local player_filters = {} local player_filters = {}
local player_filter_results = {} local player_filter_results = {}
local player_compares = {}
local player_maxpage = {} local player_maxpage = {}
local reset_player_info = function(playername) local reset_player_info = function(playername)
player_filters[playername] = nil player_filters[playername] = nil
player_filter_results[playername] = nil player_filter_results[playername] = nil
player_compares[playername] = nil
player_maxpage[playername] = nil player_maxpage[playername] = nil
end end
@ -49,33 +51,40 @@ local check_item = function(itemstring, filter)
return true return true
end end
local get_items = function(filter) local get_items = function(filter, compare)
local it = {} local it = {}
for itemstring, itemdef in pairs(minetest.registered_items) do for itemstring, itemdef in pairs(minetest.registered_items) do
if check_item(itemstring, filter) then if check_item(itemstring, filter) then
table.insert(it, {itemstring=itemstring, itemdef=itemdef}) table.insert(it, {itemstring=itemstring, itemdef=itemdef})
end end
end end
-- Default sorting: Move description-less items and local internal_compare
-- items not_in_creative_inventory=1 to the end, then sort by itemstring. if not compare then
local compare = function(t1, t2) -- Default sorting: Move description-less items and
local t1d = minetest.registered_items[t1.itemstring].description -- items not_in_creative_inventory=1 to the end, then sort by itemstring.
local t2d = minetest.registered_items[t2.itemstring].description internal_compare = function(t1, t2)
local t1g = minetest.get_item_group(t1.itemstring, "not_in_creative_inventory") local t1d = minetest.registered_items[t1.itemstring].description
local t2g = minetest.get_item_group(t2.itemstring, "not_in_creative_inventory") local t2d = minetest.registered_items[t2.itemstring].description
if (t1d == "" and t2d ~= "") then local t1g = minetest.get_item_group(t1.itemstring, "not_in_creative_inventory")
return false local t2g = minetest.get_item_group(t2.itemstring, "not_in_creative_inventory")
elseif (t1d ~= "" and t2d == "") then if (t1d == "" and t2d ~= "") then
return true 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 end
if (t1g == 1 and t2g == 0) then else
return false internal_compare = function(t1, t2)
elseif (t1g == 0 and t2g == 1) then return compare(t1.itemstring, t2.itemstring)
return true
end end
return t1.itemstring < t2.itemstring
end end
table.sort(it, compare) table.sort(it, internal_compare)
return it return it
end end
@ -85,11 +94,12 @@ local ysize_norm = 9
-- Opens the item selection dialog for player with the given filter function at page. -- Opens the item selection dialog for player with the given filter function at page.
-- The dialog has unique identifier dialogname. -- The dialog has unique identifier dialogname.
-- Returns: Number of items it displays. -- Returns: Number of items it displays.
local show_dialog_page = function(playername, dialogname, filter, page) local show_dialog_page = function(playername, dialogname, filter, compare, page)
local items local items
if player_filters[playername] == nil then if player_filters[playername] == nil then
player_filters[playername] = filter player_filters[playername] = filter
items = get_items(filter) player_compares[playername] = compare
items = get_items(filter, compare)
player_filter_results[playername] = items player_filter_results[playername] = items
end end
items = player_filter_results[playername] items = player_filter_results[playername]
@ -151,8 +161,8 @@ local show_dialog_page = function(playername, dialogname, filter, page)
return #items return #items
end end
select_item.show_dialog = function(playername, dialogname, filter) select_item.show_dialog = function(playername, dialogname, filter, compare)
show_dialog_page(playername, dialogname, filter, 1) show_dialog_page(playername, dialogname, filter, compare, 1)
end end
local callbacks = {} local callbacks = {}
@ -194,11 +204,11 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
end end
if page ~= nil then if page ~= nil then
if fields.previous and page > 1 then if fields.previous and page > 1 then
show_dialog_page(player:get_player_name(), dialogname, player_filters[playername], page - 1) show_dialog_page(player:get_player_name(), dialogname, player_filters[playername], player_compares[playername], page - 1)
elseif fields.next then elseif fields.next then
local maxpage = player_maxpage[playername] local maxpage = player_maxpage[playername]
if maxpage and (page + 1 <= maxpage) then if maxpage and (page + 1 <= maxpage) then
show_dialog_page(playername, dialogname, player_filters[playername], page + 1) show_dialog_page(playername, dialogname, player_filters[playername], player_compares[playername], page + 1)
end end
if not maxpage then if not maxpage then
minetest.log("warning", "[select_item] Player "..playername.." managed to navigate select_item menu without maxpage set!") minetest.log("warning", "[select_item] Player "..playername.." managed to navigate select_item menu without maxpage set!")