1 local F = minetest.formspec_escape
2 local S = minetest.get_translator("chest_of_everything")
4 local detached_inventories = {}
6 -- Per-player lists (indexed by player name)
7 local current_pages = {} -- current page number
8 local current_max_pages = {} -- current max. page number
9 local current_searches = {} -- current search string
13 local SLOTS = SLOTS_W * SLOTS_H
15 -- This determines how the items are sorted
16 -- "by_type": Sort by item type (tool/craftitem/node/"chest_of_everything" items), then alphabetically by itemstring
17 -- "abc": Alphabetically by itemstring
18 local SORT_MODE = "by_type"
20 local all_items_list -- cached list of all items
22 -- Create detached inventories
23 local function add_detached_inventories(player)
24 local name = player:get_player_name()
25 local inv_items = minetest.create_detached_inventory("chest_of_everything_items_"..name, {
26 allow_move = function(inv, from_list, from_index, to_list, to_index, count, player)
29 allow_put = function(inv, listname, index, stack, player)
32 allow_take = function(inv, listname, index, stack, player)
36 local inv_trash = minetest.create_detached_inventory("chest_of_everything_trash_"..name, {
37 allow_take = function(inv, listname, index, stack, player)
40 allow_move = function(inv, from_list, from_index, to_list, to_index, count, player)
43 on_put = function(inv, listname, index, stack, player)
44 inv:set_list(listname, {})
47 inv_trash:set_size("main", 1)
48 detached_inventories[name] = { items = inv_items, trash = inv_trash }
51 local sort_items_by_type = function(item1, item2)
52 --[[ Sort items in this order:
59 * Items from the 'broken' mod
61 local def1 = minetest.registered_items[item1]
62 local def2 = minetest.registered_items[item2]
63 local tool1 = def1.type == "tool"
64 local tool2 = def2.type == "tool"
65 local testtool1 = minetest.get_item_group(item1, "testtool") == 1
66 local testtool2 = minetest.get_item_group(item2, "testtool") == 1
67 local dummy1 = minetest.get_item_group(item1, "dummy") == 1
68 local dummy2 = minetest.get_item_group(item2, "dummy") == 1
69 local broken1 = def1.mod_origin == "broken"
70 local broken2 = def2.mod_origin == "broken"
71 local craftitem1 = def1.type == "craft"
72 local craftitem2 = def2.type == "craft"
73 if item1 == "chest_of_everything:bag" then
75 elseif item2 == "chest_of_everything:bag" then
77 elseif item1 == "chest_of_everything:chest" then
79 elseif item2 == "chest_of_everything:chest" then
81 elseif dummy1 and not dummy2 then
83 elseif not dummy1 and dummy2 then
85 elseif broken1 and not broken2 then
87 elseif not broken1 and broken2 then
89 elseif testtool1 and not testtool2 then
91 elseif not testtool1 and testtool2 then
93 elseif tool1 and not tool2 then
95 elseif not tool1 and tool2 then
97 elseif craftitem1 and not craftitem2 then
99 elseif not craftitem1 and craftitem2 then
106 local sort_items_alphabetically = function(item1, item2)
110 local collect_items = function(filter, lang_code)
113 filter = string.trim(filter)
114 filter = string.lower(filter) -- to make sure the search is case-insensitive
116 for itemstring, def in pairs(minetest.registered_items) do
117 if itemstring ~= "" and itemstring ~= "unknown" and itemstring ~= "ignore" then
118 if filter and lang_code then
119 local desc = ItemStack(itemstring):get_description()
121 -- First, try to match original description
123 local ldesc = string.lower(desc)
124 matches = string.match(ldesc, filter) ~= nil
125 -- Second, try to match translated description
127 local tdesc = minetest.get_translated_string(lang_code, desc)
129 tdesc = string.lower(tdesc)
130 matches = string.match(tdesc, filter) ~= nil
133 -- Third, try to match translated short description
135 local sdesc = ItemStack(itemstring):get_short_description()
137 sdesc = minetest.get_translated_string(lang_code, sdesc)
138 sdesc = string.lower(sdesc)
139 matches = string.match(sdesc, filter) ~= nil
144 -- Fourth, try to match itemstring
146 matches = string.match(itemstring, filter) ~= nil
149 -- If item was matched, add to item list
151 table.insert(items, itemstring)
154 table.insert(items, itemstring)
159 if SORT_MODE == "by_type" then
160 compare = sort_items_by_type
161 elseif SORT_MODE == "abc" then
162 compare = sort_items_alphabetically
164 table.sort(items, compare)
169 local function update_inventory(name)
170 local search = current_searches[name] or ""
173 items = all_items_list
175 local lang_code = minetest.get_player_information(name).lang_code
176 items = collect_items(search, lang_code)
178 local max_page = math.ceil(#items / SLOTS)
179 current_max_pages[name] = max_page
181 local inv = detached_inventories[name].items
182 inv:set_size("main", #items)
183 inv:set_list("main", items)
184 if not current_pages[name] then
185 current_pages[name] = 1
187 if current_pages[name] > max_page then
188 current_pages[name] = max_page
190 if current_pages[name] < 1 then
191 current_pages[name] = 1
195 local function get_formspec(page, name)
196 local start = 0 + (page-1)*SLOTS
200 local player = minetest.get_player_by_name(name)
201 local playerinvsize = player:get_inventory():get_size("main")
202 local hotbarsize = player:hud_get_hotbar_itemcount()
203 local pinv_w, pinv_h, pinv_x
205 pinv_h = math.ceil(playerinvsize / pinv_w)
206 pinv_w = math.min(pinv_w, 10)
207 pinv_h = math.min(pinv_w, 4)
214 local max_page = current_max_pages[name]
216 pagestr = "button[0,5.45;1,1;chest_of_everything_prev;"..F(S("<")).."]"..
217 "button[1,5.45;1,1;chest_of_everything_next;"..F(S(">")).."]"..
218 "label[0,5.1;"..F(S("Page: @1/@2", page, max_page)).."]"
221 local search_text = current_searches[name] or ""
224 if current_max_pages[name] > 0 then
225 inventory_list = "list[detached:chest_of_everything_items_"..name..";main;0,0;"..SLOTS_W..","..SLOTS_H..";"..start.."]"
227 inventory_list = "label[2.5,2.5;"..F(S("No items found.")).."]"
228 if search_text ~= "" then
229 inventory_list = inventory_list .. "button[2.5,3.25;3,0.8;search_button_reset_big;"..F(S("Reset search")).."]"
233 return "size[10,10.5]"..
235 "list[current_player;main;"..pinv_x..",6.75;"..pinv_w..","..pinv_h..";]" ..
236 "label[9,5.1;"..F(S("Trash:")).."]" ..
237 "list[detached:chest_of_everything_trash_"..name..";main;9,5.5;1,1]" ..
238 "field[2.2,5.75;4,1;search;;"..F(search_text).."]" ..
239 "field_close_on_enter[search;false]" ..
240 "button[6,5.45;1.6,1;search_button_start;"..F(S("Search")).."]" ..
241 "button[7.6,5.45;0.8,1;search_button_reset;"..F(S("X")).."]" ..
242 "tooltip[search_button_reset;"..F(S("Reset search")).."]" ..
244 "listring[detached:chest_of_everything_items_"..name..";main]"..
245 "listring[current_player;main]"..
246 "listring[detached:chest_of_everything_trash_"..name..";main]"
249 local show_formspec = function(name)
250 local page = current_pages[name]
251 local form = get_formspec(page, name)
252 minetest.show_formspec(name, "chest_of_everything:getitem", form)
256 minetest.register_on_player_receive_fields(function(player, formname, fields)
257 if formname ~= "chest_of_everything:getitem" then
260 local name = player:get_player_name()
261 local page = current_pages[name]
262 local old_page = page
263 -- Next page or previous page
264 if fields.chest_of_everything_next or fields.chest_of_everything_prev then
265 if fields.chest_of_everything_next then
267 elseif fields.chest_of_everything_prev then
270 -- Handle page change
274 local max_page = current_max_pages[name]
275 if page > max_page then
278 if page ~= old_page then
279 current_pages[name] = page
284 elseif (fields.search_button_start or (fields.key_enter and fields.key_enter_field == "search")) and fields.search then
285 current_searches[name] = fields.search
286 update_inventory(name)
287 show_formspec(name, fields.search)
290 elseif (fields.search_button_reset or fields.search_button_reset_big) then
291 current_searches[name] = ""
292 update_inventory(name)
298 minetest.register_tool("chest_of_everything:bag", {
299 description = S("Bag of Everything") .. "\n" ..
300 S("Grants access to all items"),
301 inventory_image = "chest_of_everything_bag.png",
302 wield_image = "chest_of_everything_bag.png",
303 groups = { disable_repair = 1 },
304 on_use = function(itemstack, user)
305 if user and user:is_player() then
306 local name = user:get_player_name()
312 minetest.register_node("chest_of_everything:chest", {
313 description = S("Chest of Everything") .. "\n" ..
314 S("Grants access to all items"),
315 tiles ={"chest_of_everything_chest.png^[sheet:2x2:0,0", "chest_of_everything_chest.png^[sheet:2x2:0,0",
316 "chest_of_everything_chest.png^[sheet:2x2:1,0", "chest_of_everything_chest.png^[sheet:2x2:1,0",
317 "chest_of_everything_chest.png^[sheet:2x2:1,0", "chest_of_everything_chest.png^[sheet:2x2:0,1"},
319 groups = { dig_immediate=2, choppy=3 },
320 is_ground_content = false,
321 on_construct = function(pos)
322 local meta = minetest.get_meta(pos)
323 meta:set_string("infotext", S("Chest of Everything"))
325 on_rightclick = function(pos, node, clicker)
326 if clicker and clicker:is_player() then
327 local name = clicker:get_player_name()
334 minetest.register_on_mods_loaded(function()
335 all_items_list = collect_items()
338 minetest.register_on_joinplayer(function(player)
339 local name = player:get_player_name()
340 current_searches[name] = ""
341 current_pages[name] = 1
342 current_max_pages[name] = 0
343 add_detached_inventories(player)
344 update_inventory(name)
347 minetest.register_on_leaveplayer(function(player)
348 local name = player:get_player_name()
349 current_pages[name] = nil
350 current_max_pages[name] = nil
351 current_searches[name] = nil