]> git.lizzy.rs Git - xdecor.git/blob - worktable.lua
Crafting guide : more code's simplification
[xdecor.git] / worktable.lua
1 local worktable = {}
2 screwdriver = screwdriver or {}
3 local xbg = default.gui_bg..default.gui_bg_img..default.gui_slots
4
5 local nodes = { -- Nodes allowed to be cut. Mod name = {node name}.
6         ["default"] = {"wood", "junglewood", "pine_wood", "acacia_wood",
7                 "tree", "jungletree", "pine_tree", "acacia_tree",
8                 "cobble", "mossycobble", "desert_cobble",
9                 "stone", "sandstone", "desert_stone", "obsidian",
10                 "stonebrick", "sandstonebrick", "desert_stonebrick", "obsidianbrick",
11                 "coalblock", "copperblock", "steelblock", "goldblock", 
12                 "bronzeblock", "mese", "diamondblock",
13                 "brick", "cactus", "ice", "meselamp", "glass", "obsidian_glass"},
14
15         ["xdecor"] = {"coalstone_tile", "desertstone_tile", "stone_rune", "stone_tile",
16                 "cactusbrick", "hard_clay", "packed_ice", "moonbrick",
17                 "woodframed_glass", "wood_tile"},
18
19         ["oresplus"] = {"emerald_block", "glowstone"},
20 }
21
22 local def = { -- Nodebox name, yield, definition.
23         {"nanoslab", 16, {-.5,-.5,-.5,0,-.4375,0}},
24         {"micropanel", 16, {-.5,-.5,-.5,.5,-.4375,0}},
25         {"microslab", 8, {-.5,-.5,-.5,.5,-.4375,.5}},
26         {"thinstair", 8, {{-.5,-.0625,-.5,.5,0,0},{-.5,.4375,0,.5,.5,.5}}},
27         {"cube", 4, {-.5,-.5,0,0,0,.5}},
28         {"panel", 4, {-.5,-.5,-.5,.5,0,0}},
29         {"slab", 2, {-.5,-.5,-.5,.5,0,.5}},
30         {"doublepanel", 2, {{-.5,-.5,-.5,.5,0,0},{-.5,0,0,.5,.5,.5}}},
31         {"halfstair", 2, {{-.5,-.5,-.5,0,0,.5},{-.5,0,0,0,.5,.5}}},
32         {"outerstair", 1, {{-.5,-.5,-.5,.5,0,.5},{-.5,0,0,0,.5,.5}}},
33         {"stair", 1, {{-.5,-.5,-.5,.5,0,.5},{-.5,0,0,.5,.5,.5}}},
34         {"innerstair", 1, {{-.5,-.5,-.5,.5,0,.5},{-.5,0,0,.5,.5,.5},{-.5,0,-.5,0,.5,0}}}
35 }
36
37 function worktable.craft_output_recipe(pos, start_i, pagenum, stackname, recipe_num, filter)
38         local meta = minetest.get_meta(pos)
39         local inv = meta:get_inventory()
40         local inventory_size = #meta:to_table().inventory.inv_items_list
41         local pagemax = math.floor((inventory_size - 1) / (8*4) + 1)
42
43         local formspec = [[ size[8,8;]
44                         list[context;item_craft_input;3,6.3;1,1;]
45                         tablecolumns[color;text;color;text]
46                         tableoptions[background=#00000000;highlight=#00000000;border=false]
47                         button[5.5,0;0.7,1;prev;<]
48                         button[7.3,0;0.7,1;next;>]
49                         button[4,0.2;0.7,0.5;search;?]
50                         button[4.6,0.2;0.7,0.5;clearfilter;X]
51                         button[0,0;1.5,1;backcraft;< Back]
52                         tooltip[search;Search]
53                         tooltip[clearfilter;Reset]
54                         label[3,5.8;Input] ]]..
55                         "list[context;inv_items_list;0,1;8,4;"..tostring(start_i).."]"..
56                         "table[6.1,0.2;1.1,0.5;pagenum;#FFFF00,"..tostring(math.floor(pagenum))..
57                         ",#FFFFFF,/ "..tostring(pagemax).."]"..
58                         "field[1.8,0.32;2.6,1;filter;;"..filter.."]"..xbg
59
60         if stackname then
61                 local items_num = #minetest.get_all_craft_recipes(stackname)
62                 if recipe_num > items_num then
63                         recipe_num = 1
64                 end
65
66                 --print(dump(minetest.get_all_craft_recipes(stackname)))
67                 local stack_width = minetest.get_all_craft_recipes(stackname)[recipe_num].width
68                 local stack_items = minetest.get_all_craft_recipes(stackname)[recipe_num].items
69                 local stack_type = minetest.get_all_craft_recipes(stackname)[recipe_num].type
70                 local stack_output = minetest.get_all_craft_recipes(stackname)[recipe_num].output
71                 local stack_count = stack_output:match("%s(%d+)")
72
73                 if items_num > 1 then
74                         formspec = formspec.."button[0,5.7;1.6,1;alternate;Alternate]"..
75                                         "label[0,5.2;Recipe "..recipe_num.." of "..items_num.."]"
76                 end
77
78                 if stack_count then
79                         inv:set_stack("item_craft_input", 1, stackname.." "..stack_count)
80                 else
81                         inv:set_stack("item_craft_input", 1, stackname)
82                 end
83
84                 if stack_type == "cooking" then
85                         formspec = formspec..[[ list[context;craft_output_recipe;5,6.3;1,1;]
86                                                 image[4.25,5.9;0.5,0.5;default_furnace_fire_fg.png] ]]
87                 else
88                         if stack_width == 0 then
89                                 formspec = formspec.."list[context;craft_output_recipe;5,5.3;3,3;]"
90                         else
91                                 formspec = formspec.."list[context;craft_output_recipe;5,5.3;"..stack_width..",3;]"
92                         end
93                 end
94
95                 local craft = {}
96                 for k, def in pairs(stack_items) do
97                         craft[#craft+1] = def
98                         if def and def:find("^group:") then
99                                 if def:find("wool$") or def:find("dye$") then
100                                         def = def:match(":([%w_]+)")..":white"
101                                 else
102                                         if minetest.registered_items["default:"..def:match("^group:([%w_,]+)$")] then
103                                                 def = def:gsub("group", "default")
104                                         else
105                                                 for node, definition in pairs(minetest.registered_items) do
106                                                 for group in pairs(definition.groups) do
107                                                         if def:match("^group:"..group.."$") or
108                                                                         ((def:find("dye") or def:find("flower")) and
109                                                                         group == def:match("^group:.*,("..group..")")) then
110                                                                 def = node
111                                                         end
112                                                 end
113                                                 end
114                                         end
115                                 end
116                         end
117
118                         inv:set_stack("craft_output_recipe", k, def)
119                 end
120
121                 formspec = formspec..[[ image[4,6.3;1,1;gui_furnace_arrow_bg.png^[transformR90]
122                                         button[0,6.5;1.6,1;trash;Clear] ]]..
123                                         "label[0,7.5;"..stackname:sub(1,30).."]"
124         end
125
126         meta:set_string("formspec", formspec)
127 end
128
129 function worktable.craftguide_update(pos, filter)
130         local meta = minetest.get_meta(pos)
131         local inv = meta:get_inventory()
132         local inv_items_list = {}
133
134         for name, def in pairs(minetest.registered_items) do
135                 if not (def.groups.not_in_creative_inventory == 1) and
136                                 minetest.get_craft_recipe(name).items and
137                                 def.description and def.description ~= "" and
138                                 (not filter or def.name:find(filter, 1, true)) then
139                         inv_items_list[#inv_items_list+1] = name
140                 end
141         end
142         table.sort(inv_items_list)
143
144         inv:set_size("inv_items_list", #inv_items_list)
145         inv:set_list("inv_items_list", inv_items_list)
146 end
147
148 function worktable.crafting(pos)
149         local meta = minetest.get_meta(pos)
150         local formspec = [[ size[8,7;]
151                         list[current_player;main;0,3.3;8,4;]
152                         image[5,1;1,1;gui_furnace_arrow_bg.png^[transformR270]
153                         button[0,0;1.5,1;back;< Back]
154                         button[0,1;1.5,1;craftguide;Guide]
155                         list[current_player;craft;2,0;3,3;]
156                         list[current_player;craftpreview;6,1;1,1;]
157                         listring[current_player;main]
158                         listring[current_player;craft] ]]
159                         ..xbg..default.get_hotbar_bg(0,3.3)
160
161         meta:set_string("formspec", formspec)
162 end
163
164 function worktable.storage(pos)
165         local meta = minetest.get_meta(pos)
166         local formspec = [[ size[8,7]
167                         list[context;storage;0,1;8,2;]
168                         list[current_player;main;0,3.25;8,4;]
169                         listring[context;storage]
170                         listring[current_player;main]
171                         button[0,0;1.5,1;back;< Back] ]]
172                         ..xbg..default.get_hotbar_bg(0,3.25)
173
174         meta:set_string("formspec", formspec)
175 end
176
177 function worktable.main(pos)
178         local meta = minetest.get_meta(pos)
179         local formspec = [[ size[8,7;]
180                         label[0.9,1.23;Cut]
181                         label[0.9,2.23;Repair]
182                         box[-0.05,1;2.05,0.9;#555555]
183                         box[-0.05,2;2.05,0.9;#555555]
184                         image[3,1;1,1;gui_furnace_arrow_bg.png^[transformR270]
185                         image[0,1;1,1;worktable_saw.png]
186                         image[0,2;1,1;worktable_anvil.png]
187                         image[3,2;1,1;hammer_layout.png]
188                         list[context;input;2,1;1,1;]
189                         list[context;tool;2,2;1,1;]
190                         list[context;hammer;3,2;1,1;]
191                         list[context;forms;4,0;4,3;]
192                         list[current_player;main;0,3.25;8,4;]
193                         button[0,0;2,1;craft;Crafting]
194                         button[2,0;2,1;storage;Storage] ]]
195                         ..xbg..default.get_hotbar_bg(0,3.25)
196
197         meta:set_string("formspec", formspec)
198 end
199
200 function worktable.construct(pos)
201         local meta = minetest.get_meta(pos)
202         local inv = meta:get_inventory()
203
204         inv:set_size("tool", 1)
205         inv:set_size("input", 1)
206         inv:set_size("hammer", 1)
207         inv:set_size("forms", 4*3)
208         inv:set_size("storage", 8*2)
209         inv:set_size("item_craft_input", 1)
210         inv:set_size("craft_output_recipe", 3*3)
211         meta:set_string("infotext", "Work Table")
212
213         worktable.main(pos)
214         worktable.craftguide_update(pos, nil)
215 end
216
217 function worktable.fields(pos, _, fields, sender)
218         local meta = minetest.get_meta(pos)
219         local inv = meta:get_inventory()
220         local formspec = meta:to_table().fields.formspec
221         local filter = formspec:match("filter;;([%w_:]+)") or ""
222         local start_i = tonumber(formspec:match("inv_items_list;.*;(%d+)%]")) or 0
223
224         if fields.storage then
225                 worktable.storage(pos)
226         elseif fields.back then
227                 worktable.main(pos)
228         elseif fields.backcraft or fields.craft then
229                 if fields.backcraft then
230                         inv:set_list("item_craft_input", {})
231                         inv:set_list("craft_output_recipe", {})
232                 end
233                 worktable.crafting(pos)
234         elseif fields.craftguide then
235                 if not meta:to_table().inventory.inv_items_list then return end -- legacy code
236                 worktable.craft_output_recipe(pos, 0, 1, nil, 1, "")
237         elseif fields.alternate then
238                 inv:set_list("craft_output_recipe", {})
239                 local inputstack = inv:get_stack("item_craft_input", 1):get_name()
240                 local recipe_num = tonumber(formspec:match("Recipe%s(%d+)")) or 1
241
242                 recipe_num = recipe_num + 1
243                 worktable.craft_output_recipe(pos, start_i, start_i / (8*4) + 1, inputstack, recipe_num, filter)
244         elseif fields.trash or fields.search or fields.clearfilter or
245                         fields.prev or fields.next then
246                 if fields.trash then
247                         worktable.craft_output_recipe(pos, start_i, start_i / (8*4) + 1, nil, 1, filter)
248                 elseif fields.search then
249                         worktable.craftguide_update(pos, fields.filter:lower())
250                         worktable.craft_output_recipe(pos, 0, 1, nil, 1, fields.filter:lower())
251                 elseif fields.clearfilter then
252                         worktable.craftguide_update(pos, nil)
253                         worktable.craft_output_recipe(pos, 0, 1, nil, 1, "")
254                 elseif fields.prev or fields.next then
255                         local inventory_size = #meta:to_table().inventory.inv_items_list
256
257                         if fields.prev or start_i >= inventory_size then
258                                 start_i = start_i - 8*4
259                         elseif fields.next or start_i < 0 then
260                                 start_i = start_i + 8*4
261                         end
262
263                         if start_i >= inventory_size then
264                                 start_i = 0
265                         elseif start_i < 0 then
266                                 start_i = inventory_size - (inventory_size % (8*4))
267                         end
268
269                         worktable.craft_output_recipe(pos, start_i, start_i / (8*4) + 1, nil, 1, filter)
270                 end
271
272                 inv:set_list("item_craft_input", {})
273                 inv:set_list("craft_output_recipe", {})
274         end
275 end
276
277 function worktable.dig(pos, _)
278         local inv = minetest.get_meta(pos):get_inventory()
279         return inv:is_empty("input") and inv:is_empty("hammer") and
280                 inv:is_empty("tool") and inv:is_empty("storage")
281 end
282
283 function worktable.contains(table, element)
284         if table then
285                 for _, value in pairs(table) do
286                         if value == element then
287                                 return true
288                         end
289                 end
290         end
291         return false
292 end
293
294 function worktable.put(_, listname, _, stack, _)
295         local stn = stack:get_name()
296         local mod, node = stn:match("([%w_]+):([%w_]+)")
297         local tdef = minetest.registered_tools[stn]
298         local twear = stack:get_wear()
299
300         if (listname == "input" and worktable.contains(nodes[mod], node)) or
301                         listname == "storage" then
302                 return stack:get_count()
303         elseif (listname == "hammer" and stn == "xdecor:hammer") or
304                         (listname == "tool" and tdef and twear > 0) then
305                 return 1
306         end
307
308         return 0
309 end
310
311 function worktable.take(pos, listname, _, stack, player)
312         if listname == "forms" then
313                 local user_inv = player:get_inventory()
314                 if user_inv:room_for_item("main", stack:get_name()) then
315                         return -1
316                 end
317                 return 0
318         elseif listname == "inv_items_list" or listname == "item_craft_input" or
319                         listname == "craft_output_recipe" then
320                 return 0
321         end
322
323         return stack:get_count()
324 end
325
326 function worktable.move(pos, from_list, from_index, to_list, to_index, count, _)
327         local meta = minetest.get_meta(pos)
328         local inv = meta:get_inventory()
329
330         if from_list == "storage" and to_list == "storage" then
331                 return count
332         elseif inv:is_empty("item_craft_input") and from_list == "inv_items_list" and
333                         to_list == "item_craft_input" then
334                 local stackname = inv:get_stack(from_list, from_index):get_name()
335                 local formspec = meta:to_table().fields.formspec
336                 local filter = formspec:match("filter;;([%w_:]+)") or ""
337                 local start_i = tonumber(formspec:match("inv_items_list;.*;(%d+)%]")) or 0
338
339                 worktable.craft_output_recipe(pos, start_i, start_i / (8*4) + 1, stackname, 1, filter)
340
341                 minetest.after(0, function()
342                         inv:set_stack(from_list, from_index, stackname)
343                 end)
344         end
345
346         return 0
347 end
348
349 local function update_inventory(inv, inputstack)
350         if inv:is_empty("input") then
351                 inv:set_list("forms", {})
352                 return
353         end
354
355         local output = {}
356         local input = inv:get_stack("input", 1)
357
358         for _, n in pairs(def) do
359                 local count = math.min(n[2] * input:get_count(), input:get_stack_max())
360                 output[#output+1] = inputstack:get_name().."_"..n[1].." "..count
361         end
362
363         inv:set_list("forms", output)
364 end
365
366 function worktable.on_put(pos, listname, _, stack, _)
367         if listname == "input" then
368                 local inv = minetest.get_meta(pos):get_inventory()
369                 update_inventory(inv, stack)
370         end
371 end
372
373 function worktable.on_take(pos, listname, index, stack, _)
374         local inv = minetest.get_meta(pos):get_inventory()
375         if listname == "input" then
376                 if stack:get_name() == inv:get_stack("input", 1):get_name() then
377                         update_inventory(inv, stack)
378                 else
379                         inv:set_list("forms", {})
380                 end
381         elseif listname == "forms" then
382                 local inputstack = inv:get_stack("input", 1)
383                 inputstack:take_item(math.ceil(stack:get_count() / def[index][2]))
384                 inv:set_stack("input", 1, inputstack)
385                 update_inventory(inv, inputstack)
386         end
387 end
388
389 xdecor.register("worktable", {
390         description = "Work Table",
391         groups = {cracky=2, choppy=2, oddly_breakable_by_hand=1},
392         sounds = default.node_sound_wood_defaults(),
393         tiles = {
394                 "xdecor_worktable_top.png", "xdecor_worktable_top.png",
395                 "xdecor_worktable_sides.png", "xdecor_worktable_sides.png",
396                 "xdecor_worktable_front.png", "xdecor_worktable_front.png"
397         },
398         on_rotate = screwdriver.rotate_simple,
399         can_dig = worktable.dig,
400         on_construct = worktable.construct,
401         on_receive_fields = worktable.fields,
402         on_metadata_inventory_put = worktable.on_put,
403         on_metadata_inventory_take = worktable.on_take,
404         allow_metadata_inventory_put = worktable.put,
405         allow_metadata_inventory_take = worktable.take,
406         allow_metadata_inventory_move = worktable.move
407 })
408
409 for _, d in pairs(def) do
410 for mod, n in pairs(nodes) do
411 for _, name in pairs(n) do
412         local ndef = minetest.registered_nodes[mod..":"..name]
413         if ndef then
414                 local groups, tiles, light = {}, {}
415                 groups.not_in_creative_inventory = 1
416
417                 for k, v in pairs(ndef.groups) do
418                         if k ~= "wood" and k ~= "stone" and k ~= "level" then
419                                 groups[k] = v
420                         end
421                 end
422
423                 if #ndef.tiles > 1 and not ndef.drawtype:find("glass") then
424                         tiles = ndef.tiles
425                 else
426                         tiles = {ndef.tiles[1]}
427                 end
428
429                 if ndef.light_source > 3 then
430                         light = ndef.light_source - 1
431                 end
432
433                 minetest.register_node(":"..mod..":"..name.."_"..d[1], {
434                         description = ndef.description.." "..d[1]:gsub("^%l", string.upper),
435                         paramtype = "light",
436                         paramtype2 = "facedir",
437                         drawtype = "nodebox",
438                         light_source = light,
439                         sounds = ndef.sounds,
440                         tiles = tiles,
441                         groups = groups,
442                         node_box = {type = "fixed", fixed = d[3]},
443                         sunlight_propagates = true,
444                         on_place = minetest.rotate_node
445                 })
446         end
447         minetest.register_alias("xdecor:"..d[1].."_"..name, mod..":"..name.."_"..d[1]) -- legacy code
448 end
449 end
450 end
451
452 minetest.register_abm({
453         nodenames = {"xdecor:worktable"},
454         interval = 3, chance = 1,
455         action = function(pos, _, _, _)
456                 local inv = minetest.get_meta(pos):get_inventory()
457                 local tool = inv:get_stack("tool", 1)
458                 local hammer = inv:get_stack("hammer", 1)
459                 local wear = tool:get_wear()
460
461                 if tool:is_empty() or hammer:is_empty() or wear == 0 then
462                         return
463                 end
464
465                 -- Wear : 0-65535 | 0 = new condition.
466                 tool:add_wear(-500)
467                 hammer:add_wear(700)
468
469                 inv:set_stack("tool", 1, tool)
470                 inv:set_stack("hammer", 1, hammer)
471         end
472 })
473