diff --git a/.test_fixtures/mesecons.lua b/.test_fixtures/mesecons.lua index 2acd6f6..537fa13 100644 --- a/.test_fixtures/mesecons.lua +++ b/.test_fixtures/mesecons.lua @@ -127,6 +127,67 @@ do end end +-- Utility node: this effector with conductor is used to test outputs. +do + local _rules = { + {x = 1, y = 0, z = 0, name = "000001"}, + {x = -1, y = 0, z = 0, name = "000010"}, + {x = 0, y = 1, z = 0, name = "000100"}, + {x = 0, y = -1, z = 0, name = "001000"}, + {x = 0, y = 0, z = 1, name = "010000"}, + {x = 0, y = 0, z = -1, name = "100000"}, +} + -- This is a list of actions in the form {, }, + -- where is "on", "off", or "overheat". + mesecon._test_eff_conductor_events = {} + local function action_on(pos, node) + table.insert(mesecon._test_eff_conductor_events, {"on", pos}) + minetest.swap_node(pos, {name = "mesecons:test_effect_conductor_on", param2 = node.param2}) + end + local function action_off(pos, node) + table.insert(mesecon._test_eff_conductor_events, {"off", pos}) + minetest.swap_node(pos, {name = "mesecons:test_effect_conductor_off", param2 = node.param2}) + end + local function action_change(pos, _node, _rule_name, _new_state) + if mesecon.do_overheat(pos) then + table.insert(mesecon._test_eff_conductor_events, {"overheat", pos}) + minetest.remove_node(pos) + return + end + -- minetest.swap_node(pos, node) + end + local off_spec = { + effector = { + rules = _rules, + action_on = action_on, + action_off = action_off, + action_change = action_change, + }, + conductor = { + state = mesecon.state.off, + rules = _rules, + onstate = "mesecons:test_effect_conductor_on", + } + } + local on_spec = { + effector = { + rules = _rules, + action_on = action_on, + action_off = action_off, + action_change = action_change, + }, + conductor = { + state = mesecon.state.on, + rules = _rules, + offstate = "mesecons:test_effect_conductor_off", + } + } + mesecon.register_node("mesecons:test_effect_conductor", { + description = "Test Effector With Conductor", + }, {mesecons = off_spec}, {mesecons = on_spec}) +end + + mesecon._test_autoconnects = {} mesecon.register_autoconnect_hook("test", function(pos, node) table.insert(mesecon._test_autoconnects, {pos, node}) diff --git a/mesecons/internal.lua b/mesecons/internal.lua index 49690d9..1bc68e4 100644 --- a/mesecons/internal.lua +++ b/mesecons/internal.lua @@ -75,6 +75,7 @@ function mesecon.get_any_outputrules(node) end end +-- TODO: howto get the rules when conductor has rules and effector has rules function mesecon.get_any_inputrules(node) if not node then return nil end @@ -182,6 +183,12 @@ mesecon.queue:add_function("activate", function (pos, rulename) if effector and effector.action_on then effector.action_on(pos, node, rulename) + elseif mesecon.is_conductor(node.name) then + local node_name = mesecon.get_conductor_off(node, rulename) + effector = mesecon.get_effector(node_name) + if effector and effector.action_on then + effector.action_on(pos, node, rulename) + end end end) @@ -205,6 +212,12 @@ mesecon.queue:add_function("deactivate", function (pos, rulename) if effector and effector.action_off then effector.action_off(pos, node, rulename) + elseif mesecon.is_conductor(node.name) then + local node_name = mesecon.get_conductor_on(node, rulename) + effector = mesecon.get_effector(node_name) + if effector and effector.action_off then + effector.action_off(pos, node, rulename) + end end end) @@ -424,34 +437,38 @@ function mesecon.turnon(pos, link) if not node then -- Area does not exist; do nothing pos_can_be_skipped[minetest.hash_node_position(f.pos)] = true - elseif mesecon.is_conductor(node.name) then - local rules = mesecon.conductor_get_rules(node) - - if mesecon.is_conductor_off(node, f.link) then - -- Call turnon on neighbors - for _, r in ipairs(mesecon.rule2meta(f.link, rules)) do - local np = vector.add(f.pos, r) - if not pos_can_be_skipped[minetest.hash_node_position(np)] then - for _, l in ipairs(mesecon.rules_link_rule_all(f.pos, r)) do - frontiers:add({pos = np, link = l}) + else + local processed = false + local isEffector = mesecon.is_effector(node.name) + local isOff = true + if mesecon.is_conductor(node.name) then + local rules = mesecon.conductor_get_rules(node) + isOff = mesecon.is_conductor_off(node, f.link) + if isOff then + -- Call turnon on neighbors + for _, r in ipairs(mesecon.rule2meta(f.link, rules)) do + local np = vector.add(f.pos, r) + if not pos_can_be_skipped[minetest.hash_node_position(np)] then + for _, l in ipairs(mesecon.rules_link_rule_all(f.pos, r)) do + frontiers:add({pos = np, link = l}) + end end end + + mesecon.swap_node_force(f.pos, mesecon.get_conductor_on(node, f.link), light_update_conductors[node.name] ~= nil) end - mesecon.swap_node_force(f.pos, mesecon.get_conductor_on(node, f.link), light_update_conductors[node.name] ~= nil) + -- Only conductors with flat rules can be reliably skipped later + processed = rules[1] and not rules[1].x end - - -- Only conductors with flat rules can be reliably skipped later - if not rules[1] or rules[1].x then - pos_can_be_skipped[minetest.hash_node_position(f.pos)] = true + if isEffector and isOff then + processed = true + 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 - 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 - else - pos_can_be_skipped[minetest.hash_node_position(f.pos)] = true + if not processed then pos_can_be_skipped[minetest.hash_node_position(f.pos)] = true end end depth = depth + 1 end @@ -488,46 +505,51 @@ function mesecon.turnoff(pos, link) if not node then -- Area does not exist; do nothing pos_can_be_skipped[minetest.hash_node_position(f.pos)] = true - elseif mesecon.is_conductor(node.name) then - local rules = mesecon.conductor_get_rules(node) + else + local processed = false + local isEffector = mesecon.is_effector(node.name) + local isOn = true + if mesecon.is_conductor(node.name) then + local rules = mesecon.conductor_get_rules(node) + isOn = mesecon.is_conductor_on(node, f.link) + if isOn then + for _, r in ipairs(mesecon.rule2meta(f.link, rules)) do + local np = vector.add(f.pos, r) - if mesecon.is_conductor_on(node, f.link) then - for _, r in ipairs(mesecon.rule2meta(f.link, rules)) do - local np = vector.add(f.pos, r) + if not pos_can_be_skipped[minetest.hash_node_position(np)] then + -- Check if an onstate receptor is connected. If that is the case, + -- abort this turnoff process by returning false. `receptor_off` will + -- discard all the changes that we made in the voxelmanip: + if mesecon.rules_link_rule_all_inverted(f.pos, r)[1] then + if mesecon.is_receptor_on(mesecon.get_node_force(np).name) then + return false + end + end - if not pos_can_be_skipped[minetest.hash_node_position(np)] then - -- Check if an onstate receptor is connected. If that is the case, - -- abort this turnoff process by returning false. `receptor_off` will - -- discard all the changes that we made in the voxelmanip: - if mesecon.rules_link_rule_all_inverted(f.pos, r)[1] then - if mesecon.is_receptor_on(mesecon.get_node_force(np).name) then - return false + -- Call turnoff on neighbors + for _, l in ipairs(mesecon.rules_link_rule_all(f.pos, r)) do + frontiers:add({pos = np, link = l}) end end - - -- Call turnoff on neighbors - for _, l in ipairs(mesecon.rules_link_rule_all(f.pos, r)) do - frontiers:add({pos = np, link = l}) - end end + + mesecon.swap_node_force(f.pos, mesecon.get_conductor_off(node, f.link), light_update_conductors[node.name] ~= nil) end - mesecon.swap_node_force(f.pos, mesecon.get_conductor_off(node, f.link), light_update_conductors[node.name] ~= nil) - end + -- Only conductors with flat rules can be reliably skipped later + processed = rules[1] and not rules[1].x + end - -- Only conductors with flat rules can be reliably skipped later - if not rules[1] or rules[1].x then - pos_can_be_skipped[minetest.hash_node_position(f.pos)] = true + if isEffector and isOn then + processed = true + table.insert(signals, { + pos = f.pos, + node = node, + link = f.link, + depth = depth + }) end - elseif mesecon.is_effector(node.name) then - table.insert(signals, { - pos = f.pos, - node = node, - link = f.link, - depth = depth - }) - else - pos_can_be_skipped[minetest.hash_node_position(f.pos)] = true + if not processed then pos_can_be_skipped[minetest.hash_node_position(f.pos)] = true end end depth = depth + 1 end diff --git a/mesecons/spec/effector_conductor_spec.lua b/mesecons/spec/effector_conductor_spec.lua new file mode 100644 index 0000000..feaafdd --- /dev/null +++ b/mesecons/spec/effector_conductor_spec.lua @@ -0,0 +1,42 @@ +require("mineunit") + +fixture("mesecons") + + +describe("action effector with conductor", function() + local layout = { + {{x = 1, y = 0, z = 0}, "mesecons:test_receptor_off"}, + {{x = 0, y = 0, z = 0}, "mesecons:test_conductor_off"}, + {{x = 0, y = 1, z = 0}, "mesecons:test_effect_conductor_off"}, + {{x = 0, y = 2, z = 0}, "mesecons:test_effect_conductor_off"}, + } + + before_each(function() + world.layout(layout) + end) + + after_each(function() + mesecon._test_reset() + world.clear() + end) + + it("executes in order", function() + world.set_node(layout[1][1], "mesecons:test_receptor_on") + mesecon.receptor_on(layout[1][1], mesecon.rules.alldirs) + mineunit:execute_globalstep() -- Execute receptor_on action + mineunit:execute_globalstep() -- Execute activate/change actions + assert.equal(2, #mesecon._test_eff_conductor_events) + assert.same({"on", layout[3][1]}, mesecon._test_eff_conductor_events[1]) + assert.same({"on", layout[4][1]}, mesecon._test_eff_conductor_events[2]) + world.set_node(layout[1][1], "mesecons:test_receptor_off") + mesecon.receptor_off(layout[1][1], mesecon.rules.alldirs) + mineunit:execute_globalstep() -- Execute receptor_off action + mineunit:execute_globalstep() -- Execute deactivate/change actions + assert.equal(4, #mesecon._test_eff_conductor_events) + assert.same({"off", layout[3][1]}, mesecon._test_eff_conductor_events[3]) + assert.same({"off", layout[4][1]}, mesecon._test_eff_conductor_events[4]) + + end) + + +end)