]> git.lizzy.rs Git - minetest.git/blob - games/devtest/mods/chest_of_everything/init.lua
DevTest: `chest_of_everything` mod: Add search, bag, improve formspec (#13064)
[minetest.git] / games / devtest / mods / chest_of_everything / init.lua
1 local F = minetest.formspec_escape
2 local S = minetest.get_translator("chest_of_everything")
3
4 local detached_inventories = {}
5
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
10
11 local SLOTS_W = 10
12 local SLOTS_H = 5
13 local SLOTS = SLOTS_W * SLOTS_H
14
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"
19
20 local all_items_list -- cached list of all items
21
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)
27                         return 0
28                 end,
29                 allow_put = function(inv, listname, index, stack, player)
30                         return 0
31                 end,
32                 allow_take = function(inv, listname, index, stack, player)
33                         return -1
34                 end,
35         }, name)
36         local inv_trash = minetest.create_detached_inventory("chest_of_everything_trash_"..name, {
37                 allow_take = function(inv, listname, index, stack, player)
38                         return 0
39                 end,
40                 allow_move = function(inv, from_list, from_index, to_list, to_index, count, player)
41                         return 0
42                 end,
43                 on_put = function(inv, listname, index, stack, player)
44                         inv:set_list(listname, {})
45                 end,
46         }, name)
47         inv_trash:set_size("main", 1)
48         detached_inventories[name] = { items = inv_items, trash = inv_trash }
49 end
50
51 local sort_items_by_type = function(item1, item2)
52         --[[ Sort items in this order:
53         * Bag of Everything
54         * Chest of Everything
55         * Test tools
56         * Other tools
57         * Craftitems
58         * Other items
59         * Items from the 'broken' mod
60         * Dummy items ]]
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
74                 return true
75         elseif item2 == "chest_of_everything:bag" then
76                 return false
77         elseif item1 == "chest_of_everything:chest" then
78                 return true
79         elseif item2 == "chest_of_everything:chest" then
80                 return false
81         elseif dummy1 and not dummy2 then
82                 return false
83         elseif not dummy1 and dummy2 then
84                 return true
85         elseif broken1 and not broken2 then
86                 return false
87         elseif not broken1 and broken2 then
88                 return true
89         elseif testtool1 and not testtool2 then
90                 return true
91         elseif not testtool1 and testtool2 then
92                 return false
93         elseif tool1 and not tool2 then
94                 return true
95         elseif not tool1 and tool2 then
96                 return false
97         elseif craftitem1 and not craftitem2 then
98                 return true
99         elseif not craftitem1 and craftitem2 then
100                 return false
101         else
102                 return item1 < item2
103         end
104 end
105
106 local sort_items_alphabetically = function(item1, item2)
107         return item1 < item2
108 end
109
110 local collect_items = function(filter, lang_code)
111         local items = {}
112         if filter then
113                 filter = string.trim(filter)
114                 filter = string.lower(filter) -- to make sure the search is case-insensitive
115         end
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()
120                                 local matches
121                                 -- First, try to match original description
122                                 if desc ~= "" then
123                                         local ldesc = string.lower(desc)
124                                         matches = string.match(ldesc, filter) ~= nil
125                                         -- Second, try to match translated description
126                                         if not matches then
127                                                 local tdesc = minetest.get_translated_string(lang_code, desc)
128                                                 if tdesc ~= "" then
129                                                         tdesc = string.lower(tdesc)
130                                                         matches = string.match(tdesc, filter) ~= nil
131                                                 end
132                                         end
133                                         -- Third, try to match translated short description
134                                         if not matches then
135                                                 local sdesc = ItemStack(itemstring):get_short_description()
136                                                 if sdesc ~= "" then
137                                                         sdesc = minetest.get_translated_string(lang_code, sdesc)
138                                                         sdesc = string.lower(sdesc)
139                                                         matches = string.match(sdesc, filter) ~= nil
140                                                 end
141                                         end
142
143                                 end
144                                 -- Fourth, try to match itemstring
145                                 if not matches then
146                                         matches = string.match(itemstring, filter) ~= nil
147                                 end
148
149                                 -- If item was matched, add to item list
150                                 if matches then
151                                         table.insert(items, itemstring)
152                                 end
153                         else
154                                 table.insert(items, itemstring)
155                         end
156                 end
157         end
158         local compare
159         if SORT_MODE == "by_type" then
160                 compare = sort_items_by_type
161         elseif SORT_MODE == "abc" then
162                 compare = sort_items_alphabetically
163         end
164         table.sort(items, compare)
165
166         return items
167 end
168
169 local function update_inventory(name)
170         local search = current_searches[name] or ""
171         local items
172         if search == "" then
173                 items = all_items_list
174         else
175                 local lang_code = minetest.get_player_information(name).lang_code
176                 items = collect_items(search, lang_code)
177         end
178         local max_page = math.ceil(#items / SLOTS)
179         current_max_pages[name] = max_page
180
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
186         end
187         if current_pages[name] > max_page then
188                 current_pages[name] = max_page
189         end
190         if current_pages[name] < 1 then
191                 current_pages[name] = 1
192         end
193 end
194
195 local function get_formspec(page, name)
196         local start = 0 + (page-1)*SLOTS
197         if not name then
198                 return ""
199         end
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
204         pinv_w = hotbarsize
205         pinv_h = math.ceil(playerinvsize / pinv_w)
206         pinv_w = math.min(pinv_w, 10)
207         pinv_h = math.min(pinv_w, 4)
208         pinv_x = 0
209         if pinv_w < 9 then
210                 pinv_x = 1
211         end
212
213         local pagestr = ""
214         local max_page = current_max_pages[name]
215         if max_page > 1 then
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)).."]"
219         end
220
221         local search_text = current_searches[name] or ""
222
223         local inventory_list
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.."]"
226         else
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")).."]"
230                 end
231         end
232
233         return "size[10,10.5]"..
234         inventory_list ..
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")).."]" ..
243         pagestr ..
244         "listring[detached:chest_of_everything_items_"..name..";main]"..
245         "listring[current_player;main]"..
246         "listring[detached:chest_of_everything_trash_"..name..";main]"
247 end
248
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)
253         return true
254 end
255
256 minetest.register_on_player_receive_fields(function(player, formname, fields)
257         if formname ~= "chest_of_everything:getitem" then
258                 return
259         end
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
266                         page = page + 1
267                 elseif fields.chest_of_everything_prev then
268                         page = page - 1
269                 end
270                 -- Handle page change
271                 if page < 1 then
272                         page = 1
273                 end
274                 local max_page = current_max_pages[name]
275                 if page > max_page then
276                         page = max_page
277                 end
278                 if page ~= old_page then
279                         current_pages[name] = page
280                         show_formspec(name)
281                 end
282                 return
283         -- Search
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)
288                 return
289         -- Reset search
290         elseif (fields.search_button_reset or fields.search_button_reset_big) then
291                 current_searches[name] = ""
292                 update_inventory(name)
293                 show_formspec(name)
294                 return
295         end
296 end)
297
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()
307                         show_formspec(name)
308                 end
309         end,
310 })
311
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"},
318         paramtype2 = "4dir",
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"))
324         end,
325         on_rightclick = function(pos, node, clicker)
326                 if clicker and clicker:is_player() then
327                         local name = clicker:get_player_name()
328                         show_formspec(name)
329                 end
330         end,
331 })
332
333
334 minetest.register_on_mods_loaded(function()
335         all_items_list = collect_items()
336 end)
337
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)
345 end)
346
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
352 end)