pipeworks/pressure_logic/flowable_node_registry_install.lua
thetaepsilon-gamedev aacd5ec829 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.
2017-10-17 14:20:55 +01:00

199 lines
8.1 KiB
Lua

-- 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