Damage anvil after falling
[MineClone/MineClone2.git] / mods / ITEMS / REDSTONE / mesecons / internal.lua
blob7986c2908b3f59ca8d989ac5208be4ae85f8c9d4
1 -- Internal.lua - The core of mesecons
2 --
3 -- For more practical developer resources see http://mesecons.net/developers.php
4 --
5 -- Function overview
6 -- mesecon.get_effector(nodename) --> Returns the mesecons.effector -specifictation in the nodedef by the nodename
7 -- mesecon.get_receptor(nodename) --> Returns the mesecons.receptor -specifictation in the nodedef by the nodename
8 -- mesecon.get_conductor(nodename) --> Returns the mesecons.conductor-specifictation in the nodedef by the nodename
9 -- mesecon.get_any_inputrules (node) --> Returns the rules of a node if it is a conductor or an effector
10 -- mesecon.get_any_outputrules (node) --> Returns the rules of a node if it is a conductor or a receptor
12 -- RECEPTORS
13 -- mesecon.is_receptor(nodename) --> Returns true if nodename is a receptor
14 -- mesecon.is_receptor_on(nodename --> Returns true if nodename is an receptor with state = mesecon.state.on
15 -- mesecon.is_receptor_off(nodename) --> Returns true if nodename is an receptor with state = mesecon.state.off
16 -- mesecon.receptor_get_rules(node) --> Returns the rules of the receptor (mesecon.rules.default if none specified)
18 -- EFFECTORS
19 -- mesecon.is_effector(nodename) --> Returns true if nodename is an effector
20 -- mesecon.is_effector_on(nodename) --> Returns true if nodename is an effector with nodedef.mesecons.effector.action_off
21 -- mesecon.is_effector_off(nodename) --> Returns true if nodename is an effector with nodedef.mesecons.effector.action_on
22 -- mesecon.effector_get_rules(node) --> Returns the input rules of the effector (mesecon.rules.default if none specified)
24 -- SIGNALS
25 -- mesecon.activate(pos, node, depth) --> Activates the effector node at the specific pos (calls nodedef.mesecons.effector.action_on), higher depths are executed later
26 -- mesecon.deactivate(pos, node, depth) --> Deactivates the effector node at the specific pos (calls nodedef.mesecons.effector.action_off), higher depths are executed later
27 -- mesecon.changesignal(pos, node, rulename, newstate, depth) --> Changes the effector node at the specific pos (calls nodedef.mesecons.effector.action_change), higher depths are executed later
29 -- CONDUCTORS
30 -- mesecon.is_conductor(nodename) --> Returns true if nodename is a conductor
31 -- mesecon.is_conductor_on(node --> Returns true if node is a conductor with state = mesecon.state.on
32 -- mesecon.is_conductor_off(node) --> Returns true if node is a conductor with state = mesecon.state.off
33 -- mesecon.get_conductor_on(node_off) --> Returns the onstate nodename of the conductor
34 -- mesecon.get_conductor_off(node_on) --> Returns the offstate nodename of the conductor
35 -- mesecon.conductor_get_rules(node) --> Returns the input+output rules of a conductor (mesecon.rules.default if none specified)
37 -- HIGH-LEVEL Internals
38 -- mesecon.is_power_on(pos) --> Returns true if pos emits power in any way
39 -- mesecon.is_power_off(pos) --> Returns true if pos does not emit power in any way
40 -- mesecon.is_powered(pos) --> Returns bool, spread. bool is true if pos is powered by a receptor, a conductor or an opaque block.
41 -- spread is true if it is powered AND also transmits its power one block further.
43 -- RULES ROTATION helpers
44 -- mesecon.rotate_rules_right(rules)
45 -- mesecon.rotate_rules_left(rules)
46 -- mesecon.rotate_rules_up(rules)
47 -- mesecon.rotate_rules_down(rules)
48 -- These functions return rules that have been rotated in the specific direction
50 -- General
51 function mesecon.get_effector(nodename)
52 if minetest.registered_nodes[nodename]
53 and minetest.registered_nodes[nodename].mesecons
54 and minetest.registered_nodes[nodename].mesecons.effector then
55 return minetest.registered_nodes[nodename].mesecons.effector
56 end
57 end
59 function mesecon.get_receptor(nodename)
60 if minetest.registered_nodes[nodename]
61 and minetest.registered_nodes[nodename].mesecons
62 and minetest.registered_nodes[nodename].mesecons.receptor then
63 return minetest.registered_nodes[nodename].mesecons.receptor
64 end
65 end
67 function mesecon.get_conductor(nodename)
68 if minetest.registered_nodes[nodename]
69 and minetest.registered_nodes[nodename].mesecons
70 and minetest.registered_nodes[nodename].mesecons.conductor then
71 return minetest.registered_nodes[nodename].mesecons.conductor
72 end
73 end
75 function mesecon.get_any_outputrules(node)
76 if not node then return nil end
78 if mesecon.is_conductor(node.name) then
79 return mesecon.conductor_get_rules(node)
80 elseif mesecon.is_receptor(node.name) then
81 return mesecon.receptor_get_rules(node)
82 elseif minetest.get_item_group(node.name, "opaque") == 1 then
83 return mesecon.rules.alldirs
84 end
85 end
87 function mesecon.get_any_inputrules(node)
88 if not node then return nil end
90 if mesecon.is_conductor(node.name) then
91 return mesecon.conductor_get_rules(node)
92 elseif mesecon.is_effector(node.name) then
93 return mesecon.effector_get_rules(node)
94 elseif minetest.get_item_group(node.name, "opaque") == 1 then
95 return mesecon.rules.alldirs
96 end
97 end
99 function mesecon.get_any_rules(node)
100 return mesecon.mergetable(mesecon.get_any_inputrules(node) or {},
101 mesecon.get_any_outputrules(node) or {})
104 -- Receptors
105 -- Nodes that can power mesecons
106 function mesecon.is_receptor_on(nodename)
107 local receptor = mesecon.get_receptor(nodename)
108 if receptor and receptor.state == mesecon.state.on then
109 return true
111 return false
114 function mesecon.is_receptor_off(nodename)
115 local receptor = mesecon.get_receptor(nodename)
116 if receptor and receptor.state == mesecon.state.off then
117 return true
119 return false
122 function mesecon.is_receptor(nodename)
123 local receptor = mesecon.get_receptor(nodename)
124 if receptor then
125 return true
127 return false
130 function mesecon.receptor_get_rules(node)
131 local receptor = mesecon.get_receptor(node.name)
132 if receptor then
133 local rules = receptor.rules
134 if type(rules) == 'function' then
135 return rules(node)
136 elseif rules then
137 return rules
141 return mesecon.rules.default
144 -- Effectors
145 -- Nodes that can be powered by mesecons
146 function mesecon.is_effector_on(nodename)
147 local effector = mesecon.get_effector(nodename)
148 if effector and effector.action_off then
149 return true
151 return false
154 function mesecon.is_effector_off(nodename)
155 local effector = mesecon.get_effector(nodename)
156 if effector and effector.action_on then
157 return true
159 return false
162 function mesecon.is_effector(nodename)
163 local effector = mesecon.get_effector(nodename)
164 if effector then
165 return true
167 return false
170 function mesecon.effector_get_rules(node)
171 local effector = mesecon.get_effector(node.name)
172 if effector then
173 local rules = effector.rules
174 if type(rules) == 'function' then
175 return rules(node)
176 elseif rules then
177 return rules
180 return mesecon.rules.default
183 -- #######################
184 -- # Signals (effectors) #
185 -- #######################
187 -- Activation:
188 mesecon.queue:add_function("activate", function (pos, rulename)
189 local node = mesecon.get_node_force(pos)
190 if not node then return end
192 local effector = mesecon.get_effector(node.name)
194 if effector and effector.action_on then
195 effector.action_on(pos, node, rulename)
197 end)
199 function mesecon.activate(pos, node, rulename, depth)
200 if rulename == nil then
201 for _,rule in ipairs(mesecon.effector_get_rules(node)) do
202 mesecon.activate(pos, node, rule, depth + 1)
204 return
206 mesecon.queue:add_action(pos, "activate", {rulename}, nil, rulename, 1 / depth)
210 -- Deactivation
211 mesecon.queue:add_function("deactivate", function (pos, rulename)
212 local node = mesecon.get_node_force(pos)
213 if not node then return end
215 local effector = mesecon.get_effector(node.name)
217 if effector and effector.action_off then
218 effector.action_off(pos, node, rulename)
220 end)
222 function mesecon.deactivate(pos, node, rulename, depth)
223 if rulename == nil then
224 for _,rule in ipairs(mesecon.effector_get_rules(node)) do
225 mesecon.deactivate(pos, node, rule, depth + 1)
227 return
229 mesecon.queue:add_action(pos, "deactivate", {rulename}, nil, rulename, 1 / depth)
233 -- Change
234 mesecon.queue:add_function("change", function (pos, rulename, changetype)
235 local node = mesecon.get_node_force(pos)
236 if not node then return end
238 local effector = mesecon.get_effector(node.name)
240 if effector and effector.action_change then
241 effector.action_change(pos, node, rulename, changetype)
243 end)
245 function mesecon.changesignal(pos, node, rulename, newstate, depth)
246 if rulename == nil then
247 for _,rule in ipairs(mesecon.effector_get_rules(node)) do
248 mesecon.changesignal(pos, node, rule, newstate, depth + 1)
250 return
253 -- Include "change" in overwritecheck so that it cannot be overwritten
254 -- by "active" / "deactivate" that will be called upon the node at the same time.
255 local overwritecheck = {"change", rulename}
256 mesecon.queue:add_action(pos, "change", {rulename, newstate}, nil, overwritecheck, 1 / depth)
259 -- Conductors
261 function mesecon.is_conductor_on(node, rulename)
262 if not node then return false end
264 local conductor = mesecon.get_conductor(node.name)
265 if conductor then
266 if conductor.state then
267 return conductor.state == mesecon.state.on
269 if conductor.states then
270 if not rulename then
271 return mesecon.getstate(node.name, conductor.states) ~= 1
273 local bit = mesecon.rule2bit(rulename, mesecon.conductor_get_rules(node))
274 local binstate = mesecon.getbinstate(node.name, conductor.states)
275 return mesecon.get_bit(binstate, bit)
279 return false
282 function mesecon.is_conductor_off(node, rulename)
283 if not node then return false end
285 local conductor = mesecon.get_conductor(node.name)
286 if conductor then
287 if conductor.state then
288 return conductor.state == mesecon.state.off
290 if conductor.states then
291 if not rulename then
292 return mesecon.getstate(node.name, conductor.states) == 1
294 local bit = mesecon.rule2bit(rulename, mesecon.conductor_get_rules(node))
295 local binstate = mesecon.getbinstate(node.name, conductor.states)
296 return not mesecon.get_bit(binstate, bit)
300 return false
303 function mesecon.is_conductor(nodename)
304 local conductor = mesecon.get_conductor(nodename)
305 if conductor then
306 return true
308 return false
311 function mesecon.get_conductor_on(node_off, rulename)
312 local conductor = mesecon.get_conductor(node_off.name)
313 if conductor then
314 if conductor.onstate then
315 return conductor.onstate
317 if conductor.states then
318 local bit = mesecon.rule2bit(rulename, mesecon.conductor_get_rules(node_off))
319 local binstate = mesecon.getbinstate(node_off.name, conductor.states)
320 binstate = mesecon.set_bit(binstate, bit, "1")
321 return conductor.states[tonumber(binstate,2)+1]
324 return offstate
327 function mesecon.get_conductor_off(node_on, rulename)
328 local conductor = mesecon.get_conductor(node_on.name)
329 if conductor then
330 if conductor.offstate then
331 return conductor.offstate
333 if conductor.states then
334 local bit = mesecon.rule2bit(rulename, mesecon.conductor_get_rules(node_on))
335 local binstate = mesecon.getbinstate(node_on.name, conductor.states)
336 binstate = mesecon.set_bit(binstate, bit, "0")
337 return conductor.states[tonumber(binstate,2)+1]
340 return onstate
343 function mesecon.conductor_get_rules(node)
344 local conductor = mesecon.get_conductor(node.name)
345 if conductor then
346 local rules = conductor.rules
347 if type(rules) == 'function' then
348 return rules(node)
349 elseif rules then
350 return rules
353 return mesecon.rules.default
356 -- some more general high-level stuff
358 function mesecon.is_power_on(pos, rulename)
359 local node = mesecon.get_node_force(pos)
360 if node and (mesecon.is_conductor_on(node, rulename) or mesecon.is_receptor_on(node.name)) then
361 return true
363 return false
366 function mesecon.is_power_off(pos, rulename)
367 local node = mesecon.get_node_force(pos)
368 if node and (mesecon.is_conductor_off(node, rulename) or mesecon.is_receptor_off(node.name)) then
369 return true
371 return false
374 -- Turn off an equipotential section starting at `pos`, which outputs in the direction of `link`.
375 -- Breadth-first search. Map is abstracted away in a voxelmanip.
376 -- Follow all all conductor paths replacing conductors that were already
377 -- looked at, activating / changing all effectors along the way.
378 function mesecon.turnon(pos, link)
379 local frontiers = {{pos = pos, link = link}}
381 local depth = 1
382 while frontiers[1] do
383 local f = table.remove(frontiers, 1)
384 local node = mesecon.get_node_force(f.pos)
386 if not node then
387 -- Area does not exist; do nothing
388 elseif mesecon.is_conductor_off(node, f.link) then
389 local rules = mesecon.conductor_get_rules(node)
391 -- Call turnon on neighbors
392 for _, r in ipairs(mesecon.rule2meta(f.link, rules)) do
393 local np = vector.add(f.pos, r)
394 for _, l in ipairs(mesecon.rules_link_rule_all(f.pos, r)) do
395 table.insert(frontiers, {pos = np, link = l})
399 mesecon.swap_node_force(f.pos, mesecon.get_conductor_on(node, f.link))
400 elseif mesecon.is_effector(node.name) then
401 mesecon.changesignal(f.pos, node, f.link, mesecon.state.on, depth)
402 if mesecon.is_effector_off(node.name) then
403 mesecon.activate(f.pos, node, f.link, depth)
406 if node and f.link.spread and minetest.get_item_group(node.name, "opaque") == 1 then
407 -- Call turnon on neighbors
408 -- Warning: A LOT of nodes need to be looked at for this to work
409 for _, r in ipairs(mesecon.rule2meta(f.link, mesecon.rules.mcl_alldirs_spread)) do
410 local np = vector.add(f.pos, r)
411 for _, l in ipairs(mesecon.rules_link_rule_all(f.pos, r)) do
412 local nlink = table.copy(l)
413 nlink.spread = false
414 table.insert(frontiers, {pos = np, link = nlink})
419 depth = depth + 1
423 -- Turn off an equipotential section starting at `pos`, which outputs in the direction of `link`.
424 -- Breadth-first search. Map is abstracted away in a voxelmanip.
425 -- Follow all all conductor paths replacing conductors that were already
426 -- looked at, deactivating / changing all effectors along the way.
427 -- In case an onstate receptor is discovered, abort the process by returning false, which will
428 -- cause `receptor_off` to discard all changes made in the voxelmanip.
429 -- Contrary to turnon, turnoff has to cache all change and deactivate signals so that they will only
430 -- be called in the very end when we can be sure that no conductor was found along the path.
432 -- Signal table entry structure:
433 -- {
434 -- pos = position of effector,
435 -- node = node descriptor (name, param1 and param2),
436 -- link = link the effector is connected to,
437 -- depth = indicates order in which signals wire fired, higher is later
438 -- }
439 function mesecon.turnoff(pos, link)
440 local frontiers = {{pos = pos, link = link}}
441 local signals = {}
443 local depth = 1
444 while frontiers[1] do
445 local f = table.remove(frontiers, 1)
446 local node = mesecon.get_node_force(f.pos)
448 if not node then
449 -- No-op
450 elseif mesecon.is_conductor_on(node, f.link) then
451 local rules = mesecon.conductor_get_rules(node)
452 for _, r in ipairs(mesecon.rule2meta(f.link, rules)) do
453 local np = vector.add(f.pos, r)
455 -- Check if an onstate receptor is connected. If that is the case,
456 -- abort this turnoff process by returning false. `receptor_off` will
457 -- discard all the changes that we made in the voxelmanip:
458 for _, l in ipairs(mesecon.rules_link_rule_all_inverted(f.pos, r)) do
459 if mesecon.is_receptor_on(mesecon.get_node_force(np).name) then
460 return false
464 -- Call turnoff on neighbors
465 for _, l in ipairs(mesecon.rules_link_rule_all(f.pos, r)) do
466 table.insert(frontiers, {pos = np, link = l})
470 mesecon.swap_node_force(f.pos, mesecon.get_conductor_off(node, f.link))
471 elseif mesecon.is_effector(node.name) then
472 table.insert(signals, {
473 pos = f.pos,
474 node = node,
475 link = f.link,
476 depth = depth
480 if node and f.link.spread and minetest.get_item_group(node.name, "opaque") == 1 then
481 -- Call turnoff on neighbors
482 -- Warning: A LOT of nodes need to be looked at for this to work
483 for _, r in ipairs(mesecon.rule2meta(f.link, mesecon.rules.mcl_alldirs_spread)) do
484 local np = vector.add(f.pos, r)
485 local n = mesecon.get_node_force(np)
486 if mesecon.is_receptor_on(n.name) then
487 local receptorrules = mesecon.receptor_get_rules(n)
488 for _, rr in pairs(receptorrules) do
489 if rr.spread and vector.equals(mesecon.invertRule(rr), r) then
490 return false
494 for _, l in ipairs(mesecon.rules_link_rule_all(f.pos, r)) do
495 local nlink = table.copy(l)
496 nlink.spread = false
497 table.insert(frontiers, {pos = np, link = nlink})
502 depth = depth + 1
505 for _, sig in ipairs(signals) do
506 mesecon.changesignal(sig.pos, sig.node, sig.link, mesecon.state.off, sig.depth)
507 if mesecon.is_effector_on(sig.node.name) and not mesecon.is_powered(sig.pos) then
508 mesecon.deactivate(sig.pos, sig.node, sig.link, sig.depth)
512 return true
515 -- Get all linking inputrules of inputnode (effector or conductor) that is connected to
516 -- outputnode (receptor or conductor) at position `output` and has an output in direction `rule`
517 function mesecon.rules_link_rule_all(output, rule)
518 local input = vector.add(output, rule)
519 local inputnode = mesecon.get_node_force(input)
520 local inputrules = mesecon.get_any_inputrules(inputnode)
521 if not inputrules then
522 return {}
524 local rules = {}
526 for _, inputrule in ipairs(mesecon.flattenrules(inputrules)) do
527 -- Check if input accepts from output
528 if vector.equals(vector.add(input, inputrule), output) then
529 local newrule = table.copy(inputrule)
530 newrule.spread = rule.spread
531 table.insert(rules, newrule)
535 return rules
538 -- Get all linking outputnodes of outputnode (receptor or conductor) that is connected to
539 -- inputnode (effector or conductor) at position `input` and has an input in direction `rule`
540 function mesecon.rules_link_rule_all_inverted(input, rule)
541 local output = vector.add(input, rule)
542 local outputnode = mesecon.get_node_force(output)
543 local outputrules = mesecon.get_any_outputrules(outputnode)
544 if not outputrules then
545 return {}
547 local rules = {}
549 for _, outputrule in ipairs(mesecon.flattenrules(outputrules)) do
550 if vector.equals(vector.add(output, outputrule), input) then
551 local newrule = table.copy(outputrule)
552 newrule = mesecon.invertRule(newrule)
553 newrule.spread = rule.spread
554 table.insert(rules, newrule)
557 return rules
560 function mesecon.is_powered(pos, rule, depth, sourcepos, home_pos)
561 if depth == nil then depth = 0 end
562 if depth > 1 then
563 return false, false
565 local node = mesecon.get_node_force(pos)
566 local rules = mesecon.get_any_inputrules(node)
567 if not rules then
568 return false, false
570 if not home_pos then
571 home_pos = pos
574 -- List of nodes that send out power to pos
575 if sourcepos == nil then
576 sourcepos = {}
579 local function power_walk(pos, home_pos, sourcepos, rulenames, rule, depth)
580 local spread = false
581 for _, rname in ipairs(rulenames) do
582 local np = vector.add(pos, rname)
583 local nn = mesecon.get_node_force(np)
584 if (mesecon.is_conductor_on (nn, mesecon.invertRule(rname))
585 or mesecon.is_receptor_on (nn.name)) then
586 if not vector.equals(home_pos, np) then
587 local rulez = mesecon.get_any_outputrules(nn)
588 local spread_tmp = false
589 for r=1, #rulez do
590 if vector.equals(mesecon.invertRule(rname), rulez[r]) then
591 if rulez[r].spread then
592 spread_tmp = true
596 if depth == 0 or spread_tmp then
597 table.insert(sourcepos, np)
598 if spread_tmp then
599 spread = true
603 elseif depth == 0 and minetest.get_item_group(nn.name, "opaque") == 1 then
604 local more_sourcepos = mesecon.is_powered(np, nil, depth + 1, sourcepos, home_pos)
605 if more_sourcepos and #more_sourcepos > 0 then
606 mesecon.mergetable(sourcepos, more_sourcepos)
610 return sourcepos, spread
613 local spread = false
614 if not rule then
615 for _, rule in ipairs(mesecon.flattenrules(rules)) do
616 local spread_temp
617 local rulenames = mesecon.rules_link_rule_all_inverted(pos, rule)
618 sourcepos, spread_temp = power_walk(pos, home_pos, sourcepos, rulenames, rule, depth)
619 if spread_temp then
620 spread = true
623 else
624 local rulenames = mesecon.rules_link_rule_all_inverted(pos, rule)
625 sourcepos, spread = power_walk(pos, home_pos, sourcepos, rulenames, rule, depth)
628 -- Return FALSE if not powered, return list of sources if is powered
630 if (#sourcepos == 0) then
631 return false, false
632 else
633 return sourcepos, spread