Namespacing pt. 2
This commit is contained in:
204
mods/ip_waterworks/execute.lua
Normal file
204
mods/ip_waterworks/execute.lua
Normal file
@@ -0,0 +1,204 @@
|
||||
local pressure_margin = 20
|
||||
|
||||
local pipe_cache = {}
|
||||
|
||||
local cardinal_dirs = {
|
||||
{x= 0, y=0, z= 1},
|
||||
{x= 1, y=0, z= 0},
|
||||
{x= 0, y=0, z=-1},
|
||||
{x=-1, y=0, z= 0},
|
||||
{x= 0, y=-1, z= 0},
|
||||
{x= 0, y=1, z= 0},
|
||||
}
|
||||
|
||||
local sort_by_pressure = function(first, second)
|
||||
local first_pressure = first.pressure
|
||||
local second_pressure = second.pressure
|
||||
if first_pressure == nil or second_pressure == nil then
|
||||
minetest.log("error", "[waterworks] attempted to sort something by pressure that had no pressure value: " .. dump(first) .. "\n" .. dump(second))
|
||||
return
|
||||
end
|
||||
|
||||
return first_pressure > second_pressure
|
||||
end
|
||||
|
||||
local valid_sink = function(node_name)
|
||||
return node_name == "air" or node_name == "default:water_flowing"
|
||||
end
|
||||
local valid_source = function(node_name)
|
||||
return node_name == "default:water_source"
|
||||
end
|
||||
|
||||
-- breadth-first search passing through water searching for air or flowing water, limited to y <= pressure.
|
||||
-- I could try to be fancy about water flowing downward preferentially, let's leave that as a TODO for now.
|
||||
local flood_search_outlet = function(start_pos, pressure)
|
||||
local start_node = minetest.get_node(start_pos)
|
||||
local start_node_name = start_node.name
|
||||
if valid_sink(start_node_name) then
|
||||
return start_pos
|
||||
end
|
||||
|
||||
local visited = {}
|
||||
visited[minetest.hash_node_position(start_pos)] = true
|
||||
local queue = {start_pos}
|
||||
local queue_pointer = 1
|
||||
|
||||
while #queue >= queue_pointer do
|
||||
local current_pos = queue[queue_pointer]
|
||||
queue_pointer = queue_pointer + 1
|
||||
for _, cardinal_dir in ipairs(cardinal_dirs) do
|
||||
local new_pos = vector.add(current_pos, cardinal_dir)
|
||||
local new_hash = minetest.hash_node_position(new_pos)
|
||||
if visited[new_hash] == nil and new_pos.y <= pressure then
|
||||
local new_node = minetest.get_node(new_pos)
|
||||
local new_node_name = new_node.name
|
||||
if valid_sink(new_node_name) then
|
||||
return new_pos
|
||||
end
|
||||
visited[new_hash] = true
|
||||
if valid_source(new_node_name) then
|
||||
table.insert(queue, new_pos)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
local upward_dirs = {
|
||||
{x= 0, y=0, z= 1},
|
||||
{x= 1, y=0, z= 0},
|
||||
{x= 0, y=0, z=-1},
|
||||
{x=-1, y=0, z= 0},
|
||||
{x= 0, y=1, z= 0},
|
||||
}
|
||||
|
||||
local shuffle = function(tbl)
|
||||
for i = #tbl, 2, -1 do
|
||||
local rand = math.random(i)
|
||||
tbl[i], tbl[rand] = tbl[rand], tbl[i]
|
||||
end
|
||||
return tbl
|
||||
end
|
||||
|
||||
-- depth-first random-walk search trending in an upward direction, returns when it gets cornered
|
||||
local find_source = function(start_pos)
|
||||
local current_node = minetest.get_node(start_pos)
|
||||
local current_node_name = current_node.name
|
||||
if not valid_source(current_node_name) then
|
||||
return nil
|
||||
end
|
||||
|
||||
local visited = {[minetest.hash_node_position(start_pos)] = true}
|
||||
local current_pos = start_pos
|
||||
|
||||
local continue = true
|
||||
while continue do
|
||||
continue = false
|
||||
shuffle(upward_dirs)
|
||||
for _, dir in ipairs(upward_dirs) do
|
||||
local next_pos = vector.add(current_pos, dir)
|
||||
local next_hash = minetest.hash_node_position(next_pos)
|
||||
if visited[next_hash] == nil then
|
||||
visited[next_hash] = true
|
||||
local next_node = minetest.get_node(next_pos)
|
||||
local next_node_name = next_node.name
|
||||
if valid_source(next_node_name) then
|
||||
current_pos = next_pos
|
||||
continue = true
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return current_pos
|
||||
end
|
||||
|
||||
|
||||
waterworks.execute_pipes = function(net_index, net_capacity)
|
||||
local net = waterworks.pipe_networks[net_index]
|
||||
if net == nil then
|
||||
minetest.log("error", "[waterworks] Invalid net index given to execute: " .. tostring(net_index))
|
||||
return
|
||||
end
|
||||
|
||||
local inlets
|
||||
local outlets
|
||||
|
||||
if net.cache_valid then
|
||||
-- We don't need to recalculate, nothing about the pipe network has changed since last time
|
||||
inlets = pipe_cache[net_index].inlets
|
||||
outlets = pipe_cache[net_index].outlets
|
||||
else
|
||||
-- Find all the inlets and outlets and sort them by pressure
|
||||
inlets = {}
|
||||
if net.connected.inlet ~= nil then
|
||||
for _, inlet_set in pairs(net.connected.inlet) do
|
||||
for _, inlet in pairs(inlet_set) do
|
||||
table.insert(inlets, inlet)
|
||||
end
|
||||
end
|
||||
end
|
||||
table.sort(inlets, sort_by_pressure)
|
||||
|
||||
outlets = {}
|
||||
if net.connected.outlet ~= nil then
|
||||
for _, outlet_set in pairs(net.connected.outlet) do
|
||||
for _, outlet in pairs(outlet_set) do
|
||||
table.insert(outlets, outlet)
|
||||
end
|
||||
end
|
||||
end
|
||||
table.sort(outlets, sort_by_pressure)
|
||||
|
||||
-- Cache the results
|
||||
pipe_cache[net_index] = {}
|
||||
pipe_cache[net_index].inlets = inlets
|
||||
pipe_cache[net_index].outlets = outlets
|
||||
|
||||
net.cache_valid = true
|
||||
end
|
||||
|
||||
local inlet_index = 1
|
||||
local outlet_index = #outlets
|
||||
local inlet_count = #inlets
|
||||
|
||||
local count = 0
|
||||
|
||||
-- Starting with the highest-pressure inlet and the lowest-pressure outlet, attempt to move water.
|
||||
-- We then proceed to steadily lower-pressure inlets and higher-pressure outlets until we meet in the middle, at which point
|
||||
-- the system is in equilibrium.
|
||||
while inlet_index <= inlet_count and outlet_index > 0 and count < net_capacity do
|
||||
local source = inlets[inlet_index]
|
||||
local sink = outlets[outlet_index]
|
||||
|
||||
--minetest.debug("source: " .. dump(source))
|
||||
--minetest.debug("sink: " .. dump(sink))
|
||||
|
||||
-- pressure_margin allows us to check sources that are a little bit below sinks,
|
||||
-- in case the extra pressure from their water depth is sufficient to force water through
|
||||
if source.pressure + pressure_margin >= sink.pressure then
|
||||
local source_pos = find_source(source.target)
|
||||
local sink_pos
|
||||
if source_pos ~= nil then
|
||||
sink_pos = flood_search_outlet(sink.target, math.max(source.pressure, source_pos.y))
|
||||
if sink_pos ~= nil then
|
||||
minetest.swap_node(sink_pos, {name="default:water_source"})
|
||||
minetest.swap_node(source_pos, {name="air"})
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
|
||||
if source_pos == nil then
|
||||
-- the outlet had available space but the inlet didn't provide
|
||||
inlet_index = inlet_index + 1
|
||||
elseif sink_pos == nil then
|
||||
-- the inlet provided but the outlet didn't have space
|
||||
outlet_index = outlet_index - 1
|
||||
end
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user