From 888b0ebfec8c2eff9015163549a7e47443cb8665 Mon Sep 17 00:00:00 2001 From: Vanessa Ezekowitz Date: Fri, 1 Apr 2016 21:00:20 -0400 Subject: "explode" all modpacks into their individual components (you can't have a modpack buried inside a modpack) --- mesecons/internal.lua | 652 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 652 insertions(+) create mode 100644 mesecons/internal.lua (limited to 'mesecons/internal.lua') diff --git a/mesecons/internal.lua b/mesecons/internal.lua new file mode 100644 index 0000000..741abf1 --- /dev/null +++ b/mesecons/internal.lua @@ -0,0 +1,652 @@ +-- Internal.lua - The core of mesecons +-- +-- For more practical developer resources see http://mesecons.net/developers.php +-- +-- Function overview +-- mesecon.get_effector(nodename) --> Returns the mesecons.effector -specifictation in the nodedef by the nodename +-- mesecon.get_receptor(nodename) --> Returns the mesecons.receptor -specifictation in the nodedef by the nodename +-- mesecon.get_conductor(nodename) --> Returns the mesecons.conductor-specifictation in the nodedef by the nodename +-- mesecon.get_any_inputrules (node) --> Returns the rules of a node if it is a conductor or an effector +-- mesecon.get_any_outputrules (node) --> Returns the rules of a node if it is a conductor or a receptor + +-- RECEPTORS +-- mesecon.is_receptor(nodename) --> Returns true if nodename is a receptor +-- mesecon.is_receptor_on(nodename --> Returns true if nodename is an receptor with state = mesecon.state.on +-- mesecon.is_receptor_off(nodename) --> Returns true if nodename is an receptor with state = mesecon.state.off +-- mesecon.receptor_get_rules(node) --> Returns the rules of the receptor (mesecon.rules.default if none specified) + +-- EFFECTORS +-- mesecon.is_effector(nodename) --> Returns true if nodename is an effector +-- mesecon.is_effector_on(nodename) --> Returns true if nodename is an effector with nodedef.mesecons.effector.action_off +-- mesecon.is_effector_off(nodename) --> Returns true if nodename is an effector with nodedef.mesecons.effector.action_on +-- mesecon.effector_get_rules(node) --> Returns the input rules of the effector (mesecon.rules.default if none specified) + +-- SIGNALS +-- mesecon.activate(pos, node, depth) --> Activates the effector node at the specific pos (calls nodedef.mesecons.effector.action_on), higher depths are executed later +-- mesecon.deactivate(pos, node, depth) --> Deactivates the effector node at the specific pos (calls nodedef.mesecons.effector.action_off), higher depths are executed later +-- 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 + +-- CONDUCTORS +-- mesecon.is_conductor(nodename) --> Returns true if nodename is a conductor +-- mesecon.is_conductor_on(node --> Returns true if node is a conductor with state = mesecon.state.on +-- mesecon.is_conductor_off(node) --> Returns true if node is a conductor with state = mesecon.state.off +-- mesecon.get_conductor_on(node_off) --> Returns the onstate nodename of the conductor +-- mesecon.get_conductor_off(node_on) --> Returns the offstate nodename of the conductor +-- mesecon.conductor_get_rules(node) --> Returns the input+output rules of a conductor (mesecon.rules.default if none specified) + +-- HIGH-LEVEL Internals +-- mesecon.is_power_on(pos) --> Returns true if pos emits power in any way +-- mesecon.is_power_off(pos) --> Returns true if pos does not emit power in any way +-- mesecon.turnon(pos, link) --> link is the input rule that caused calling turnon, turns on every connected node, iterative +-- mesecon.turnoff(pos, link) --> link is the input rule that caused calling turnoff, turns off every connected node, iterative +-- mesecon.connected_to_receptor(pos, link) --> Returns true if pos is connected to a receptor directly or via conductors, iterative +-- mesecon.rules_link(output, input, dug_outputrules) --> Returns true if outputposition + outputrules = inputposition and inputposition + inputrules = outputposition (if the two positions connect) +-- mesecon.rules_link_anydir(outp., inp., d_outpr.) --> Same as rules mesecon.rules_link but also returns true if output and input are swapped +-- mesecon.is_powered(pos) --> Returns true if pos is powered by a receptor or a conductor + +-- RULES ROTATION helpers +-- mesecon.rotate_rules_right(rules) +-- mesecon.rotate_rules_left(rules) +-- mesecon.rotate_rules_up(rules) +-- mesecon.rotate_rules_down(rules) +-- These functions return rules that have been rotated in the specific direction + +-- General +function mesecon.get_effector(nodename) + if minetest.registered_nodes[nodename] + and minetest.registered_nodes[nodename].mesecons + and minetest.registered_nodes[nodename].mesecons.effector then + return minetest.registered_nodes[nodename].mesecons.effector + end +end + +function mesecon.get_receptor(nodename) + if minetest.registered_nodes[nodename] + and minetest.registered_nodes[nodename].mesecons + and minetest.registered_nodes[nodename].mesecons.receptor then + return minetest.registered_nodes[nodename].mesecons.receptor + end +end + +function mesecon.get_conductor(nodename) + if minetest.registered_nodes[nodename] + and minetest.registered_nodes[nodename].mesecons + and minetest.registered_nodes[nodename].mesecons.conductor then + return minetest.registered_nodes[nodename].mesecons.conductor + end +end + +function mesecon.get_any_outputrules (node) + if mesecon.is_conductor(node.name) then + return mesecon.conductor_get_rules(node) + elseif mesecon.is_receptor(node.name) then + return mesecon.receptor_get_rules(node) + end +end + +function mesecon.get_any_inputrules (node) + if mesecon.is_conductor(node.name) then + return mesecon.conductor_get_rules(node) + elseif mesecon.is_effector(node.name) then + return mesecon.effector_get_rules(node) + end +end + +function mesecon.get_any_rules (node) + return mesecon.mergetable(mesecon.get_any_inputrules(node) or {}, + mesecon.get_any_outputrules(node) or {}) +end + +-- Receptors +-- Nodes that can power mesecons +function mesecon.is_receptor_on(nodename) + local receptor = mesecon.get_receptor(nodename) + if receptor and receptor.state == mesecon.state.on then + return true + end + return false +end + +function mesecon.is_receptor_off(nodename) + local receptor = mesecon.get_receptor(nodename) + if receptor and receptor.state == mesecon.state.off then + return true + end + return false +end + +function mesecon.is_receptor(nodename) + local receptor = mesecon.get_receptor(nodename) + if receptor then + return true + end + return false +end + +function mesecon.receptor_get_rules(node) + local receptor = mesecon.get_receptor(node.name) + if receptor then + local rules = receptor.rules + if type(rules) == 'function' then + return rules(node) + elseif rules then + return rules + end + end + + return mesecon.rules.default +end + +-- Effectors +-- Nodes that can be powered by mesecons +function mesecon.is_effector_on(nodename) + local effector = mesecon.get_effector(nodename) + if effector and effector.action_off then + return true + end + return false +end + +function mesecon.is_effector_off(nodename) + local effector = mesecon.get_effector(nodename) + if effector and effector.action_on then + return true + end + return false +end + +function mesecon.is_effector(nodename) + local effector = mesecon.get_effector(nodename) + if effector then + return true + end + return false +end + +function mesecon.effector_get_rules(node) + local effector = mesecon.get_effector(node.name) + if effector then + local rules = effector.rules + if type(rules) == 'function' then + return rules(node) + elseif rules then + return rules + end + end + return mesecon.rules.default +end + +-- ####################### +-- # Signals (effectors) # +-- ####################### + +-- Activation: +mesecon.queue:add_function("activate", function (pos, rulename) + local node = minetest.get_node(pos) + local effector = mesecon.get_effector(node.name) + + if effector and effector.action_on then + effector.action_on(pos, node, rulename) + end +end) + +function mesecon.activate(pos, node, rulename, depth) + if rulename == nil then + for _,rule in ipairs(mesecon.effector_get_rules(node)) do + mesecon.activate(pos, node, rule, depth + 1) + end + return + end + mesecon.queue:add_action(pos, "activate", {rulename}, nil, rulename, 1 / depth) +end + + +-- Deactivation +mesecon.queue:add_function("deactivate", function (pos, rulename) + local node = minetest.get_node(pos) + local effector = mesecon.get_effector(node.name) + + if effector and effector.action_off then + effector.action_off(pos, node, rulename) + end +end) + +function mesecon.deactivate(pos, node, rulename, depth) + if rulename == nil then + for _,rule in ipairs(mesecon.effector_get_rules(node)) do + mesecon.deactivate(pos, node, rule, depth + 1) + end + return + end + mesecon.queue:add_action(pos, "deactivate", {rulename}, nil, rulename, 1 / depth) +end + + +-- Change +mesecon.queue:add_function("change", function (pos, rulename, changetype) + local node = minetest.get_node(pos) + local effector = mesecon.get_effector(node.name) + + if effector and effector.action_change then + effector.action_change(pos, node, rulename, changetype) + end +end) + +function mesecon.changesignal(pos, node, rulename, newstate, depth) + if rulename == nil then + for _,rule in ipairs(mesecon.effector_get_rules(node)) do + mesecon.changesignal(pos, node, rule, newstate, depth + 1) + end + return + end + + -- Include "change" in overwritecheck so that it cannot be overwritten + -- by "active" / "deactivate" that will be called upon the node at the same time. + local overwritecheck = {"change", rulename} + mesecon.queue:add_action(pos, "change", {rulename, newstate}, nil, overwritecheck, 1 / depth) +end + +-- Conductors + +function mesecon.is_conductor_on(node, rulename) + local conductor = mesecon.get_conductor(node.name) + if conductor then + if conductor.state then + return conductor.state == mesecon.state.on + end + if conductor.states then + if not rulename then + return mesecon.getstate(node.name, conductor.states) ~= 1 + end + local bit = mesecon.rule2bit(rulename, mesecon.conductor_get_rules(node)) + local binstate = mesecon.getbinstate(node.name, conductor.states) + return mesecon.get_bit(binstate, bit) + end + end + return false +end + +function mesecon.is_conductor_off(node, rulename) + local conductor = mesecon.get_conductor(node.name) + if conductor then + if conductor.state then + return conductor.state == mesecon.state.off + end + if conductor.states then + if not rulename then + return mesecon.getstate(node.name, conductor.states) == 1 + end + local bit = mesecon.rule2bit(rulename, mesecon.conductor_get_rules(node)) + local binstate = mesecon.getbinstate(node.name, conductor.states) + return not mesecon.get_bit(binstate, bit) + end + end + return false +end + +function mesecon.is_conductor(nodename) + local conductor = mesecon.get_conductor(nodename) + if conductor then + return true + end + return false +end + +function mesecon.get_conductor_on(node_off, rulename) + local conductor = mesecon.get_conductor(node_off.name) + if conductor then + if conductor.onstate then + return conductor.onstate + end + if conductor.states then + local bit = mesecon.rule2bit(rulename, mesecon.conductor_get_rules(node_off)) + local binstate = mesecon.getbinstate(node_off.name, conductor.states) + binstate = mesecon.set_bit(binstate, bit, "1") + return conductor.states[tonumber(binstate,2)+1] + end + end + return offstate +end + +function mesecon.get_conductor_off(node_on, rulename) + local conductor = mesecon.get_conductor(node_on.name) + if conductor then + if conductor.offstate then + return conductor.offstate + end + if conductor.states then + local bit = mesecon.rule2bit(rulename, mesecon.conductor_get_rules(node_on)) + local binstate = mesecon.getbinstate(node_on.name, conductor.states) + binstate = mesecon.set_bit(binstate, bit, "0") + return conductor.states[tonumber(binstate,2)+1] + end + end + return onstate +end + +function mesecon.conductor_get_rules(node) + local conductor = mesecon.get_conductor(node.name) + if conductor then + local rules = conductor.rules + if type(rules) == 'function' then + return rules(node) + elseif rules then + return rules + end + end + return mesecon.rules.default +end + +-- some more general high-level stuff + +function mesecon.is_power_on(pos, rulename) + local node = minetest.get_node(pos) + if mesecon.is_conductor_on(node, rulename) or mesecon.is_receptor_on(node.name) then + return true + end + return false +end + +function mesecon.is_power_off(pos, rulename) + local node = minetest.get_node(pos) + if mesecon.is_conductor_off(node, rulename) or mesecon.is_receptor_off(node.name) then + return true + end + return false +end + +function mesecon.turnon(pos, link) + local frontiers = {{pos = pos, link = link}} + + local depth = 1 + while frontiers[depth] do + local f = frontiers[depth] + local node = minetest.get_node_or_nil(f.pos) + + -- area not loaded, postpone action + if not node then + mesecon.queue:add_action(f.pos, "turnon", {link}, nil, true) + elseif mesecon.is_conductor_off(node, f.link) then + local rules = mesecon.conductor_get_rules(node) + + minetest.swap_node(f.pos, {name = mesecon.get_conductor_on(node, f.link), + param2 = node.param2}) + + -- call turnon on neighbors: normal rules + for _, r in ipairs(mesecon.rule2meta(f.link, rules)) do + local np = mesecon.addPosRule(f.pos, r) + + -- area not loaded, postpone action + if not minetest.get_node_or_nil(np) then + mesecon.queue:add_action(np, "turnon", {rulename}, + nil, true) + else + local links = mesecon.rules_link_rule_all(f.pos, r) + for _, l in ipairs(links) do + table.insert(frontiers, {pos = np, link = l}) + end + end + end + elseif mesecon.is_effector(node.name) then + mesecon.changesignal(f.pos, node, f.link, mesecon.state.on, depth) + if mesecon.is_effector_off(node.name) then + mesecon.activate(f.pos, node, f.link, depth) + end + end + depth = depth + 1 + end +end + +mesecon.queue:add_function("turnon", function (pos, rulename, recdepth) + mesecon.turnon(pos, rulename, recdepth) +end) + +function mesecon.turnoff(pos, link) + local frontiers = {{pos = pos, link = link}} + + local depth = 1 + while frontiers[depth] do + local f = frontiers[depth] + local node = minetest.get_node_or_nil(f.pos) + + -- area not loaded, postpone action + if not node then + mesecon.queue:add_action(f.pos, "turnoff", {link}, nil, true) + elseif mesecon.is_conductor_on(node, f.link) then + local rules = mesecon.conductor_get_rules(node) + + minetest.swap_node(f.pos, {name = mesecon.get_conductor_off(node, f.link), + param2 = node.param2}) + + -- call turnoff on neighbors: normal rules + for _, r in ipairs(mesecon.rule2meta(f.link, rules)) do + local np = mesecon.addPosRule(f.pos, r) + + -- area not loaded, postpone action + if not minetest.get_node_or_nil(np) then + mesecon.queue:add_action(np, "turnoff", {rulename}, + nil, true) + else + local links = mesecon.rules_link_rule_all(f.pos, r) + for _, l in ipairs(links) do + table.insert(frontiers, {pos = np, link = l}) + end + end + end + elseif mesecon.is_effector(node.name) then + mesecon.changesignal(f.pos, node, f.link, mesecon.state.off, depth) + if mesecon.is_effector_on(node.name) and not mesecon.is_powered(f.pos) then + mesecon.deactivate(f.pos, node, f.link, depth) + end + end + depth = depth + 1 + end +end + +mesecon.queue:add_function("turnoff", function (pos, rulename, recdepth) + mesecon.turnoff(pos, rulename, recdepth) +end) + + +function mesecon.connected_to_receptor(pos, link) + local node = minetest.get_node(pos) + + -- Check if conductors around are connected + local rules = mesecon.get_any_inputrules(node) + if not rules then return false end + + for _, rule in ipairs(mesecon.rule2meta(link, rules)) do + local links = mesecon.rules_link_rule_all_inverted(pos, rule) + for _, l in ipairs(links) do + local np = mesecon.addPosRule(pos, l) + if mesecon.find_receptor_on(np, mesecon.invertRule(l)) then + return true + end + end + end + + return false +end + +function mesecon.find_receptor_on(pos, link) + local frontiers = {{pos = pos, link = link}} + local checked = {} + + -- List of positions that have been searched for onstate receptors + local depth = 1 + while frontiers[depth] do + local f = frontiers[depth] + local node = minetest.get_node_or_nil(f.pos) + + if not node then return false end + if mesecon.is_receptor_on(node.name) then return true end + if mesecon.is_conductor_on(node, f.link) then + local rules = mesecon.conductor_get_rules(node) + + -- call turnoff on neighbors: normal rules + for _, r in ipairs(mesecon.rule2meta(f.link, rules)) do + local np = mesecon.addPosRule(f.pos, r) + + local links = mesecon.rules_link_rule_all_inverted(f.pos, r) + for _, l in ipairs(links) do + local checkedstring = np.x..np.y..np.z..l.x..l.y..l.z + if not checked[checkedstring] then + table.insert(frontiers, {pos = np, link = l}) + checked[checkedstring] = true + end + end + end + + end + depth = depth + 1 + end +end + +function mesecon.rules_link(output, input, dug_outputrules) --output/input are positions (outputrules optional, used if node has been dug), second return value: the name of the affected input rule + local outputnode = minetest.get_node(output) + local inputnode = minetest.get_node(input) + local outputrules = dug_outputrules or mesecon.get_any_outputrules (outputnode) + local inputrules = mesecon.get_any_inputrules (inputnode) + if not outputrules or not inputrules then + return + end + + for _, outputrule in ipairs(mesecon.flattenrules(outputrules)) do + -- Check if output sends to input + if mesecon.cmpPos(mesecon.addPosRule(output, outputrule), input) then + for _, inputrule in ipairs(mesecon.flattenrules(inputrules)) do + -- Check if input accepts from output + if mesecon.cmpPos(mesecon.addPosRule(input, inputrule), output) then + return true, inputrule + end + end + end + end + return false +end + +function mesecon.rules_link_rule_all(output, rule) + local input = mesecon.addPosRule(output, rule) + local inputnode = minetest.get_node(input) + local inputrules = mesecon.get_any_inputrules (inputnode) + if not inputrules then + return {} + end + local rules = {} + + for _, inputrule in ipairs(mesecon.flattenrules(inputrules)) do + -- Check if input accepts from output + if mesecon.cmpPos(mesecon.addPosRule(input, inputrule), output) then + table.insert(rules, inputrule) + end + end + return rules +end + +function mesecon.rules_link_rule_all_inverted(input, rule) + --local irule = mesecon.invertRule(rule) + local output = mesecon.addPosRule(input, rule) + local outputnode = minetest.get_node(output) + local outputrules = mesecon.get_any_outputrules (outputnode) + if not outputrules then + return {} + end + local rules = {} + + for _, outputrule in ipairs(mesecon.flattenrules(outputrules)) do + if mesecon.cmpPos(mesecon.addPosRule(output, outputrule), input) then + table.insert(rules, mesecon.invertRule(outputrule)) + end + end + return rules +end + +function mesecon.rules_link_anydir(pos1, pos2) + return mesecon.rules_link(pos1, pos2) or mesecon.rules_link(pos2, pos1) +end + +function mesecon.is_powered(pos, rule) + local node = minetest.get_node(pos) + local rules = mesecon.get_any_inputrules(node) + if not rules then return false end + + -- List of nodes that send out power to pos + local sourcepos = {} + + if not rule then + for _, rule in ipairs(mesecon.flattenrules(rules)) do + local rulenames = mesecon.rules_link_rule_all_inverted(pos, rule) + for _, rname in ipairs(rulenames) do + local np = mesecon.addPosRule(pos, rname) + local nn = minetest.get_node(np) + if (mesecon.is_conductor_on (nn, mesecon.invertRule(rname)) + or mesecon.is_receptor_on (nn.name)) then + table.insert(sourcepos, np) + end + end + end + else + local rulenames = mesecon.rules_link_rule_all_inverted(pos, rule) + for _, rname in ipairs(rulenames) do + local np = mesecon.addPosRule(pos, rname) + local nn = minetest.get_node(np) + if (mesecon.is_conductor_on (nn, mesecon.invertRule(rname)) + or mesecon.is_receptor_on (nn.name)) then + table.insert(sourcepos, np) + end + end + end + + -- Return FALSE if not powered, return list of sources if is powered + if (#sourcepos == 0) then return false + else return sourcepos end +end + +--Rules rotation Functions: +function mesecon.rotate_rules_right(rules) + local nr = {} + for i, rule in ipairs(rules) do + table.insert(nr, { + x = -rule.z, + y = rule.y, + z = rule.x, + name = rule.name}) + end + return nr +end + +function mesecon.rotate_rules_left(rules) + local nr = {} + for i, rule in ipairs(rules) do + table.insert(nr, { + x = rule.z, + y = rule.y, + z = -rule.x, + name = rule.name}) + end + return nr +end + +function mesecon.rotate_rules_down(rules) + local nr = {} + for i, rule in ipairs(rules) do + table.insert(nr, { + x = -rule.y, + y = rule.x, + z = rule.z, + name = rule.name}) + end + return nr +end + +function mesecon.rotate_rules_up(rules) + local nr = {} + for i, rule in ipairs(rules) do + table.insert(nr, { + x = rule.y, + y = -rule.x, + z = rule.z, + name = rule.name}) + end + return nr +end -- cgit v1.2.3