summaryrefslogtreecommitdiff
path: root/pressure_logic
diff options
context:
space:
mode:
authorthetaepsilon-gamedev <thetaepsilon-gamedev@noreply.users.github.com>2017-10-17 14:20:55 +0100
committerthetaepsilon-gamedev <thetaepsilon-gamedev@noreply.users.github.com>2017-10-17 14:20:55 +0100
commitaacd5ec829e531c808881021d5fe36aeedcfc2fd (patch)
tree25cf4bdc076f1bff2e9ad9be6e6a123c06982427 /pressure_logic
parent7bacbdf0150d4aee1244ec2ad7076ccd2da7956d (diff)
downloadpipeworks-aacd5ec829e531c808881021d5fe36aeedcfc2fd.tar
pipeworks-aacd5ec829e531c808881021d5fe36aeedcfc2fd.tar.gz
pipeworks-aacd5ec829e531c808881021d5fe36aeedcfc2fd.tar.bz2
pipeworks-aacd5ec829e531c808881021d5fe36aeedcfc2fd.tar.xz
pipeworks-aacd5ec829e531c808881021d5fe36aeedcfc2fd.zip
rename new_flow_logic subdirectory to a less ambiguous name
The "new flow logic" name was supposed to indicate that it was a continuation of the old branch by the same name, but it is beginning to become clear that it's not "new" any more and it might lead to confusion with "classic mode" flow logic while that still co-exists. Explicitly name the subdirectory "pressure logic" to give a better idea of what goes in it, init.lua edited accordingly.
Diffstat (limited to 'pressure_logic')
-rw-r--r--pressure_logic/abm_register.lua27
-rw-r--r--pressure_logic/abms.lua342
-rw-r--r--pressure_logic/flowable_node_registry.lua56
-rw-r--r--pressure_logic/flowable_node_registry_install.lua198
4 files changed, 623 insertions, 0 deletions
diff --git a/pressure_logic/abm_register.lua b/pressure_logic/abm_register.lua
new file mode 100644
index 0000000..a8e3abc
--- /dev/null
+++ b/pressure_logic/abm_register.lua
@@ -0,0 +1,27 @@
+-- register new flow logic ABMs
+-- written 2017 by thetaepsilon
+
+local register = {}
+pipeworks.flowlogic.abmregister = register
+
+local flowlogic = pipeworks.flowlogic
+
+-- register node list for the main logic function.
+-- see flowlogic.run() in abms.lua.
+
+local register_flowlogic_abm = function(nodename)
+ if pipeworks.toggles.pressure_logic then
+ minetest.register_abm({
+ label = "pipeworks new_flow_logic run",
+ nodenames = { nodename },
+ interval = 1,
+ chance = 1,
+ action = function(pos, node, active_object_count, active_object_count_wider)
+ flowlogic.run(pos, node)
+ end
+ })
+ else
+ minetest.log("warning", "pipeworks pressure_logic not enabled but register.flowlogic() requested")
+ end
+end
+register.flowlogic = register_flowlogic_abm
diff --git a/pressure_logic/abms.lua b/pressure_logic/abms.lua
new file mode 100644
index 0000000..c14c124
--- /dev/null
+++ b/pressure_logic/abms.lua
@@ -0,0 +1,342 @@
+-- reimplementation of new_flow_logic branch: processing functions
+-- written 2017 by thetaepsilon
+
+
+
+local flowlogic = {}
+flowlogic.helpers = {}
+pipeworks.flowlogic = flowlogic
+
+
+
+-- borrowed from above: might be useable to replace the above coords tables
+local make_coords_offsets = function(pos, include_base)
+ local coords = {
+ {x=pos.x,y=pos.y-1,z=pos.z},
+ {x=pos.x,y=pos.y+1,z=pos.z},
+ {x=pos.x-1,y=pos.y,z=pos.z},
+ {x=pos.x+1,y=pos.y,z=pos.z},
+ {x=pos.x,y=pos.y,z=pos.z-1},
+ {x=pos.x,y=pos.y,z=pos.z+1},
+ }
+ if include_base then table.insert(coords, pos) end
+ return coords
+end
+
+-- create positions from list of offsets
+-- see in use of directional flow logic below
+local apply_coords_offsets = function(pos, offsets)
+ local result = {}
+ for index, offset in ipairs(offsets) do
+ table.insert(result, vector.add(pos, offset))
+ end
+ return result
+end
+
+
+
+-- local debuglog = function(msg) print("## "..msg) end
+
+
+
+local formatvec = function(vec) local sep="," return "("..tostring(vec.x)..sep..tostring(vec.y)..sep..tostring(vec.z)..")" end
+
+-- new version of liquid check
+-- accepts a limit parameter to only delete water blocks that the receptacle can accept,
+-- and returns it so that the receptacle can update it's pressure values.
+local check_for_liquids_v2 = function(pos, limit)
+ local coords = make_coords_offsets(pos, false)
+ local total = 0
+ for index, tpos in ipairs(coords) do
+ if total >= limit then break end
+ local name = minetest.get_node(tpos).name
+ if name == "default:water_source" then
+ minetest.remove_node(tpos)
+ total = total + 1
+ end
+ end
+ --pipeworks.logger("check_for_liquids_v2@"..formatvec(pos).." total "..total)
+ return total
+end
+flowlogic.check_for_liquids_v2 = check_for_liquids_v2
+
+
+
+local label_pressure = "pipeworks.water_pressure"
+local get_pressure_access = function(pos)
+ local metaref = minetest.get_meta(pos)
+ return {
+ get = function()
+ return metaref:get_float(label_pressure)
+ end,
+ set = function(v)
+ metaref:set_float(label_pressure, v)
+ end
+ }
+end
+
+
+-- logging is unreliable when something is crashing...
+local nilexplode = function(caller, label, value)
+ if value == nil then
+ error(caller..": "..label.." was nil")
+ end
+end
+
+
+
+local finitemode = pipeworks.toggles.finite_water
+flowlogic.run = function(pos, node)
+ local nodename = node.name
+ -- get the current pressure value.
+ local nodepressure = get_pressure_access(pos)
+ local currentpressure = nodepressure.get()
+ local oldpressure = currentpressure
+
+ -- if node is an input: run intake phase
+ local inputdef = pipeworks.flowables.inputs.list[nodename]
+ if inputdef then
+ currentpressure = flowlogic.run_input(pos, node, currentpressure, inputdef)
+ --debuglog("post-intake currentpressure is "..currentpressure)
+ --nilexplode("run()", "currentpressure", currentpressure)
+ end
+
+ -- balance pressure with neighbours
+ currentpressure = flowlogic.balance_pressure(pos, node, currentpressure)
+
+ -- if node is an output: run output phase
+ local outputdef = pipeworks.flowables.outputs.list[nodename]
+ if outputdef then
+ currentpressure = flowlogic.run_output(
+ pos,
+ node,
+ currentpressure,
+ oldpressure,
+ outputdef,
+ finitemode)
+ end
+
+ -- if node has pressure transitions: determine new node
+ if pipeworks.flowables.transitions.list[nodename] then
+ local newnode = flowlogic.run_transition(node, currentpressure)
+ --pipeworks.logger("flowlogic.run()@"..formatvec(pos).." transition, new node name = "..dump(newnode).." pressure "..tostring(currentpressure))
+ minetest.swap_node(pos, newnode)
+ flowlogic.run_transition_post(pos, newnode)
+ end
+
+ -- set the new pressure
+ nodepressure.set(currentpressure)
+end
+
+
+
+flowlogic.balance_pressure = function(pos, node, currentpressure)
+ -- local dname = "flowlogic.balance_pressure()@"..formatvec(pos).." "
+ -- check the pressure of all nearby flowable nodes, and average it out.
+
+ -- pressure handles to average over
+ local connections = {}
+ -- unconditionally include self in nodes to average over.
+ -- result of averaging will be returned as new pressure for main flow logic callback
+ local totalv = currentpressure
+ local totalc = 1
+
+ -- get list of node neighbours.
+ -- if this node is directional and only flows on certain sides,
+ -- invoke the callback to retrieve the set.
+ -- for simple flowables this is just an auto-gen'd list of all six possible neighbours.
+ local candidates = {}
+ if pipeworks.flowables.list.simple[node.name] then
+ candidates = make_coords_offsets(pos, false)
+ else
+ -- directional flowables: call the callback to get the list
+ local directional = pipeworks.flowables.list.directional[node.name]
+ if directional then
+ --pipeworks.logger(dname.."invoking neighbourfn")
+ local offsets = directional.neighbourfn(node)
+ candidates = apply_coords_offsets(pos, offsets)
+ end
+ end
+
+ -- then handle neighbours, but if not a pressure node don't consider them at all
+ for _, npos in ipairs(candidates) do
+ local nodename = minetest.get_node(npos).name
+ -- for now, just check if it's in the simple table.
+ -- TODO: the "can flow from" logic in flowable_node_registry.lua
+ local haspressure = (pipeworks.flowables.list.simple[nodename])
+ if haspressure then
+ local neighbour = get_pressure_access(npos)
+ --pipeworks.logger("balance_pressure @ "..formatvec(pos).." "..nodename.." "..formatvec(npos).." added to neighbour set")
+ local n = neighbour.get()
+ table.insert(connections, neighbour)
+ totalv = totalv + n
+ totalc = totalc + 1
+ end
+ end
+
+ local average = totalv / totalc
+ for _, target in ipairs(connections) do
+ target.set(average)
+ end
+
+ return average
+end
+
+
+
+flowlogic.run_input = function(pos, node, currentpressure, inputdef)
+ -- intakefn allows a given input node to define it's own intake logic.
+ -- this function will calculate the maximum amount of water that can be taken in;
+ -- the intakefn will be given this and is expected to return the actual absorption amount.
+
+ local maxpressure = inputdef.maxpressure
+ local intake_limit = maxpressure - currentpressure
+ if intake_limit <= 0 then return currentpressure end
+
+ local actual_intake = inputdef.intakefn(pos, intake_limit)
+ --pipeworks.logger("run_input@"..formatvec(pos).." oldpressure "..currentpressure.." intake_limit "..intake_limit.." actual_intake "..actual_intake)
+ if actual_intake <= 0 then return currentpressure end
+
+ local newpressure = actual_intake + currentpressure
+ --debuglog("run_input() end, oldpressure "..currentpressure.." intake_limit "..intake_limit.." actual_intake "..actual_intake.." newpressure "..newpressure)
+ return newpressure
+end
+
+
+
+-- flowlogic output helper implementation:
+-- outputs water by trying to place water nodes nearby in the world.
+-- neighbours is a list of node offsets to try placing water in.
+-- this is a constructor function, returning another function which satisfies the output helper requirements.
+-- note that this does *not* take rotation into account.
+flowlogic.helpers.make_neighbour_output_fixed = function(neighbours)
+ return function(pos, node, currentpressure, finitemode)
+ local taken = 0
+ for _, offset in pairs(neighbours) do
+ local npos = vector.add(pos, offset)
+ local name = minetest.get_node(npos).name
+ if currentpressure < 1 then break end
+ -- take pressure anyway in non-finite mode, even if node is water source already.
+ -- in non-finite mode, pressure has to be sustained to keep the sources there.
+ -- so in non-finite mode, placing water is dependent on the target node;
+ -- draining pressure is not.
+ local canplace = (name == "air") or (name == "default:water_flowing")
+ if canplace then
+ minetest.swap_node(npos, {name="default:water_source"})
+ end
+ if (not finitemode) or canplace then
+ taken = taken + 1
+ currentpressure = currentpressure - 1
+ end
+ end
+ return taken
+ end
+end
+
+-- complementary function to the above when using non-finite mode:
+-- removes water sources from neighbor positions when the output is "off" due to lack of pressure.
+flowlogic.helpers.make_neighbour_cleanup_fixed = function(neighbours)
+ return function(pos, node, currentpressure)
+ --pipeworks.logger("neighbour_cleanup_fixed@"..formatvec(pos))
+ for _, offset in pairs(neighbours) do
+ local npos = vector.add(pos, offset)
+ local name = minetest.get_node(npos).name
+ if (name == "default:water_source") then
+ --pipeworks.logger("neighbour_cleanup_fixed removing "..formatvec(npos))
+ minetest.remove_node(npos)
+ end
+ end
+ end
+end
+
+
+
+flowlogic.run_output = function(pos, node, currentpressure, oldpressure, outputdef, finitemode)
+ -- processing step for water output devices.
+ -- takes care of checking a minimum pressure value and updating the resulting pressure level
+ -- the outputfn is provided the current pressure and returns the pressure "taken".
+ -- as an example, using this with the above spigot function,
+ -- the spigot function tries to output a water source if it will fit in the world.
+ --pipeworks.logger("flowlogic.run_output() pos "..formatvec(pos).." old -> currentpressure "..tostring(oldpressure).." "..tostring(currentpressure).." finitemode "..tostring(finitemode))
+ local upper = outputdef.upper
+ local lower = outputdef.lower
+ local result = currentpressure
+ local threshold = nil
+ if finitemode then threshold = lower else threshold = upper end
+ if currentpressure > threshold then
+ local takenpressure = outputdef.outputfn(pos, node, currentpressure, finitemode)
+ local newpressure = currentpressure - takenpressure
+ if newpressure < 0 then newpressure = 0 end
+ result = newpressure
+ end
+ if (not finitemode) and (currentpressure < lower) and (oldpressure < lower) then
+ --pipeworks.logger("flowlogic.run_output() invoking cleanup currentpressure="..tostring(currentpressure))
+ outputdef.cleanupfn(pos, node, currentpressure)
+ end
+ return result
+end
+
+
+
+-- determine which node to switch to based on current pressure
+flowlogic.run_transition = function(node, currentpressure)
+ local simplesetdef = pipeworks.flowables.transitions.simple[node.name]
+ local result = node
+ local found = false
+
+ -- simple transition sets: assumes all nodes in the set share param values.
+ if simplesetdef then
+ -- assumes that the set has been checked to contain at least one element...
+ local nodename_prev = simplesetdef[1].nodename
+ local result_nodename = node.name
+
+ for index, element in ipairs(simplesetdef) do
+ -- find the highest element that is below the current pressure.
+ local threshold = element.threshold
+ if threshold > currentpressure then
+ result_nodename = nodename_prev
+ found = true
+ break
+ end
+ nodename_prev = element.nodename
+ end
+
+ -- use last element if no threshold is greater than current pressure
+ if not found then
+ result_nodename = nodename_prev
+ found = true
+ end
+
+ -- preserve param1/param2 values
+ result = { name=result_nodename, param1=node.param1, param2=node.param2 }
+ end
+
+ if not found then
+ pipeworks.logger("flowlogic.run_transition() BUG no transition definitions found! nodename="..nodename.." currentpressure="..tostring(currentpressure))
+ end
+
+ return result
+end
+
+-- post-update hook for run_transition
+-- among other things, updates mesecons if present.
+-- node here means the new node, returned from run_transition() above
+flowlogic.run_transition_post = function(pos, node)
+ local mesecons_def = minetest.registered_nodes[node.name].mesecons
+ local mesecons_rules = pipeworks.flowables.transitions.mesecons[node.name]
+ if minetest.global_exists("mesecon") and (mesecons_def ~= nil) and mesecons_rules then
+ if type(mesecons_def) ~= "table" then
+ pipeworks.logger("flowlogic.run_transition_post() BUG mesecons def for "..node.name.."not a table: got "..tostring(mesecons_def))
+ else
+ local receptor = mesecons_def.receptor
+ if receptor then
+ local state = receptor.state
+ if state == mesecon.state.on then
+ mesecon.receptor_on(pos, mesecons_rules)
+ elseif state == mesecon.state.off then
+ mesecon.receptor_off(pos, mesecons_rules)
+ end
+ end
+ end
+ end
+end
diff --git a/pressure_logic/flowable_node_registry.lua b/pressure_logic/flowable_node_registry.lua
new file mode 100644
index 0000000..c60a39e
--- /dev/null
+++ b/pressure_logic/flowable_node_registry.lua
@@ -0,0 +1,56 @@
+-- registry of flowable node behaviours in new flow logic
+-- written 2017 by thetaepsilon
+
+-- the actual registration functions which edit these tables can be found in flowable_node_registry_install.lua
+-- this is because the ABM code needs to inspect these tables,
+-- but the registration code needs to reference said ABM code.
+-- so those functions were split out to resolve a circular dependency.
+
+
+
+pipeworks.flowables = {}
+pipeworks.flowables.list = {}
+pipeworks.flowables.list.all = {}
+-- pipeworks.flowables.list.nodenames = {}
+
+-- simple flowables - balance pressure in any direction
+pipeworks.flowables.list.simple = {}
+pipeworks.flowables.list.simple_nodenames = {}
+
+-- directional flowables - can only flow on certain sides
+-- format per entry is a table with the following fields:
+-- neighbourfn: function(node),
+-- called to determine which nodes to consider as neighbours.
+-- can be used to e.g. inspect the node's param values for facedir etc.
+-- returns: array of vector offsets to look for possible neighbours in
+pipeworks.flowables.list.directional = {}
+
+-- simple intakes - try to absorb any adjacent water nodes
+pipeworks.flowables.inputs = {}
+pipeworks.flowables.inputs.list = {}
+pipeworks.flowables.inputs.nodenames = {}
+
+-- outputs - takes pressure from pipes and update world to do something with it
+pipeworks.flowables.outputs = {}
+pipeworks.flowables.outputs.list = {}
+-- not currently any nodenames arraylist for this one as it's not currently needed.
+
+-- nodes with registered node transitions
+-- nodes will be switched depending on pressure level
+pipeworks.flowables.transitions = {}
+pipeworks.flowables.transitions.list = {} -- master list
+pipeworks.flowables.transitions.simple = {} -- nodes that change based purely on pressure
+pipeworks.flowables.transitions.mesecons = {} -- table of mesecons rules to apply on transition
+
+
+
+-- checks if a given node can flow in a given direction.
+-- used to implement directional devices such as pumps,
+-- which only visually connect in a certain direction.
+-- node is the usual name + param structure.
+-- direction is an x/y/z vector of the flow direction;
+-- this function answers the question "can this node flow in this direction?"
+pipeworks.flowables.flow_check = function(node, direction)
+ minetest.log("warning", "pipeworks.flowables.flow_check() stub!")
+ return true
+end
diff --git a/pressure_logic/flowable_node_registry_install.lua b/pressure_logic/flowable_node_registry_install.lua
new file mode 100644
index 0000000..a49c31a
--- /dev/null
+++ b/pressure_logic/flowable_node_registry_install.lua
@@ -0,0 +1,198 @@
+-- flowable node registry: add entries and install ABMs if new flow logic is enabled
+-- written 2017 by thetaepsilon
+
+
+
+-- use for hooking up ABMs as nodes are registered
+local abmregister = pipeworks.flowlogic.abmregister
+
+-- registration functions
+pipeworks.flowables.register = {}
+local register = pipeworks.flowables.register
+
+-- some sanity checking for passed args, as this could potentially be made an external API eventually
+local checkexists = function(nodename)
+ if type(nodename) ~= "string" then error("pipeworks.flowables nodename must be a string!") end
+ return pipeworks.flowables.list.all[nodename]
+end
+
+local insertbase = function(nodename)
+ if checkexists(nodename) then error("pipeworks.flowables duplicate registration!") end
+ pipeworks.flowables.list.all[nodename] = true
+ -- table.insert(pipeworks.flowables.list.nodenames, nodename)
+ if pipeworks.toggles.pressure_logic then
+ abmregister.flowlogic(nodename)
+ end
+end
+
+local regwarning = function(kind, nodename)
+ local tail = ""
+ if not pipeworks.toggles.pressure_logic then tail = " but pressure logic not enabled" end
+ --pipeworks.logger(kind.." flow logic registry requested for "..nodename..tail)
+end
+
+-- Register a node as a simple flowable.
+-- Simple flowable nodes have no considerations for direction of flow;
+-- A cluster of adjacent simple flowables will happily average out in any direction.
+register.simple = function(nodename)
+ insertbase(nodename)
+ pipeworks.flowables.list.simple[nodename] = true
+ table.insert(pipeworks.flowables.list.simple_nodenames, nodename)
+ regwarning("simple", nodename)
+end
+
+-- Register a node as a directional flowable:
+-- has a helper function which determines which nodes to consider valid neighbours.
+register.directional = function(nodename, neighbourfn)
+ insertbase(nodename)
+ pipeworks.flowables.list.directional[nodename] = { neighbourfn = neighbourfn }
+ regwarning("directional", nodename)
+end
+
+
+
+local checkbase = function(nodename)
+ if not checkexists(nodename) then error("pipeworks.flowables node doesn't exist as a flowable!") end
+end
+
+local duplicateerr = function(kind, nodename) error(kind.." duplicate registration for "..nodename) end
+
+
+
+-- Registers a node as a fluid intake.
+-- intakefn is used to determine the water that can be taken in a node-specific way.
+-- Expects node to be registered as a flowable (is present in flowables.list.all),
+-- so that water can move out of it.
+-- maxpressure is the maximum pipeline pressure that this node can drive;
+-- if the input's node exceeds this the callback is not run.
+-- possible WISHME here: technic-driven high-pressure pumps
+register.intake = function(nodename, maxpressure, intakefn)
+ -- check for duplicate registration of this node
+ local list = pipeworks.flowables.inputs.list
+ checkbase(nodename)
+ if list[nodename] then duplicateerr("pipeworks.flowables.inputs", nodename) end
+ list[nodename] = { maxpressure=maxpressure, intakefn=intakefn }
+ regwarning("intake", nodename)
+end
+
+
+
+-- Register a node as a simple intake:
+-- tries to absorb water source nodes from it's surroundings.
+-- may exceed limit slightly due to needing to absorb whole nodes.
+register.intake_simple = function(nodename, maxpressure)
+ register.intake(nodename, maxpressure, pipeworks.flowlogic.check_for_liquids_v2)
+end
+
+
+
+-- Register a node as an output.
+-- Expects node to already be a flowable.
+-- upper and lower thresholds have different meanings depending on whether finite liquid mode is in effect.
+-- if not (the default unless auto-detected),
+-- nodes above their upper threshold have their outputfn invoked (and pressure deducted),
+-- nodes between upper and lower are left idle,
+-- and nodes below lower have their cleanup fn invoked (to say remove water sources).
+-- the upper and lower difference acts as a hysteresis to try and avoid "gaps" in the flow.
+-- if finite mode is on, upper is ignored and lower is used to determine whether to run outputfn;
+-- cleanupfn is ignored in this mode as finite mode assumes something causes water to move itself.
+register.output = function(nodename, upper, lower, outputfn, cleanupfn)
+ if pipeworks.flowables.outputs.list[nodename] then
+ error("pipeworks.flowables.outputs duplicate registration!")
+ end
+ checkbase(nodename)
+ pipeworks.flowables.outputs.list[nodename] = {
+ upper=upper,
+ lower=lower,
+ outputfn=outputfn,
+ cleanupfn=cleanupfn,
+ }
+ -- output ABM now part of main flow logic ABM to preserve ordering.
+ -- note that because outputs have to be a flowable first
+ -- (and the installation of the flow logic ABM is conditional),
+ -- registered output nodes for new_flow_logic is also still conditional on the enable flag.
+ regwarning("output node", nodename)
+end
+
+-- register a simple output:
+-- drains pressure by attempting to place water in nearby nodes,
+-- which can be set by passing a list of offset vectors.
+-- will attempt to drain as many whole nodes as there are positions in the offset list.
+-- for meanings of upper and lower, see register.output() above.
+-- non-finite mode:
+-- above upper pressure: places water sources as appropriate, keeps draining pressure.
+-- below lower presssure: removes it's neighbour water sources.
+-- finite mode:
+-- same as for above pressure in non-finite mode,
+-- but only drains pressure when water source nodes are actually placed.
+register.output_simple = function(nodename, upper, lower, neighbours)
+ local outputfn = pipeworks.flowlogic.helpers.make_neighbour_output_fixed(neighbours)
+ local cleanupfn = pipeworks.flowlogic.helpers.make_neighbour_cleanup_fixed(neighbours)
+ register.output(nodename, upper, lower, outputfn, cleanupfn)
+end
+
+
+
+-- common base checking for transition nodes
+-- ensures the node has only been registered once as a transition.
+local transition_list = pipeworks.flowables.transitions.list
+local insert_transition_base = function(nodename)
+ checkbase(nodename)
+ if transition_list[nodename] then duplicateerr("base transition", nodename) end
+ transition_list[nodename] = true
+end
+
+
+
+-- register a simple transition set.
+-- expects a table with nodenames as keys and threshold pressures as values.
+-- internally, the table is sorted by value, and when one of these nodes needs to transition,
+-- the table is searched starting from the lowest (even if it's value is non-zero),
+-- until a value is found which is higher than or equal to the current node pressure.
+-- ex. nodeset = { ["mod:level_0"] = 0, ["mod:level_1"] = 1, --[[ ... ]] }
+local simpleseterror = function(msg)
+ error("register.transition_simple_set(): "..msg)
+end
+local simple_transitions = pipeworks.flowables.transitions.simple
+
+register.transition_simple_set = function(nodeset, extras)
+ local set = {}
+ if extras == nil then extras = {} end
+
+ local length = #nodeset
+ if length < 2 then simpleseterror("nodeset needs at least two elements!") end
+ for index, element in ipairs(nodeset) do
+ if type(element) ~= "table" then simpleseterror("element "..tostring(index).." in nodeset was not table!") end
+ local nodename = element[1]
+ local value = element[2]
+ if type(nodename) ~= "string" then simpleseterror("nodename "..tostring(nodename).."was not a string!") end
+ if type(value) ~= "number" then simpleseterror("pressure value "..tostring(value).."was not a number!") end
+ insert_transition_base(nodename)
+ if simple_transitions[nodename] then duplicateerr("simple transition set", nodename) end
+ -- assigning set to table is done separately below
+
+ table.insert(set, { nodename=nodename, threshold=value })
+ end
+
+ -- sort pressure values, smallest first
+ local smallest_first = function(a, b)
+ return a.threshold < b.threshold
+ end
+ table.sort(set, smallest_first)
+
+ -- individual registration of each node, all sharing this set,
+ -- so each node in the set will transition to the correct target node.
+ for _, element in ipairs(set) do
+ --pipeworks.logger("register.transition_simple_set() after sort: nodename "..element.nodename.." value "..tostring(element.threshold))
+ simple_transitions[element.nodename] = set
+ end
+
+ -- handle extra options
+ -- if mesecons rules table was passed, set for each node
+ if extras.mesecons then
+ local mesecons_rules = pipeworks.flowables.transitions.mesecons
+ for _, element in ipairs(set) do
+ mesecons_rules[element.nodename] = extras.mesecons
+ end
+ end
+end