forked from mtcontrib/pipeworks
Merge pull request #204 from thetaepsilon-gamedev/master
More pressure_logic work
This commit is contained in:
commit
a028aef9c9
9
autodetect-finite-water.lua
Normal file
9
autodetect-finite-water.lua
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
-- enable finite liquid in the presence of dynamic liquid to preserve water volume.
|
||||||
|
local enable = false
|
||||||
|
|
||||||
|
if minetest.get_modpath("dynamic_liquid") then
|
||||||
|
pipeworks.logger("detected mod dynamic_liquid, enabling finite liquid flag")
|
||||||
|
enable = true
|
||||||
|
end
|
||||||
|
|
||||||
|
pipeworks.toggles.finite_water = enable
|
@ -1,6 +1,38 @@
|
|||||||
Changelog
|
Changelog
|
||||||
---------
|
---------
|
||||||
|
|
||||||
|
2017-10-08 (thetaepsilon)
|
||||||
|
A lot more of the new flow logic work.
|
||||||
|
There are two sub-modes of this now, non-finite and finite mode.
|
||||||
|
Non-finite mode most closely resembles "classic mode", whereas finite mode is more intended for use with mods such as dynamic_liquids which enable water sources to move themselves.
|
||||||
|
Everything that was functional in classic mode more or less works correctly now.
|
||||||
|
Still TODO:
|
||||||
|
+ Flow directionality - things like flow sensors and airtight panels will flow in directions that don't make sense from their visuals.
|
||||||
|
Possible feature requests:
|
||||||
|
+ Making tanks and gratings do something useful.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
2017-09-27 (thetaepsilon)
|
||||||
|
Start of new flow logic re-implementation.
|
||||||
|
This mode is current *very* incomplete, and requires a per-world setting to enable.
|
||||||
|
Adds a pressure value stored in all pipe node metadata, and a mechanism to balance it out with nearby nodes on ABM trigger.
|
||||||
|
Currently, this inhibits the old behaviour when enabled, and (again WHEN ENABLED) breaks pretty much everything but normal pipes, spigots and pumps.
|
||||||
|
For this reason it is far from being intended as the default for some time to come yet.
|
||||||
|
What *does* work:
|
||||||
|
+ Pumps will try to take in water (and removes it!) as long as internal pressure does not exceed a threshold.
|
||||||
|
- a TODO is to make this pressure threshold configurable.
|
||||||
|
+ Pipes will balance this pressure between themselves, and will slowly average out over time.
|
||||||
|
+ Spigots will try to make the node beneath them a water source if the pressure is great enough and the existing node is flowing water or air.
|
||||||
|
- This is admittedly of fairly limited use with default water mechanics; those looking for more realistic mechanics might want to look at the dynamic_liquid mod, though that mod comes with it's own caveats (most notably drastic changes to previous worlds...).
|
||||||
|
What *does not* work:
|
||||||
|
+ Flow sensors, valves. Valves in particular currently do not function as a barrier to water's path under the experimental logic.
|
||||||
|
- TODO: internal code to allow this to be overriden.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
*seems this hasn't been updated in a while*
|
||||||
|
|
||||||
2013-01-13: Tubes can transport items now! Namely, I added Novatux/Nore's item
|
2013-01-13: Tubes can transport items now! Namely, I added Novatux/Nore's item
|
||||||
transport mod as a default part of this mod, to make tubes do something useful!
|
transport mod as a default part of this mod, to make tubes do something useful!
|
||||||
Thanks to Nore and RealBadAngel for the code contributions!
|
Thanks to Nore and RealBadAngel for the code contributions!
|
||||||
|
@ -27,8 +27,6 @@ local settings = {
|
|||||||
drop_on_routing_fail = false,
|
drop_on_routing_fail = false,
|
||||||
|
|
||||||
delete_item_on_clearobject = true,
|
delete_item_on_clearobject = true,
|
||||||
|
|
||||||
enable_new_flow_logic = false,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for name, value in pairs(settings) do
|
for name, value in pairs(settings) do
|
||||||
|
88
devices.lua
88
devices.lua
@ -1,3 +1,4 @@
|
|||||||
|
local new_flow_logic_register = pipeworks.flowables.register
|
||||||
|
|
||||||
-- rotation handlers
|
-- rotation handlers
|
||||||
|
|
||||||
@ -129,7 +130,8 @@ for s in ipairs(states) do
|
|||||||
dgroups = {snappy=3, pipe=1, not_in_creative_inventory=1}
|
dgroups = {snappy=3, pipe=1, not_in_creative_inventory=1}
|
||||||
end
|
end
|
||||||
|
|
||||||
minetest.register_node("pipeworks:pump_"..states[s], {
|
local pumpname = "pipeworks:pump_"..states[s]
|
||||||
|
minetest.register_node(pumpname, {
|
||||||
description = "Pump/Intake Module",
|
description = "Pump/Intake Module",
|
||||||
drawtype = "mesh",
|
drawtype = "mesh",
|
||||||
mesh = "pipeworks_pump.obj",
|
mesh = "pipeworks_pump.obj",
|
||||||
@ -162,8 +164,15 @@ for s in ipairs(states) do
|
|||||||
-- FIXME - does this preserve metadata? need to look at this
|
-- FIXME - does this preserve metadata? need to look at this
|
||||||
on_rotate = screwdriver.rotate_simple
|
on_rotate = screwdriver.rotate_simple
|
||||||
})
|
})
|
||||||
|
-- FIXME: currently a simple flow device, but needs directionality checking
|
||||||
|
new_flow_logic_register.simple(pumpname)
|
||||||
|
local pump_drive = 4
|
||||||
|
if states[s] ~= "off" then
|
||||||
|
new_flow_logic_register.intake_simple(pumpname, pump_drive)
|
||||||
|
end
|
||||||
|
|
||||||
minetest.register_node("pipeworks:valve_"..states[s].."_empty", {
|
local nodename_valve_empty = "pipeworks:valve_"..states[s].."_empty"
|
||||||
|
minetest.register_node(nodename_valve_empty, {
|
||||||
description = "Valve",
|
description = "Valve",
|
||||||
drawtype = "mesh",
|
drawtype = "mesh",
|
||||||
mesh = "pipeworks_valve_"..states[s]..".obj",
|
mesh = "pipeworks_valve_"..states[s]..".obj",
|
||||||
@ -201,9 +210,16 @@ for s in ipairs(states) do
|
|||||||
end,
|
end,
|
||||||
on_rotate = pipeworks.fix_after_rotation
|
on_rotate = pipeworks.fix_after_rotation
|
||||||
})
|
})
|
||||||
|
-- only register flow logic for the "on" ABM.
|
||||||
|
-- this means that the off state automatically blocks flow by not participating in the balancing operation.
|
||||||
|
if states[s] ~= "off" then
|
||||||
|
-- FIXME: this still a simple device, directionality not honoured
|
||||||
|
new_flow_logic_register.simple(nodename_valve_empty)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
minetest.register_node("pipeworks:valve_on_loaded", {
|
local nodename_valve_loaded = "pipeworks:valve_on_loaded"
|
||||||
|
minetest.register_node(nodename_valve_loaded, {
|
||||||
description = "Valve",
|
description = "Valve",
|
||||||
drawtype = "mesh",
|
drawtype = "mesh",
|
||||||
mesh = "pipeworks_valve_on.obj",
|
mesh = "pipeworks_valve_on.obj",
|
||||||
@ -241,9 +257,16 @@ minetest.register_node("pipeworks:valve_on_loaded", {
|
|||||||
end,
|
end,
|
||||||
on_rotate = pipeworks.fix_after_rotation
|
on_rotate = pipeworks.fix_after_rotation
|
||||||
})
|
})
|
||||||
|
-- register this the same as the on-but-empty variant, so existing nodes of this type work also.
|
||||||
|
-- note that as new_flow_logic code does not distinguish empty/full in node states,
|
||||||
|
-- right-clicking a "loaded" valve (becoming an off valve) then turning it on again will yield a on-but-empty valve,
|
||||||
|
-- but the flow logic will still function.
|
||||||
|
-- thus under new_flow_logic this serves as a kind of migration.
|
||||||
|
new_flow_logic_register.simple(nodename_valve_loaded)
|
||||||
|
|
||||||
-- grating
|
-- grating
|
||||||
|
|
||||||
|
-- FIXME: should this do anything useful in the new flow logic?
|
||||||
minetest.register_node("pipeworks:grating", {
|
minetest.register_node("pipeworks:grating", {
|
||||||
description = "Decorative grating",
|
description = "Decorative grating",
|
||||||
tiles = {
|
tiles = {
|
||||||
@ -276,7 +299,8 @@ minetest.register_node("pipeworks:grating", {
|
|||||||
|
|
||||||
-- outlet spigot
|
-- outlet spigot
|
||||||
|
|
||||||
minetest.register_node("pipeworks:spigot", {
|
local nodename_spigot_empty = "pipeworks:spigot"
|
||||||
|
minetest.register_node(nodename_spigot_empty, {
|
||||||
description = "Spigot outlet",
|
description = "Spigot outlet",
|
||||||
drawtype = "mesh",
|
drawtype = "mesh",
|
||||||
mesh = "pipeworks_spigot.obj",
|
mesh = "pipeworks_spigot.obj",
|
||||||
@ -306,7 +330,8 @@ minetest.register_node("pipeworks:spigot", {
|
|||||||
on_rotate = pipeworks.fix_after_rotation
|
on_rotate = pipeworks.fix_after_rotation
|
||||||
})
|
})
|
||||||
|
|
||||||
minetest.register_node("pipeworks:spigot_pouring", {
|
local nodename_spigot_loaded = "pipeworks:spigot_pouring"
|
||||||
|
minetest.register_node(nodename_spigot_loaded, {
|
||||||
description = "Spigot outlet",
|
description = "Spigot outlet",
|
||||||
drawtype = "mesh",
|
drawtype = "mesh",
|
||||||
mesh = "pipeworks_spigot_pouring.obj",
|
mesh = "pipeworks_spigot_pouring.obj",
|
||||||
@ -348,6 +373,17 @@ minetest.register_node("pipeworks:spigot_pouring", {
|
|||||||
drop = "pipeworks:spigot",
|
drop = "pipeworks:spigot",
|
||||||
on_rotate = pipeworks.fix_after_rotation
|
on_rotate = pipeworks.fix_after_rotation
|
||||||
})
|
})
|
||||||
|
-- new flow logic does not currently distinguish between these two visual states.
|
||||||
|
-- register both so existing flowing spigots continue to work (even if the visual doesn't match the spigot's behaviour).
|
||||||
|
new_flow_logic_register.simple(nodename_spigot_empty)
|
||||||
|
new_flow_logic_register.simple(nodename_spigot_loaded)
|
||||||
|
local spigot_upper = 1.0
|
||||||
|
local spigot_lower = 1.0
|
||||||
|
local spigot_neighbours={{x=0, y=-1, z=0}}
|
||||||
|
new_flow_logic_register.output_simple(nodename_spigot_empty, spigot_upper, spigot_lower, spigot_neighbours)
|
||||||
|
new_flow_logic_register.output_simple(nodename_spigot_loaded, spigot_upper, spigot_lower, spigot_neighbours)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- sealed pipe entry/exit (horizontal pipe passing through a metal
|
-- sealed pipe entry/exit (horizontal pipe passing through a metal
|
||||||
-- wall, for use in places where walls should look like they're airtight)
|
-- wall, for use in places where walls should look like they're airtight)
|
||||||
@ -360,7 +396,8 @@ local panel_cbox = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
minetest.register_node("pipeworks:entry_panel_empty", {
|
local nodename_panel_empty = "pipeworks:entry_panel_empty"
|
||||||
|
minetest.register_node(nodename_panel_empty, {
|
||||||
description = "Airtight Pipe entry/exit",
|
description = "Airtight Pipe entry/exit",
|
||||||
drawtype = "mesh",
|
drawtype = "mesh",
|
||||||
mesh = "pipeworks_entry_panel.obj",
|
mesh = "pipeworks_entry_panel.obj",
|
||||||
@ -379,7 +416,8 @@ minetest.register_node("pipeworks:entry_panel_empty", {
|
|||||||
on_rotate = pipeworks.fix_after_rotation
|
on_rotate = pipeworks.fix_after_rotation
|
||||||
})
|
})
|
||||||
|
|
||||||
minetest.register_node("pipeworks:entry_panel_loaded", {
|
local nodename_panel_loaded = "pipeworks:entry_panel_loaded"
|
||||||
|
minetest.register_node(nodename_panel_loaded, {
|
||||||
description = "Airtight Pipe entry/exit",
|
description = "Airtight Pipe entry/exit",
|
||||||
drawtype = "mesh",
|
drawtype = "mesh",
|
||||||
mesh = "pipeworks_entry_panel.obj",
|
mesh = "pipeworks_entry_panel.obj",
|
||||||
@ -398,8 +436,15 @@ minetest.register_node("pipeworks:entry_panel_loaded", {
|
|||||||
drop = "pipeworks:entry_panel_empty",
|
drop = "pipeworks:entry_panel_empty",
|
||||||
on_rotate = pipeworks.fix_after_rotation
|
on_rotate = pipeworks.fix_after_rotation
|
||||||
})
|
})
|
||||||
|
-- FIXME requires-directionality
|
||||||
|
-- TODO: AFAIK the two panels have no visual difference, so are redundant under new flow logic - alias?
|
||||||
|
new_flow_logic_register.simple(nodename_panel_empty)
|
||||||
|
new_flow_logic_register.simple(nodename_panel_loaded)
|
||||||
|
|
||||||
minetest.register_node("pipeworks:flow_sensor_empty", {
|
|
||||||
|
|
||||||
|
local nodename_sensor_empty = "pipeworks:flow_sensor_empty"
|
||||||
|
minetest.register_node(nodename_sensor_empty, {
|
||||||
description = "Flow Sensor",
|
description = "Flow Sensor",
|
||||||
drawtype = "mesh",
|
drawtype = "mesh",
|
||||||
mesh = "pipeworks_flow_sensor.obj",
|
mesh = "pipeworks_flow_sensor.obj",
|
||||||
@ -437,7 +482,8 @@ minetest.register_node("pipeworks:flow_sensor_empty", {
|
|||||||
on_rotate = pipeworks.fix_after_rotation
|
on_rotate = pipeworks.fix_after_rotation
|
||||||
})
|
})
|
||||||
|
|
||||||
minetest.register_node("pipeworks:flow_sensor_loaded", {
|
local nodename_sensor_loaded = "pipeworks:flow_sensor_loaded"
|
||||||
|
minetest.register_node(nodename_sensor_loaded, {
|
||||||
description = "Flow sensor (on)",
|
description = "Flow sensor (on)",
|
||||||
drawtype = "mesh",
|
drawtype = "mesh",
|
||||||
mesh = "pipeworks_flow_sensor.obj",
|
mesh = "pipeworks_flow_sensor.obj",
|
||||||
@ -475,9 +521,18 @@ minetest.register_node("pipeworks:flow_sensor_loaded", {
|
|||||||
mesecons = pipereceptor_on,
|
mesecons = pipereceptor_on,
|
||||||
on_rotate = pipeworks.fix_after_rotation
|
on_rotate = pipeworks.fix_after_rotation
|
||||||
})
|
})
|
||||||
|
-- FIXME requires-directionality
|
||||||
|
new_flow_logic_register.simple(nodename_sensor_empty)
|
||||||
|
new_flow_logic_register.simple(nodename_sensor_loaded)
|
||||||
|
-- activate flow sensor at roughly half the pressure pumps drive pipes
|
||||||
|
local sensor_pressure_set = { { nodename_sensor_empty, 0.0 }, { nodename_sensor_loaded, 1.0 } }
|
||||||
|
new_flow_logic_register.transition_simple_set(sensor_pressure_set, { mesecons=pipeworks.mesecons_rules })
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- tanks
|
-- tanks
|
||||||
|
|
||||||
|
-- TODO flow-logic-stub: these don't currently do anything under the new flow logic.
|
||||||
for fill = 0, 10 do
|
for fill = 0, 10 do
|
||||||
local filldesc="empty"
|
local filldesc="empty"
|
||||||
local sgroups = {snappy=3, pipe=1, tankfill=fill+1}
|
local sgroups = {snappy=3, pipe=1, tankfill=fill+1}
|
||||||
@ -548,7 +603,8 @@ end
|
|||||||
|
|
||||||
-- fountainhead
|
-- fountainhead
|
||||||
|
|
||||||
minetest.register_node("pipeworks:fountainhead", {
|
local nodename_fountain_empty = "pipeworks:fountainhead"
|
||||||
|
minetest.register_node(nodename_fountain_empty, {
|
||||||
description = "Fountainhead",
|
description = "Fountainhead",
|
||||||
drawtype = "mesh",
|
drawtype = "mesh",
|
||||||
mesh = "pipeworks_fountainhead.obj",
|
mesh = "pipeworks_fountainhead.obj",
|
||||||
@ -581,7 +637,8 @@ minetest.register_node("pipeworks:fountainhead", {
|
|||||||
on_rotate = false
|
on_rotate = false
|
||||||
})
|
})
|
||||||
|
|
||||||
minetest.register_node("pipeworks:fountainhead_pouring", {
|
local nodename_fountain_loaded = "pipeworks:fountainhead_pouring"
|
||||||
|
minetest.register_node(nodename_fountain_loaded, {
|
||||||
description = "Fountainhead",
|
description = "Fountainhead",
|
||||||
drawtype = "mesh",
|
drawtype = "mesh",
|
||||||
mesh = "pipeworks_fountainhead.obj",
|
mesh = "pipeworks_fountainhead.obj",
|
||||||
@ -615,6 +672,15 @@ minetest.register_node("pipeworks:fountainhead_pouring", {
|
|||||||
drop = "pipeworks:fountainhead",
|
drop = "pipeworks:fountainhead",
|
||||||
on_rotate = false
|
on_rotate = false
|
||||||
})
|
})
|
||||||
|
new_flow_logic_register.simple(nodename_fountain_empty)
|
||||||
|
new_flow_logic_register.simple(nodename_fountain_loaded)
|
||||||
|
local fountain_upper = 1.0
|
||||||
|
local fountain_lower = 1.0
|
||||||
|
local fountain_neighbours={{x=0, y=1, z=0}}
|
||||||
|
new_flow_logic_register.output_simple(nodename_fountain_empty, fountain_upper, fountain_lower, fountain_neighbours)
|
||||||
|
new_flow_logic_register.output_simple(nodename_fountain_loaded, fountain_upper, fountain_lower, fountain_neighbours)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
minetest.register_alias("pipeworks:valve_off_loaded", "pipeworks:valve_off_empty")
|
minetest.register_alias("pipeworks:valve_off_loaded", "pipeworks:valve_off_empty")
|
||||||
minetest.register_alias("pipeworks:entry_panel", "pipeworks:entry_panel_empty")
|
minetest.register_alias("pipeworks:entry_panel", "pipeworks:entry_panel_empty")
|
||||||
|
30
init.lua
30
init.lua
@ -17,14 +17,16 @@ dofile(pipeworks.modpath.."/default_settings.lua")
|
|||||||
-- Read the external config file if it exists.
|
-- Read the external config file if it exists.
|
||||||
|
|
||||||
|
|
||||||
|
-- please add any new feature toggles to be a flag in this table...
|
||||||
|
pipeworks.toggles = {}
|
||||||
local worldsettingspath = pipeworks.worldpath.."/pipeworks_settings.txt"
|
local worldsettingspath = pipeworks.worldpath.."/pipeworks_settings.txt"
|
||||||
local worldsettingsfile = io.open(worldsettingspath, "r")
|
local worldsettingsfile = io.open(worldsettingspath, "r")
|
||||||
if worldsettingsfile then
|
if worldsettingsfile then
|
||||||
worldsettingsfile:close()
|
worldsettingsfile:close()
|
||||||
dofile(worldsettingspath)
|
dofile(worldsettingspath)
|
||||||
end
|
end
|
||||||
if pipeworks.enable_new_flow_logic then
|
if pipeworks.toggles.pressure_logic then
|
||||||
minetest.log("warning", "pipeworks new_flow_logic is WIP and incomplete!")
|
minetest.log("warning", "pipeworks pressure-based logic is WIP and incomplete!")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Random variables
|
-- Random variables
|
||||||
@ -94,9 +96,18 @@ function pipeworks.replace_name(tbl,tr,name)
|
|||||||
return ntbl
|
return ntbl
|
||||||
end
|
end
|
||||||
|
|
||||||
|
pipeworks.logger = function(msg)
|
||||||
|
print("[pipeworks] "..msg)
|
||||||
|
end
|
||||||
|
|
||||||
-------------------------------------------
|
-------------------------------------------
|
||||||
-- Load the various other parts of the mod
|
-- Load the various other parts of the mod
|
||||||
|
|
||||||
|
-- early auto-detection for finite water mode if not explicitly disabled
|
||||||
|
if pipeworks.toggles.finite_water == nil then
|
||||||
|
dofile(pipeworks.modpath.."/autodetect-finite-water.lua")
|
||||||
|
end
|
||||||
|
|
||||||
dofile(pipeworks.modpath.."/common.lua")
|
dofile(pipeworks.modpath.."/common.lua")
|
||||||
dofile(pipeworks.modpath.."/models.lua")
|
dofile(pipeworks.modpath.."/models.lua")
|
||||||
dofile(pipeworks.modpath.."/autoplace_pipes.lua")
|
dofile(pipeworks.modpath.."/autoplace_pipes.lua")
|
||||||
@ -115,14 +126,19 @@ dofile(pipeworks.modpath.."/filter-injector.lua")
|
|||||||
dofile(pipeworks.modpath.."/trashcan.lua")
|
dofile(pipeworks.modpath.."/trashcan.lua")
|
||||||
dofile(pipeworks.modpath.."/wielder.lua")
|
dofile(pipeworks.modpath.."/wielder.lua")
|
||||||
|
|
||||||
|
local logicdir = "/new_flow_logic/"
|
||||||
|
|
||||||
|
-- note that even with these files the new flow logic is not yet default.
|
||||||
|
-- registration will take place but no actual ABMs/node logic will be installed,
|
||||||
|
-- unless the toggle flag is specifically enabled in the per-world settings flag.
|
||||||
|
dofile(pipeworks.modpath..logicdir.."flowable_node_registry.lua")
|
||||||
|
dofile(pipeworks.modpath..logicdir.."abms.lua")
|
||||||
|
dofile(pipeworks.modpath..logicdir.."abm_register.lua")
|
||||||
|
dofile(pipeworks.modpath..logicdir.."flowable_node_registry_install.lua")
|
||||||
|
|
||||||
if pipeworks.enable_pipes then dofile(pipeworks.modpath.."/pipes.lua") end
|
if pipeworks.enable_pipes then dofile(pipeworks.modpath.."/pipes.lua") end
|
||||||
if pipeworks.enable_teleport_tube then dofile(pipeworks.modpath.."/teleport_tube.lua") end
|
if pipeworks.enable_teleport_tube then dofile(pipeworks.modpath.."/teleport_tube.lua") end
|
||||||
if pipeworks.enable_pipe_devices then dofile(pipeworks.modpath.."/devices.lua") end
|
if pipeworks.enable_pipe_devices then dofile(pipeworks.modpath.."/devices.lua") end
|
||||||
-- individual enable flags also checked in register_flow_logic.lua
|
|
||||||
if pipeworks.enable_new_flow_logic then
|
|
||||||
dofile(pipeworks.modpath.."/new_flow_logic.lua")
|
|
||||||
dofile(pipeworks.modpath.."/register_flow_logic.lua")
|
|
||||||
end
|
|
||||||
|
|
||||||
if pipeworks.enable_redefines then
|
if pipeworks.enable_redefines then
|
||||||
dofile(pipeworks.modpath.."/compat-chests.lua")
|
dofile(pipeworks.modpath.."/compat-chests.lua")
|
||||||
|
@ -1,121 +0,0 @@
|
|||||||
-- reimplementation of new_flow_logic branch: processing functions
|
|
||||||
-- written 2017 by thetaepsilon
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- global values and thresholds for water behaviour
|
|
||||||
-- TODO: add some way of setting this per-world
|
|
||||||
local thresholds = {}
|
|
||||||
-- limit on pump pressure - will not absorb more than can be taken
|
|
||||||
thresholds.pump_pressure = 2
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- 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
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- local debuglog = function(msg) print("## "..msg) 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.
|
|
||||||
-- this should ensure that water blocks aren't vanished from existance.
|
|
||||||
-- will take care of zero or negative-valued limits.
|
|
||||||
pipeworks.check_for_liquids_v2 = function(pos, limit)
|
|
||||||
if not limit then
|
|
||||||
limit = 6
|
|
||||||
end
|
|
||||||
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
|
|
||||||
return total
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
local label_pressure = "pipeworks.water_pressure"
|
|
||||||
local label_haspressure = "pipeworks.is_pressure_node"
|
|
||||||
pipeworks.balance_pressure = function(pos, node)
|
|
||||||
-- debuglog("balance_pressure() "..node.name.." at "..pos.x.." "..pos.y.." "..pos.z)
|
|
||||||
-- check the pressure of all nearby nodes, and average it out.
|
|
||||||
-- for the moment, only balance neighbour nodes if it already has a pressure value.
|
|
||||||
-- XXX: maybe this could be used to add fluid behaviour to other mod's nodes too?
|
|
||||||
|
|
||||||
-- unconditionally include self in nodes to average over
|
|
||||||
local meta = minetest.get_meta(pos)
|
|
||||||
local currentpressure = meta:get_float(label_pressure)
|
|
||||||
meta:set_int(label_haspressure, 1)
|
|
||||||
local connections = { meta }
|
|
||||||
local totalv = currentpressure
|
|
||||||
local totalc = 1
|
|
||||||
|
|
||||||
-- then handle neighbours, but if not a pressure node don't consider them at all
|
|
||||||
for _, npos in ipairs(make_coords_offsets(pos, false)) do
|
|
||||||
local neighbour = minetest.get_meta(npos)
|
|
||||||
local haspressure = (neighbour:get_int(label_haspressure) ~= 0)
|
|
||||||
if haspressure then
|
|
||||||
local n = neighbour:get_float(label_pressure)
|
|
||||||
table.insert(connections, neighbour)
|
|
||||||
totalv = totalv + n
|
|
||||||
totalc = totalc + 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local average = totalv / totalc
|
|
||||||
for _, targetmeta in ipairs(connections) do
|
|
||||||
targetmeta:set_float(label_pressure, average)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
pipeworks.run_pump_intake = function(pos, node)
|
|
||||||
-- try to absorb nearby water nodes, but only up to limit.
|
|
||||||
-- NB: check_for_liquids_v2 handles zero or negative from the following subtraction
|
|
||||||
local meta = minetest.get_meta(pos)
|
|
||||||
local currentpressure = meta:get_float(label_pressure)
|
|
||||||
local intake_limit = thresholds.pump_pressure - currentpressure
|
|
||||||
local actual_intake = pipeworks.check_for_liquids_v2(pos, intake_limit)
|
|
||||||
local newpressure = actual_intake + currentpressure
|
|
||||||
-- debuglog("oldpressure "..currentpressure.." intake_limit "..intake_limit.." actual_intake "..actual_intake.." newpressure "..newpressure)
|
|
||||||
meta:set_float(label_pressure, newpressure)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
pipeworks.run_spigot_output = function(pos, node)
|
|
||||||
-- try to output a water source node if there's enough pressure and space below.
|
|
||||||
local meta = minetest.get_meta(pos)
|
|
||||||
local currentpressure = meta:get_float(label_pressure)
|
|
||||||
if currentpressure > 1 then
|
|
||||||
local below = {x=pos.x, y=pos.y-1, z=pos.z}
|
|
||||||
local name = minetest.get_node(below).name
|
|
||||||
if (name == "air") or (name == "default:water_flowing") then
|
|
||||||
minetest.set_node(below, {name="default:water_source"})
|
|
||||||
meta:set_float(label_pressure, currentpressure - 1)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
26
new_flow_logic/abm_register.lua
Normal file
26
new_flow_logic/abm_register.lua
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
-- 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({
|
||||||
|
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
|
315
new_flow_logic/abms.lua
Normal file
315
new_flow_logic/abms.lua
Normal file
@ -0,0 +1,315 @@
|
|||||||
|
-- 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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- 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)
|
||||||
|
-- debuglog("balance_pressure() "..node.name.." at "..pos.x.." "..pos.y.." "..pos.z)
|
||||||
|
-- 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
|
||||||
|
|
||||||
|
-- then handle neighbours, but if not a pressure node don't consider them at all
|
||||||
|
for _, npos in ipairs(make_coords_offsets(pos, false)) 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
|
48
new_flow_logic/flowable_node_registry.lua
Normal file
48
new_flow_logic/flowable_node_registry.lua
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
-- 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 = {}
|
||||||
|
|
||||||
|
-- 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
|
188
new_flow_logic/flowable_node_registry_install.lua
Normal file
188
new_flow_logic/flowable_node_registry_install.lua
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
-- 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)
|
||||||
|
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)
|
||||||
|
if pipeworks.toggles.pressure_logic then
|
||||||
|
abmregister.flowlogic(nodename)
|
||||||
|
end
|
||||||
|
regwarning("simple", 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
|
38
pipes.lua
38
pipes.lua
@ -5,6 +5,8 @@ local REGISTER_COMPATIBILITY = true
|
|||||||
local pipes_empty_nodenames = {}
|
local pipes_empty_nodenames = {}
|
||||||
local pipes_full_nodenames = {}
|
local pipes_full_nodenames = {}
|
||||||
|
|
||||||
|
local new_flow_logic_register = pipeworks.flowables.register
|
||||||
|
|
||||||
local vti = {4, 3, 2, 1, 6, 5}
|
local vti = {4, 3, 2, 1, 6, 5}
|
||||||
local cconnects = {{}, {1}, {1, 2}, {1, 3}, {1, 3, 5}, {1, 2, 3}, {1, 2, 3, 5}, {1, 2, 3, 4}, {1, 2, 3, 4, 5}, {1, 2, 3, 4, 5, 6}}
|
local cconnects = {{}, {1}, {1, 2}, {1, 3}, {1, 3, 5}, {1, 2, 3}, {1, 2, 3, 5}, {1, 2, 3, 4}, {1, 2, 3, 4, 5}, {1, 2, 3, 4, 5, 6}}
|
||||||
for index, connects in ipairs(cconnects) do
|
for index, connects in ipairs(cconnects) do
|
||||||
@ -116,8 +118,12 @@ for index, connects in ipairs(cconnects) do
|
|||||||
on_rotate = false
|
on_rotate = false
|
||||||
})
|
})
|
||||||
|
|
||||||
table.insert(pipes_empty_nodenames, "pipeworks:pipe_"..index.."_empty")
|
local emptypipe = "pipeworks:pipe_"..index.."_empty"
|
||||||
table.insert(pipes_full_nodenames, "pipeworks:pipe_"..index.."_loaded")
|
local fullpipe = "pipeworks:pipe_"..index.."_loaded"
|
||||||
|
table.insert(pipes_empty_nodenames, emptypipe)
|
||||||
|
table.insert(pipes_full_nodenames, fullpipe)
|
||||||
|
new_flow_logic_register.simple(emptypipe)
|
||||||
|
new_flow_logic_register.simple(fullpipe)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@ -182,14 +188,24 @@ if REGISTER_COMPATIBILITY then
|
|||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
table.insert(pipes_empty_nodenames,"pipeworks:valve_on_empty")
|
|
||||||
table.insert(pipes_empty_nodenames,"pipeworks:valve_off_empty")
|
|
||||||
table.insert(pipes_empty_nodenames,"pipeworks:entry_panel_empty")
|
|
||||||
table.insert(pipes_empty_nodenames,"pipeworks:flow_sensor_empty")
|
|
||||||
|
|
||||||
table.insert(pipes_full_nodenames,"pipeworks:valve_on_loaded")
|
|
||||||
table.insert(pipes_full_nodenames,"pipeworks:entry_panel_loaded")
|
local valve_on = "pipeworks:valve_on_empty"
|
||||||
table.insert(pipes_full_nodenames,"pipeworks:flow_sensor_loaded")
|
local valve_off = "pipeworks:valve_off_empty"
|
||||||
|
local entry_panel_empty = "pipeworks:entry_panel_empty"
|
||||||
|
local flow_sensor_empty = "pipeworks:flow_sensor_empty"
|
||||||
|
-- XXX: why aren't these in devices.lua!?
|
||||||
|
table.insert(pipes_empty_nodenames, valve_on)
|
||||||
|
table.insert(pipes_empty_nodenames, valve_off)
|
||||||
|
table.insert(pipes_empty_nodenames, entry_panel_empty)
|
||||||
|
table.insert(pipes_empty_nodenames, flow_sensor_empty)
|
||||||
|
|
||||||
|
local valve_on_loaded = "pipeworks:valve_on_loaded"
|
||||||
|
local entry_panel_loaded = "pipeworks:entry_panel_loaded"
|
||||||
|
local flow_sensor_loaded = "pipeworks:flow_sensor_loaded"
|
||||||
|
table.insert(pipes_full_nodenames, valve_on_loaded)
|
||||||
|
table.insert(pipes_full_nodenames, entry_panel_loaded)
|
||||||
|
table.insert(pipes_full_nodenames, flow_sensor_loaded)
|
||||||
|
|
||||||
pipeworks.pipes_full_nodenames = pipes_full_nodenames
|
pipeworks.pipes_full_nodenames = pipes_full_nodenames
|
||||||
pipeworks.pipes_empty_nodenames = pipes_empty_nodenames
|
pipeworks.pipes_empty_nodenames = pipes_empty_nodenames
|
||||||
@ -197,8 +213,8 @@ pipeworks.pipes_empty_nodenames = pipes_empty_nodenames
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
if not pipeworks.enable_new_flow_logic then
|
if not pipeworks.toggles.pressure_logic then
|
||||||
-- sorry, no indents... it messes with the patchlogs too much
|
|
||||||
|
|
||||||
|
|
||||||
minetest.register_abm({
|
minetest.register_abm({
|
||||||
|
@ -1,58 +0,0 @@
|
|||||||
-- register new flow logic ABMs
|
|
||||||
-- written 2017 by thetaepsilon
|
|
||||||
|
|
||||||
local pipes_full_nodenames = pipeworks.pipes_full_nodenames
|
|
||||||
local pipes_empty_nodenames = pipeworks.pipes_empty_nodenames
|
|
||||||
|
|
||||||
-- run pressure balancing ABM over all water-moving nodes
|
|
||||||
-- FIXME: DRY principle, get this from elsewhere in the code
|
|
||||||
local pump_on = "pipeworks:pump_on"
|
|
||||||
local pump_off = "pipeworks:pump_off"
|
|
||||||
local spigot_off = "pipeworks:spigot"
|
|
||||||
local spigot_on = "pipeworks:spigot_pouring"
|
|
||||||
|
|
||||||
local pipes_all_nodenames = pipes_full_nodenames
|
|
||||||
for _, pipe in ipairs(pipes_empty_nodenames) do
|
|
||||||
table.insert(pipes_all_nodenames, pipe)
|
|
||||||
end
|
|
||||||
|
|
||||||
if pipeworks.enable_pipe_devices then
|
|
||||||
table.insert(pipes_all_nodenames, pump_off)
|
|
||||||
table.insert(pipes_all_nodenames, pump_on)
|
|
||||||
table.insert(pipes_all_nodenames, spigot_on)
|
|
||||||
table.insert(pipes_all_nodenames, spigot_off)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
if pipeworks.enable_pipes then
|
|
||||||
minetest.register_abm({
|
|
||||||
nodenames = pipes_all_nodenames,
|
|
||||||
interval = 1,
|
|
||||||
chance = 1,
|
|
||||||
action = function(pos, node, active_object_count, active_object_count_wider)
|
|
||||||
pipeworks.balance_pressure(pos, node)
|
|
||||||
end
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
if pipeworks.enable_pipe_devices then
|
|
||||||
-- absorb water into pumps if it'll fit
|
|
||||||
minetest.register_abm({
|
|
||||||
nodenames = { pump_on },
|
|
||||||
interval = 1,
|
|
||||||
chance = 1,
|
|
||||||
action = function(pos, node, active_object_count, active_object_count_wider)
|
|
||||||
pipeworks.run_pump_intake(pos, node)
|
|
||||||
end
|
|
||||||
})
|
|
||||||
-- output water from spigots
|
|
||||||
-- add both "on/off" spigots so one can be used to indicate a certain level of fluid.
|
|
||||||
minetest.register_abm({
|
|
||||||
nodenames = { spigot_on, spigot_off },
|
|
||||||
interval = 1,
|
|
||||||
chance = 1,
|
|
||||||
action = function(pos, node, active_object_count, active_object_count_wider)
|
|
||||||
pipeworks.run_spigot_output(pos, node)
|
|
||||||
end
|
|
||||||
})
|
|
||||||
end
|
|
10
todo/new_flow_logic.txt
Normal file
10
todo/new_flow_logic.txt
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
-- Directionality code
|
||||||
|
Currently, only "simple" flowable nodes exist, and they will always equalise pressure with all six neighbours.
|
||||||
|
A more sophisticated behaviour for this would be flowable node registration with some kind of custom callback, such that water can only flow into or out of these nodes in certain directions.
|
||||||
|
This would enable devices such as the airtight panels, sensor tubes and valves to have correct flow behaviour.
|
||||||
|
|
||||||
|
-- (may not be possible) stop removing water nodes that were not placed by outputs when off
|
||||||
|
In non-finite mode, spigots and fountainheads will vanish water sources in their output positions, even if those output nodes did not place them there.
|
||||||
|
This is annoying though not game-breaking in non-finite mode, where water sources can at least be easily replenished.
|
||||||
|
Fixing this would require some kind of metadata marker on water nodes placed by spigots and fountains, such that only water sources placed while the device is "on" are removed when it is "off".
|
||||||
|
It is debateable whether existing water sources should be marked for removal when the device turns on again.
|
Loading…
Reference in New Issue
Block a user