1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
|
-- 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.
-- this is mostly for legacy reasons and SHOULD NOT BE USED IN NEW CODE.
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 pipeworks nodes use a fixed value for vertical facing nodes
-- if that is detected, just return that directly.
if param2 == 17 then
return {{x=0,y=1,z=0}, {x=0,y=-1,z=0}}
end
-- 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 _, 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
|