Minor repeater refactor
[MineClone/MineClone2.git] / mods / ITEMS / REDSTONE / mesecons_delayer / init.lua
blobfee8e2562041dd074fb1b6b46dbc4c99f60e77ec
1 -- Function that get the input/output rules of the delayer
2 local delayer_get_output_rules = function(node)
3 local rules = {{x = -1, y = 0, z = 0, spread=true}}
4 for i = 0, node.param2 do
5 rules = mesecon.rotate_rules_left(rules)
6 end
7 return rules
8 end
10 local delayer_get_input_rules = function(node)
11 local rules = {{x = 1, y = 0, z = 0}}
12 for i = 0, node.param2 do
13 rules = mesecon.rotate_rules_left(rules)
14 end
15 return rules
16 end
18 -- Return the sides of a delayer.
19 -- Those are used to toggle the lock state.
20 local delayer_get_sides = function(node)
21 local rules = {
22 {x = 0, y = 0, z = -1},
23 {x = 0, y = 0, z = 1},
25 for i = 0, node.param2 do
26 rules = mesecon.rotate_rules_left(rules)
27 end
28 return rules
29 end
31 -- Make the repeater at pos try to lock any repeater it faces.
32 -- Returns true if a repeater was locked.
33 local check_lock_repeater = function(pos, node)
34 -- Check the repeater at pos and look if it faces
35 -- a repeater placed sideways.
36 -- If yes, lock the second repeater.
37 local r = delayer_get_output_rules(node)[1]
38 local lpos = vector.add(pos, r)
39 local lnode = minetest.get_node(lpos)
40 local ldef = minetest.registered_nodes[lnode.name]
41 local g = minetest.get_item_group(lnode.name, "redstone_repeater")
42 if g >= 1 and g <= 4 then
43 local lrs = delayer_get_input_rules(lnode)
44 local fail = false
45 for _, lr in pairs(lrs) do
46 if lr.x == r.x or lr.z == r.z then
47 fail = true
48 break
49 end
50 end
51 if not fail then
52 minetest.set_node(lpos, {name=ldef.delayer_lockstate, param2=lnode.param2})
53 local meta = minetest.get_meta(lpos)
54 meta:set_int("delay", g)
55 return true
56 end
57 end
58 return false
59 end
61 -- Make the repeater at pos try to unlock any repeater it faces.
62 -- Returns true if a repeater was unlocked.
63 local check_unlock_repeater = function(pos, node)
64 -- Check the repeater at pos and look if it faces
65 -- a repeater placed sideways.
66 -- If yes, also check if the second repeater doesn't receive
67 -- a locking signal on the other side. If not, unlock the second repeater.
68 local r = delayer_get_output_rules(node)[1]
69 local lpos = vector.add(pos, r)
70 local lnode = minetest.get_node(lpos)
71 local ldef = minetest.registered_nodes[lnode.name]
72 local g = minetest.get_item_group(lnode.name, "redstone_repeater")
73 -- Are we facing a locked repeater?
74 if g == 5 then
75 -- First check the orientation of the faced repeater
76 local lrs = delayer_get_input_rules(lnode)
77 for _, lr in pairs(lrs) do
78 if lr.x == r.x or lr.z == r.z then
79 -- Invalid orientation. Do nothing
80 return false
81 end
82 end
83 -- Now we check if there's a powered repeater on the other side of the
84 -- locked repeater.
85 -- To get to the other side, we just take another step in the direction which we already face.
86 local other_side = vector.add(lpos, r)
87 local other_node = minetest.get_node(other_side)
88 if minetest.get_item_group(other_node.name, "redstone_repeater") ~= 0 and mesecon.is_receptor_on(other_node.name) then
89 -- Final check: The other repeater must also face the right way
90 local other_face = delayer_get_output_rules(other_node)[1]
91 local other_facing_pos = vector.add(other_side, other_face)
92 if vector.equals(other_facing_pos, lpos) then
93 -- Powered repeater found AND it's facing the locked repeater. Do NOT unlock!
94 return false
95 end
96 end
97 local lmeta = minetest.get_meta(lpos)
98 local ldelay = lmeta:get_int("delay")
99 if tonumber(ldelay) == nil or ldelay < 1 or ldelay > 4 then
100 ldelay = 1
102 if mesecon.is_powered(lpos, delayer_get_input_rules(lnode)[1]) then
103 minetest.set_node(lpos, {name="mesecons_delayer:delayer_on_"..ldelay, param2=lnode.param2})
104 mesecon.queue:add_action(lpos, "receptor_on", {delayer_get_output_rules(lnode)}, ldef.delayer_time, nil)
105 else
106 minetest.set_node(lpos, {name="mesecons_delayer:delayer_off_"..ldelay, param2=lnode.param2})
107 mesecon.queue:add_action(lpos, "receptor_off", {delayer_get_output_rules(lnode)}, ldef.delayer_time, nil)
109 return true
111 return false
114 -- Functions that are called after the delay time
115 local delayer_activate = function(pos, node)
116 local def = minetest.registered_nodes[node.name]
117 local time = def.delayer_time
118 minetest.set_node(pos, {name=def.delayer_onstate, param2=node.param2})
119 mesecon.queue:add_action(pos, "receptor_on", {delayer_get_output_rules(node)}, time, nil)
121 check_lock_repeater(pos, node)
124 local delayer_deactivate = function(pos, node)
125 local def = minetest.registered_nodes[node.name]
126 local time = def.delayer_time
127 minetest.set_node(pos, {name=def.delayer_offstate, param2=node.param2})
128 mesecon.queue:add_action(pos, "receptor_off", {delayer_get_output_rules(node)}, time, nil)
130 check_unlock_repeater(pos, node)
133 -- Register the 2 (states) x 4 (delay times) delayers
135 for i = 1, 4 do
136 local groups = {}
137 if i == 1 then
138 groups = {dig_immediate=3,dig_by_water=1,destroy_by_lava_flow=1,dig_by_piston=1,attached_node=1,redstone_repeater=i}
139 else
140 groups = {dig_immediate=3,dig_by_water=1,destroy_by_lava_flow=1,dig_by_piston=1,attached_node=1,redstone_repeater=i,not_in_creative_inventory=1}
143 local delaytime
144 if i == 1 then delaytime = 0.1
145 elseif i == 2 then delaytime = 0.2
146 elseif i == 3 then delaytime = 0.3
147 elseif i == 4 then delaytime = 0.4
150 local boxes
151 if i == 1 then
152 boxes = {
153 { -8/16, -8/16, -8/16, 8/16, -6/16, 8/16 }, -- the main slab
154 { -1/16, -6/16, 6/16, 1/16, -1/16, 4/16}, -- still torch
155 { -1/16, -6/16, 0/16, 1/16, -1/16, 2/16}, -- moved torch
157 elseif i == 2 then
158 boxes = {
159 { -8/16, -8/16, -8/16, 8/16, -6/16, 8/16 }, -- the main slab
160 { -1/16, -6/16, 6/16, 1/16, -1/16, 4/16}, -- still torch
161 { -1/16, -6/16, -2/16, 1/16, -1/16, 0/16}, -- moved torch
163 elseif i == 3 then
164 boxes = {
165 { -8/16, -8/16, -8/16, 8/16, -6/16, 8/16 }, -- the main slab
166 { -1/16, -6/16, 6/16, 1/16, -1/16, 4/16}, -- still torch
167 { -1/16, -6/16, -4/16, 1/16, -1/16, -2/16}, -- moved torch
169 elseif i == 4 then
170 boxes = {
171 { -8/16, -8/16, -8/16, 8/16, -6/16, 8/16 }, -- the main slab
172 { -1/16, -6/16, 6/16, 1/16, -1/16, 4/16}, -- still torch
173 { -1/16, -6/16, -6/16, 1/16, -1/16, -4/16}, -- moved torch
177 local help, longdesc, usagehelp, icon, on_construct
178 if i == 1 then
179 help = true
180 longdesc = "Redstone repeaters are versatile redstone components with multiple purposes: 1. They only allow signals to travel in one direction. 2. They delay the signal. 3. Optionally, they can lock their output in one state."
181 usagehelp = "To power a redstone repeater, send a signal in “arrow” direction (the input). The signal goes out on the opposite side (the output) with a delay. To change the delay, rightclick the redstone repeater. The delay is between 0.1 and 0.4 seconds long and can be changed in steps of 0.1 seconds. It is indicated by the position of the moving redstone torch.".."\n"..
182 "To lock a repeater, send a signal from an adjacent repeater into one of its sides. While locked, the moving redstone torch disappears, the output doesn't change and the input signal is ignored."
183 icon = "mesecons_delayer_item.png"
184 on_construct = function(pos)
185 local node = minetest.get_node(pos)
186 local sides = delayer_get_sides(node)
187 for s=1, #sides do
188 local spos = vector.add(pos, sides[s])
189 local snode = minetest.get_node(spos)
190 local g = minetest.get_item_group(snode.name, "redstone_repeater")
191 local sdef = minetest.registered_nodes[snode.name]
192 if g ~= 0 and mesecon.is_receptor_on(snode.name) then
193 if mesecon.is_powered(pos, delayer_get_input_rules(node)[1]) ~= false then
194 minetest.set_node(pos, {name="mesecons_delayer:delayer_on_locked", param2 = node.param2})
195 else
196 minetest.set_node(pos, {name="mesecons_delayer:delayer_off_locked", param2 = node.param2})
198 break
202 else
203 help = false
206 local on_rotate
207 if minetest.get_modpath("screwdriver") then
208 on_rotate = screwdriver.disallow
212 minetest.register_node("mesecons_delayer:delayer_off_"..tostring(i), {
213 description = "Redstone Repeater",
214 inventory_image = icon,
215 wield_image = icon,
216 _doc_items_create_entry = help,
217 _doc_items_longdesc = longdesc,
218 _doc_items_usagehelp = usagehelp,
219 drawtype = "nodebox",
220 tiles = {
221 "mesecons_delayer_off.png",
222 "mcl_stairs_stone_slab_top.png",
223 "mesecons_delayer_sides_off.png",
224 "mesecons_delayer_sides_off.png",
225 "mesecons_delayer_ends_off.png",
226 "mesecons_delayer_ends_off.png",
228 wield_image = "mesecons_delayer_off.png",
229 walkable = true,
230 selection_box = {
231 type = "fixed",
232 fixed = { -8/16, -8/16, -8/16, 8/16, -6/16, 8/16 },
234 collision_box = {
235 type = "fixed",
236 fixed = { -8/16, -8/16, -8/16, 8/16, -6/16, 8/16 },
238 node_box = {
239 type = "fixed",
240 fixed = boxes
242 groups = groups,
243 paramtype = "light",
244 paramtype2 = "facedir",
245 sunlight_propagates = false,
246 is_ground_content = false,
247 drop = 'mesecons_delayer:delayer_off_1',
248 on_rightclick = function (pos, node)
249 if node.name=="mesecons_delayer:delayer_off_1" then
250 minetest.set_node(pos, {name="mesecons_delayer:delayer_off_2", param2=node.param2})
251 elseif node.name=="mesecons_delayer:delayer_off_2" then
252 minetest.set_node(pos, {name="mesecons_delayer:delayer_off_3", param2=node.param2})
253 elseif node.name=="mesecons_delayer:delayer_off_3" then
254 minetest.set_node(pos, {name="mesecons_delayer:delayer_off_4", param2=node.param2})
255 elseif node.name=="mesecons_delayer:delayer_off_4" then
256 minetest.set_node(pos, {name="mesecons_delayer:delayer_off_1", param2=node.param2})
258 end,
259 on_construct = on_construct,
260 delayer_time = delaytime,
261 delayer_onstate = "mesecons_delayer:delayer_on_"..tostring(i),
262 delayer_lockstate = "mesecons_delayer:delayer_off_locked",
263 sounds = mcl_sounds.node_sound_stone_defaults(),
264 mesecons = {
265 receptor =
267 state = mesecon.state.off,
268 rules = delayer_get_output_rules
270 effector =
272 rules = delayer_get_input_rules,
273 action_on = delayer_activate
276 on_rotate = on_rotate,
280 minetest.register_node("mesecons_delayer:delayer_on_"..tostring(i), {
281 description = "Redstone Repeater (Powered)",
282 _doc_items_create_entry = false,
283 drawtype = "nodebox",
284 tiles = {
285 "mesecons_delayer_on.png",
286 "mcl_stairs_stone_slab_top.png",
287 "mesecons_delayer_sides_on.png",
288 "mesecons_delayer_sides_on.png",
289 "mesecons_delayer_ends_on.png",
290 "mesecons_delayer_ends_on.png",
292 walkable = true,
293 selection_box = {
294 type = "fixed",
295 fixed = { -8/16, -8/16, -8/16, 8/16, -6/16, 8/16 },
297 collision_box = {
298 type = "fixed",
299 fixed = { -8/16, -8/16, -8/16, 8/16, -6/16, 8/16 },
301 node_box = {
302 type = "fixed",
303 fixed = boxes
305 groups = {dig_immediate = 3, dig_by_water=1,destroy_by_lava_flow=1, dig_by_piston=1, attached_node=1, redstone_repeater=i, not_in_creative_inventory = 1},
306 paramtype = "light",
307 paramtype2 = "facedir",
308 sunlight_propagates = false,
309 is_ground_content = false,
310 drop = 'mesecons_delayer:delayer_off_1',
311 on_rightclick = function (pos, node)
312 if node.name=="mesecons_delayer:delayer_on_1" then
313 minetest.set_node(pos, {name="mesecons_delayer:delayer_on_2",param2=node.param2})
314 elseif node.name=="mesecons_delayer:delayer_on_2" then
315 minetest.set_node(pos, {name="mesecons_delayer:delayer_on_3",param2=node.param2})
316 elseif node.name=="mesecons_delayer:delayer_on_3" then
317 minetest.set_node(pos, {name="mesecons_delayer:delayer_on_4",param2=node.param2})
318 elseif node.name=="mesecons_delayer:delayer_on_4" then
319 minetest.set_node(pos, {name="mesecons_delayer:delayer_on_1",param2=node.param2})
321 end,
322 after_dig_node = function(pos, oldnode)
323 check_unlock_repeater(pos, oldnode)
324 end,
325 delayer_time = delaytime,
326 delayer_offstate = "mesecons_delayer:delayer_off_"..tostring(i),
327 delayer_lockstate = "mesecons_delayer:delayer_on_locked",
328 sounds = mcl_sounds.node_sound_stone_defaults(),
329 mesecons = {
330 receptor =
332 state = mesecon.state.on,
333 rules = delayer_get_output_rules
335 effector =
337 rules = delayer_get_input_rules,
338 action_off = delayer_deactivate
341 on_rotate = on_rotate,
347 -- Locked repeater
349 minetest.register_node("mesecons_delayer:delayer_off_locked", {
350 description = "Redstone Repeater (Locked)",
351 inventory_image = icon,
352 wield_image = icon,
353 _doc_items_create_entry = false,
354 drawtype = "nodebox",
355 -- FIXME: Textures of torch and the lock bar overlap. Nodeboxes are (sadly) not suitable for this.
356 -- So this needs to be turned into a mesh.
357 tiles = {
358 "mesecons_delayer_locked_off.png",
359 "mcl_stairs_stone_slab_top.png",
360 "mesecons_delayer_sides_locked_off.png",
361 "mesecons_delayer_sides_locked_off.png",
362 "mesecons_delayer_front_locked_off.png",
363 "mesecons_delayer_end_locked_off.png",
365 wield_image = "mesecons_delayer_locked_off.png",
366 walkable = true,
367 selection_box = {
368 type = "fixed",
369 fixed = { -8/16, -8/16, -8/16, 8/16, -6/16, 8/16 },
371 collision_box = {
372 type = "fixed",
373 fixed = { -8/16, -8/16, -8/16, 8/16, -6/16, 8/16 },
375 node_box = {
376 type = "fixed",
377 fixed = {
378 { -8/16, -8/16, -8/16, 8/16, -6/16, 8/16 }, -- the main slab
379 { -1/16, -6/16, 6/16, 1/16, -1/16, 4/16}, -- still torch
380 { -6/16, -6/16, -1/16, 6/16, -4/16, 1/16}, -- lock
383 groups = {dig_immediate = 3, dig_by_water=1,destroy_by_lava_flow=1, dig_by_piston=1, attached_node=1, redstone_repeater=5, not_in_creative_inventory = 1},
384 paramtype = "light",
385 paramtype2 = "facedir",
386 sunlight_propagates = false,
387 is_ground_content = false,
388 drop = 'mesecons_delayer:delayer_off_1',
389 delayer_time = 0.1,
390 sounds = mcl_sounds.node_sound_stone_defaults(),
391 mesecons = {
392 receptor =
394 state = mesecon.state.off,
395 rules = delayer_get_output_rules
397 effector =
399 rules = delayer_get_input_rules,
402 on_rotate = on_rotate,
405 minetest.register_node("mesecons_delayer:delayer_on_locked", {
406 description = "Redstone Repeater (Locked, Powered)",
407 _doc_items_create_entry = false,
408 drawtype = "nodebox",
409 tiles = {
410 "mesecons_delayer_locked_on.png",
411 "mcl_stairs_stone_slab_top.png",
412 "mesecons_delayer_sides_locked_on.png",
413 "mesecons_delayer_sides_locked_on.png",
414 "mesecons_delayer_front_locked_on.png",
415 "mesecons_delayer_end_locked_on.png",
417 walkable = true,
418 selection_box = {
419 type = "fixed",
420 fixed = { -8/16, -8/16, -8/16, 8/16, -6/16, 8/16 },
422 collision_box = {
423 type = "fixed",
424 fixed = { -8/16, -8/16, -8/16, 8/16, -6/16, 8/16 },
426 node_box = {
427 type = "fixed",
428 fixed = {
429 { -8/16, -8/16, -8/16, 8/16, -6/16, 8/16 }, -- the main slab
430 { -1/16, -6/16, 6/16, 1/16, -1/16, 4/16}, -- still torch
431 { -6/16, -6/16, -1/16, 6/16, -4/16, 1/16}, -- lock
434 after_dig_node = function(pos, oldnode)
435 check_unlock_repeater(pos, oldnode)
436 end,
437 groups = {dig_immediate = 3, dig_by_water=1,destroy_by_lava_flow=1, dig_by_piston=1, attached_node=1, redstone_repeater=5, not_in_creative_inventory = 1},
438 paramtype = "light",
439 paramtype2 = "facedir",
440 sunlight_propagates = false,
441 is_ground_content = false,
442 drop = 'mesecons_delayer:delayer_off_1',
443 delayer_time = 0.1,
444 sounds = mcl_sounds.node_sound_stone_defaults(),
445 mesecons = {
446 receptor =
448 state = mesecon.state.on,
449 rules = delayer_get_output_rules
451 effector =
453 rules = delayer_get_input_rules,
456 on_rotate = on_rotate,
459 minetest.register_craft({
460 output = "mesecons_delayer:delayer_off_1",
461 recipe = {
462 {"mesecons_torch:mesecon_torch_on", "mesecons:redstone", "mesecons_torch:mesecon_torch_on"},
463 {"mcl_core:stone","mcl_core:stone", "mcl_core:stone"},
467 -- Add entry aliases for the Help
468 if minetest.get_modpath("doc") then
469 doc.add_entry_alias("nodes", "mesecons_delayer:delayer_off_1", "nodes", "mesecons_delayer:delayer_off_2")
470 doc.add_entry_alias("nodes", "mesecons_delayer:delayer_off_1", "nodes", "mesecons_delayer:delayer_off_3")
471 doc.add_entry_alias("nodes", "mesecons_delayer:delayer_off_1", "nodes", "mesecons_delayer:delayer_off_4")
472 doc.add_entry_alias("nodes", "mesecons_delayer:delayer_off_1", "nodes", "mesecons_delayer:delayer_off_locked")
473 doc.add_entry_alias("nodes", "mesecons_delayer:delayer_off_1", "nodes", "mesecons_delayer:delayer_on_1")
474 doc.add_entry_alias("nodes", "mesecons_delayer:delayer_off_1", "nodes", "mesecons_delayer:delayer_on_2")
475 doc.add_entry_alias("nodes", "mesecons_delayer:delayer_off_1", "nodes", "mesecons_delayer:delayer_on_3")
476 doc.add_entry_alias("nodes", "mesecons_delayer:delayer_off_1", "nodes", "mesecons_delayer:delayer_on_4")
477 doc.add_entry_alias("nodes", "mesecons_delayer:delayer_off_1", "nodes", "mesecons_delayer:delayer_on_locked")