Repeater: Fix sometimes bad lock on place
[MineClone/MineClone2.git] / mods / ITEMS / REDSTONE / mesecons_delayer / init.lua
blob612e7639102576c021de2a07cc8b00eba6fac018
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"
185 -- Check sides of constructed repeater and lock it, if required
186 on_construct = function(pos)
187 local node = minetest.get_node(pos)
188 local sides = delayer_get_sides(node)
189 for s=1, #sides do
190 local spos = vector.add(pos, sides[s])
191 local snode = minetest.get_node(spos)
192 -- Is there a powered repeater at one of our sides?
193 local g = minetest.get_item_group(snode.name, "redstone_repeater")
194 if g ~= 0 and mesecon.is_receptor_on(snode.name) then
195 -- The other repeater must also face towards the constructed node
196 local sface = delayer_get_output_rules(snode)[1]
197 local sface_pos = vector.add(spos, sface)
198 if vector.equals(sface_pos, pos) then
199 -- Repeater is facing towards us! Now we just need to lock the costructed node
200 if mesecon.is_powered(pos, delayer_get_input_rules(node)[1]) ~= false then
201 minetest.set_node(pos, {name="mesecons_delayer:delayer_on_locked", param2 = node.param2})
202 else
203 minetest.set_node(pos, {name="mesecons_delayer:delayer_off_locked", param2 = node.param2})
205 break
210 else
211 help = false
214 local on_rotate
215 if minetest.get_modpath("screwdriver") then
216 on_rotate = screwdriver.disallow
220 minetest.register_node("mesecons_delayer:delayer_off_"..tostring(i), {
221 description = "Redstone Repeater",
222 inventory_image = icon,
223 wield_image = icon,
224 _doc_items_create_entry = help,
225 _doc_items_longdesc = longdesc,
226 _doc_items_usagehelp = usagehelp,
227 drawtype = "nodebox",
228 tiles = {
229 "mesecons_delayer_off.png",
230 "mcl_stairs_stone_slab_top.png",
231 "mesecons_delayer_sides_off.png",
232 "mesecons_delayer_sides_off.png",
233 "mesecons_delayer_ends_off.png",
234 "mesecons_delayer_ends_off.png",
236 wield_image = "mesecons_delayer_off.png",
237 walkable = true,
238 selection_box = {
239 type = "fixed",
240 fixed = { -8/16, -8/16, -8/16, 8/16, -6/16, 8/16 },
242 collision_box = {
243 type = "fixed",
244 fixed = { -8/16, -8/16, -8/16, 8/16, -6/16, 8/16 },
246 node_box = {
247 type = "fixed",
248 fixed = boxes
250 groups = groups,
251 paramtype = "light",
252 paramtype2 = "facedir",
253 sunlight_propagates = false,
254 is_ground_content = false,
255 drop = 'mesecons_delayer:delayer_off_1',
256 on_rightclick = function (pos, node)
257 if node.name=="mesecons_delayer:delayer_off_1" then
258 minetest.set_node(pos, {name="mesecons_delayer:delayer_off_2", param2=node.param2})
259 elseif node.name=="mesecons_delayer:delayer_off_2" then
260 minetest.set_node(pos, {name="mesecons_delayer:delayer_off_3", param2=node.param2})
261 elseif node.name=="mesecons_delayer:delayer_off_3" then
262 minetest.set_node(pos, {name="mesecons_delayer:delayer_off_4", param2=node.param2})
263 elseif node.name=="mesecons_delayer:delayer_off_4" then
264 minetest.set_node(pos, {name="mesecons_delayer:delayer_off_1", param2=node.param2})
266 end,
267 on_construct = on_construct,
268 delayer_time = delaytime,
269 delayer_onstate = "mesecons_delayer:delayer_on_"..tostring(i),
270 delayer_lockstate = "mesecons_delayer:delayer_off_locked",
271 sounds = mcl_sounds.node_sound_stone_defaults(),
272 mesecons = {
273 receptor =
275 state = mesecon.state.off,
276 rules = delayer_get_output_rules
278 effector =
280 rules = delayer_get_input_rules,
281 action_on = delayer_activate
284 on_rotate = on_rotate,
288 minetest.register_node("mesecons_delayer:delayer_on_"..tostring(i), {
289 description = "Redstone Repeater (Powered)",
290 _doc_items_create_entry = false,
291 drawtype = "nodebox",
292 tiles = {
293 "mesecons_delayer_on.png",
294 "mcl_stairs_stone_slab_top.png",
295 "mesecons_delayer_sides_on.png",
296 "mesecons_delayer_sides_on.png",
297 "mesecons_delayer_ends_on.png",
298 "mesecons_delayer_ends_on.png",
300 walkable = true,
301 selection_box = {
302 type = "fixed",
303 fixed = { -8/16, -8/16, -8/16, 8/16, -6/16, 8/16 },
305 collision_box = {
306 type = "fixed",
307 fixed = { -8/16, -8/16, -8/16, 8/16, -6/16, 8/16 },
309 node_box = {
310 type = "fixed",
311 fixed = boxes
313 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},
314 paramtype = "light",
315 paramtype2 = "facedir",
316 sunlight_propagates = false,
317 is_ground_content = false,
318 drop = 'mesecons_delayer:delayer_off_1',
319 on_rightclick = function (pos, node)
320 if node.name=="mesecons_delayer:delayer_on_1" then
321 minetest.set_node(pos, {name="mesecons_delayer:delayer_on_2",param2=node.param2})
322 elseif node.name=="mesecons_delayer:delayer_on_2" then
323 minetest.set_node(pos, {name="mesecons_delayer:delayer_on_3",param2=node.param2})
324 elseif node.name=="mesecons_delayer:delayer_on_3" then
325 minetest.set_node(pos, {name="mesecons_delayer:delayer_on_4",param2=node.param2})
326 elseif node.name=="mesecons_delayer:delayer_on_4" then
327 minetest.set_node(pos, {name="mesecons_delayer:delayer_on_1",param2=node.param2})
329 end,
330 after_dig_node = function(pos, oldnode)
331 check_unlock_repeater(pos, oldnode)
332 end,
333 delayer_time = delaytime,
334 delayer_offstate = "mesecons_delayer:delayer_off_"..tostring(i),
335 delayer_lockstate = "mesecons_delayer:delayer_on_locked",
336 sounds = mcl_sounds.node_sound_stone_defaults(),
337 mesecons = {
338 receptor =
340 state = mesecon.state.on,
341 rules = delayer_get_output_rules
343 effector =
345 rules = delayer_get_input_rules,
346 action_off = delayer_deactivate
349 on_rotate = on_rotate,
355 -- Locked repeater
357 minetest.register_node("mesecons_delayer:delayer_off_locked", {
358 description = "Redstone Repeater (Locked)",
359 inventory_image = icon,
360 wield_image = icon,
361 _doc_items_create_entry = false,
362 drawtype = "nodebox",
363 -- FIXME: Textures of torch and the lock bar overlap. Nodeboxes are (sadly) not suitable for this.
364 -- So this needs to be turned into a mesh.
365 tiles = {
366 "mesecons_delayer_locked_off.png",
367 "mcl_stairs_stone_slab_top.png",
368 "mesecons_delayer_sides_locked_off.png",
369 "mesecons_delayer_sides_locked_off.png",
370 "mesecons_delayer_front_locked_off.png",
371 "mesecons_delayer_end_locked_off.png",
373 wield_image = "mesecons_delayer_locked_off.png",
374 walkable = true,
375 selection_box = {
376 type = "fixed",
377 fixed = { -8/16, -8/16, -8/16, 8/16, -6/16, 8/16 },
379 collision_box = {
380 type = "fixed",
381 fixed = { -8/16, -8/16, -8/16, 8/16, -6/16, 8/16 },
383 node_box = {
384 type = "fixed",
385 fixed = {
386 { -8/16, -8/16, -8/16, 8/16, -6/16, 8/16 }, -- the main slab
387 { -1/16, -6/16, 6/16, 1/16, -1/16, 4/16}, -- still torch
388 { -6/16, -6/16, -1/16, 6/16, -4/16, 1/16}, -- lock
391 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},
392 paramtype = "light",
393 paramtype2 = "facedir",
394 sunlight_propagates = false,
395 is_ground_content = false,
396 drop = 'mesecons_delayer:delayer_off_1',
397 delayer_time = 0.1,
398 sounds = mcl_sounds.node_sound_stone_defaults(),
399 mesecons = {
400 receptor =
402 state = mesecon.state.off,
403 rules = delayer_get_output_rules
405 effector =
407 rules = delayer_get_input_rules,
410 on_rotate = on_rotate,
413 minetest.register_node("mesecons_delayer:delayer_on_locked", {
414 description = "Redstone Repeater (Locked, Powered)",
415 _doc_items_create_entry = false,
416 drawtype = "nodebox",
417 tiles = {
418 "mesecons_delayer_locked_on.png",
419 "mcl_stairs_stone_slab_top.png",
420 "mesecons_delayer_sides_locked_on.png",
421 "mesecons_delayer_sides_locked_on.png",
422 "mesecons_delayer_front_locked_on.png",
423 "mesecons_delayer_end_locked_on.png",
425 walkable = true,
426 selection_box = {
427 type = "fixed",
428 fixed = { -8/16, -8/16, -8/16, 8/16, -6/16, 8/16 },
430 collision_box = {
431 type = "fixed",
432 fixed = { -8/16, -8/16, -8/16, 8/16, -6/16, 8/16 },
434 node_box = {
435 type = "fixed",
436 fixed = {
437 { -8/16, -8/16, -8/16, 8/16, -6/16, 8/16 }, -- the main slab
438 { -1/16, -6/16, 6/16, 1/16, -1/16, 4/16}, -- still torch
439 { -6/16, -6/16, -1/16, 6/16, -4/16, 1/16}, -- lock
442 after_dig_node = function(pos, oldnode)
443 check_unlock_repeater(pos, oldnode)
444 end,
445 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},
446 paramtype = "light",
447 paramtype2 = "facedir",
448 sunlight_propagates = false,
449 is_ground_content = false,
450 drop = 'mesecons_delayer:delayer_off_1',
451 delayer_time = 0.1,
452 sounds = mcl_sounds.node_sound_stone_defaults(),
453 mesecons = {
454 receptor =
456 state = mesecon.state.on,
457 rules = delayer_get_output_rules
459 effector =
461 rules = delayer_get_input_rules,
464 on_rotate = on_rotate,
467 minetest.register_craft({
468 output = "mesecons_delayer:delayer_off_1",
469 recipe = {
470 {"mesecons_torch:mesecon_torch_on", "mesecons:redstone", "mesecons_torch:mesecon_torch_on"},
471 {"mcl_core:stone","mcl_core:stone", "mcl_core:stone"},
475 -- Add entry aliases for the Help
476 if minetest.get_modpath("doc") then
477 doc.add_entry_alias("nodes", "mesecons_delayer:delayer_off_1", "nodes", "mesecons_delayer:delayer_off_2")
478 doc.add_entry_alias("nodes", "mesecons_delayer:delayer_off_1", "nodes", "mesecons_delayer:delayer_off_3")
479 doc.add_entry_alias("nodes", "mesecons_delayer:delayer_off_1", "nodes", "mesecons_delayer:delayer_off_4")
480 doc.add_entry_alias("nodes", "mesecons_delayer:delayer_off_1", "nodes", "mesecons_delayer:delayer_off_locked")
481 doc.add_entry_alias("nodes", "mesecons_delayer:delayer_off_1", "nodes", "mesecons_delayer:delayer_on_1")
482 doc.add_entry_alias("nodes", "mesecons_delayer:delayer_off_1", "nodes", "mesecons_delayer:delayer_on_2")
483 doc.add_entry_alias("nodes", "mesecons_delayer:delayer_off_1", "nodes", "mesecons_delayer:delayer_on_3")
484 doc.add_entry_alias("nodes", "mesecons_delayer:delayer_off_1", "nodes", "mesecons_delayer:delayer_on_4")
485 doc.add_entry_alias("nodes", "mesecons_delayer:delayer_off_1", "nodes", "mesecons_delayer:delayer_on_locked")