summaryrefslogtreecommitdiff
path: root/pipeworks/pressure_logic/flowable_node_registry_install.lua
diff options
context:
space:
mode:
Diffstat (limited to 'pipeworks/pressure_logic/flowable_node_registry_install.lua')
-rw-r--r--pipeworks/pressure_logic/flowable_node_registry_install.lua257
1 files changed, 257 insertions, 0 deletions
diff --git a/pipeworks/pressure_logic/flowable_node_registry_install.lua b/pipeworks/pressure_logic/flowable_node_registry_install.lua
new file mode 100644
index 0000000..5cf1941
--- /dev/null
+++ b/pipeworks/pressure_logic/flowable_node_registry_install.lua
@@ -0,0 +1,257 @@
+-- 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.pipe_mode == "pressure" then
+ abmregister.flowlogic(nodename)
+ end
+end
+
+local regwarning = function(kind, nodename)
+ local tail = ""
+ if pipeworks.toggles.pipe_mode ~= "pressure" 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, directionfn)
+ insertbase(nodename)
+ pipeworks.flowables.list.directional[nodename] = {
+ neighbourfn = neighbourfn,
+ directionfn = directionfn
+ }
+ regwarning("directional", nodename)
+end
+
+-- register a node as a directional flowable that can only flow through either the top or bottom side.
+-- used for fountainheads (bottom side) and pumps (top side).
+-- this is in world terms, not facedir relative!
+register.directional_vertical_fixed = function(nodename, topside)
+ local y
+ if topside then y = 1 else y = -1 end
+ local side = { x=0, y=y, z=0 }
+ local neighbourfn = function(node) return { side } end
+ local directionfn = function(node, direction)
+ return vector.equals(direction, side)
+ end
+ register.directional(nodename, neighbourfn, directionfn)
+end
+
+-- register a node as a directional flowable whose accepting sides depends upon param2 rotation.
+-- used for entry panels, valves, flow sensors and spigots,
+-- whose facing axis is always upwards and can only rotate horizontally.
+register.directional_horizonal_rotate = function(nodename, doubleended)
+ local rotations = {
+ {x= 0,y= 0,z= 1},
+ {x= 1,y= 0,z= 0},
+ {x= 0,y= 0,z=-1},
+ {x=-1,y= 0,z= 0},
+ }
+ local getends = function(node)
+ --local dname = "horizontal rotate getends() "
+ local param2 = node.param2
+ -- the sole end of the spigot points in the direction the rotation bits suggest
+ -- also note to self: lua arrays start at one...
+ local mainend = (param2 % 4) + 1
+ -- use modulus wrap-around to find other end for straight-run devices like the valve
+ local otherend = ((param2 + 2) % 4) + 1
+ local mainrot = rotations[mainend]
+ --pipeworks.logger(dname.."mainrot: "..dump(mainrot))
+ local result
+ if doubleended then
+ result = { mainrot, rotations[otherend] }
+ else
+ result = { mainrot }
+ end
+ --pipeworks.logger(dname.."result: "..dump(result))
+ return result
+ end
+ local neighbourfn = function(node)
+ return getends(node)
+ end
+ local directionfn = function(node, direction)
+ local result = false
+ for index, endvec in ipairs(getends(node)) do
+ if vector.equals(direction, endvec) then result = true end
+ end
+ return result
+ end
+ register.directional(nodename, neighbourfn, directionfn)
+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