2 screwdriver = screwdriver or {}
4 -- Nodes allowed to be cut.
5 -- Only the regular, solid blocks without formspec or explosivity can be cut.
6 function worktable:nodes(def)
7 return (def.drawtype == "normal" or def.drawtype:find("glass")) and
8 (def.groups.cracky or def.groups.choppy) and not
9 def.on_construct and not def.after_place_node and not
10 def.after_place_node and not def.on_rightclick and not
11 def.on_blast and not def.allow_metadata_inventory_take and not
12 (def.groups.not_in_creative_inventory == 1) and not
13 def.groups.wool and not def.description:find("Ore") and
14 def.description and def.description ~= "" and def.light_source == 0
17 -- Nodeboxes definitions.
19 -- Name Yield X Y Z W H L
20 {"nanoslab", 16, { 0, 0, 0, 8, 1, 8 }},
21 {"micropanel", 16, { 0, 0, 0, 16, 1, 8 }},
22 {"microslab", 8, { 0, 0, 0, 16, 1, 16 }},
23 {"thinstair", 8, { 0, 7, 0, 16, 1, 8 },
24 { 0, 15, 8, 16, 1, 8 }},
25 {"cube", 4, { 0, 0, 0, 8, 8, 8 }},
26 {"panel", 4, { 0, 0, 0, 16, 8, 8 }},
28 {"doublepanel", 2, { 0, 0, 0, 16, 8, 8 },
29 { 0, 8, 8, 16, 8, 8 }},
30 {"halfstair", 2, { 0, 0, 0, 8, 8, 16 },
31 { 0, 8, 8, 8, 8, 8 }},
32 {"outerstair", 1, { 0, 0, 0, 16, 8, 16 },
33 { 0, 8, 8, 8, 8, 8 }},
35 {"innerstair", 1, { 0, 0, 0, 16, 8, 16 },
36 { 0, 8, 8, 16, 8, 8 },
40 -- Tools allowed to be repaired.
41 worktable.repairable_tools = [[
42 pick, axe, shovel, sword, hoe, armor, shield
45 function worktable:get_recipe(item)
46 if item:sub(1,6) == "group:" then
47 if item:sub(-4) == "wool" or item:sub(-3) == "dye" then
48 item = item:sub(7)..":white"
49 elseif minetest.registered_items["default:"..item:sub(7)] then
50 item = item:gsub("group:", "default:")
51 else for node, def in pairs(minetest.registered_items) do
52 if def.groups[item:match("[^,:]+$")] then item = node end
59 function worktable:craftguide_formspec(meta, pagenum, item, recipe_num, filter)
60 local inv_size = meta:get_int("inv_size")
61 local npp, i, s = 8*3, 0, 0
62 local pagemax = math.floor((inv_size - 1) / npp + 1)
64 if pagenum > pagemax then pagenum = 1
65 elseif pagenum == 0 then pagenum = pagemax end
67 local formspec = [[ size[8,6.6;]
68 tablecolumns[color;text;color;text]
69 tableoptions[background=#00000000;highlight=#00000000;border=false]
70 button[5.5,0;0.7,0.95;prev;<]
71 button[7.3,0;0.7,0.95;next;>]
72 button[4,0.2;0.7,0.5;search;?]
73 button[4.6,0.2;0.7,0.5;clearfilter;X]
74 button[0,0;1.5,1;backcraft;< Back]
75 tooltip[search;Search]
76 tooltip[clearfilter;Reset] ]] ..
77 "table[6.1,0.18;1.1,0.5;pagenum;#FFFF00,"..tostring(pagenum)..
78 ",#FFFFFF,/ "..tostring(pagemax).."]"..
79 "field[1.8,0.32;2.6,1;filter;;"..filter.."]"..xbg
81 for _, name in pairs(self:craftguide_items(meta, filter)) do
82 if s < (pagenum - 1) * npp then
84 else if i >= npp then break end
85 formspec = formspec.."item_image_button["..(i%8)..","..
86 (math.floor(i/8)+1)..";1,1;"..name..";"..name..";]"
91 if item and minetest.registered_items[item] then
92 --print(dump(minetest.get_all_craft_recipes(item)))
93 local items_num = #minetest.get_all_craft_recipes(item)
94 if recipe_num > items_num then recipe_num = 1 end
96 if items_num > 1 then formspec = formspec..
97 "button[0,6;1.6,1;alternate;Alternate]"..
98 "label[0,5.5;Recipe "..recipe_num.." of "..items_num.."]"
101 local type = minetest.get_all_craft_recipes(item)[recipe_num].type
102 if type == "cooking" then formspec = formspec..
103 "image[3.75,4.6;0.5,0.5;default_furnace_fire_fg.png]"
106 local items = minetest.get_all_craft_recipes(item)[recipe_num].items
107 local width = minetest.get_all_craft_recipes(item)[recipe_num].width
108 if width == 0 then width = math.min(3, #items) end
109 -- Lua 5.3 removed `table.maxn`, use `xdecor.maxn` in case of breakage.
110 local rows = math.ceil(table.maxn(items) / width)
112 local function is_group(item)
113 if item:sub(1,6) == "group:" then return "\nG" end
117 for i, v in pairs(items) do formspec = formspec..
118 "item_image_button["..((i-1) % width + 4.5)..","..
119 (math.floor((i-1) / width + (6 - math.min(2, rows))))..";1,1;"..
120 self:get_recipe(v)..";"..self:get_recipe(v)..";"..is_group(v).."]"
123 local output = minetest.get_all_craft_recipes(item)[recipe_num].output
124 formspec = formspec.."item_image_button[2.5,5;1,1;"..output..";"..item..";]"..
125 "image[3.5,5;1,1;gui_furnace_arrow_bg.png^[transformR90]"
128 meta:set_string("formspec", formspec)
131 function worktable:craftguide_items(meta, filter)
132 local items_list = {}
133 for name, def in pairs(minetest.registered_items) do
134 if not (def.groups.not_in_creative_inventory == 1) and
135 minetest.get_craft_recipe(name).items and
136 def.description and def.description ~= "" and
137 (not filter or def.name:find(filter, 1, true) or
138 def.description:lower():find(filter, 1, true)) then
139 items_list[#items_list+1] = name
143 meta:set_int("inv_size", #items_list)
144 table.sort(items_list)
148 function worktable:get_output(inv, input, name)
149 if inv:is_empty("input") then
150 inv:set_list("forms", {}) return
154 for _, n in pairs(self.defs) do
155 local count = math.min(n[2] * input:get_count(), input:get_stack_max())
156 local item = name.."_"..n[1]
157 if not n[3] then item = "stairs:"..n[1].."_"..name:match(":(.*)") end
158 output[#output+1] = item.." "..count
160 inv:set_list("forms", output)
163 function worktable.formspecs(meta, id)
166 [[ label[0.9,1.23;Cut]
167 label[0.9,2.23;Repair]
168 box[-0.05,1;2.05,0.9;#555555]
169 box[-0.05,2;2.05,0.9;#555555]
170 button[0,0;2,1;craft;Crafting]
171 button[2,0;2,1;storage;Storage]
172 image[3,1;1,1;gui_furnace_arrow_bg.png^[transformR270]
173 image[0,1;1,1;worktable_saw.png]
174 image[0,2;1,1;worktable_anvil.png]
175 image[3,2;1,1;hammer_layout.png]
176 list[context;input;2,1;1,1;]
177 list[context;tool;2,2;1,1;]
178 list[context;hammer;3,2;1,1;]
179 list[context;forms;4,0;4,3;] ]],
180 -- Crafting formspec.
181 [[ image[5,1;1,1;gui_furnace_arrow_bg.png^[transformR270]
182 button[0,0;1.5,1;back;< Back]
183 list[current_player;craft;2,0;3,3;]
184 list[current_player;craftpreview;6,1;1,1;]
185 listring[current_player;main]
186 listring[current_player;craft] ]],
188 [[ list[context;storage;0,1;8,2;]
189 button[0,0;1.5,1;back;< Back]
190 listring[context;storage]
191 listring[current_player;main] ]]
194 meta:set_string("formspec", "size[8,7;]list[current_player;main;0,3.25;8,4;]"..
195 formspecs[id]..xbg..default.get_hotbar_bg(0,3.25))
198 function worktable.construct(pos)
199 local meta = minetest.get_meta(pos)
200 local inv = meta:get_inventory()
202 inv:set_size("tool", 1)
203 inv:set_size("input", 1)
204 inv:set_size("hammer", 1)
205 inv:set_size("forms", 4*3)
206 inv:set_size("storage", 8*2)
208 meta:set_string("infotext", "Work Table")
209 worktable.formspecs(meta, 1)
212 function worktable.fields(pos, _, fields)
213 if fields.quit then return end
214 local meta = minetest.get_meta(pos)
215 local formspec = meta:to_table().fields.formspec
216 local filter = formspec:match("filter;;([%w_:]+)") or ""
217 local pagenum = tonumber(formspec:match("#FFFF00,(%d+)")) or 1
220 worktable.formspecs(meta, 1)
221 elseif fields.craft then
222 worktable.formspecs(meta, 2)
223 elseif fields.storage then
224 worktable.formspecs(meta, 3)
225 elseif fields.craftguide or fields.clearfilter then
226 worktable:craftguide_items(meta, nil)
227 worktable:craftguide_formspec(meta, 1, nil, 1, "")
228 elseif fields.alternate then
229 local item = formspec:match("image_button%[.*;([%w_:]+);.*%]") or ""
230 local recipe_num = tonumber(formspec:match("Recipe%s(%d+)")) or 1
231 recipe_num = recipe_num + 1
232 worktable:craftguide_formspec(meta, pagenum, item, recipe_num, filter)
233 elseif fields.search then
234 worktable:craftguide_items(meta, fields.filter:lower())
235 worktable:craftguide_formspec(meta, 1, nil, 1, fields.filter:lower())
236 elseif fields.prev or fields.next then
237 if fields.prev then pagenum = pagenum - 1
238 else pagenum = pagenum + 1 end
239 worktable:craftguide_formspec(meta, pagenum, nil, 1, filter)
240 else for item in pairs(fields) do
241 if minetest.get_craft_recipe(item).items then
242 worktable:craftguide_formspec(meta, pagenum, item, 1, filter)
248 function worktable.dig(pos)
249 local inv = minetest.get_meta(pos):get_inventory()
250 return inv:is_empty("input") and inv:is_empty("hammer") and
251 inv:is_empty("tool") and inv:is_empty("storage")
254 function worktable.timer(pos)
255 local timer = minetest.get_node_timer(pos)
256 local inv = minetest.get_meta(pos):get_inventory()
257 local tool = inv:get_stack("tool", 1)
258 local hammer = inv:get_stack("hammer", 1)
260 if tool:is_empty() or hammer:is_empty() or tool:get_wear() == 0 then
264 -- Tool's wearing range: 0-65535 | 0 = new condition.
268 inv:set_stack("tool", 1, tool)
269 inv:set_stack("hammer", 1, hammer)
274 function worktable.put(_, listname, _, stack)
275 local stackname = stack:get_name()
276 if (listname == "tool" and stack:get_wear() > 0 and
277 worktable.repairable_tools:find(stackname:match(":(%w+)"))) or
278 (listname == "input" and minetest.registered_nodes[stackname.."_cube"]) or
279 (listname == "hammer" and stackname == "xdecor:hammer") or
280 listname == "storage" then
281 return stack:get_count()
286 function worktable.take(_, listname, _, stack, player)
287 if listname == "forms" then
288 local inv = player:get_inventory()
289 if inv:room_for_item("main", stack:get_name()) then return -1 end
292 return stack:get_count()
295 function worktable.move(_, _, _, to_list, _, count)
296 if to_list == "storage" then return count end
300 function worktable.on_put(pos, listname, _, stack)
301 local inv = minetest.get_meta(pos):get_inventory()
302 if listname == "input" then
303 local input = inv:get_stack("input", 1)
304 worktable:get_output(inv, input, stack:get_name())
305 elseif listname == "tool" or listname == "hammer" then
306 local timer = minetest.get_node_timer(pos)
311 function worktable.on_take(pos, listname, index, stack)
312 local inv = minetest.get_meta(pos):get_inventory()
313 local input = inv:get_stack("input", 1)
315 if listname == "input" then
316 if stack:get_name() == input:get_name() then
317 worktable:get_output(inv, input, stack:get_name())
318 else inv:set_list("forms", {}) end
319 elseif listname == "forms" then
320 input:take_item(math.ceil(stack:get_count() / worktable.defs[index][2]))
321 inv:set_stack("input", 1, input)
322 worktable:get_output(inv, input, input:get_name())
326 xdecor.register("worktable", {
327 description = "Work Table",
328 groups = {cracky=2, choppy=2, oddly_breakable_by_hand=1},
329 sounds = default.node_sound_wood_defaults(),
331 "xdecor_worktable_top.png", "xdecor_worktable_top.png",
332 "xdecor_worktable_sides.png", "xdecor_worktable_sides.png",
333 "xdecor_worktable_front.png", "xdecor_worktable_front.png"
335 on_rotate = screwdriver.rotate_simple,
336 can_dig = worktable.dig,
337 on_timer = worktable.timer,
338 on_construct = worktable.construct,
339 on_receive_fields = worktable.fields,
340 on_metadata_inventory_put = worktable.on_put,
341 on_metadata_inventory_take = worktable.on_take,
342 on_metadata_inventory_move = worktable.on_move,
343 allow_metadata_inventory_put = worktable.put,
344 allow_metadata_inventory_take = worktable.take,
345 allow_metadata_inventory_move = worktable.move
348 for _, d in pairs(worktable.defs) do
349 for node in pairs(minetest.registered_nodes) do
350 local def = minetest.registered_nodes[node]
351 if worktable:nodes(def) and d[3] then
352 local groups, tiles = {}, {}
353 groups.not_in_creative_inventory = 1
355 for k, v in pairs(def.groups) do
356 if k ~= "wood" and k ~= "stone" and k ~= "level" then
362 if #def.tiles > 1 and not def.drawtype:find("glass") then
364 else tiles = {def.tiles[1]} end
366 tiles = {def.tile_images[1]}
369 if not minetest.registered_nodes["stairs:slab_"..node:match(":(.*)")] then
370 stairs.register_stair_and_slab(node:match(":(.*)"), node, groups, tiles,
371 def.description.." Stair", def.description.." Slab", def.sounds)
374 minetest.register_node(":"..node.."_"..d[1], {
375 description = def.description.." "..d[1]:gsub("^%l", string.upper),
377 paramtype2 = "facedir",
378 drawtype = "nodebox",
382 -- `unpack` has been changed to `table.unpack` in newest Lua versions.
383 node_box = xdecor.pixelbox(16, {unpack(d, 3)}),
384 sunlight_propagates = true,
385 on_place = minetest.rotate_node
388 if node:match(":mese") then
389 if d[3] then minetest.register_alias(node.."_"..d[1], "default:glass_"..d[1])
390 else minetest.register_alias("stairs:"..d[1].."_"..node:match(":(.*)"), "stairs:"..d[1].."_glass") end
391 elseif worktable:nodes(def) and not d[3] then
392 minetest.register_alias(node.."_"..d[1], "stairs:"..d[1].."_"..node:match(":(.*)"))