]> git.lizzy.rs Git - xdecor.git/blob - src/workbench.lua
fix workbench inv movement bug; add protection checking
[xdecor.git] / src / workbench.lua
1 local function log(level, message, ...)
2         minetest.log(level, '[xdecor] ' .. message:format(...))
3 end
4
5 local workbench = {}
6 WB = {}
7 screwdriver = screwdriver or {}
8 local min, ceil = math.min, math.ceil
9 local registered_nodes = minetest.registered_nodes
10
11 -- Nodes allowed to be cut
12 -- Only the regular, solid blocks without metas or explosivity can be cut
13 local nodes = {}
14 for node, def in pairs(registered_nodes) do
15         if xdecor.stairs_valid_def(def) then
16                 nodes[#nodes + 1] = node
17         end
18 end
19
20 -- Optionally, you can register custom cuttable nodes in the workbench
21 WB.custom_nodes_register = {
22         -- "default:leaves",
23 }
24
25 setmetatable(nodes, {
26         __concat = function(t1, t2)
27                 for i = 1, #t2 do
28                         t1[#t1 + 1] = t2[i]
29                 end
30
31                 return t1
32         end
33 })
34
35 nodes = nodes .. WB.custom_nodes_register
36
37 -- Nodeboxes definitions
38 workbench.defs = {
39         -- Name YieldX  YZ  WH  L
40         {"nanoslab",    16, { 0, 0,  0, 8,  1, 8  }},
41         {"micropanel",  16, { 0, 0,  0, 16, 1, 8  }},
42         {"microslab",   8,  { 0, 0,  0, 16, 1, 16 }},
43         {"thinstair",   8,  { 0, 7,  0, 16, 1, 8  },
44                          { 0, 15, 8, 16, 1, 8  }},
45         {"cube",        4,  { 0, 0,  0, 8,  8, 8  }},
46         {"panel",       4,  { 0, 0,  0, 16, 8, 8  }},
47         {"slab",        2,  nil                   },
48         {"doublepanel", 2,  { 0, 0,  0, 16, 8, 8  },
49                          { 0, 8,  8, 16, 8, 8  }},
50         {"halfstair",   2,  { 0, 0,  0, 8,  8, 16 },
51                          { 0, 8,  8, 8,  8, 8  }},
52         {"stair_outer", 1,  nil                   },
53         {"stair",       1,  nil                   },
54         {"stair_inner", 1,  nil                   }
55 }
56
57 local repairable_tools = {"pick", "axe", "shovel", "sword", "hoe", "armor", "shield"}
58
59 -- Tools allowed to be repaired
60 function workbench:repairable(stack)
61         for _, t in ipairs(repairable_tools) do
62                 if stack:find(t) then
63                         return true
64                 end
65         end
66 end
67
68 function workbench:get_output(inv, input, name)
69         local output = {}
70         for i = 1, #self.defs do
71                 local nbox = self.defs[i]
72                 local count = min(nbox[2] * input:get_count(), input:get_stack_max())
73                 local item = name .. "_" .. nbox[1]
74
75                 item = nbox[3] and item or "stairs:" .. nbox[1] .. "_" .. name:match(":(.*)")
76                 output[i] = item .. " " .. count
77         end
78
79         inv:set_list("forms", output)
80 end
81
82 local main_fs = [[
83         label[0.9,1.23;Cut]
84         label[0.9,2.23;Repair]
85         box[-0.05,1;2.05,0.9;#555555]
86         box[-0.05,2;2.05,0.9;#555555]
87         button[0,0;2,1;craft;Crafting]
88         button[2,0;2,1;storage;Storage]
89         image[3,1;1,1;gui_furnace_arrow_bg.png^[transformR270]
90         image[0,1;1,1;worktable_saw.png]
91         image[0,2;1,1;worktable_anvil.png]
92         image[3,2;1,1;hammer_layout.png]
93         list[context;input;2,1;1,1;]
94         list[context;tool;2,2;1,1;]
95         list[context;hammer;3,2;1,1;]
96         list[context;forms;4,0;4,3;]
97         listring[current_player;main]
98         listring[context;tool]
99         listring[current_player;main]
100         listring[context;hammer]
101         listring[current_player;main]
102         listring[context;forms]
103         listring[current_player;main]
104         listring[context;input]
105 ]]
106
107 local crafting_fs = [[
108         image[5,1;1,1;gui_furnace_arrow_bg.png^[transformR270]
109         button[0,0;1.5,1;back;< Back]
110         list[current_player;craft;2,0;3,3;]
111         list[current_player;craftpreview;6,1;1,1;]
112         listring[current_player;main]
113         listring[current_player;craft]
114 ]]
115
116 local storage_fs = [[
117         list[context;storage;0,1;8,2;]
118         button[0,0;1.5,1;back;< Back]
119         listring[context;storage]
120         listring[current_player;main]
121 ]]
122
123 local formspecs = {
124         -- Main formspec
125         main_fs,
126
127         -- Crafting formspec
128         crafting_fs,
129
130         -- Storage formspec
131         storage_fs,
132 }
133
134 function workbench:set_formspec(meta, id)
135         meta:set_string("formspec",
136                 "size[8,7;]list[current_player;main;0,3.25;8,4;]" ..
137                 formspecs[id] .. xbg .. default.get_hotbar_bg(0,3.25))
138 end
139
140 function workbench.construct(pos)
141         local meta = minetest.get_meta(pos)
142         local inv = meta:get_inventory()
143
144         inv:set_size("tool", 1)
145         inv:set_size("input", 1)
146         inv:set_size("hammer", 1)
147         inv:set_size("forms", 4*3)
148         inv:set_size("storage", 8*2)
149
150         meta:set_string("infotext", "Work Bench")
151         workbench:set_formspec(meta, 1)
152 end
153
154 function workbench.fields(pos, _, fields)
155         if fields.quit then return end
156
157         local meta = minetest.get_meta(pos)
158         local id = fields.back and 1 or fields.craft and 2 or fields.storage and 3
159         if not id then return end
160
161         workbench:set_formspec(meta, id)
162 end
163
164 function workbench.dig(pos)
165         local inv = minetest.get_meta(pos):get_inventory()
166         return inv:is_empty("input") and inv:is_empty("hammer") and
167                inv:is_empty("tool") and inv:is_empty("storage")
168 end
169
170 function workbench.timer(pos)
171         local timer = minetest.get_node_timer(pos)
172         local inv = minetest.get_meta(pos):get_inventory()
173         local tool = inv:get_stack("tool", 1)
174         local hammer = inv:get_stack("hammer", 1)
175
176         if tool:is_empty() or hammer:is_empty() or tool:get_wear() == 0 then
177                 timer:stop()
178                 return
179         end
180
181         -- Tool's wearing range: 0-65535; 0 = new condition
182         tool:add_wear(-500)
183         hammer:add_wear(700)
184
185         inv:set_stack("tool", 1, tool)
186         inv:set_stack("hammer", 1, hammer)
187
188         return true
189 end
190
191 function workbench.allow_put(pos, listname, index, stack, player)
192         local player_name = player:get_player_name()
193         if (
194                 minetest.is_protected(pos, player_name) and
195                 not minetest.check_player_privs(player_name, {protection_bypass=true})
196         ) then
197                 minetest.record_protection_violation(pos, player_name)
198                 return 0
199         end
200
201         local stackname = stack:get_name()
202         if (listname == "tool" and stack:get_wear() > 0 and
203                 workbench:repairable(stackname)) or
204            (listname == "input" and registered_nodes[stackname .. "_cube"]) or
205            (listname == "hammer" and stackname == "xdecor:hammer") or
206             listname == "storage" then
207                 return stack:get_count()
208         end
209
210         return 0
211 end
212
213 function workbench.on_put(pos, listname, index, stack, player)
214         log('action',
215                 '%s put %s in workbench at %s',
216                 player:get_player_name(),
217                 stack:get_name(),
218                 minetest.pos_to_string(pos))
219
220         local inv = minetest.get_meta(pos):get_inventory()
221         if listname == "input" then
222                 local input = inv:get_stack("input", 1)
223                 workbench:get_output(inv, input, stack:get_name())
224         elseif listname == "tool" or listname == "hammer" then
225                 local timer = minetest.get_node_timer(pos)
226                 timer:start(3.0)
227         end
228 end
229
230 function workbench.allow_move(pos, from_list, from_index, to_list, to_index, count, player)
231         local player_name = player:get_player_name()
232         if (
233                 minetest.is_protected(pos, player_name) and
234                 not minetest.check_player_privs(player_name, {protection_bypass=true})
235         ) then
236                 minetest.record_protection_violation(pos, player_name)
237                 return 0
238         end
239
240         return (to_list == "storage" and from_list ~= "forms") and count or 0
241 end
242
243 function workbench.on_move(pos, from_list, from_index, to_list, to_index, count, player)
244         local meta = minetest.get_meta(pos)
245         local inv = meta:get_inventory()
246         local from_stack = inv:get_stack(from_list, from_index)
247         local to_stack = inv:get_stack(to_list, to_index)
248
249         log('action',
250                 '%s moved %s in workbench at %s',
251                 player:get_player_name(),
252                 to_stack:get_name(),
253                 minetest.pos_to_string(pos))
254
255         workbench.on_take(pos, from_list, from_index, from_stack, player)
256         workbench.on_put(pos, to_list, to_index, to_stack, player)
257 end
258
259 function workbench.allow_take(pos, listname, index, stack, player)
260         local player_name = player:get_player_name()
261         if (
262                 minetest.is_protected(pos, player_name) and
263                 not minetest.check_player_privs(player_name, {protection_bypass=true})
264         ) then
265                 minetest.record_protection_violation(pos, player_name)
266                 return 0
267         end
268
269         return stack:get_count()
270 end
271
272 function workbench.on_take(pos, listname, index, stack, player)
273         log('action',
274                 '%s took %s from workbench at %s',
275                 player:get_player_name(),
276                 stack:get_name(),
277                 minetest.pos_to_string(pos))
278
279         local inv = minetest.get_meta(pos):get_inventory()
280         local input = inv:get_stack("input", 1)
281         local inputname = input:get_name()
282         local stackname = stack:get_name()
283
284         if listname == "input" then
285                 if stackname == inputname and registered_nodes[inputname .. "_cube"] then
286                         workbench:get_output(inv, input, stackname)
287                 else
288                         inv:set_list("forms", {})
289                 end
290         elseif listname == "forms" then
291                 local fromstack = inv:get_stack(listname, index)
292                 if not fromstack:is_empty() and fromstack:get_name() ~= stackname then
293                         local player_inv = player:get_inventory()
294                         if player_inv:room_for_item("main", fromstack) then
295                                 player_inv:add_item("main", fromstack)
296                         end
297                 end
298
299                 input:take_item(ceil(stack:get_count() / workbench.defs[index][2]))
300                 inv:set_stack("input", 1, input)
301                 workbench:get_output(inv, input, inputname)
302         end
303 end
304
305 xdecor.register("workbench", {
306         description = "Work Bench",
307         groups = {cracky = 2, choppy = 2, oddly_breakable_by_hand = 1},
308         sounds = default.node_sound_wood_defaults(),
309         tiles = {
310                 "xdecor_workbench_top.png","xdecor_workbench_top.png",
311                 "xdecor_workbench_sides.png", "xdecor_workbench_sides.png",
312                 "xdecor_workbench_front.png", "xdecor_workbench_front.png"
313         },
314         on_rotate = screwdriver.rotate_simple,
315         can_dig = workbench.dig,
316         on_timer = workbench.timer,
317         on_construct = workbench.construct,
318         on_receive_fields = workbench.fields,
319         on_metadata_inventory_put = workbench.on_put,
320         on_metadata_inventory_take = workbench.on_take,
321         on_metadata_inventory_move = workbench.on_move,
322         allow_metadata_inventory_put = workbench.allow_put,
323         allow_metadata_inventory_take = workbench.allow_take,
324         allow_metadata_inventory_move = workbench.allow_move
325 })
326
327 for _, d in ipairs(workbench.defs) do
328 for i = 1, #nodes do
329         local node = nodes[i]
330         local mod_name, item_name = node:match("^(.-):(.*)")
331         local def = registered_nodes[node]
332
333         if item_name and d[3] then
334                 local groups = {}
335                 local tiles
336                 groups.not_in_creative_inventory = 1
337
338                 for k, v in pairs(def.groups) do
339                         if k ~= "wood" and k ~= "stone" and k ~= "level" then
340                                 groups[k] = v
341                         end
342                 end
343
344                 if def.tiles then
345                         if #def.tiles > 1 and (def.drawtype:sub(1,5) ~= "glass") then
346                                 tiles = def.tiles
347                         else
348                                 tiles = {def.tiles[1]}
349                         end
350                 else
351                         tiles = {def.tile_images[1]}
352                 end
353
354                 if not registered_nodes["stairs:slab_" .. item_name] then
355                         stairs.register_stair_and_slab(item_name, node,
356                                 groups, tiles, def.description .. " Stair",
357                                 def.description .. " Slab", def.sounds)
358                 end
359
360                 minetest.register_node(":" .. node .. "_" .. d[1], {
361                         description = def.description .. " " .. d[1]:gsub("^%l", string.upper),
362                         paramtype = "light",
363                         paramtype2 = "facedir",
364                         drawtype = "nodebox",
365                         sounds = def.sounds,
366                         tiles = tiles,
367                         groups = groups,
368                         -- `unpack` has been changed to `table.unpack` in newest Lua versions
369                         node_box = xdecor.pixelbox(16, {unpack(d, 3)}),
370                         sunlight_propagates = true,
371                         on_place = minetest.rotate_node
372                 })
373
374         elseif item_name and mod_name then
375                 minetest.register_alias_force(
376                         ("%s:%s_innerstair"):format(mod_name, item_name),
377                         ("stairs:stair_inner_%s"):format(item_name)
378                 )
379                 minetest.register_alias_force(
380                         ("%s:%s_outerstair"):format(mod_name, item_name),
381                         ("stairs:stair_outer_%s"):format(item_name)
382                 )
383         end
384 end
385 end
386
387 -- Craft items
388
389 minetest.register_tool("xdecor:hammer", {
390         description = "Hammer",
391         inventory_image = "xdecor_hammer.png",
392         wield_image = "xdecor_hammer.png",
393         on_use = function() do
394                 return end
395         end
396 })
397
398 -- Recipes
399
400 minetest.register_craft({
401         output = "xdecor:hammer",
402         recipe = {
403                 {"default:steel_ingot", "group:stick", "default:steel_ingot"},
404                 {"", "group:stick", ""}
405         }
406 })
407
408 minetest.register_craft({
409         output = "xdecor:workbench",
410         recipe = {
411                 {"group:wood", "group:wood"},
412                 {"group:wood", "group:wood"}
413         }
414 })