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).
## 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
one item (which triggers a callback) or abort selection
(in which case nothing happens).
@ -21,13 +21,20 @@ to filter out unwanted items.
#### Parameters
* `playername`: Name of player to show dialog to
* `dialogname`: Identifier of the dialog (must not contain “%%”)
* `filter`: Optional filter function to narrow down the visible
items (see below)
* `filter`: (optional) Filter function to narrow down the visible
items (see below)
* `compare`: (optional) Custom compare function for sorting,
used in `table.sort`.
Recommended form of `dialogname` is “`<modname>:<name>`”. Almost all
names are allowed, but they must never contain the substring “%%”.
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
The filter function has the function signature `filter(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
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
@ -49,33 +51,40 @@ local check_item = function(itemstring, filter)
return true
end
local get_items = function(filter)
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
-- Default sorting: Move description-less items and
-- items not_in_creative_inventory=1 to the end, then sort by itemstring.
local 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
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
if (t1g == 1 and t2g == 0) then
return false
elseif (t1g == 0 and t2g == 1) then
return true
else
internal_compare = function(t1, t2)
return compare(t1.itemstring, t2.itemstring)
end
return t1.itemstring < t2.itemstring
end
table.sort(it, compare)
table.sort(it, internal_compare)
return it
end
@ -85,11 +94,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, page)
local show_dialog_page = function(playername, dialogname, filter, compare, page)
local items
if player_filters[playername] == nil then
player_filters[playername] = filter
items = get_items(filter)
player_compares[playername] = compare
items = get_items(filter, compare)
player_filter_results[playername] = items
end
items = player_filter_results[playername]
@ -151,8 +161,8 @@ local show_dialog_page = function(playername, dialogname, filter, page)
return #items
end
select_item.show_dialog = function(playername, dialogname, filter)
show_dialog_page(playername, dialogname, filter, 1)
select_item.show_dialog = function(playername, dialogname, filter, compare)
show_dialog_page(playername, dialogname, filter, compare, 1)
end
local callbacks = {}
@ -194,11 +204,11 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
end
if page ~= nil 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
local maxpage = player_maxpage[playername]
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
if not maxpage then
minetest.log("warning", "[select_item] Player "..playername.." managed to navigate select_item menu without maxpage set!")