diff --git a/mods/ITEMS/REDSTONE/mesecons/actionqueue.lua b/mods/ITEMS/REDSTONE/mesecons/actionqueue.lua new file mode 100644 index 0000000000..f3479ce59e --- /dev/null +++ b/mods/ITEMS/REDSTONE/mesecons/actionqueue.lua @@ -0,0 +1,105 @@ +mesecon.queue.actions={} -- contains all ActionQueue actions + +function mesecon.queue:add_function(name, func) + mesecon.queue.funcs[name] = func +end + +-- If add_action with twice the same overwritecheck and same position are called, the first one is overwritten +-- use overwritecheck nil to never overwrite, but just add the event to the queue +-- priority specifies the order actions are executed within one globalstep, highest first +-- should be between 0 and 1 +function mesecon.queue:add_action(pos, func, params, time, overwritecheck, priority) + -- Create Action Table: + time = time or 0 -- time <= 0 --> execute, time > 0 --> wait time until execution + priority = priority or 1 + local action = { pos=mesecon.tablecopy(pos), + func=func, + params=mesecon.tablecopy(params or {}), + time=time, + owcheck=(overwritecheck and mesecon.tablecopy(overwritecheck)) or nil, + priority=priority} + + local toremove = nil + -- Otherwise, add the action to the queue + if overwritecheck then -- check if old action has to be overwritten / removed: + for i, ac in ipairs(mesecon.queue.actions) do + if(vector.equals(pos, ac.pos) + and mesecon.cmpAny(overwritecheck, ac.owcheck)) then + toremove = i + break + end + end + end + + if (toremove ~= nil) then + table.remove(mesecon.queue.actions, toremove) + end + + table.insert(mesecon.queue.actions, action) +end + +-- execute the stored functions on a globalstep +-- if however, the pos of a function is not loaded (get_node_or_nil == nil), do NOT execute the function +-- this makes sure that resuming mesecons circuits when restarting minetest works fine +-- However, even that does not work in some cases, that's why we delay the time the globalsteps +-- start to be execute by 5 seconds +local get_highest_priority = function (actions) + local highestp = -1 + local highesti + for i, ac in ipairs(actions) do + if ac.priority > highestp then + highestp = ac.priority + highesti = i + end + end + + return highesti +end + +local m_time = 0 +local resumetime = mesecon.setting("resumetime", 4) +minetest.register_globalstep(function (dtime) + m_time = m_time + dtime + -- don't even try if server has not been running for XY seconds; resumetime = time to wait + -- after starting the server before processing the ActionQueue, don't set this too low + if (m_time < resumetime) then return end + local actions = mesecon.tablecopy(mesecon.queue.actions) + local actions_now={} + + mesecon.queue.actions = {} + + -- sort actions into two categories: + -- those toexecute now (actions_now) and those to execute later (mesecon.queue.actions) + for i, ac in ipairs(actions) do + if ac.time > 0 then + ac.time = ac.time - dtime -- executed later + table.insert(mesecon.queue.actions, ac) + else + table.insert(actions_now, ac) + end + end + + while(#actions_now > 0) do -- execute highest priorities first, until all are executed + local hp = get_highest_priority(actions_now) + mesecon.queue:execute(actions_now[hp]) + table.remove(actions_now, hp) + end +end) + +function mesecon.queue:execute(action) + -- ignore if action queue function name doesn't exist, + -- (e.g. in case the action queue savegame was written by an old mesecons version) + if mesecon.queue.funcs[action.func] then + mesecon.queue.funcs[action.func](action.pos, unpack(action.params)) + end +end + + +-- Store and read the ActionQueue to / from a file +-- so that upcoming actions are remembered when the game +-- is restarted +mesecon.queue.actions = mesecon.file2table("mesecon_actionqueue") + +minetest.register_on_shutdown(function() + mesecon.table2file("mesecon_actionqueue", mesecon.queue.actions) +end) diff --git a/mods/ITEMS/REDSTONE/mesecons/init.lua b/mods/ITEMS/REDSTONE/mesecons/init.lua index d667759857..83e611b555 100644 --- a/mods/ITEMS/REDSTONE/mesecons/init.lua +++ b/mods/ITEMS/REDSTONE/mesecons/init.lua @@ -3,7 +3,7 @@ -- | \/ | |___ ____ |___ | | | | \ | |____ -- | | | | | | | | | \ | | -- | | |___ ____| |___ |____ |____| | \| ____| --- by Jeija, Uberi (Temperest), sfan5, VanessaE +-- by Jeija, Uberi (Temperest), sfan5, VanessaE, Hawk777 and contributors -- -- -- @@ -11,7 +11,7 @@ -- See the documentation on the forum for additional information, especially about crafting -- -- --- For developer documentation see the Developers' section on mesecons.TK +-- For basic development resources, see http://mesecons.net/developers.html -- -- -- @@ -30,7 +30,7 @@ -- action_change = function -- rules = rules/get_rules -- }, --- conductor = +-- conductor = -- { -- state = mesecon.state.on/off -- offstate = opposite state (for state = on only) @@ -39,28 +39,26 @@ -- } --} -local init = os.clock() -- PUBLIC VARIABLES mesecon={} -- contains all functions and all global variables -mesecon.actions_on={} -- Saves registered function callbacks for mesecon on | DEPRECATED -mesecon.actions_off={} -- Saves registered function callbacks for mesecon off | DEPRECATED -mesecon.actions_change={} -- Saves registered function callbacks for mesecon change | DEPRECATED -mesecon.receptors={} -- saves all information about receptors | DEPRECATED -mesecon.effectors={} -- saves all information about effectors | DEPRECATED -mesecon.conductors={} -- saves all information about conductors | DEPRECATED +mesecon.queue={} -- contains the ActionQueue +mesecon.queue.funcs={} -- contains all ActionQueue functions -- Settings dofile(minetest.get_modpath("mesecons").."/settings.lua") --- Presets (eg default rules) -dofile(minetest.get_modpath("mesecons").."/presets.lua"); - - -- Utilities like comparing positions, -- adding positions and rules, -- mostly things that make the source look cleaner dofile(minetest.get_modpath("mesecons").."/util.lua"); +-- Presets (eg default rules) +dofile(minetest.get_modpath("mesecons").."/presets.lua"); + +-- The ActionQueue +-- Saves all the actions that have to be execute in the future +dofile(minetest.get_modpath("mesecons").."/actionqueue.lua"); + -- Internal stuff -- This is the most important file -- it handles signal transmission and basically everything else @@ -68,47 +66,63 @@ dofile(minetest.get_modpath("mesecons").."/util.lua"); -- like calling action_on/off/change dofile(minetest.get_modpath("mesecons").."/internal.lua"); --- Deprecated stuff --- To be removed in future releases --- Currently there is nothing here -dofile(minetest.get_modpath("mesecons").."/legacy.lua"); - -- API -- these are the only functions you need to remember -function mesecon:receptor_on(pos, rules) +mesecon.queue:add_function("receptor_on", function (pos, rules) + mesecon.vm_begin() + rules = rules or mesecon.rules.default - for _, rule in ipairs(rules) do - local np = mesecon:addPosRule(pos, rule) - local link, rulename = mesecon:rules_link(pos, np, rules) - if link then - mesecon:turnon(np, rulename) + -- Call turnon on all linking positions + for _, rule in ipairs(mesecon.flattenrules(rules)) do + local np = vector.add(pos, rule) + local rulenames = mesecon.rules_link_rule_all(pos, rule) + for _, rulename in ipairs(rulenames) do + mesecon.turnon(np, rulename) end end + + mesecon.vm_commit() +end) + +function mesecon.receptor_on(pos, rules) + mesecon.queue:add_action(pos, "receptor_on", {rules}, nil, rules) end -function mesecon:receptor_off(pos, rules) +mesecon.queue:add_function("receptor_off", function (pos, rules) rules = rules or mesecon.rules.default - for _, rule in ipairs(rules) do - local np = mesecon:addPosRule(pos, rule) - local link, rulename = mesecon:rules_link(pos, np, rules) - if link then - if not mesecon:connected_to_receptor(np) then - mesecon:turnoff(np, rulename) + -- Call turnoff on all linking positions + for _, rule in ipairs(mesecon.flattenrules(rules)) do + local np = vector.add(pos, rule) + local rulenames = mesecon.rules_link_rule_all(pos, rule) + for _, rulename in ipairs(rulenames) do + mesecon.vm_begin() + mesecon.changesignal(np, minetest.get_node(np), rulename, mesecon.state.off, 2) + + -- Turnoff returns true if turnoff process was successful, no onstate receptor + -- was found along the way. Commit changes that were made in voxelmanip. If turnoff + -- returns true, an onstate receptor was found, abort voxelmanip transaction. + if (mesecon.turnoff(np, rulename)) then + mesecon.vm_commit() else - mesecon:changesignal(np, minetest.get_node(np), rulename, mesecon.state.off) + mesecon.vm_abort() end end end +end) + +function mesecon.receptor_off(pos, rules) + mesecon.queue:add_action(pos, "receptor_off", {rules}, nil, rules) end ---The actual wires -dofile(minetest.get_modpath("mesecons").."/wires.lua"); + +print("[OK] Mesecons") + +-- Deprecated stuff +-- To be removed in future releases +dofile(minetest.get_modpath("mesecons").."/legacy.lua"); --Services like turnoff receptor on dignode and so on dofile(minetest.get_modpath("mesecons").."/services.lua"); - -local time_to_load= os.clock() - init -print(string.format("[MOD] "..minetest.get_current_modname().." loaded in %.4f s", time_to_load)) diff --git a/mods/ITEMS/REDSTONE/mesecons/internal.lua b/mods/ITEMS/REDSTONE/mesecons/internal.lua index e0c6a6b675..e5ba91eb98 100644 --- a/mods/ITEMS/REDSTONE/mesecons/internal.lua +++ b/mods/ITEMS/REDSTONE/mesecons/internal.lua @@ -1,62 +1,53 @@ -- Internal.lua - The core of mesecons -- --- For more practical developer resources see mesecons.tk +-- 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 +-- 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) +-- 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) +-- 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) --> Activates the effector node at the specific pos (calls nodedef.mesecons.effector.action_on) --- mesecon:deactivate(pos, node) --> Deactivates the effector node at the specific pos (calls nodedef.mesecons.effector.action_off) --- mesecon:changesignal(pos, node, rulename, newstate) --> Changes the effector node at the specific pos (calls nodedef.mesecons.effector.action_change) - --- RULES --- mesecon:add_rules(name, rules) | deprecated? --> Saves rules table by name --- mesecon:get_rules(name, rules) | deprecated? --> Loads rules table with name +-- 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(nodename) --> Returns true if nodename is a conductor with state = mesecon.state.on --- mesecon:is_conductor_off(nodename) --> Returns true if nodename is a conductor with state = mesecon.state.off --- mesecon:get_conductor_on(offstate) --> Returns the onstate nodename of the conductor with the name offstate --- mesecon:get_conductor_off(onstate) --> Returns the offstate nodename of the conductor with the name onstate --- mesecon:conductor_get_rules(node) --> Returns the input+output rules of a conductor (mesecon.rules.default if none specified) +-- 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, rulename) --> Returns true whatever there is at pos. Calls itself for connected nodes (if pos is a conductor) --> recursive, the rulename is the name of the input rule that caused calling turnon --- mesecon:turnoff(pos, rulename) --> Turns off whatever there is at pos. Calls itself for connected nodes (if pos is a conductor) --> recursive, the rulename is the name of the input rule that caused calling turnoff --- mesecon:connected_to_receptor(pos) --> Returns true if pos is connected to a receptor directly or via conductors; calls itself if pos is a conductor --> recursive --- 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 +-- 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.is_powered(pos) --> Returns true if pos is powered by a receptor or a conductor --- RULES ROTATION helpsers --- mesecon:rotate_rules_right(rules) --- mesecon:rotate_rules_left(rules) --- mesecon:rotate_rules_up(rules) --- mesecon:rotate_rules_down(rules) +-- 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) +function mesecon.get_effector(nodename) if minetest.registered_nodes[nodename] and minetest.registered_nodes[nodename].mesecons and minetest.registered_nodes[nodename].mesecons.effector then @@ -64,7 +55,7 @@ function mesecon:get_effector(nodename) end end -function mesecon:get_receptor(nodename) +function mesecon.get_receptor(nodename) if minetest.registered_nodes[nodename] and minetest.registered_nodes[nodename].mesecons and minetest.registered_nodes[nodename].mesecons.receptor then @@ -72,7 +63,7 @@ function mesecon:get_receptor(nodename) end end -function mesecon:get_conductor(nodename) +function mesecon.get_conductor(nodename) if minetest.registered_nodes[nodename] and minetest.registered_nodes[nodename].mesecons and minetest.registered_nodes[nodename].mesecons.conductor then @@ -80,52 +71,59 @@ function mesecon:get_conductor(nodename) 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) +function mesecon.get_any_outputrules(node) + if not node then return nil end + + 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 - return false 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) +function mesecon.get_any_inputrules(node) + if not node then return nil end + + 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 - return false +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) +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) +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) +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) +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 @@ -140,32 +138,32 @@ end -- Effectors -- Nodes that can be powered by mesecons -function mesecon:is_effector_on(nodename) - local effector = mesecon:get_effector(nodename) +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) +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) +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) +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 @@ -177,83 +175,168 @@ function mesecon:effector_get_rules(node) return mesecon.rules.default end ---Signals +-- ####################### +-- # Signals (effectors) # +-- ####################### + +-- Activation: +mesecon.queue:add_function("activate", function (pos, rulename) + local node = mesecon.get_node_force(pos) + if not node then return end + + local effector = mesecon.get_effector(node.name) -function mesecon:activate(pos, node, rulename) - local effector = mesecon:get_effector(node.name) if effector and effector.action_on then - effector.action_on (pos, node, rulename) + 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 -function mesecon:deactivate(pos, node, rulename) - local effector = mesecon:get_effector(node.name) + +-- Deactivation +mesecon.queue:add_function("deactivate", function (pos, rulename) + local node = mesecon.get_node_force(pos) + if not node then return end + + local effector = mesecon.get_effector(node.name) + if effector and effector.action_off then - effector.action_off (pos, node, rulename) + 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 -function mesecon:changesignal(pos, node, rulename, newstate) - local effector = mesecon:get_effector(node.name) + +-- Change +mesecon.queue:add_function("change", function (pos, rulename, changetype) + local node = mesecon.get_node_force(pos) + if not node then return end + + local effector = mesecon.get_effector(node.name) + if effector and effector.action_change then - effector.action_change (pos, node, rulename, newstate) + effector.action_change(pos, node, rulename, changetype) end -end +end) ---Rules +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 -function mesecon:add_rules(name, rules) - mesecon.rules[name] = rules -end - -function mesecon:get_rules(name) - return mesecon.rules[name] + -- 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(nodename) - local conductor = mesecon:get_conductor(nodename) - if conductor and conductor.state == mesecon.state.on then - return true +function mesecon.is_conductor_on(node, rulename) + if not node then return false end + + 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(nodename) - local conductor = mesecon:get_conductor(nodename) - if conductor and conductor.state == mesecon.state.off then - return true +function mesecon.is_conductor_off(node, rulename) + if not node then return false end + + 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) +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(offstate) - local conductor = mesecon:get_conductor(offstate) +function mesecon.get_conductor_on(node_off, rulename) + local conductor = mesecon.get_conductor(node_off.name) if conductor then - return conductor.onstate + 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 false + return offstate end -function mesecon:get_conductor_off(onstate) - local conductor = mesecon:get_conductor(onstate) +function mesecon.get_conductor_off(node_on, rulename) + local conductor = mesecon.get_conductor(node_on.name) if conductor then - return conductor.offstate + 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 false + return onstate end -function mesecon:conductor_get_rules(node) - local conductor = mesecon:get_conductor(node.name) +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 @@ -267,206 +350,250 @@ end -- some more general high-level stuff -function mesecon:is_power_on(pos) - local node = minetest.get_node(pos) - if mesecon:is_conductor_on(node.name) or mesecon:is_receptor_on(node.name) then +function mesecon.is_power_on(pos, rulename) + local node = mesecon.get_node_force(pos) + if node and (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) - local node = minetest.get_node(pos) - if mesecon:is_conductor_off(node.name) or mesecon:is_receptor_off(node.name) then +function mesecon.is_power_off(pos, rulename) + local node = mesecon.get_node_force(pos) + if node and (mesecon.is_conductor_off(node, rulename) or mesecon.is_receptor_off(node.name)) then return true end return false end -function mesecon:turnon(pos, rulename) - local node = minetest.get_node(pos) +-- Turn off an equipotential section starting at `pos`, which outputs in the direction of `link`. +-- Breadth-first search. Map is abstracted away in a voxelmanip. +-- Follow all all conductor paths replacing conductors that were already +-- looked at, activating / changing all effectors along the way. +function mesecon.turnon(pos, link) + local frontiers = {{pos = pos, link = link}} - if mesecon:is_conductor_off(node.name) then - local rules = mesecon:conductor_get_rules(node) - minetest.add_node(pos, {name = mesecon:get_conductor_on(node.name), param2 = node.param2}) + local depth = 1 + while frontiers[1] do + local f = table.remove(frontiers, 1) + local node = mesecon.get_node_force(f.pos) - for _, rule in ipairs(rules) do - local np = mesecon:addPosRule(pos, rule) - local link, rulename = mesecon:rules_link(pos, np) + if not node then + -- Area does not exist; do nothing + elseif mesecon.is_conductor_off(node, f.link) then + local rules = mesecon.conductor_get_rules(node) - if link then - mesecon:turnon(np, rulename) + -- Call turnon on neighbors + for _, r in ipairs(mesecon.rule2meta(f.link, rules)) do + local np = vector.add(f.pos, r) + for _, l in ipairs(mesecon.rules_link_rule_all(f.pos, r)) do + table.insert(frontiers, {pos = np, link = l}) + end + end + + mesecon.swap_node_force(f.pos, mesecon.get_conductor_on(node, f.link)) + 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 - elseif mesecon:is_effector(node.name) then - mesecon:changesignal(pos, node, rulename, mesecon.state.on) - if mesecon:is_effector_off(node.name) then - mesecon:activate(pos, node, rulename) - end + depth = depth + 1 end end -function mesecon:turnoff(pos, rulename) - local node = minetest.get_node(pos) +-- Turn on an equipotential section starting at `pos`, which outputs in the direction of `link`. +-- Breadth-first search. Map is abstracted away in a voxelmanip. +-- Follow all all conductor paths replacing conductors that were already +-- looked at, deactivating / changing all effectors along the way. +-- In case an onstate receptor is discovered, abort the process by returning false, which will +-- cause `receptor_off` to discard all changes made in the voxelmanip. +-- Contrary to turnon, turnoff has to cache all change and deactivate signals so that they will only +-- be called in the very end when we can be sure that no conductor was found along the path. +-- +-- Signal table entry structure: +-- { +-- pos = position of effector, +-- node = node descriptor (name, param1 and param2), +-- link = link the effector is connected to, +-- depth = indicates order in which signals wire fired, higher is later +-- } +function mesecon.turnoff(pos, link) + local frontiers = {{pos = pos, link = link}} + local signals = {} - if mesecon:is_conductor_on(node.name) then - local rules = mesecon:conductor_get_rules(node) - minetest.add_node(pos, {name = mesecon:get_conductor_off(node.name), param2 = node.param2}) + local depth = 1 + while frontiers[1] do + local f = table.remove(frontiers, 1) + local node = mesecon.get_node_force(f.pos) - for _, rule in ipairs(rules) do - local np = mesecon:addPosRule(pos, rule) - local link, rulename = mesecon:rules_link(pos, np) + if not node then + -- Area does not exist; do nothing + elseif mesecon.is_conductor_on(node, f.link) then + local rules = mesecon.conductor_get_rules(node) + for _, r in ipairs(mesecon.rule2meta(f.link, rules)) do + local np = vector.add(f.pos, r) - if link then - mesecon:turnoff(np, rulename) + -- 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: + for _, l in ipairs(mesecon.rules_link_rule_all_inverted(f.pos, r)) do + if mesecon.is_receptor_on(mesecon.get_node_force(np).name) then + return false + end + end + + -- Call turnoff on neighbors + for _, l in ipairs(mesecon.rules_link_rule_all(f.pos, r)) do + table.insert(frontiers, {pos = np, link = l}) + end end + + mesecon.swap_node_force(f.pos, mesecon.get_conductor_off(node, f.link)) + elseif mesecon.is_effector(node.name) then + table.insert(signals, { + pos = f.pos, + node = node, + link = f.link, + depth = depth + }) end - elseif mesecon:is_effector(node.name) then - mesecon:changesignal(pos, node, rulename, mesecon.state.off) - if mesecon:is_effector_on(node.name) - and not mesecon:is_powered(pos) then - mesecon:deactivate(pos, node, rulename) + depth = depth + 1 + end + + for _, sig in ipairs(signals) do + mesecon.changesignal(sig.pos, sig.node, sig.link, mesecon.state.off, sig.depth) + if mesecon.is_effector_on(sig.node.name) and not mesecon.is_powered(sig.pos) then + mesecon.deactivate(sig.pos, sig.node, sig.link, sig.depth) end end + + return true end +-- Get all linking inputrules of inputnode (effector or conductor) that is connected to +-- outputnode (receptor or conductor) at position `output` and has an output in direction `rule` +function mesecon.rules_link_rule_all(output, rule) + local input = vector.add(output, rule) + local inputnode = mesecon.get_node_force(input) + local inputrules = mesecon.get_any_inputrules(inputnode) + if not inputrules then + return {} + end + local rules = {} -function mesecon:connected_to_receptor(pos) - local node = minetest.get_node(pos) + for _, inputrule in ipairs(mesecon.flattenrules(inputrules)) do + -- Check if input accepts from output + if vector.equals(vector.add(input, inputrule), output) then + table.insert(rules, inputrule) + end + end - -- Check if conductors around are connected - local rules = mesecon:get_any_inputrules(node) + return rules +end + +-- Get all linking outputnodes of outputnode (receptor or conductor) that is connected to +-- inputnode (effector or conductor) at position `input` and has an input in direction `rule` +function mesecon.rules_link_rule_all_inverted(input, rule) + local output = vector.add(input, rule) + local outputnode = mesecon.get_node_force(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 vector.equals(vector.add(output, outputrule), input) then + table.insert(rules, mesecon.invertRule(outputrule)) + end + end + return rules +end + +function mesecon.is_powered(pos, rule) + local node = mesecon.get_node_force(pos) + local rules = mesecon.get_any_inputrules(node) if not rules then return false end - for _, rule in ipairs(rules) do - local np = mesecon:addPosRule(pos, rule) - if mesecon:rules_link(np, pos) then - if mesecon:find_receptor_on(np, {}) then - return true - end - end - end + -- List of nodes that send out power to pos + local sourcepos = {} - return false -end + 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 = vector.add(pos, rname) + local nn = mesecon.get_node_force(np) -function mesecon:find_receptor_on(pos, checked) - -- find out if node has already been checked (to prevent from endless loop) - for _, cp in ipairs(checked) do - if mesecon:cmpPos(cp, pos) then - return false, checked - end - end - - -- add current position to checked - table.insert(checked, {x=pos.x, y=pos.y, z=pos.z}) - local node = minetest.get_node(pos) - - if mesecon:is_receptor_on(node.name) then - return true - end - - if mesecon:is_conductor(node.name) then - local rules = mesecon:conductor_get_rules(node) - for _, rule in ipairs(rules) do - local np = mesecon:addPosRule(pos, rule) - if mesecon:rules_link(np, pos) then - if mesecon:find_receptor_on(np, checked) then - return true + if (mesecon.is_conductor_on(nn, mesecon.invertRule(rname)) + or mesecon.is_receptor_on(nn.name)) then + table.insert(sourcepos, np) end end end - end - - return false -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(outputrules) do - -- Check if output sends to input - if mesecon:cmpPos(mesecon:addPosRule(output, outputrule), input) then - for _, inputrule in ipairs(inputrules) do - -- Check if input accepts from output - if mesecon:cmpPos(mesecon:addPosRule(input, inputrule), output) then - return true, inputrule.name - end + else + local rulenames = mesecon.rules_link_rule_all_inverted(pos, rule) + for _, rname in ipairs(rulenames) do + local np = vector.add(pos, rname) + local nn = mesecon.get_node_force(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 -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) - local node = minetest.get_node(pos) - local rules = mesecon:get_any_inputrules(node) - if not rules then return false end - - for _, rule in ipairs(rules) do - local np = mesecon:addPosRule(pos, rule) - local nn = minetest.get_node(np) - - if (mesecon:is_conductor_on (nn.name) or mesecon:is_receptor_on (nn.name)) - and mesecon:rules_link(np, pos) then - return true - end - end - - return false + -- 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) +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}) + x = -rule.z, + y = rule.y, + z = rule.x, + name = rule.name}) end return nr end -function mesecon:rotate_rules_left(rules) +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}) + x = rule.z, + y = rule.y, + z = -rule.x, + name = rule.name}) end return nr end -function mesecon:rotate_rules_down(rules) +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}) + x = -rule.y, + y = rule.x, + z = rule.z, + name = rule.name}) end return nr end -function mesecon:rotate_rules_up(rules) +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}) + x = rule.y, + y = -rule.x, + z = rule.z, + name = rule.name}) end return nr end diff --git a/mods/ITEMS/REDSTONE/mesecons/legacy.lua b/mods/ITEMS/REDSTONE/mesecons/legacy.lua index e69de29bb2..ad7093a0bb 100644 --- a/mods/ITEMS/REDSTONE/mesecons/legacy.lua +++ b/mods/ITEMS/REDSTONE/mesecons/legacy.lua @@ -0,0 +1,14 @@ +-- Un-forceload any forceloaded mapblocks from older versions of Mesecons which +-- used forceloading instead of VoxelManipulators. +local BLOCKSIZE = 16 + +-- convert block hash --> node position +local function unhash_blockpos(hash) + return vector.multiply(minetest.get_position_from_hash(hash), BLOCKSIZE) +end + +local old_forceloaded_blocks = mesecon.file2table("mesecon_forceloaded") +for hash, _ in pairs(old_forceloaded_blocks) do + minetest.forceload_free_block(unhash_blockpos(hash)) +end +os.remove(minetest.get_worldpath()..DIR_DELIM.."mesecon_forceloaded") diff --git a/mods/ITEMS/REDSTONE/mesecons/presets.lua b/mods/ITEMS/REDSTONE/mesecons/presets.lua index 6c8d3eac0c..8c3ed67bdd 100644 --- a/mods/ITEMS/REDSTONE/mesecons/presets.lua +++ b/mods/ITEMS/REDSTONE/mesecons/presets.lua @@ -15,11 +15,13 @@ mesecon.rules.default = {x=0, y=1, z=-1}, {x=0, y=-1, z=-1}} +mesecon.rules.pplate = mesecon.mergetable(mesecon.rules.default, {{x=0, y=-2, z=0}}) + mesecon.rules.buttonlike = {{x = 1, y = 0, z = 0}, {x = 1, y = 1, z = 0}, {x = 1, y =-1, z = 0}, - {x = 1, y =-1, z = 1}, + {x = 1, y =-1, z = 1}, {x = 1, y =-1, z =-1}, {x = 2, y = 0, z = 0}} @@ -28,15 +30,30 @@ mesecon.rules.flat = {x =-1, y = 0, z = 0}, {x = 0, y = 0, z = 1}, {x = 0, y = 0, z =-1}} - + +mesecon.rules.alldirs = +{{x= 1, y= 0, z= 0}, + {x=-1, y= 0, z= 0}, + {x= 0, y= 1, z= 0}, + {x= 0, y=-1, z= 0}, + {x= 0, y= 0, z= 1}, + {x= 0, y= 0, z=-1}} + mesecon.rules.buttonlike_get = function(node) local rules = mesecon.rules.buttonlike - if node.param2 == 2 then - rules=mesecon:rotate_rules_left(rules) - elseif node.param2 == 3 then - rules=mesecon:rotate_rules_right(mesecon:rotate_rules_right(rules)) - elseif node.param2 == 0 then - rules=mesecon:rotate_rules_right(rules) + local dir = minetest.facedir_to_dir(node.param2) + if dir.x == 1 then + -- No action needed + elseif dir.z == -1 then + rules=mesecon.rotate_rules_left(rules) + elseif dir.x == -1 then + rules=mesecon.rotate_rules_right(mesecon.rotate_rules_right(rules)) + elseif dir.z == 1 then + rules=mesecon.rotate_rules_right(rules) + elseif dir.y == -1 then + rules=mesecon.rotate_rules_up(rules) + elseif dir.y == 1 then + rules=mesecon.rotate_rules_down(rules) end return rules end diff --git a/mods/ITEMS/REDSTONE/mesecons/services.lua b/mods/ITEMS/REDSTONE/mesecons/services.lua index a3aab430a2..1e12de08e6 100644 --- a/mods/ITEMS/REDSTONE/mesecons/services.lua +++ b/mods/ITEMS/REDSTONE/mesecons/services.lua @@ -1,28 +1,128 @@ -mesecon.on_placenode = function (pos, node) - if mesecon:is_receptor_on(node.name) then - mesecon:receptor_on(pos, mesecon:receptor_get_rules(node)) - elseif mesecon:is_powered(pos) then - if mesecon:is_conductor(node.name) then - mesecon:turnon (pos) - mesecon:receptor_on (pos, mesecon:conductor_get_rules(node)) - else - mesecon:changesignal(pos, node) - mesecon:activate(pos, node) +-- Dig and place services + +mesecon.on_placenode = function(pos, node) + mesecon.execute_autoconnect_hooks_now(pos, node) + + -- Receptors: Send on signal when active + if mesecon.is_receptor_on(node.name) then + mesecon.receptor_on(pos, mesecon.receptor_get_rules(node)) + end + + -- Conductors: Send turnon signal when powered or replace by respective offstate conductor + -- if placed conductor is an onstate one + if mesecon.is_conductor(node.name) then + local sources = mesecon.is_powered(pos) + if sources then + -- also call receptor_on if itself is powered already, so that neighboring + -- conductors will be activated (when pushing an on-conductor with a piston) + for _, s in ipairs(sources) do + local rule = vector.subtract(pos, s) + mesecon.turnon(pos, rule) + end + --mesecon.receptor_on (pos, mesecon.conductor_get_rules(node)) + elseif mesecon.is_conductor_on(node) then + minetest.swap_node(pos, {name = mesecon.get_conductor_off(node)}) + end + end + + -- Effectors: Send changesignal and activate or deactivate + if mesecon.is_effector(node.name) then + local powered_rules = {} + local unpowered_rules = {} + + -- for each input rule, check if powered + for _, r in ipairs(mesecon.effector_get_rules(node)) do + local powered = mesecon.is_powered(pos, r) + if powered then table.insert(powered_rules, r) + else table.insert(unpowered_rules, r) end + + local state = powered and mesecon.state.on or mesecon.state.off + mesecon.changesignal(pos, node, r, state, 1) + end + + if (#powered_rules > 0) then + for _, r in ipairs(powered_rules) do + mesecon.activate(pos, node, r, 1) + end + else + for _, r in ipairs(unpowered_rules) do + mesecon.deactivate(pos, node, r, 1) + end end - elseif mesecon:is_conductor_on(node.name) then - mesecon:swap_node(pos, mesecon:get_conductor_off(node.name)) - elseif mesecon:is_effector_on (node.name) then - mesecon:deactivate(pos, node) end end -mesecon.on_dignode = function (pos, node) - if mesecon:is_conductor_on(node.name) then - mesecon:receptor_off(pos, mesecon:conductor_get_rules(node)) - elseif mesecon:is_receptor_on(node.name) then - mesecon:receptor_off(pos, mesecon:receptor_get_rules(node)) +mesecon.on_dignode = function(pos, node) + if mesecon.is_conductor_on(node) then + mesecon.receptor_off(pos, mesecon.conductor_get_rules(node)) + elseif mesecon.is_receptor_on(node.name) then + mesecon.receptor_off(pos, mesecon.receptor_get_rules(node)) end + + mesecon.execute_autoconnect_hooks_queue(pos, node) end minetest.register_on_placenode(mesecon.on_placenode) minetest.register_on_dignode(mesecon.on_dignode) + +-- Overheating service for fast circuits +local OVERHEAT_MAX = mesecon.setting("overheat_max", 20) +local COOLDOWN_TIME = mesecon.setting("cooldown_time", 2.0) +local COOLDOWN_STEP = mesecon.setting("cooldown_granularity", 0.5) +local COOLDOWN_MULTIPLIER = OVERHEAT_MAX / COOLDOWN_TIME +local cooldown_timer = 0.0 +local object_heat = {} + +-- returns true if heat is too high +function mesecon.do_overheat(pos) + local id = minetest.hash_node_position(pos) + local heat = (object_heat[id] or 0) + 1 + object_heat[id] = heat + if heat >= OVERHEAT_MAX then + minetest.log("action", "Node overheats at " .. minetest.pos_to_string(pos)) + object_heat[id] = nil + return true + end + return false +end + +function mesecon.do_cooldown(pos) + local id = minetest.hash_node_position(pos) + object_heat[id] = nil +end + +function mesecon.get_heat(pos) + local id = minetest.hash_node_position(pos) + return object_heat[id] or 0 +end + +function mesecon.move_hot_nodes(moved_nodes) + local new_heat = {} + for _, n in ipairs(moved_nodes) do + local old_id = minetest.hash_node_position(n.oldpos) + local new_id = minetest.hash_node_position(n.pos) + new_heat[new_id] = object_heat[old_id] + object_heat[old_id] = nil + end + for id, heat in pairs(new_heat) do + object_heat[id] = heat + end +end + +local function global_cooldown(dtime) + cooldown_timer = cooldown_timer + dtime + if cooldown_timer < COOLDOWN_STEP then + return -- don't overload the CPU + end + local cooldown = COOLDOWN_MULTIPLIER * cooldown_timer + cooldown_timer = 0 + for id, heat in pairs(object_heat) do + heat = heat - cooldown + if heat <= 0 then + object_heat[id] = nil -- free some RAM + else + object_heat[id] = heat + end + end +end +minetest.register_globalstep(global_cooldown) diff --git a/mods/ITEMS/REDSTONE/mesecons/settings.lua b/mods/ITEMS/REDSTONE/mesecons/settings.lua index 64f4d7fc6d..1ebbfde400 100644 --- a/mods/ITEMS/REDSTONE/mesecons/settings.lua +++ b/mods/ITEMS/REDSTONE/mesecons/settings.lua @@ -1,4 +1,15 @@ --- SETTINGS -NEW_STYLE_WIRES = true -- true = new nodebox wires, false = old raillike wires -PRESSURE_PLATE_INTERVAL = 0.04 -PISTON_MAXIMUM_PUSH = 12 +-- SETTINGS +function mesecon.setting(setting, default) + if type(default) == "boolean" then + local read = minetest.setting_getbool("mesecon."..setting) + if read == nil then + return default + else + return read + end + elseif type(default) == "string" then + return minetest.setting_get("mesecon."..setting) or default + elseif type(default) == "number" then + return tonumber(minetest.setting_get("mesecon."..setting) or default) + end +end diff --git a/mods/ITEMS/REDSTONE/mesecons/util.lua b/mods/ITEMS/REDSTONE/mesecons/util.lua index fc381f653f..39f56968d0 100644 --- a/mods/ITEMS/REDSTONE/mesecons/util.lua +++ b/mods/ITEMS/REDSTONE/mesecons/util.lua @@ -1,24 +1,390 @@ -function mesecon:swap_node(pos, name) - local node = minetest.get_node(pos) - local data = minetest.get_meta(pos):to_table() - node.name = name - minetest.add_node(pos, node) - minetest.get_meta(pos):from_table(data) -end - -function mesecon:move_node(pos, newpos) +function mesecon.move_node(pos, newpos) local node = minetest.get_node(pos) local meta = minetest.get_meta(pos):to_table() minetest.remove_node(pos) - minetest.add_node(newpos, node) + minetest.set_node(newpos, node) minetest.get_meta(pos):from_table(meta) end +function mesecon.flattenrules(allrules) +--[[ + { + { + {xyz}, + {xyz}, + }, + { + {xyz}, + {xyz}, + }, + } +--]] + if allrules[1] and + allrules[1].x then + return allrules + end -function mesecon:addPosRule(p, r) - return {x = p.x + r.x, y = p.y + r.y, z = p.z + r.z} + local shallowrules = {} + for _, metarule in ipairs( allrules) do + for _, rule in ipairs(metarule ) do + table.insert(shallowrules, rule) + end + end + return shallowrules +--[[ + { + {xyz}, + {xyz}, + {xyz}, + {xyz}, + } +--]] end -function mesecon:cmpPos(p1, p2) - return (p1.x == p2.x and p1.y == p2.y and p1.z == p2.z) +function mesecon.rule2bit(findrule, allrules) + --get the bit of the metarule the rule is in, or bit 1 + if (allrules[1] and + allrules[1].x) or + not findrule then + return 1 + end + for m,metarule in ipairs( allrules) do + for _, rule in ipairs(metarule ) do + if vector.equals(findrule, rule) then + return m + end + end + end +end + +function mesecon.rule2metaindex(findrule, allrules) + --get the metarule the rule is in, or allrules + if allrules[1].x then + return nil + end + + if not(findrule) then + return mesecon.flattenrules(allrules) + end + + for m, metarule in ipairs( allrules) do + for _, rule in ipairs(metarule ) do + if vector.equals(findrule, rule) then + return m + end + end + end +end + +function mesecon.rule2meta(findrule, allrules) + if #allrules == 0 then return {} end + + local index = mesecon.rule2metaindex(findrule, allrules) + if index == nil then + if allrules[1].x then + return allrules + else + return {} + end + end + return allrules[index] +end + +function mesecon.dec2bin(n) + local x, y = math.floor(n / 2), n % 2 + if (n > 1) then + return mesecon.dec2bin(x)..y + else + return ""..y + end +end + +function mesecon.getstate(nodename, states) + for state, name in ipairs(states) do + if name == nodename then + return state + end + end + error(nodename.." doesn't mention itself in "..dump(states)) +end + +function mesecon.getbinstate(nodename, states) + return mesecon.dec2bin(mesecon.getstate(nodename, states)-1) +end + +function mesecon.get_bit(binary,bit) + bit = bit or 1 + local c = binary:len()-(bit-1) + return binary:sub(c,c) == "1" +end + +function mesecon.set_bit(binary,bit,value) + if value == "1" then + if not mesecon.get_bit(binary,bit) then + return mesecon.dec2bin(tonumber(binary,2)+math.pow(2,bit-1)) + end + elseif value == "0" then + if mesecon.get_bit(binary,bit) then + return mesecon.dec2bin(tonumber(binary,2)-math.pow(2,bit-1)) + end + end + return binary + +end + +function mesecon.invertRule(r) + return vector.multiply(r, -1) +end + +function mesecon.tablecopy(table) -- deep table copy + if type(table) ~= "table" then return table end -- no need to copy + local newtable = {} + + for idx, item in pairs(table) do + if type(item) == "table" then + newtable[idx] = mesecon.tablecopy(item) + else + newtable[idx] = item + end + end + + return newtable +end + +function mesecon.cmpAny(t1, t2) + if type(t1) ~= type(t2) then return false end + if type(t1) ~= "table" and type(t2) ~= "table" then return t1 == t2 end + + for i, e in pairs(t1) do + if not mesecon.cmpAny(e, t2[i]) then return false end + end + + return true +end + +-- does not overwrite values; number keys (ipairs) are appended, not overwritten +function mesecon.mergetable(source, dest) + local rval = mesecon.tablecopy(dest) + + for k, v in pairs(source) do + rval[k] = dest[k] or mesecon.tablecopy(v) + end + for i, v in ipairs(source) do + table.insert(rval, mesecon.tablecopy(v)) + end + + return rval +end + +function mesecon.register_node(name, spec_common, spec_off, spec_on) + spec_common.drop = spec_common.drop or name .. "_off" + spec_common.__mesecon_basename = name + spec_on.__mesecon_state = "on" + spec_off.__mesecon_state = "off" + + spec_on = mesecon.mergetable(spec_common, spec_on); + spec_off = mesecon.mergetable(spec_common, spec_off); + + minetest.register_node(name .. "_on", spec_on) + minetest.register_node(name .. "_off", spec_off) +end + +-- swap onstate and offstate nodes, returns new state +function mesecon.flipstate(pos, node) + local nodedef = minetest.registered_nodes[node.name] + local newstate + if (nodedef.__mesecon_state == "on") then newstate = "off" end + if (nodedef.__mesecon_state == "off") then newstate = "on" end + + minetest.swap_node(pos, {name = nodedef.__mesecon_basename .. "_" .. newstate, + param2 = node.param2}) + + return newstate +end + +-- File writing / reading utilities +local wpath = minetest.get_worldpath() +function mesecon.file2table(filename) + local f = io.open(wpath..DIR_DELIM..filename, "r") + if f == nil then return {} end + local t = f:read("*all") + f:close() + if t == "" or t == nil then return {} end + return minetest.deserialize(t) +end + +function mesecon.table2file(filename, table) + local f = io.open(wpath..DIR_DELIM..filename, "w") + f:write(minetest.serialize(table)) + f:close() +end + +-- Block position "hashing" (convert to integer) functions for voxelmanip cache +local BLOCKSIZE = 16 + +-- convert node position --> block hash +local function hash_blockpos(pos) + return minetest.hash_node_position({ + x = math.floor(pos.x/BLOCKSIZE), + y = math.floor(pos.y/BLOCKSIZE), + z = math.floor(pos.z/BLOCKSIZE) + }) +end + +-- Maps from a hashed mapblock position (as returned by hash_blockpos) to a +-- table. +-- +-- Contents of the table are: +-- “vm” → the VoxelManipulator +-- “va” → the VoxelArea +-- “data” → the data array +-- “param1” → the param1 array +-- “param2” → the param2 array +-- “dirty” → true if data has been modified +-- +-- Nil if no VM-based transaction is in progress. +local vm_cache = nil + +-- Starts a VoxelManipulator-based transaction. +-- +-- During a VM transaction, calls to vm_get_node and vm_swap_node operate on a +-- cached copy of the world loaded via VoxelManipulators. That cache can later +-- be committed to the real map by means of vm_commit or discarded by means of +-- vm_abort. +function mesecon.vm_begin() + vm_cache = {} +end + +-- Finishes a VoxelManipulator-based transaction, freeing the VMs and map data +-- and writing back any modified areas. +function mesecon.vm_commit() + for hash, tbl in pairs(vm_cache) do + if tbl.dirty then + local vm = tbl.vm + vm:set_data(tbl.data) + vm:write_to_map() + vm:update_map() + end + end + vm_cache = nil +end + +-- Finishes a VoxelManipulator-based transaction, freeing the VMs and throwing +-- away any modified areas. +function mesecon.vm_abort() + vm_cache = nil +end + +-- Gets the cache entry covering a position, populating it if necessary. +local function vm_get_or_create_entry(pos) + local hash = hash_blockpos(pos) + local tbl = vm_cache[hash] + if not tbl then + local vm = minetest.get_voxel_manip(pos, pos) + local min_pos, max_pos = vm:get_emerged_area() + local va = VoxelArea:new{MinEdge = min_pos, MaxEdge = max_pos} + tbl = {vm = vm, va = va, data = vm:get_data(), param1 = vm:get_light_data(), param2 = vm:get_param2_data(), dirty = false} + vm_cache[hash] = tbl + end + return tbl +end + +-- Gets the node at a given position during a VoxelManipulator-based +-- transaction. +function mesecon.vm_get_node(pos) + local tbl = vm_get_or_create_entry(pos) + local index = tbl.va:indexp(pos) + local node_value = tbl.data[index] + if node_value == core.CONTENT_IGNORE then + return nil + else + local node_param1 = tbl.param1[index] + local node_param2 = tbl.param2[index] + return {name = minetest.get_name_from_content_id(node_value), param1 = node_param1, param2 = node_param2} + end +end + +-- Sets a node’s name during a VoxelManipulator-based transaction. +-- +-- Existing param1, param2, and metadata are left alone. +function mesecon.vm_swap_node(pos, name) + local tbl = vm_get_or_create_entry(pos) + local index = tbl.va:indexp(pos) + tbl.data[index] = minetest.get_content_id(name) + tbl.dirty = true +end + +-- Gets the node at a given position, regardless of whether it is loaded or +-- not, respecting a transaction if one is in progress. +-- +-- Outside a VM transaction, if the mapblock is not loaded, it is pulled into +-- the server’s main map data cache and then accessed from there. +-- +-- Inside a VM transaction, the transaction’s VM cache is used. +function mesecon.get_node_force(pos) + if vm_cache then + return mesecon.vm_get_node(pos) + else + local node = minetest.get_node_or_nil(pos) + if node == nil then + -- Node is not currently loaded; use a VoxelManipulator to prime + -- the mapblock cache and try again. + minetest.get_voxel_manip(pos, pos) + node = minetest.get_node_or_nil(pos) + end + return node + end +end + +-- Swaps the node at a given position, regardless of whether it is loaded or +-- not, respecting a transaction if one is in progress. +-- +-- Outside a VM transaction, if the mapblock is not loaded, it is pulled into +-- the server’s main map data cache and then accessed from there. +-- +-- Inside a VM transaction, the transaction’s VM cache is used. +-- +-- This function can only be used to change the node’s name, not its parameters +-- or metadata. +function mesecon.swap_node_force(pos, name) + if vm_cache then + return mesecon.vm_swap_node(pos, name) + else + -- This serves to both ensure the mapblock is loaded and also hand us + -- the old node table so we can preserve param2. + local node = mesecon.get_node_force(pos) + node.name = name + minetest.swap_node(pos, node) + end +end + +-- Autoconnect Hooks +-- Nodes like conductors may change their appearance and their connection rules +-- right after being placed or after being dug, e.g. the default wires use this +-- to automatically connect to linking nodes after placement. +-- After placement, the update function will be executed immediately so that the +-- possibly changed rules can be taken into account when recalculating the circuit. +-- After digging, the update function will be queued and executed after +-- recalculating the circuit. The update function must take care of updating the +-- node at the given position itself, but also all of the other nodes the given +-- position may have (had) a linking connection to. +mesecon.autoconnect_hooks = {} + +-- name: A unique name for the hook, e.g. "foowire". Used to name the actionqueue function. +-- fct: The update function with parameters function(pos, node) +function mesecon.register_autoconnect_hook(name, fct) + mesecon.autoconnect_hooks[name] = fct + mesecon.queue:add_function("autoconnect_hook_"..name, fct) +end + +function mesecon.execute_autoconnect_hooks_now(pos, node) + for _, fct in pairs(mesecon.autoconnect_hooks) do + fct(pos, node) + end +end + +function mesecon.execute_autoconnect_hooks_queue(pos, node) + for name in pairs(mesecon.autoconnect_hooks) do + mesecon.queue:add_action(pos, "autoconnect_hook_"..name, {node}) + end end diff --git a/mods/ITEMS/REDSTONE/mesecons_button/init.lua b/mods/ITEMS/REDSTONE/mesecons_button/init.lua index 4db6b3750c..c4d8228915 100644 --- a/mods/ITEMS/REDSTONE/mesecons_button/init.lua +++ b/mods/ITEMS/REDSTONE/mesecons_button/init.lua @@ -20,13 +20,13 @@ end mesecon.button_turnoff = function (pos) local node = minetest.get_node(pos) if node.name=="mesecons_button:button_stone_on" then --has not been dug - mesecon:swap_node(pos, "mesecons_button:button_stone_off") + minetest.swap_node(pos, {name="mesecons_button:button_stone_off",param2=node.param2}) minetest.sound_play("mesecons_button_pop", {pos=pos}) - mesecon:receptor_off(pos, button_get_output_rules(node)) + mesecon.receptor_off(pos, button_get_output_rules(node)) elseif node.name=="mesecons_button:button_wood_on" then --has not been dug - mesecon:swap_node(pos, "mesecons_button:button_wood_off") + minetest.swap_node(pos, {name="mesecons_button:button_wood_off",param2=node.param2}) minetest.sound_play("mesecons_button_pop", {pos=pos}) - mesecon:receptor_off(pos, button_get_output_rules(node)) + mesecon.receptor_off(pos, button_get_output_rules(node)) end end @@ -111,8 +111,8 @@ minetest.register_node("mesecons_button:button_stone_off", { on_place = on_button_place, node_placement_prediction = "", on_rightclick = function (pos, node) - mesecon:swap_node(pos, "mesecons_button:button_stone_on") - mesecon:receptor_on(pos, button_get_output_rules(node)) + minetest.swap_node(pos, {name="mesecons_button:button_stone_on", param2=node.param2}) + mesecon.receptor_on(pos, button_get_output_rules(node)) minetest.sound_play("mesecons_button_push", {pos=pos}) minetest.after(1, mesecon.button_turnoff, pos) end, @@ -170,8 +170,8 @@ minetest.register_node("mesecons_button:button_wood_off", { on_place = on_button_place, node_placement_prediction = "", on_rightclick = function (pos, node) - mesecon:swap_node(pos, "mesecons_button:button_wood_on") - mesecon:receptor_on(pos, button_get_output_rules(node)) + minetest.swap_node(pos, {name="mesecons_button:button_wood_on", param2=node.param2}) + mesecon.receptor_on(pos, button_get_output_rules(node)) minetest.sound_play("mesecons_button_push", {pos=pos}) minetest.after(1.5, mesecon.button_turnoff, pos) end, diff --git a/mods/ITEMS/REDSTONE/mesecons_delayer/init.lua b/mods/ITEMS/REDSTONE/mesecons_delayer/init.lua index 3cfdcfc224..3fb2fec570 100644 --- a/mods/ITEMS/REDSTONE/mesecons_delayer/init.lua +++ b/mods/ITEMS/REDSTONE/mesecons_delayer/init.lua @@ -2,7 +2,7 @@ local delayer_get_output_rules = function(node) local rules = {{x = -1, y = 0, z = 0}} for i = 0, node.param2 do - rules = mesecon:rotate_rules_left(rules) + rules = mesecon.rotate_rules_left(rules) end return rules end @@ -10,7 +10,7 @@ end local delayer_get_input_rules = function(node) local rules = {{x = 1, y = 0, z = 0}} for i = 0, node.param2 do - rules = mesecon:rotate_rules_left(rules) + rules = mesecon.rotate_rules_left(rules) end return rules end @@ -19,25 +19,25 @@ end local delayer_turnon = function(params) local rules = delayer_get_output_rules(params.node) - mesecon:receptor_on(params.pos, rules) + mesecon.receptor_on(params.pos, rules) end local delayer_turnoff = function(params) local rules = delayer_get_output_rules(params.node) - mesecon:receptor_off(params.pos, rules) + mesecon.receptor_off(params.pos, rules) end local delayer_activate = function(pos, node) local def = minetest.registered_nodes[node.name] local time = def.delayer_time - mesecon:swap_node(pos, def.delayer_onstate) + minetest.swap_node(pos, {name=def.delayer_onstate, param2=node.param2}) minetest.after(time, delayer_turnon , {pos = pos, node = node}) end local delayer_deactivate = function(pos, node) local def = minetest.registered_nodes[node.name] local time = def.delayer_time - mesecon:swap_node(pos, def.delayer_offstate) + minetest.swap_node(pos, {name=def.delayer_offstate, param2=node.param2}) minetest.after(time, delayer_turnoff, {pos = pos, node = node}) end @@ -133,13 +133,13 @@ minetest.register_node("mesecons_delayer:delayer_off_"..tostring(i), { drop = 'mesecons_delayer:delayer_off_1', on_rightclick = function (pos, node) if node.name=="mesecons_delayer:delayer_off_1" then - mesecon:swap_node(pos,"mesecons_delayer:delayer_off_2") + minetest.swap_node(pos, {name="mesecons_delayer:delayer_off_2", param2=node.param2}) elseif node.name=="mesecons_delayer:delayer_off_2" then - mesecon:swap_node(pos,"mesecons_delayer:delayer_off_3") + minetest.swap_node(pos, {name="mesecons_delayer:delayer_off_3", param2=node.param2}) elseif node.name=="mesecons_delayer:delayer_off_3" then - mesecon:swap_node(pos,"mesecons_delayer:delayer_off_4") + minetest.swap_node(pos, {name="mesecons_delayer:delayer_off_4", param2=node.param2}) elseif node.name=="mesecons_delayer:delayer_off_4" then - mesecon:swap_node(pos,"mesecons_delayer:delayer_off_1") + minetest.swap_node(pos, {name="mesecons_delayer:delayer_off_1", param2=node.param2}) end end, delayer_time = delaytime, @@ -193,13 +193,13 @@ minetest.register_node("mesecons_delayer:delayer_on_"..tostring(i), { drop = 'mesecons_delayer:delayer_off_1', on_rightclick = function (pos, node) if node.name=="mesecons_delayer:delayer_on_1" then - mesecon:swap_node(pos,"mesecons_delayer:delayer_on_2") + minetest.swap_node(pos, {name="mesecons_delayer:delayer_on_2",param2=node.param2}) elseif node.name=="mesecons_delayer:delayer_on_2" then - mesecon:swap_node(pos,"mesecons_delayer:delayer_on_3") + minetest.swap_node(pos, {name="mesecons_delayer:delayer_on_3",param2=node.param2}) elseif node.name=="mesecons_delayer:delayer_on_3" then - mesecon:swap_node(pos,"mesecons_delayer:delayer_on_4") + minetest.swap_node(pos, {name="mesecons_delayer:delayer_on_4",param2=node.param2}) elseif node.name=="mesecons_delayer:delayer_on_4" then - mesecon:swap_node(pos,"mesecons_delayer:delayer_on_1") + minetest.swap_node(pos, {name="mesecons_delayer:delayer_on_1",param2=node.param2}) end end, delayer_time = delaytime, diff --git a/mods/ITEMS/REDSTONE/mesecons_lightstone/init.lua b/mods/ITEMS/REDSTONE/mesecons_lightstone/init.lua index 87e639eb3d..60bd05bfda 100644 --- a/mods/ITEMS/REDSTONE/mesecons_lightstone/init.lua +++ b/mods/ITEMS/REDSTONE/mesecons_lightstone/init.lua @@ -8,7 +8,7 @@ minetest.register_node("mesecons_lightstone:lightstone_off", { sounds = mcl_sounds.node_sound_glass_defaults(), mesecons = {effector = { action_on = function (pos, node) - mesecon:swap_node(pos, "mesecons_lightstone:lightstone_on") + minetest.swap_node(pos, {name="mesecons_lightstone:lightstone_on", param2 = node.param2}) end }}, _mcl_blast_resistance = 1.5, @@ -27,7 +27,7 @@ minetest.register_node("mesecons_lightstone:lightstone_on", { sounds = mcl_sounds.node_sound_glass_defaults(), mesecons = {effector = { action_off = function (pos, node) - mesecon:swap_node(pos, "mesecons_lightstone:lightstone_off") + minetest.swap_node(pos, {name="mesecons_lightstone:lightstone_off", param2 = node.param2}) end }}, _mcl_blast_resistance = 1.5, diff --git a/mods/ITEMS/REDSTONE/mesecons_mvps/init.lua b/mods/ITEMS/REDSTONE/mesecons_mvps/init.lua index 8c1718e045..a9cc374b5f 100644 --- a/mods/ITEMS/REDSTONE/mesecons_mvps/init.lua +++ b/mods/ITEMS/REDSTONE/mesecons_mvps/init.lua @@ -4,7 +4,7 @@ mesecon.mvps_stoppers={} -- Register nodes which drop as item when pushed or pulled mesecon.mvps_droppers={} -function mesecon:is_mvps_stopper(node, pushdir, stack, stackid) +function mesecon.is_mvps_stopper(node, pushdir, stack, stackid) local get_stopper = mesecon.mvps_stoppers[node.name] if type (get_stopper) == "function" then get_stopper = get_stopper(node, pushdir, stack, stackid) @@ -12,14 +12,14 @@ function mesecon:is_mvps_stopper(node, pushdir, stack, stackid) return get_stopper end -function mesecon:register_mvps_stopper(nodename, get_stopper) +function mesecon.register_mvps_stopper(nodename, get_stopper) if get_stopper == nil then get_stopper = true end mesecon.mvps_stoppers[nodename] = get_stopper end -function mesecon:is_mvps_dropper(node, pushdir, stack, stackid) +function mesecon.is_mvps_dropper(node, pushdir, stack, stackid) local get_dropper = mesecon.mvps_droppers[node.name] if type (get_dropper) == "function" then get_dropper = get_dropper(node, pushdir, stack, stackid) @@ -30,23 +30,23 @@ function mesecon:is_mvps_dropper(node, pushdir, stack, stackid) return get_dropper end -function mesecon:register_mvps_dropper(nodename, get_dropper) +function mesecon.register_mvps_dropper(nodename, get_dropper) if get_dropper == nil then get_dropper = true end mesecon.mvps_droppers[nodename] = get_dropper end -function mesecon:mvps_process_stack(stack) +function mesecon.mvps_process_stack(stack) -- update mesecons for placed nodes ( has to be done after all nodes have been added ) for _, n in ipairs(stack) do core.check_for_falling(n.pos) mesecon.on_placenode(n.pos, minetest.get_node(n.pos)) - mesecon:update_autoconnect(n.pos) + mesecon.update_autoconnect(n.pos) end end -function mesecon:mvps_push(pos, dir, maximum) -- pos: pos of mvps; dir: direction of push; maximum: maximum nodes to be pushed +function mesecon.mvps_push(pos, dir, maximum) -- pos: pos of mvps; dir: direction of push; maximum: maximum nodes to be pushed np = {x = pos.x, y = pos.y, z = pos.z} -- determine the number of nodes to be pushed @@ -65,12 +65,12 @@ function mesecon:mvps_push(pos, dir, maximum) -- pos: pos of mvps; dir: directio table.insert (nodes, {node = nn, pos = np}) - np = mesecon:addPosRule(np, dir) + np = vector.add(np, dir) end -- determine if one of the nodes blocks the push for id, n in ipairs(nodes) do - if mesecon:is_mvps_stopper(n.node, dir, nodes, id) then + if mesecon.is_mvps_stopper(n.node, dir, nodes, id) then return end end @@ -79,7 +79,7 @@ function mesecon:mvps_push(pos, dir, maximum) -- pos: pos of mvps; dir: directio -- remove all nodes for id, n in ipairs(nodes) do n.meta = minetest.get_meta(n.pos):to_table() - local is_dropper = mesecon:is_mvps_dropper(n.node, dir, nodes, id) + local is_dropper = mesecon.is_mvps_dropper(n.node, dir, nodes, id) if is_dropper then local drops = minetest.get_node_drops(n.node.name, "") local droppos = vector.add(n.pos, dir) @@ -98,7 +98,7 @@ function mesecon:mvps_push(pos, dir, maximum) -- pos: pos of mvps; dir: directio break end mesecon.on_dignode(n.pos, n.node) - mesecon:update_autoconnect(n.pos) + mesecon.update_autoconnect(n.pos) end -- add nodes @@ -106,7 +106,7 @@ function mesecon:mvps_push(pos, dir, maximum) -- pos: pos of mvps; dir: directio if first_dropper and id >= first_dropper then break end - np = mesecon:addPosRule(n.pos, dir) + np = vector.add(n.pos, dir) minetest.add_node(np, n.node) minetest.get_meta(np):from_table(n.meta) end @@ -115,19 +115,19 @@ function mesecon:mvps_push(pos, dir, maximum) -- pos: pos of mvps; dir: directio if first_dropper and i >= first_dropper then break end - nodes[i].pos = mesecon:addPosRule(nodes[i].pos, dir) + nodes[i].pos = vector.add(nodes[i].pos, dir) end return true, nodes end -function mesecon:mvps_pull_single(pos, dir) -- pos: pos of mvps; direction: direction of pull (matches push direction for sticky pistons) - np = mesecon:addPosRule(pos, dir) +function mesecon.mvps_pull_single(pos, dir) -- pos: pos of mvps; direction: direction of pull (matches push direction for sticky pistons) + np = vector.add(pos, dir) nn = minetest.get_node(np) if minetest.registered_nodes[nn.name].liquidtype == "none" - and not mesecon:is_mvps_stopper(nn, {x = -dir.x, y = -dir.y, z = -dir.z}, {{pos = np, node = nn}}, 1) - and not mesecon:is_mvps_dropper(nn, {x = -dir.x, y = -dir.y, z = -dir.z}, {{pos = np, node = nn}}, 1) then + and not mesecon.is_mvps_stopper(nn, {x = -dir.x, y = -dir.y, z = -dir.z}, {{pos = np, node = nn}}, 1) + and not mesecon.is_mvps_dropper(nn, {x = -dir.x, y = -dir.y, z = -dir.z}, {{pos = np, node = nn}}, 1) then local meta = minetest.get_meta(np):to_table() minetest.remove_node(np) minetest.add_node(pos, nn) @@ -136,12 +136,12 @@ function mesecon:mvps_pull_single(pos, dir) -- pos: pos of mvps; direction: dire core.check_for_falling(np) core.check_for_falling(pos) mesecon.on_dignode(np, nn) - mesecon:update_autoconnect(np) + mesecon.update_autoconnect(np) end return {{pos = np, node = {param2 = 0, name = "air"}}, {pos = pos, node = nn}} end -function mesecon:mvps_pull_all(pos, direction) -- pos: pos of mvps; direction: direction of pull +function mesecon.mvps_pull_all(pos, direction) -- pos: pos of mvps; direction: direction of pull local lpos = {x=pos.x-direction.x, y=pos.y-direction.y, z=pos.z-direction.z} -- 1 away local lnode = minetest.get_node(lpos) local lpos2 = {x=pos.x-direction.x*2, y=pos.y-direction.y*2, z=pos.z-direction.z*2} -- 2 away @@ -164,42 +164,42 @@ function mesecon:mvps_pull_all(pos, direction) -- pos: pos of mvps; direction: d minetest.remove_node(oldpos) end -mesecon:register_mvps_stopper("mcl_core:obsidian") -mesecon:register_mvps_stopper("mcl_core:bedrock") -mesecon:register_mvps_stopper("mcl_core:barrier") -mesecon:register_mvps_stopper("mcl_core:void") -mesecon:register_mvps_stopper("mcl_chests:chest") -mesecon:register_mvps_stopper("mcl_chests:chest_left") -mesecon:register_mvps_stopper("mcl_chests:chest_right") -mesecon:register_mvps_stopper("mcl_chests:trapped_chest") -mesecon:register_mvps_stopper("mcl_chests:trapped_chest_left") -mesecon:register_mvps_stopper("mcl_chests:trapped_chest_right") -mesecon:register_mvps_stopper("mcl_chests:trapped_chest_on") -mesecon:register_mvps_stopper("mcl_chests:trapped_chest_on_left") -mesecon:register_mvps_stopper("mcl_chests:trapped_chest_on_right") -mesecon:register_mvps_stopper("mcl_chests:ender_chest") -mesecon:register_mvps_stopper("mcl_furnaces:furnace") -mesecon:register_mvps_stopper("mcl_furnaces:furnace_active") -mesecon:register_mvps_stopper("mcl_hoppers:hopper") -mesecon:register_mvps_stopper("mcl_hoppers:hopper_side") -mesecon:register_mvps_stopper("mcl_droppers:dropper") -mesecon:register_mvps_stopper("mcl_droppers:dropper_up") -mesecon:register_mvps_stopper("mcl_droppers:dropper_down") -mesecon:register_mvps_stopper("mcl_dispensers:dispenser") -mesecon:register_mvps_stopper("mcl_dispensers:dispenser_up") -mesecon:register_mvps_stopper("mcl_dispensers:dispenser_down") -mesecon:register_mvps_stopper("mcl_anvils:anvil") -mesecon:register_mvps_stopper("mcl_anvils:anvil_damage_1") -mesecon:register_mvps_stopper("mcl_anvils:anvil_damage_2") -mesecon:register_mvps_stopper("mcl_jukebox:jukebox") -mesecon:register_mvps_stopper("mcl_mobspawners:spawner") -mesecon:register_mvps_stopper("mcl_signs:standing_sign") -mesecon:register_mvps_stopper("mcl_signs:wall_sign") -mesecon:register_mvps_stopper("mesecons_commandblock:commandblock_off") -mesecon:register_mvps_stopper("mesecons_commandblock:commandblock_on") -mesecon:register_mvps_stopper("mesecons_solarpanel:solar_panel_off") -mesecon:register_mvps_stopper("mesecons_solarpanel:solar_panel_on") -mesecon:register_mvps_stopper("mesecons_solarpanel:solar_panel_inverted_off") -mesecon:register_mvps_stopper("mesecons_solarpanel:solar_panel_inverted_on") -mesecon:register_mvps_stopper("mesecons_noteblock:noteblock") -mesecon:register_mvps_stopper("3d_armor_stand:armor_stand") +mesecon.register_mvps_stopper("mcl_core:obsidian") +mesecon.register_mvps_stopper("mcl_core:bedrock") +mesecon.register_mvps_stopper("mcl_core:barrier") +mesecon.register_mvps_stopper("mcl_core:void") +mesecon.register_mvps_stopper("mcl_chests:chest") +mesecon.register_mvps_stopper("mcl_chests:chest_left") +mesecon.register_mvps_stopper("mcl_chests:chest_right") +mesecon.register_mvps_stopper("mcl_chests:trapped_chest") +mesecon.register_mvps_stopper("mcl_chests:trapped_chest_left") +mesecon.register_mvps_stopper("mcl_chests:trapped_chest_right") +mesecon.register_mvps_stopper("mcl_chests:trapped_chest_on") +mesecon.register_mvps_stopper("mcl_chests:trapped_chest_on_left") +mesecon.register_mvps_stopper("mcl_chests:trapped_chest_on_right") +mesecon.register_mvps_stopper("mcl_chests:ender_chest") +mesecon.register_mvps_stopper("mcl_furnaces:furnace") +mesecon.register_mvps_stopper("mcl_furnaces:furnace_active") +mesecon.register_mvps_stopper("mcl_hoppers:hopper") +mesecon.register_mvps_stopper("mcl_hoppers:hopper_side") +mesecon.register_mvps_stopper("mcl_droppers:dropper") +mesecon.register_mvps_stopper("mcl_droppers:dropper_up") +mesecon.register_mvps_stopper("mcl_droppers:dropper_down") +mesecon.register_mvps_stopper("mcl_dispensers:dispenser") +mesecon.register_mvps_stopper("mcl_dispensers:dispenser_up") +mesecon.register_mvps_stopper("mcl_dispensers:dispenser_down") +mesecon.register_mvps_stopper("mcl_anvils:anvil") +mesecon.register_mvps_stopper("mcl_anvils:anvil_damage_1") +mesecon.register_mvps_stopper("mcl_anvils:anvil_damage_2") +mesecon.register_mvps_stopper("mcl_jukebox:jukebox") +mesecon.register_mvps_stopper("mcl_mobspawners:spawner") +mesecon.register_mvps_stopper("mcl_signs:standing_sign") +mesecon.register_mvps_stopper("mcl_signs:wall_sign") +mesecon.register_mvps_stopper("mesecons_commandblock:commandblock_off") +mesecon.register_mvps_stopper("mesecons_commandblock:commandblock_on") +mesecon.register_mvps_stopper("mesecons_solarpanel:solar_panel_off") +mesecon.register_mvps_stopper("mesecons_solarpanel:solar_panel_on") +mesecon.register_mvps_stopper("mesecons_solarpanel:solar_panel_inverted_off") +mesecon.register_mvps_stopper("mesecons_solarpanel:solar_panel_inverted_on") +mesecon.register_mvps_stopper("mesecons_noteblock:noteblock") +mesecon.register_mvps_stopper("3d_armor_stand:armor_stand") diff --git a/mods/ITEMS/REDSTONE/mesecons_pistons/init.lua b/mods/ITEMS/REDSTONE/mesecons_pistons/init.lua index e7e85a018e..b5de596fb0 100644 --- a/mods/ITEMS/REDSTONE/mesecons_pistons/init.lua +++ b/mods/ITEMS/REDSTONE/mesecons_pistons/init.lua @@ -1,3 +1,5 @@ +local PISTON_MAXIMUM_PUSH = 12 + -- Get mesecon rules of pistons piston_rules = {{x=0, y=0, z=1}, --everything apart from z- (pusher side) @@ -33,7 +35,7 @@ local piston_down_rules = local piston_get_rules = function (node) local rules = piston_rules for i = 1, node.param2 do - rules = mesecon:rotate_rules_left(rules) + rules = mesecon.rotate_rules_left(rules) end return rules end @@ -41,7 +43,7 @@ end piston_facedir_direction = function (node) local rules = {{x = 0, y = 0, z = -1}} for i = 1, node.param2 do - rules = mesecon:rotate_rules_left(rules) + rules = mesecon.rotate_rules_left(rules) end return rules[1] end @@ -58,7 +60,7 @@ local piston_remove_pusher = function (pos, node) local pistonspec = minetest.registered_nodes[node.name].mesecons_piston local dir = piston_get_direction(pistonspec.dir, node) - local pusherpos = mesecon:addPosRule(pos, dir) + local pusherpos = vector.add(pos, dir) local pushername = minetest.get_node(pusherpos).name if pushername == pistonspec.pusher then --make sure there actually is a pusher (for compatibility reasons mainly) @@ -76,8 +78,8 @@ local piston_on = function (pos, node) local pistonspec = minetest.registered_nodes[node.name].mesecons_piston dir = piston_get_direction(pistonspec.dir, node) - local np = mesecon:addPosRule(pos, dir) - success, stack = mesecon:mvps_push(np, dir, PISTON_MAXIMUM_PUSH) + local np = vector.add(pos, dir) + success, stack = mesecon.mvps_push(np, dir, PISTON_MAXIMUM_PUSH) if success then minetest.add_node(pos, {param2 = node.param2, name = pistonspec.onname}) minetest.add_node(np, {param2 = node.param2, name = pistonspec.pusher}) @@ -85,7 +87,7 @@ local piston_on = function (pos, node) if below.name == "mcl_farming:soil" or below.name == "mcl_farming:soil_wet" then minetest.set_node({x=np.x,y=np.y-1,z=np.z}, {name = "mcl_core:dirt"}) end - mesecon:mvps_process_stack(stack) + mesecon.mvps_process_stack(stack) minetest.sound_play("piston_extend", { pos = pos, max_hear_distance = 20, @@ -101,9 +103,9 @@ local piston_off = function (pos, node) if pistonspec.sticky then dir = piston_get_direction(pistonspec.dir, node) - pullpos = mesecon:addPosRule(pos, dir) - stack = mesecon:mvps_pull_single(pullpos, dir) - mesecon:mvps_process_stack(stack) + pullpos = vector.add(pos, dir) + stack = mesecon.mvps_pull_single(pullpos, dir) + mesecon.mvps_process_stack(stack) end end @@ -759,14 +761,14 @@ local piston_pusher_up_down_get_stopper = function (node, dir, stack, stackid) return true end -mesecon:register_mvps_stopper("mesecons_pistons:piston_pusher_normal", piston_pusher_get_stopper) -mesecon:register_mvps_stopper("mesecons_pistons:piston_pusher_sticky", piston_pusher_get_stopper) +mesecon.register_mvps_stopper("mesecons_pistons:piston_pusher_normal", piston_pusher_get_stopper) +mesecon.register_mvps_stopper("mesecons_pistons:piston_pusher_sticky", piston_pusher_get_stopper) -mesecon:register_mvps_stopper("mesecons_pistons:piston_up_pusher_normal", piston_pusher_up_down_get_stopper) -mesecon:register_mvps_stopper("mesecons_pistons:piston_up_pusher_sticky", piston_pusher_up_down_get_stopper) +mesecon.register_mvps_stopper("mesecons_pistons:piston_up_pusher_normal", piston_pusher_up_down_get_stopper) +mesecon.register_mvps_stopper("mesecons_pistons:piston_up_pusher_sticky", piston_pusher_up_down_get_stopper) -mesecon:register_mvps_stopper("mesecons_pistons:piston_down_pusher_normal", piston_pusher_up_down_get_stopper) -mesecon:register_mvps_stopper("mesecons_pistons:piston_down_pusher_sticky", piston_pusher_up_down_get_stopper) +mesecon.register_mvps_stopper("mesecons_pistons:piston_down_pusher_normal", piston_pusher_up_down_get_stopper) +mesecon.register_mvps_stopper("mesecons_pistons:piston_down_pusher_sticky", piston_pusher_up_down_get_stopper) -- Register pistons as stoppers if they would be seperated from the stopper @@ -783,12 +785,12 @@ end local piston_get_stopper = function (node, dir, stack, stackid) pistonspec = minetest.registered_nodes[node.name].mesecons_piston dir = piston_get_direction(pistonspec.dir, node) - local pusherpos = mesecon:addPosRule(stack[stackid].pos, dir) + local pusherpos = vector.add(stack[stackid].pos, dir) local pushernode = minetest.get_node(pusherpos) if minetest.registered_nodes[node.name].mesecons_piston.pusher == pushernode.name then for _, s in ipairs(stack) do - if mesecon:cmpPos(s.pos, pusherpos) -- pusher is also to be pushed + if mesecon.cmpPos(s.pos, pusherpos) -- pusher is also to be pushed and s.node.param2 == node.param2 then return false end @@ -797,14 +799,14 @@ local piston_get_stopper = function (node, dir, stack, stackid) return true end -mesecon:register_mvps_stopper("mesecons_pistons:piston_normal_on", piston_get_stopper) -mesecon:register_mvps_stopper("mesecons_pistons:piston_sticky_on", piston_get_stopper) +mesecon.register_mvps_stopper("mesecons_pistons:piston_normal_on", piston_get_stopper) +mesecon.register_mvps_stopper("mesecons_pistons:piston_sticky_on", piston_get_stopper) -mesecon:register_mvps_stopper("mesecons_pistons:piston_up_normal_on", piston_up_down_get_stopper) -mesecon:register_mvps_stopper("mesecons_pistons:piston_up_sticky_on", piston_up_down_get_stopper) +mesecon.register_mvps_stopper("mesecons_pistons:piston_up_normal_on", piston_up_down_get_stopper) +mesecon.register_mvps_stopper("mesecons_pistons:piston_up_sticky_on", piston_up_down_get_stopper) -mesecon:register_mvps_stopper("mesecons_pistons:piston_down_normal_on", piston_up_down_get_stopper) -mesecon:register_mvps_stopper("mesecons_pistons:piston_down_sticky_on", piston_up_down_get_stopper) +mesecon.register_mvps_stopper("mesecons_pistons:piston_down_normal_on", piston_up_down_get_stopper) +mesecon.register_mvps_stopper("mesecons_pistons:piston_down_sticky_on", piston_up_down_get_stopper) --craft recipes minetest.register_craft({ diff --git a/mods/ITEMS/REDSTONE/mesecons_pressureplates/init.lua b/mods/ITEMS/REDSTONE/mesecons_pressureplates/init.lua index e323fc9d4b..b0fe8643dd 100644 --- a/mods/ITEMS/REDSTONE/mesecons_pressureplates/init.lua +++ b/mods/ITEMS/REDSTONE/mesecons_pressureplates/init.lua @@ -1,3 +1,5 @@ +local PRESSURE_PLATE_INTERVAL = 0.04 + local pp_box_off = { type = "fixed", fixed = { -7/16, -8/16, -7/16, 7/16, -7/16, 7/16 }, @@ -8,32 +10,26 @@ local pp_box_on = { fixed = { -7/16, -8/16, -7/16, 7/16, -7.5/16, 7/16 }, } -pp_on_timer = function (pos, elapsed) - local node = minetest.get_node(pos) - local ppspec = minetest.registered_nodes[node.name].pressureplate +local function pp_on_timer(pos, elapsed) + local node = minetest.get_node(pos) + local basename = minetest.registered_nodes[node.name].pressureplate_basename -- This is a workaround for a strange bug that occurs when the server is started -- For some reason the first time on_timer is called, the pos is wrong - if not ppspec then return end + if not basename then return end local objs = minetest.get_objects_inside_radius(pos, 1) - local two_below = mesecon:addPosRule(pos, {x = 0, y = -2, z = 0}) + local two_below = vector.add(pos, vector.new(0, -2, 0)) - if objs[1] == nil and node.name == ppspec.onstate then - minetest.add_node(pos, {name = ppspec.offstate}) - mesecon:receptor_off(pos) - -- force deactivation of mesecon two blocks below (hacky) - if not mesecon:connected_to_receptor(two_below) then - mesecon:turnoff(two_below) - end - else + if objs[1] == nil and node.name == basename .. "_on" then + minetest.set_node(pos, {name = basename .. "_off"}) + mesecon.receptor_off(pos, mesecon.rules.pplate) + elseif node.name == basename .. "_off" then for k, obj in pairs(objs) do local objpos = obj:getpos() if objpos.y > pos.y-1 and objpos.y < pos.y then - minetest.add_node(pos, {name=ppspec.onstate}) - mesecon:receptor_on(pos) - -- force activation of mesecon two blocks below (hacky) - mesecon:turnon(two_below) + minetest.set_node(pos, {name = basename .. "_on"}) + mesecon.receptor_on(pos, mesecon.rules.pplate ) end end end @@ -41,112 +37,83 @@ pp_on_timer = function (pos, elapsed) end -- Register a Pressure Plate --- offstate: name of the pressure plate when inactive --- onstate: name of the pressure plate when active +-- basename: base name of the pressure plate -- description: description displayed in the player's inventory --- tiles_off: textures of the pressure plate when inactive --- tiles_on: textures of the pressure plate when active --- image: inventory and wield image of the pressure plate +-- textures_off:textures of the pressure plate when inactive +-- textures_on: textures of the pressure plate when active +-- image_w: wield image of the pressure plate +-- image_i: inventory image of the pressure plate -- recipe: crafting recipe of the pressure plate -- sounds: sound table (like in minetest.register_node) -- plusgroups: group memberships (attached_node=1 and not_in_creative_inventory=1 are already used) -function mesecon:register_pressure_plate(offstate, onstate, description, texture_off, texture_on, recipe, sounds, plusgroups) - local ppspec = { - offstate = offstate, - onstate = onstate - } - +function mesecon.register_pressure_plate(basename, description, textures_off, textures_on, image_w, image_i, recipe, sounds, plusgroups) local groups_off = table.copy(plusgroups) groups_off.attached_node = 1 groups_off.dig_by_piston = 1 - - minetest.register_node(offstate, { - drawtype = "nodebox", - tiles = {texture_off}, - wield_image = texture_off, - wield_scale = { x=1, y=1, z=0.5 }, - paramtype = "light", - sunlight_propagates = true, - selection_box = pp_box_off, - node_box = pp_box_off, - groups = groups_off, - is_ground_content = false, - description = description, - _doc_items_longdesc = "A pressure plate is a redstone component which supplies its surrounding blocks with redstone power while someone or something rests on top of it.", - pressureplate = ppspec, - on_timer = pp_on_timer, - sounds = sounds, - mesecons = {receptor = { - state = mesecon.state.off - }}, - on_construct = function(pos) - minetest.get_node_timer(pos):start(PRESSURE_PLATE_INTERVAL) - end, - _mcl_blast_resistance = 2.5, - _mcl_hardness = 0.5, - }) - - local groups_on = table.copy(groups_off) + groups_on = table.copy(groups_off) groups_on.not_in_creative_inventory = 1 - minetest.register_node(onstate, { + mesecon.register_node(basename, { drawtype = "nodebox", - tiles = {texture_on}, - wield_image = texture_on, - wield_scale = { x=1, y=1, z=0.25 }, + inventory_image = image_i, + wield_image = image_w, paramtype = "light", - sunlight_propagates = true, - selection_box = pp_box_on, - node_box = pp_box_on, - groups = groups_on, - is_ground_content = false, - drop = offstate, - pressureplate = ppspec, + description = description, on_timer = pp_on_timer, - sounds = sounds, - mesecons = {receptor = { - state = mesecon.state.on - }}, on_construct = function(pos) minetest.get_node_timer(pos):start(PRESSURE_PLATE_INTERVAL) end, - after_dig_node = function(pos) - local two_below = mesecon:addPosRule(pos, {x = 0, y = -2, z = 0}) - if not mesecon:connected_to_receptor(two_below) then - mesecon:turnoff(two_below) - end - end, + sounds = sounds, + + pressureplate_basename = basename, _mcl_blast_resistance = 2.5, _mcl_hardness = 0.5, + },{ + node_box = pp_box_off, + selection_box = pp_box_off, + groups = groups_off, + tiles = textures_off, + + mesecons = {receptor = { state = mesecon.state.off, rules = mesecon.rules.pplate }}, + _doc_items_longdesc = "A pressure plate is a redstone component which supplies its surrounding blocks with redstone power while someone or something rests on top of it.", + },{ + node_box = pp_box_on, + selection_box = pp_box_on, + groups = groups_on, + tiles = textures_on, + + mesecons = {receptor = { state = mesecon.state.on, rules = mesecon.rules.pplate }}, }) minetest.register_craft({ - output = offstate, + output = basename .. "_off", recipe = recipe, }) if minetest.get_modpath("doc") then - doc.add_entry_alias("nodes", offstate, "nodes", onstate) + doc.add_entry_alias("nodes", basename .. "_off", "nodes", basename .. "_on") end end -mesecon:register_pressure_plate( - "mesecons_pressureplates:pressure_plate_wood_off", - "mesecons_pressureplates:pressure_plate_wood_on", +mesecon.register_pressure_plate( + "mesecons_pressureplates:pressure_plate_wood", "Wooden Pressure Plate", + {"default_wood.png"}, + {"default_wood.png"}, "default_wood.png", - "default_wood.png", + nil, {{"group:wood", "group:wood"}}, mcl_sounds.node_sound_wood_defaults(), {axey=1, material_wood=1}) -mesecon:register_pressure_plate( - "mesecons_pressureplates:pressure_plate_stone_off", - "mesecons_pressureplates:pressure_plate_stone_on", +mesecon.register_pressure_plate( + "mesecons_pressureplates:pressure_plate_stone", "Stone Pressure Plate", + {"default_stone.png"}, + {"default_stone.png"}, "default_stone.png", - "default_stone.png", + nil, {{"mcl_core:stone", "mcl_core:stone"}}, mcl_sounds.node_sound_stone_defaults(), {pickaxey=1, material_stone=1}) diff --git a/mods/ITEMS/REDSTONE/mesecons_pressureplates/textures/default_stone.png b/mods/ITEMS/REDSTONE/mesecons_pressureplates/textures/default_stone.png new file mode 100644 index 0000000000..8831ffb9b5 Binary files /dev/null and b/mods/ITEMS/REDSTONE/mesecons_pressureplates/textures/default_stone.png differ diff --git a/mods/ITEMS/REDSTONE/mesecons_pressureplates/textures/default_wood.png b/mods/ITEMS/REDSTONE/mesecons_pressureplates/textures/default_wood.png new file mode 100644 index 0000000000..ee97b8d8a1 Binary files /dev/null and b/mods/ITEMS/REDSTONE/mesecons_pressureplates/textures/default_wood.png differ diff --git a/mods/ITEMS/REDSTONE/mesecons_solarpanel/init.lua b/mods/ITEMS/REDSTONE/mesecons_solarpanel/init.lua index 1825d90ef9..9ed18c6a4c 100644 --- a/mods/ITEMS/REDSTONE/mesecons_solarpanel/init.lua +++ b/mods/ITEMS/REDSTONE/mesecons_solarpanel/init.lua @@ -28,7 +28,7 @@ minetest.register_node("mesecons_solarpanel:solar_panel_on", { }}, on_rightclick = function(pos, node, clicker, pointed_thing) minetest.swap_node(pos, {name = "mesecons_solarpanel:solar_panel_inverted_off"}) - mesecon:receptor_off(pos) + mesecon.receptor_off(pos) end, _mcl_blast_resistance = 1, _mcl_hardness = 0.2, @@ -62,7 +62,7 @@ minetest.register_node("mesecons_solarpanel:solar_panel_off", { }}, on_rightclick = function(pos, node, clicker, pointed_thing) minetest.swap_node(pos, {name = "mesecons_solarpanel:solar_panel_inverted_on"}) - mesecon:receptor_on(pos) + mesecon.receptor_on(pos) end, _mcl_blast_resistance = 1, _mcl_hardness = 0.2, @@ -87,7 +87,7 @@ minetest.register_abm({ if light >= 12 and minetest.get_timeofday() > 0.2 and minetest.get_timeofday() < 0.8 then minetest.set_node(pos, {name="mesecons_solarpanel:solar_panel_on", param2=node.param2}) - mesecon:receptor_on(pos) + mesecon.receptor_on(pos) end end, }) @@ -102,7 +102,7 @@ minetest.register_abm({ if light < 12 then minetest.set_node(pos, {name="mesecons_solarpanel:solar_panel_off", param2=node.param2}) - mesecon:receptor_off(pos) + mesecon.receptor_off(pos) end end, }) @@ -137,7 +137,7 @@ minetest.register_node("mesecons_solarpanel:solar_panel_inverted_on", { }}, on_rightclick = function(pos, node, clicker, pointed_thing) minetest.swap_node(pos, {name = "mesecons_solarpanel:solar_panel_off"}) - mesecon:receptor_off(pos) + mesecon.receptor_off(pos) end, _mcl_blast_resistance = 1, _mcl_hardness = 0.2, @@ -172,7 +172,7 @@ minetest.register_node("mesecons_solarpanel:solar_panel_inverted_off", { }}, on_rightclick = function(pos, node, clicker, pointed_thing) minetest.swap_node(pos, {name = "mesecons_solarpanel:solar_panel_on"}) - mesecon:receptor_on(pos) + mesecon.receptor_on(pos) end, _mcl_blast_resistance = 1, _mcl_hardness = 0.2, @@ -188,7 +188,7 @@ minetest.register_abm({ if light < 12 then minetest.set_node(pos, {name="mesecons_solarpanel:solar_panel_inverted_on", param2=node.param2}) - mesecon:receptor_on(pos) + mesecon.receptor_on(pos) end end, }) @@ -203,7 +203,7 @@ minetest.register_abm({ if light >= 12 and minetest.get_timeofday() > 0.8 and minetest.get_timeofday() < 0.2 then minetest.set_node(pos, {name="mesecons_solarpanel:solar_panel_inverted_off", param2=node.param2}) - mesecon:receptor_off(pos) + mesecon.receptor_off(pos) end end, }) diff --git a/mods/ITEMS/REDSTONE/mesecons_torch/init.lua b/mods/ITEMS/REDSTONE/mesecons_torch/init.lua index f2b2136b4d..6b8d43fed7 100644 --- a/mods/ITEMS/REDSTONE/mesecons_torch/init.lua +++ b/mods/ITEMS/REDSTONE/mesecons_torch/init.lua @@ -2,15 +2,15 @@ local rotate_torch_rules = function (rules, param2) if param2 == 5 then - return mesecon:rotate_rules_right(rules) + return mesecon.rotate_rules_right(rules) elseif param2 == 2 then - return mesecon:rotate_rules_right(mesecon:rotate_rules_right(rules)) --180 degrees + return mesecon.rotate_rules_right(mesecon.rotate_rules_right(rules)) --180 degrees elseif param2 == 4 then - return mesecon:rotate_rules_left(rules) + return mesecon.rotate_rules_left(rules) elseif param2 == 1 then - return mesecon:rotate_rules_down(rules) + return mesecon.rotate_rules_down(rules) elseif param2 == 0 then - return mesecon:rotate_rules_up(rules) + return mesecon.rotate_rules_up(rules) else return rules end @@ -128,26 +128,26 @@ minetest.register_abm({ action = function(pos, node) local is_powered = false for _, rule in ipairs(torch_get_input_rules(node)) do - local src = mesecon:addPosRule(pos, rule) - if mesecon:is_power_on(src) then + local src = vector.add(pos, rule) + if mesecon.is_power_on(src) then is_powered = true end end if is_powered then if node.name == "mesecons_torch:mesecon_torch_on" then - mesecon:swap_node(pos, "mesecons_torch:mesecon_torch_off") - mesecon:receptor_off(pos, torch_get_output_rules(node)) + minetest.swap_node(pos, {name="mesecons_torch:mesecon_torch_off", param2=node.param2}) + mesecon.receptor_off(pos, torch_get_output_rules(node)) elseif node.name == "mesecons_torch:mesecon_torch_on_wall" then - mesecon:swap_node(pos, "mesecons_torch:mesecon_torch_off_wall") - mesecon:receptor_off(pos, torch_get_output_rules(node)) + minetest.swap_node(pos, {name="mesecons_torch:mesecon_torch_off_wall", param2=node.param2}) + mesecon.receptor_off(pos, torch_get_output_rules(node)) end elseif node.name == "mesecons_torch:mesecon_torch_off" then - mesecon:swap_node(pos, "mesecons_torch:mesecon_torch_on") - mesecon:receptor_on(pos, torch_get_output_rules(node)) + minetest.swap_node(pos, {name="mesecons_torch:mesecon_torch_on", param2=node.param2}) + mesecon.receptor_on(pos, torch_get_output_rules(node)) elseif node.name == "mesecons_torch:mesecon_torch_off_wall" then - mesecon:swap_node(pos, "mesecons_torch:mesecon_torch_on_wall") - mesecon:receptor_on(pos, torch_get_output_rules(node)) + minetest.swap_node(pos, {name="mesecons_torch:mesecon_torch_on_wall", param2=node.param2}) + mesecon.receptor_on(pos, torch_get_output_rules(node)) end end }) diff --git a/mods/ITEMS/REDSTONE/mesecons_walllever/init.lua b/mods/ITEMS/REDSTONE/mesecons_walllever/init.lua index b75c7b9dfc..951aa37fbd 100644 --- a/mods/ITEMS/REDSTONE/mesecons_walllever/init.lua +++ b/mods/ITEMS/REDSTONE/mesecons_walllever/init.lua @@ -33,8 +33,8 @@ minetest.register_node("mesecons_walllever:wall_lever_off", { _doc_items_longdesc = "A lever is a redstone component which can be flipped on and off. It supplies redstone power to the blocks behind while it is in the “on” state.", _doc_items_usagehelp = "Right-click the lever to flip it on or off.", on_rightclick = function (pos, node) - mesecon:swap_node(pos, "mesecons_walllever:wall_lever_on") - mesecon:receptor_on(pos, mesecon.rules.buttonlike_get(node)) + minetest.swap_node(pos, {name="mesecons_walllever:wall_lever_on", param2=node.param2}) + mesecon.receptor_on(pos, mesecon.rules.buttonlike_get(node)) minetest.sound_play("mesecons_lever", {pos=pos}) end, sounds = mcl_sounds.node_sound_wood_defaults(), @@ -76,8 +76,8 @@ minetest.register_node("mesecons_walllever:wall_lever_on", { description="Lever", _doc_items_create_entry = false, on_rightclick = function (pos, node) - mesecon:swap_node(pos, "mesecons_walllever:wall_lever_off") - mesecon:receptor_off(pos, mesecon.rules.buttonlike_get(node)) + minetest.swap_node(pos, {name="mesecons_walllever:wall_lever_off", param2=node.param2}) + mesecon.receptor_off(pos, mesecon.rules.buttonlike_get(node)) minetest.sound_play("mesecons_lever", {pos=pos}) end, sounds = mcl_sounds.node_sound_wood_defaults(), diff --git a/mods/ITEMS/REDSTONE/mesecons_wires/depends.txt b/mods/ITEMS/REDSTONE/mesecons_wires/depends.txt new file mode 100644 index 0000000000..acaa924122 --- /dev/null +++ b/mods/ITEMS/REDSTONE/mesecons_wires/depends.txt @@ -0,0 +1 @@ +mesecons diff --git a/mods/ITEMS/REDSTONE/mesecons_wires/init.lua b/mods/ITEMS/REDSTONE/mesecons_wires/init.lua new file mode 100644 index 0000000000..46947f7248 --- /dev/null +++ b/mods/ITEMS/REDSTONE/mesecons_wires/init.lua @@ -0,0 +1,264 @@ +-- naming scheme: wire:(xp)(zp)(xm)(zm)(xpyp)(zpyp)(xmyp)(zmyp)_on/off +-- where x= x direction, z= z direction, y= y direction, p = +1, m = -1, e.g. xpym = {x=1, y=-1, z=0} +-- The (xp)/(zpyp)/.. statements shall be replaced by either 0 or 1 +-- Where 0 means the wire has no visual connection to that direction and +-- 1 means that the wire visually connects to that other node. + +-- ####################### +-- ## Update wire looks ## +-- ####################### + +-- self_pos = pos of any mesecon node, from_pos = pos of conductor to getconnect for +local wire_getconnect = function (from_pos, self_pos) + local node = minetest.get_node(self_pos) + if minetest.registered_nodes[node.name] + and minetest.registered_nodes[node.name].mesecons then + -- rules of node to possibly connect to + local rules = {} + if (minetest.registered_nodes[node.name].mesecon_wire) then + rules = mesecon.rules.default + else + rules = mesecon.get_any_rules(node) + end + + for _, r in ipairs(mesecon.flattenrules(rules)) do + if (vector.equals(vector.add(self_pos, r), from_pos)) then + return true + end + end + end + return false +end + +-- Update this node +local wire_updateconnect = function (pos) + local connections = {} + + for _, r in ipairs(mesecon.rules.default) do + if wire_getconnect(pos, vector.add(pos, r)) then + table.insert(connections, r) + end + end + + local nid = {} + for _, vec in ipairs(connections) do + -- flat component + if vec.x == 1 then nid[0] = "1" end + if vec.z == 1 then nid[1] = "1" end + if vec.x == -1 then nid[2] = "1" end + if vec.z == -1 then nid[3] = "1" end + + -- slopy component + if vec.y == 1 then + if vec.x == 1 then nid[4] = "1" end + if vec.z == 1 then nid[5] = "1" end + if vec.x == -1 then nid[6] = "1" end + if vec.z == -1 then nid[7] = "1" end + end + end + + local nodeid = (nid[0] or "0")..(nid[1] or "0")..(nid[2] or "0")..(nid[3] or "0") + ..(nid[4] or "0")..(nid[5] or "0")..(nid[6] or "0")..(nid[7] or "0") + + local state_suffix = string.find(minetest.get_node(pos).name, "_off") and "_off" or "_on" + minetest.set_node(pos, {name = "mesecons:wire_"..nodeid..state_suffix}) +end + +local update_on_place_dig = function (pos, node) + -- Update placed node (get_node again as it may have been dug) + local nn = minetest.get_node(pos) + if (minetest.registered_nodes[nn.name]) + and (minetest.registered_nodes[nn.name].mesecon_wire) then + wire_updateconnect(pos) + end + + -- Update nodes around it + local rules = {} + if minetest.registered_nodes[node.name] + and minetest.registered_nodes[node.name].mesecon_wire then + rules = mesecon.rules.default + else + rules = mesecon.get_any_rules(node) + end + if (not rules) then return end + + for _, r in ipairs(mesecon.flattenrules(rules)) do + local np = vector.add(pos, r) + if minetest.registered_nodes[minetest.get_node(np).name] + and minetest.registered_nodes[minetest.get_node(np).name].mesecon_wire then + wire_updateconnect(np) + end + end +end + +mesecon.register_autoconnect_hook("wire", update_on_place_dig) + +-- ############################ +-- ## Wire node registration ## +-- ############################ +-- Nodeboxes: +local box_center = {-1/16, -.5, -1/16, 1/16, -.5+1/64, 1/16} +local box_bump1 = { -2/16, -8/16, -2/16, 2/16, -.5+1/64, 2/16 } + +local nbox_nid = +{ + [0] = {1/16, -.5, -1/16, 8/16, -.5+1/64, 1/16}, -- x positive + [1] = {-1/16, -.5, 1/16, 1/16, -.5+1/64, 8/16}, -- z positive + [2] = {-8/16, -.5, -1/16, -1/16, -.5+1/64, 1/16}, -- x negative + [3] = {-1/16, -.5, -8/16, 1/16, -.5+1/64, -1/16}, -- z negative + + [4] = {.5-1/16, -.5+1/16, -1/16, .5, .4999+1/64, 1/16}, -- x positive up + [5] = {-1/16, -.5+1/16, .5-1/16, 1/16, .4999+1/64, .5}, -- z positive up + [6] = {-.5, -.5+1/16, -1/16, -.5+1/16, .4999+1/64, 1/16}, -- x negative up + [7] = {-1/16, -.5+1/16, -.5, 1/16, .4999+1/64, -.5+1/16} -- z negative up +} + +local selectionbox = +{ + type = "fixed", + fixed = {-.5, -.5, -.5, .5, -.5+1/16, .5} +} + +-- go to the next nodeid (ex.: 01000011 --> 01000100) +local nid_inc = function() end +nid_inc = function (nid) + local i = 0 + while nid[i-1] ~= 1 do + nid[i] = (nid[i] ~= 1) and 1 or 0 + i = i + 1 + end + + -- BUT: Skip impossible nodeids: + if ((nid[0] == 0 and nid[4] == 1) or (nid[1] == 0 and nid[5] == 1) + or (nid[2] == 0 and nid[6] == 1) or (nid[3] == 0 and nid[7] == 1)) then + return nid_inc(nid) + end + + return i <= 8 +end + +local function register_wires() + local nid = {} + while true do + -- Create group specifiction and nodeid string (see note above for details) + local nodeid = (nid[0] or "0")..(nid[1] or "0")..(nid[2] or "0")..(nid[3] or "0") + ..(nid[4] or "0")..(nid[5] or "0")..(nid[6] or "0")..(nid[7] or "0") + + -- Calculate nodebox + local nodebox = {type = "fixed", fixed={box_center}} + for i=0,7 do + if nid[i] == 1 then + table.insert(nodebox.fixed, nbox_nid[i]) + end + end + + -- Add bump to nodebox if curved + if (nid[0] == 1 and nid[1] == 1) or (nid[1] == 1 and nid[2] == 1) + or (nid[2] == 1 and nid[3] == 1) or (nid[3] == 1 and nid[0] == 1) then + table.insert(nodebox.fixed, box_bump1) + end + + -- If nothing to connect to, still make a nodebox of a straight wire + if nodeid == "00000000" then + nodebox.fixed = {-8/16, -.5, -1/16, 8/16, -.5+1/16, 1/16} + end + + local rules = {} + if (nid[0] == 1) then table.insert(rules, vector.new( 1, 0, 0)) end + if (nid[1] == 1) then table.insert(rules, vector.new( 0, 0, 1)) end + if (nid[2] == 1) then table.insert(rules, vector.new(-1, 0, 0)) end + if (nid[3] == 1) then table.insert(rules, vector.new( 0, 0, -1)) end + + if (nid[0] == 1) then table.insert(rules, vector.new( 1, -1, 0)) end + if (nid[1] == 1) then table.insert(rules, vector.new( 0, -1, 1)) end + if (nid[2] == 1) then table.insert(rules, vector.new(-1, -1, 0)) end + if (nid[3] == 1) then table.insert(rules, vector.new( 0, -1, -1)) end + + if (nid[4] == 1) then table.insert(rules, vector.new( 1, 1, 0)) end + if (nid[5] == 1) then table.insert(rules, vector.new( 0, 1, 1)) end + if (nid[6] == 1) then table.insert(rules, vector.new(-1, 1, 0)) end + if (nid[7] == 1) then table.insert(rules, vector.new( 0, 1, -1)) end + + local meseconspec_off = { conductor = { + rules = rules, + state = mesecon.state.off, + onstate = "mesecons:wire_"..nodeid.."_on" + }} + + local meseconspec_on = { conductor = { + rules = rules, + state = mesecon.state.on, + offstate = "mesecons:wire_"..nodeid.."_off" + }} + + local groups_on = {dig_immediate = 3, mesecon_conductor_craftable = 1, + not_in_creative_inventory = 1, attached_node = 1, dig_by_water = 1,destroy_by_lava_flow=1, dig_by_piston = 1} + local groups_off = {dig_immediate = 3, mesecon_conductor_craftable = 1, + attached_node = 1, dig_by_water = 1,destroy_by_lava_flow=1, dig_by_piston = 1} + if nodeid ~= "00000000" then + groups_off["not_in_creative_inventory"] = 1 + end + + -- Wire textures + local ratio_off = 128 + local ratio_on = 192 + local crossing_off = "(redstone_redstone_dust_dot.png^redstone_redstone_dust_line0.png^(redstone_redstone_dust_line1.png^[transformR90))^[colorize:#FF0000:"..ratio_off + local crossing_on = "(redstone_redstone_dust_dot.png^redstone_redstone_dust_line0.png^(redstone_redstone_dust_line1.png^[transformR90))^[colorize:#FF0000:"..ratio_on + local straight0_off = "redstone_redstone_dust_line0.png^[colorize:#FF0000:"..ratio_off + local straight0_on = "redstone_redstone_dust_line0.png^[colorize:#FF0000:"..ratio_on + local straight1_off = "redstone_redstone_dust_line0.png^[colorize:#FF0000:"..ratio_off + local straight1_on = "redstone_redstone_dust_line0.png^[colorize:#FF0000:"..ratio_on + local dot_off = "redstone_redstone_dust_dot.png^[colorize:#FF0000:"..ratio_off + local dot_on = "redstone_redstone_dust_dot.png^[colorize:#FF0000:"..ratio_on + + local tiles_off = { crossing_off, crossing_off, straight0_off, straight1_off, straight0_off, straight1_off } + local tiles_on = { crossing_on, crossing_on, straight0_on, straight1_on, straight0_on, straight1_on } + + if nodeid == "00000000" then + -- Non-connected redstone wire + nodebox.fixed = {-8/16, -.5, -8/16, 8/16, -.5+1/64, 8/16} + -- “Dot” texture + tiles_off = { dot_off, dot_off, "blank.png", "blank.png", "blank.png", "blank.png" } + tiles_on = { dot_on, dot_on, "blank.png", "blank.png", "blank.png", "blank.png" } + elseif adjx and adjz and (xp + zp + xm + zm > 2) then + -- Connected redstone wire (crossing or t-junction) + table.insert(nodebox, box_bump1) + tiles_off = { crossing_off, crossing_off, straight0_off, straight1_off, straight0_off, straight1_off, } + tiles_on = { crossing_on, crossing_on, straight0_on, straight1_on, straight0_on, straight1_on, } + else + -- Connected redstone wire (straight line or curve) + table.insert(nodebox, box_center) + tiles_off = { crossing_off, crossing_off, straight0_off, straight1_off, straight0_off, straight1_off, } + tiles_on = { crossing_on, crossing_on, straight0_on, straight1_on, straight0_on, straight1_on, } + end + + mesecon.register_node(":mesecons:wire_"..nodeid, { + description = "Mesecon", + drawtype = "nodebox", + inventory_image = "redstone_redstone_dust.png", + wield_image = "redstone_redstone_dust.png", + paramtype = "light", + paramtype2 = "facedir", + sunlight_propagates = true, + selection_box = selectionbox, + node_box = nodebox, + walkable = false, + drop = "mesecons:wire_00000000_off", + mesecon_wire = true + }, {tiles = tiles_off, mesecons = meseconspec_off, groups = groups_off}, + {tiles = tiles_on, mesecons = meseconspec_on, groups = groups_on}) + + if (nid_inc(nid) == false) then return end + end +end +register_wires() + +minetest.register_alias("mesecons:redstone", "mesecons:wire_00000000_off") + +minetest.register_craft({ + type = "cooking", + output = "mesecons:redstone", + recipe = "mcl_core:stone_with_redstone", + cooktime = 10, +}) + diff --git a/mods/ITEMS/REDSTONE/mesecons/textures/redstone_redstone_dust.png b/mods/ITEMS/REDSTONE/mesecons_wires/textures/redstone_redstone_dust.png similarity index 100% rename from mods/ITEMS/REDSTONE/mesecons/textures/redstone_redstone_dust.png rename to mods/ITEMS/REDSTONE/mesecons_wires/textures/redstone_redstone_dust.png diff --git a/mods/ITEMS/REDSTONE/mesecons/textures/redstone_redstone_dust_dot.png b/mods/ITEMS/REDSTONE/mesecons_wires/textures/redstone_redstone_dust_dot.png similarity index 100% rename from mods/ITEMS/REDSTONE/mesecons/textures/redstone_redstone_dust_dot.png rename to mods/ITEMS/REDSTONE/mesecons_wires/textures/redstone_redstone_dust_dot.png diff --git a/mods/ITEMS/REDSTONE/mesecons/textures/redstone_redstone_dust_line0.png b/mods/ITEMS/REDSTONE/mesecons_wires/textures/redstone_redstone_dust_line0.png similarity index 100% rename from mods/ITEMS/REDSTONE/mesecons/textures/redstone_redstone_dust_line0.png rename to mods/ITEMS/REDSTONE/mesecons_wires/textures/redstone_redstone_dust_line0.png diff --git a/mods/ITEMS/REDSTONE/mesecons/textures/redstone_redstone_dust_line1.png b/mods/ITEMS/REDSTONE/mesecons_wires/textures/redstone_redstone_dust_line1.png similarity index 100% rename from mods/ITEMS/REDSTONE/mesecons/textures/redstone_redstone_dust_line1.png rename to mods/ITEMS/REDSTONE/mesecons_wires/textures/redstone_redstone_dust_line1.png diff --git a/mods/ITEMS/REDSTONE/mesecons/wires.lua b/mods/ITEMS/REDSTONE/mesecons_wires/wires.lua similarity index 100% rename from mods/ITEMS/REDSTONE/mesecons/wires.lua rename to mods/ITEMS/REDSTONE/mesecons_wires/wires.lua diff --git a/mods/ITEMS/mcl_chests/init.lua b/mods/ITEMS/mcl_chests/init.lua index 989a82563c..4c6e6b1f1b 100644 --- a/mods/ITEMS/mcl_chests/init.lua +++ b/mods/ITEMS/mcl_chests/init.lua @@ -387,18 +387,18 @@ register_chest("trapped_chest", local meta = minetest.get_meta(pos) meta:set_int("players", 1) minetest.swap_node(pos, {name="mcl_chests:trapped_chest_on", param2 = node.param2}) - mesecon:receptor_on(pos, trapped_chest_mesecons_rules) + mesecon.receptor_on(pos, trapped_chest_mesecons_rules) end, function(pos, node, clicker) local meta = minetest.get_meta(pos) meta:set_int("players", 1) minetest.swap_node(pos, {name="mcl_chests:trapped_chest_on_left", param2 = node.param2}) - mesecon:receptor_on(pos, trapped_chest_mesecons_rules) + mesecon.receptor_on(pos, trapped_chest_mesecons_rules) local pos_other = get_chest_neighborpos(pos, node.param2, "left") minetest.swap_node(pos_other, {name="mcl_chests:trapped_chest_on_right", param2 = node.param2}) - mesecon:receptor_on(pos_other, trapped_chest_mesecons_rules) + mesecon.receptor_on(pos_other, trapped_chest_mesecons_rules) end, function(pos, node, clicker) local pos_other = get_chest_neighborpos(pos, node.param2, "right") @@ -408,10 +408,10 @@ register_chest("trapped_chest", meta:set_int("players", 1) minetest.swap_node(pos, {name="mcl_chests:trapped_chest_on_right", param2 = node.param2}) - mesecon:receptor_on(pos, trapped_chest_mesecons_rules) + mesecon.receptor_on(pos, trapped_chest_mesecons_rules) minetest.swap_node(pos_other, {name="mcl_chests:trapped_chest_on_left", param2 = node.param2}) - mesecon:receptor_on(pos_other, trapped_chest_mesecons_rules) + mesecon.receptor_on(pos_other, trapped_chest_mesecons_rules) end ) @@ -467,7 +467,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) if players <= 0 then meta:set_int("players", 0) minetest.swap_node(pos, {name="mcl_chests:trapped_chest", param2 = node.param2}) - mesecon:receptor_off(pos, trapped_chest_mesecons_rules) + mesecon.receptor_off(pos, trapped_chest_mesecons_rules) else meta:set_int("players", players) end @@ -475,11 +475,11 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) if players <= 0 then meta:set_int("players", 0) minetest.swap_node(pos, {name="mcl_chests:trapped_chest_left", param2 = node.param2}) - mesecon:receptor_off(pos, trapped_chest_mesecons_rules) + mesecon.receptor_off(pos, trapped_chest_mesecons_rules) pos_other = get_chest_neighborpos(pos, node.param2, "left") minetest.swap_node(pos_other, {name="mcl_chests:trapped_chest_right", param2 = node.param2}) - mesecon:receptor_off(pos_other, trapped_chest_mesecons_rules) + mesecon.receptor_off(pos_other, trapped_chest_mesecons_rules) else meta:set_int("players", players) end @@ -487,10 +487,10 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) if players <= 0 then meta:set_int("players", 0) minetest.swap_node(pos, {name="mcl_chests:trapped_chest_right", param2 = node.param2}) - mesecon:receptor_off(pos, trapped_chest_mesecons_rules) + mesecon.receptor_off(pos, trapped_chest_mesecons_rules) minetest.swap_node(pos_other, {name="mcl_chests:trapped_chest_left", param2 = node.param2}) - mesecon:receptor_off(pos_other, trapped_chest_mesecons_rules) + mesecon.receptor_off(pos_other, trapped_chest_mesecons_rules) else meta:set_int("players", players) end