From a3177b89d8e68d39b8e2ad24e88cd87820d1135a Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Mon, 9 Jan 2023 21:40:41 +0100 Subject: [PATCH] DevTest: `chest_of_everything` mod: Add search, bag, improve formspec (#13064) --- .../devtest/mods/chest_of_everything/init.lua | 457 +++++++++++++----- .../devtest/mods/chest_of_everything/mod.conf | 2 +- .../textures/chest_of_everything_bag.png | Bin 0 -> 197 bytes .../devtest/mods/give_initial_stuff/init.lua | 2 +- 4 files changed, 335 insertions(+), 126 deletions(-) create mode 100644 games/devtest/mods/chest_of_everything/textures/chest_of_everything_bag.png diff --git a/games/devtest/mods/chest_of_everything/init.lua b/games/devtest/mods/chest_of_everything/init.lua index 11bc93d21..ece236f22 100644 --- a/games/devtest/mods/chest_of_everything/init.lua +++ b/games/devtest/mods/chest_of_everything/init.lua @@ -1,91 +1,56 @@ local F = minetest.formspec_escape +local S = minetest.get_translator("chest_of_everything") --- Create a detached inventory -local inv_everything = minetest.create_detached_inventory("everything", { - allow_move = function(inv, from_list, from_index, to_list, to_index, count, player) - return 0 - end, - allow_put = function(inv, listname, index, stack, player) - return 0 - end, - allow_take = function(inv, listname, index, stack, player) - return -1 - end, -}) -local inv_trash = minetest.create_detached_inventory("trash", { - allow_take = function(inv, listname, index, stack, player) - return 0 - end, - allow_move = function(inv, from_list, from_index, to_list, to_index, count, player) - return 0 - end, - on_put = function(inv, listname, index, stack, player) - inv:set_list("main", {}) - end, -}) -inv_trash:set_size("main", 1) +local detached_inventories = {} -local max_page = 1 +-- Per-player lists (indexed by player name) +local current_pages = {} -- current page number +local current_max_pages = {} -- current max. page number +local current_searches = {} -- current search string -local function get_chest_formspec(page) - local start = 0 + (page-1)*32 - return "size[8,9]".. - "list[detached:everything;main;0,0;8,4;"..start.."]".. - "list[current_player;main;0,5;8,4;]" .. - "label[6,4;Trash:]" .. - "list[detached:trash;main;7,4;1,1]" .. - "button[0,4;1,1;chest_of_everything_prev;"..F("<").."]".. - "button[1,4;1,1;chest_of_everything_next;"..F(">").."]".. - "label[2,4;"..F("Page: "..page).."]".. - "listring[detached:everything;main]".. - "listring[current_player;main]".. - "listring[detached:trash;main]" +local SLOTS_W = 10 +local SLOTS_H = 5 +local SLOTS = SLOTS_W * SLOTS_H + +-- This determines how the items are sorted +-- "by_type": Sort by item type (tool/craftitem/node/"chest_of_everything" items), then alphabetically by itemstring +-- "abc": Alphabetically by itemstring +local SORT_MODE = "by_type" + +local all_items_list -- cached list of all items + +-- Create detached inventories +local function add_detached_inventories(player) + local name = player:get_player_name() + local inv_items = minetest.create_detached_inventory("chest_of_everything_items_"..name, { + allow_move = function(inv, from_list, from_index, to_list, to_index, count, player) + return 0 + end, + allow_put = function(inv, listname, index, stack, player) + return 0 + end, + allow_take = function(inv, listname, index, stack, player) + return -1 + end, + }, name) + local inv_trash = minetest.create_detached_inventory("chest_of_everything_trash_"..name, { + allow_take = function(inv, listname, index, stack, player) + return 0 + end, + allow_move = function(inv, from_list, from_index, to_list, to_index, count, player) + return 0 + end, + on_put = function(inv, listname, index, stack, player) + inv:set_list(listname, {}) + end, + }, name) + inv_trash:set_size("main", 1) + detached_inventories[name] = { items = inv_items, trash = inv_trash } end -minetest.register_node("chest_of_everything:chest", { - description = "Chest of Everything" .. "\n" .. - "Grants access to all items", - tiles ={"chest_of_everything_chest.png^[sheet:2x2:0,0", "chest_of_everything_chest.png^[sheet:2x2:0,0", - "chest_of_everything_chest.png^[sheet:2x2:1,0", "chest_of_everything_chest.png^[sheet:2x2:1,0", - "chest_of_everything_chest.png^[sheet:2x2:1,0", "chest_of_everything_chest.png^[sheet:2x2:0,1"}, - paramtype2 = "4dir", - groups = {dig_immediate=2,choppy=3}, - is_ground_content = false, - on_construct = function(pos) - local meta = minetest.get_meta(pos) - meta:set_string("infotext", "Chest of Everything") - meta:set_int("page", 1) - meta:set_string("formspec", get_chest_formspec(1)) - end, - on_receive_fields = function(pos, formname, fields, sender) - if formname == "" then - local meta = minetest.get_meta(pos) - local page = meta:get_int("page") - if fields.chest_of_everything_prev then - page = page - 1 - elseif fields.chest_of_everything_next then - page = page + 1 - end - if page < 1 then - page = 1 - end - if page > max_page then - page = max_page - end - meta:set_int("page", page) - meta:set_string("formspec", get_chest_formspec(page)) - end - end, -}) - -minetest.register_on_mods_loaded(function() - local items = {} - for itemstring,_ in pairs(minetest.registered_items) do - if itemstring ~= "" and itemstring ~= "unknown" and itemstring ~= "ignore" then - table.insert(items, itemstring) - end - end +local sort_items_by_type = function(item1, item2) --[[ Sort items in this order: + * Bag of Everything * Chest of Everything * Test tools * Other tools @@ -93,51 +58,295 @@ minetest.register_on_mods_loaded(function() * Other items * Items from the 'broken' mod * Dummy items ]] - local function compare(item1, item2) - local def1 = minetest.registered_items[item1] - local def2 = minetest.registered_items[item2] - local tool1 = def1.type == "tool" - local tool2 = def2.type == "tool" - local testtool1 = minetest.get_item_group(item1, "testtool") == 1 - local testtool2 = minetest.get_item_group(item2, "testtool") == 1 - local dummy1 = minetest.get_item_group(item1, "dummy") == 1 - local dummy2 = minetest.get_item_group(item2, "dummy") == 1 - local broken1 = def1.mod_origin == "broken" - local broken2 = def2.mod_origin == "broken" - local craftitem1 = def1.type == "craft" - local craftitem2 = def2.type == "craft" - if item1 == "chest_of_everything:chest" then - return true - elseif item2 == "chest_of_everything:chest" then - return false - elseif dummy1 and not dummy2 then - return false - elseif not dummy1 and dummy2 then - return true - elseif broken1 and not broken2 then - return false - elseif not broken1 and broken2 then - return true - elseif testtool1 and not testtool2 then - return true - elseif not testtool1 and testtool2 then - return false - elseif tool1 and not tool2 then - return true - elseif not tool1 and tool2 then - return false - elseif craftitem1 and not craftitem2 then - return true - elseif not craftitem1 and craftitem2 then - return false - else - return item1 < item2 + local def1 = minetest.registered_items[item1] + local def2 = minetest.registered_items[item2] + local tool1 = def1.type == "tool" + local tool2 = def2.type == "tool" + local testtool1 = minetest.get_item_group(item1, "testtool") == 1 + local testtool2 = minetest.get_item_group(item2, "testtool") == 1 + local dummy1 = minetest.get_item_group(item1, "dummy") == 1 + local dummy2 = minetest.get_item_group(item2, "dummy") == 1 + local broken1 = def1.mod_origin == "broken" + local broken2 = def2.mod_origin == "broken" + local craftitem1 = def1.type == "craft" + local craftitem2 = def2.type == "craft" + if item1 == "chest_of_everything:bag" then + return true + elseif item2 == "chest_of_everything:bag" then + return false + elseif item1 == "chest_of_everything:chest" then + return true + elseif item2 == "chest_of_everything:chest" then + return false + elseif dummy1 and not dummy2 then + return false + elseif not dummy1 and dummy2 then + return true + elseif broken1 and not broken2 then + return false + elseif not broken1 and broken2 then + return true + elseif testtool1 and not testtool2 then + return true + elseif not testtool1 and testtool2 then + return false + elseif tool1 and not tool2 then + return true + elseif not tool1 and tool2 then + return false + elseif craftitem1 and not craftitem2 then + return true + elseif not craftitem1 and craftitem2 then + return false + else + return item1 < item2 + end +end + +local sort_items_alphabetically = function(item1, item2) + return item1 < item2 +end + +local collect_items = function(filter, lang_code) + local items = {} + if filter then + filter = string.trim(filter) + filter = string.lower(filter) -- to make sure the search is case-insensitive + end + for itemstring, def in pairs(minetest.registered_items) do + if itemstring ~= "" and itemstring ~= "unknown" and itemstring ~= "ignore" then + if filter and lang_code then + local desc = ItemStack(itemstring):get_description() + local matches + -- First, try to match original description + if desc ~= "" then + local ldesc = string.lower(desc) + matches = string.match(ldesc, filter) ~= nil + -- Second, try to match translated description + if not matches then + local tdesc = minetest.get_translated_string(lang_code, desc) + if tdesc ~= "" then + tdesc = string.lower(tdesc) + matches = string.match(tdesc, filter) ~= nil + end + end + -- Third, try to match translated short description + if not matches then + local sdesc = ItemStack(itemstring):get_short_description() + if sdesc ~= "" then + sdesc = minetest.get_translated_string(lang_code, sdesc) + sdesc = string.lower(sdesc) + matches = string.match(sdesc, filter) ~= nil + end + end + + end + -- Fourth, try to match itemstring + if not matches then + matches = string.match(itemstring, filter) ~= nil + end + + -- If item was matched, add to item list + if matches then + table.insert(items, itemstring) + end + else + table.insert(items, itemstring) + end end end + local compare + if SORT_MODE == "by_type" then + compare = sort_items_by_type + elseif SORT_MODE == "abc" then + compare = sort_items_alphabetically + end table.sort(items, compare) - inv_everything:set_size("main", #items) - max_page = math.ceil(#items / 32) - for i=1, #items do - inv_everything:add_item("main", items[i]) + + return items +end + +local function update_inventory(name) + local search = current_searches[name] or "" + local items + if search == "" then + items = all_items_list + else + local lang_code = minetest.get_player_information(name).lang_code + items = collect_items(search, lang_code) + end + local max_page = math.ceil(#items / SLOTS) + current_max_pages[name] = max_page + + local inv = detached_inventories[name].items + inv:set_size("main", #items) + inv:set_list("main", items) + if not current_pages[name] then + current_pages[name] = 1 + end + if current_pages[name] > max_page then + current_pages[name] = max_page + end + if current_pages[name] < 1 then + current_pages[name] = 1 + end +end + +local function get_formspec(page, name) + local start = 0 + (page-1)*SLOTS + if not name then + return "" + end + local player = minetest.get_player_by_name(name) + local playerinvsize = player:get_inventory():get_size("main") + local hotbarsize = player:hud_get_hotbar_itemcount() + local pinv_w, pinv_h, pinv_x + pinv_w = hotbarsize + pinv_h = math.ceil(playerinvsize / pinv_w) + pinv_w = math.min(pinv_w, 10) + pinv_h = math.min(pinv_w, 4) + pinv_x = 0 + if pinv_w < 9 then + pinv_x = 1 + end + + local pagestr = "" + local max_page = current_max_pages[name] + if max_page > 1 then + pagestr = "button[0,5.45;1,1;chest_of_everything_prev;"..F(S("<")).."]".. + "button[1,5.45;1,1;chest_of_everything_next;"..F(S(">")).."]".. + "label[0,5.1;"..F(S("Page: @1/@2", page, max_page)).."]" + end + + local search_text = current_searches[name] or "" + + local inventory_list + if current_max_pages[name] > 0 then + inventory_list = "list[detached:chest_of_everything_items_"..name..";main;0,0;"..SLOTS_W..","..SLOTS_H..";"..start.."]" + else + inventory_list = "label[2.5,2.5;"..F(S("No items found.")).."]" + if search_text ~= "" then + inventory_list = inventory_list .. "button[2.5,3.25;3,0.8;search_button_reset_big;"..F(S("Reset search")).."]" + end + end + + return "size[10,10.5]".. + inventory_list .. + "list[current_player;main;"..pinv_x..",6.75;"..pinv_w..","..pinv_h..";]" .. + "label[9,5.1;"..F(S("Trash:")).."]" .. + "list[detached:chest_of_everything_trash_"..name..";main;9,5.5;1,1]" .. + "field[2.2,5.75;4,1;search;;"..F(search_text).."]" .. + "field_close_on_enter[search;false]" .. + "button[6,5.45;1.6,1;search_button_start;"..F(S("Search")).."]" .. + "button[7.6,5.45;0.8,1;search_button_reset;"..F(S("X")).."]" .. + "tooltip[search_button_reset;"..F(S("Reset search")).."]" .. + pagestr .. + "listring[detached:chest_of_everything_items_"..name..";main]".. + "listring[current_player;main]".. + "listring[detached:chest_of_everything_trash_"..name..";main]" +end + +local show_formspec = function(name) + local page = current_pages[name] + local form = get_formspec(page, name) + minetest.show_formspec(name, "chest_of_everything:getitem", form) + return true +end + +minetest.register_on_player_receive_fields(function(player, formname, fields) + if formname ~= "chest_of_everything:getitem" then + return + end + local name = player:get_player_name() + local page = current_pages[name] + local old_page = page + -- Next page or previous page + if fields.chest_of_everything_next or fields.chest_of_everything_prev then + if fields.chest_of_everything_next then + page = page + 1 + elseif fields.chest_of_everything_prev then + page = page - 1 + end + -- Handle page change + if page < 1 then + page = 1 + end + local max_page = current_max_pages[name] + if page > max_page then + page = max_page + end + if page ~= old_page then + current_pages[name] = page + show_formspec(name) + end + return + -- Search + elseif (fields.search_button_start or (fields.key_enter and fields.key_enter_field == "search")) and fields.search then + current_searches[name] = fields.search + update_inventory(name) + show_formspec(name, fields.search) + return + -- Reset search + elseif (fields.search_button_reset or fields.search_button_reset_big) then + current_searches[name] = "" + update_inventory(name) + show_formspec(name) + return end end) + +minetest.register_tool("chest_of_everything:bag", { + description = S("Bag of Everything") .. "\n" .. + S("Grants access to all items"), + inventory_image = "chest_of_everything_bag.png", + wield_image = "chest_of_everything_bag.png", + groups = { disable_repair = 1 }, + on_use = function(itemstack, user) + if user and user:is_player() then + local name = user:get_player_name() + show_formspec(name) + end + end, +}) + +minetest.register_node("chest_of_everything:chest", { + description = S("Chest of Everything") .. "\n" .. + S("Grants access to all items"), + tiles ={"chest_of_everything_chest.png^[sheet:2x2:0,0", "chest_of_everything_chest.png^[sheet:2x2:0,0", + "chest_of_everything_chest.png^[sheet:2x2:1,0", "chest_of_everything_chest.png^[sheet:2x2:1,0", + "chest_of_everything_chest.png^[sheet:2x2:1,0", "chest_of_everything_chest.png^[sheet:2x2:0,1"}, + paramtype2 = "4dir", + groups = { dig_immediate=2, choppy=3 }, + is_ground_content = false, + on_construct = function(pos) + local meta = minetest.get_meta(pos) + meta:set_string("infotext", S("Chest of Everything")) + end, + on_rightclick = function(pos, node, clicker) + if clicker and clicker:is_player() then + local name = clicker:get_player_name() + show_formspec(name) + end + end, +}) + + +minetest.register_on_mods_loaded(function() + all_items_list = collect_items() +end) + +minetest.register_on_joinplayer(function(player) + local name = player:get_player_name() + current_searches[name] = "" + current_pages[name] = 1 + current_max_pages[name] = 0 + add_detached_inventories(player) + update_inventory(name) +end) + +minetest.register_on_leaveplayer(function(player) + local name = player:get_player_name() + current_pages[name] = nil + current_max_pages[name] = nil + current_searches[name] = nil +end) diff --git a/games/devtest/mods/chest_of_everything/mod.conf b/games/devtest/mods/chest_of_everything/mod.conf index 4a4425e05..50dc2d547 100644 --- a/games/devtest/mods/chest_of_everything/mod.conf +++ b/games/devtest/mods/chest_of_everything/mod.conf @@ -1,2 +1,2 @@ name = chest_of_everything -description = Adds the chest of everything from which you can take all items +description = Adds the chest of everything and bag of everything from which you can take all items diff --git a/games/devtest/mods/chest_of_everything/textures/chest_of_everything_bag.png b/games/devtest/mods/chest_of_everything/textures/chest_of_everything_bag.png new file mode 100644 index 0000000000000000000000000000000000000000..7c189076d57d8349294c13db2cc688b43fd75e5e GIT binary patch literal 197 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`J)SO(Ar*|t2bljYPB1#~|N7jv z|I?RfG#YfaDe^d&u^cv7#QSl}MYcp9XO5rp%LI=cJMhEgP)=i}n?#Gaf=SY6>qDv* z3^9*CZ|0CX$R^!z{k?{ny~UA+b84p;6x?o974tK5On$@E#lw&o6W4H3n8!n=(Idv^ vz?{y$#Fq^Xj2zAdiHklTsJ|m2%)szrj{N