Initial version
|
@ -0,0 +1,54 @@
|
|||
This mod provides a set of nodes that allow large quantities of water to be pumped from one place to another, potentially over very long distances.
|
||||
|
||||
**Important Note:** The default behaviour of water in Minetest does not actually lend itself well to this kind of activity. In particular, ``default:water_source`` has ``liquid_renewable = true`` set in its node definition, which often causes new water nodes to be created where old ones are removed.
|
||||
|
||||
This mod includes an optional setting that disables ``liquid_renewable`` for default water, but for best effect it is highly recommended that this mod be used in conjunction with the [dynamic_liquid](https://github.com/minetest-mods/dynamic_liquid) mod.
|
||||
|
||||
The [airtanks](https://github.com/minetest-mods/airtanks) mod can also be helpful for players who are interested in doing large-scale underwater construction projects.
|
||||
|
||||
## Pipes
|
||||
|
||||
The core node type introduced by this mod is the pipe. When pipes are laid adjacent to each other they connect up to form a pipe network, to which inlets, outlets, and pumps can be connected. All contiguously pipes are part of the same network, and all terminal nodes connected to that network will be able to interact with each other.
|
||||
|
||||
Pipes automatically connect to other pipes through any of their six faces.
|
||||
|
||||
## Terminals
|
||||
|
||||
Terminals can only be connected to a pipe network via one face, the side that by default is facing away from the player when they place the node in world. They interact with water only on the opposite face - the one facing toward the player when they place the node in world.
|
||||
|
||||
A screwdriver can be used to reorient terminals if you want one facing upward or downward.
|
||||
|
||||
The types of terminals in this mod are:
|
||||
|
||||
* Inlets let water enter the pipe but not leave
|
||||
* Outlets let water out but not in
|
||||
* Grates let water flow either way depending on pressure
|
||||
* Pumps are inlets that force water into the network at a higher pressure than their elevation would normally give it.
|
||||
|
||||
## Valves
|
||||
|
||||
A valve can be used to connect or disconnect sections of a pipe network with a simple right-click. When a valve is "open" it acts like a pipe section, and when it's "closed" it does not act like a pipe.
|
||||
|
||||
## Elevation and pressure
|
||||
|
||||
The flow of water through the network is determined by two main factors; the directionality of each type of terminal, and the pressure of the water at that terminal.
|
||||
|
||||
Water flows from high pressure terminals to low pressure terminals. The rise and fall of the pipe in between those two terminals doesn't really matter, just the pressure at the terminals themselves.
|
||||
|
||||
The following figure illustrates the basics of how this works with a very simple three-terminal pipe network:
|
||||
|
||||
![Figure 1](/screenshots/waterworks_figure_1.png)
|
||||
|
||||
The two terminals on the left side are "inlets", only permitting water to enter the network, and the terminal on the right is a grate that allows water in or out.
|
||||
|
||||
If terminal 1 were to be immersed in water, water nodes would be transferred from terminal 1 to terminal 2 because terminal 1's higher elevation gives it higher pressure than terminal 2. Water would *not* be transferred to terminal 3 as terminal 3 is an inlet only.
|
||||
|
||||
If terminal 2 were to be immersed, likewise no water would be transferred because although terminal 2 can allow water to enter the pipe (it's a grate) there are no valid outlet terminals it could go to.
|
||||
|
||||
If terminal 3 were immersed in water, no water would be transferred because terminal 2 is higher elevation and therefore there isn't enough pressure at terminal 3 to reach it.
|
||||
|
||||
![Figure 2](/screenshots/waterworks_figure_2.png)
|
||||
|
||||
In this example termimal 3 is a pump, which acts as if it were an inlet located at an elevation 100 meters higher than it actually is. There are two potential outlets for water entering the system. Water is preferentially emitted from the *lowest* pressure outlet, so if terminal 3 was immersed in water it would be sent to terminal 2. However, if terminal 2 was contained in an enclosed space that had run out of room for additional water, the water would then be sent to the next-lowest outlet and come out of terminal 1.
|
||||
|
||||
If terminal 1 was immersed, then water would transfer from it to terminal 2. Terminal 3 is an inlet, so water wouldn't come out of it.
|
|
@ -0,0 +1 @@
|
|||
default
|
|
@ -0,0 +1,197 @@
|
|||
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
|
||||
inlets = pipe_cache[net_index].inlets
|
||||
outlets = pipe_cache[net_index].outlets
|
||||
else
|
||||
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)
|
||||
|
||||
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))
|
||||
|
||||
if source.pressure >= 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
|
|
@ -0,0 +1,112 @@
|
|||
local worldpath = minetest.get_worldpath()
|
||||
local network_filename = worldpath.."/waterworks_network.json"
|
||||
|
||||
-- Json storage
|
||||
|
||||
local save_data = function()
|
||||
if waterworks.dirty_data ~= true then
|
||||
return
|
||||
end
|
||||
local file = io.open(network_filename, "w")
|
||||
if file then
|
||||
file:write(minetest.serialize(waterworks.pipe_networks))
|
||||
file:close()
|
||||
waterworks.dirty_data = false
|
||||
end
|
||||
end
|
||||
|
||||
local read_data = function()
|
||||
local file = io.open(network_filename, "r")
|
||||
if file then
|
||||
waterworks.pipe_networks = minetest.deserialize(file:read("*all")) -- note: any cached references to pipe_networks is invalidated here, so do this once at the beginning of the run and never again thereafter.
|
||||
file:close()
|
||||
else
|
||||
waterworks.pipe_networks = {}
|
||||
end
|
||||
waterworks.dirty_data = false
|
||||
for _, net in ipairs(waterworks.pipe_networks) do
|
||||
net.cache_valid = false
|
||||
end
|
||||
end
|
||||
|
||||
read_data()
|
||||
|
||||
----------------------------------------------
|
||||
|
||||
local nets_near_players = {}
|
||||
|
||||
minetest.register_abm ({
|
||||
label = "Active connected node tracking",
|
||||
nodenames = {"group:waterworks_connected"},
|
||||
interval = 1.0,
|
||||
chance = 1,
|
||||
action = function(pos, node, active_object_count, active_object_count_wider)
|
||||
local player_close = false
|
||||
for _, player in ipairs(minetest.get_connected_players()) do
|
||||
local player_pos = player:get_pos()
|
||||
if math.abs(player_pos.x - pos.x) < 81 and math.abs(player_pos.z - pos.z) < 81 and math.abs(player_pos.y - pos.y) < 81 then
|
||||
player_close = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if not player_close then return end
|
||||
|
||||
local hash = minetest.hash_node_position(pos) + waterworks.facedir_to_hash(node.param2)
|
||||
local net_index = waterworks.find_network_for_pipe_hash(hash)
|
||||
if net_index < 0 then return end
|
||||
--minetest.chat_send_all("net near player " .. tostring(net_index))
|
||||
nets_near_players[net_index] = 5.0
|
||||
end,
|
||||
})
|
||||
|
||||
local forceloads = {}
|
||||
local timer = 0
|
||||
minetest.register_globalstep(function(dtime)
|
||||
timer = timer + dtime
|
||||
if timer > 1.0 then
|
||||
|
||||
if waterworks.dirty_data then
|
||||
-- it's possible that a pipe network was split or merged, invalidating the nets_near_players values here.
|
||||
-- Best to clear them and do nothing for one globalstep, they'll be repopulated shortly.
|
||||
nets_near_players = {}
|
||||
end
|
||||
|
||||
-- find connected node positions for all networks with connected nodes near players
|
||||
local ensure_forceload = {}
|
||||
for index, live_time in pairs(nets_near_players) do
|
||||
local new_time = live_time - timer
|
||||
--minetest.chat_send_all("new time " .. tostring(new_time))
|
||||
if new_time < 0 then
|
||||
nets_near_players[index] = nil
|
||||
else
|
||||
nets_near_players[index] = new_time
|
||||
for connection_type, connections in pairs(waterworks.pipe_networks[index].connected) do
|
||||
for hash, _ in pairs(connections) do
|
||||
ensure_forceload[hash] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- clear forceloads that are no longer needed
|
||||
for hash, _ in pairs(forceloads) do
|
||||
if not ensure_forceload[hash] then
|
||||
minetest.forceload_free_block(minetest.get_position_from_hash(hash), true)
|
||||
end
|
||||
end
|
||||
forceloads = ensure_forceload
|
||||
-- enable forceloads that are needed
|
||||
for hash, _ in pairs(forceloads) do
|
||||
minetest.forceload_block(minetest.get_position_from_hash(hash), true)
|
||||
end
|
||||
|
||||
timer = timer - 1.0
|
||||
save_data()
|
||||
for index, _ in pairs(nets_near_players) do
|
||||
--minetest.chat_send_all("executing index " .. tostring(index))
|
||||
waterworks.execute_pipes(index, 8)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
waterworks = {}
|
||||
|
||||
local modpath = minetest.get_modpath(minetest.get_current_modname())
|
||||
|
||||
dofile(modpath .. "/globalstep.lua")
|
||||
dofile(modpath .. "/network.lua")
|
||||
dofile(modpath .. "/execute.lua")
|
||||
dofile(modpath .. "/nodes.lua")
|
||||
|
||||
if minetest.settings:get_bool("waterworks_make_default_water_non_renewable") then
|
||||
local override_def = {liquid_renewable = false}
|
||||
minetest.override_item("default:water_source", override_def)
|
||||
minetest.override_item("default:water_flowing", override_def)
|
||||
|
||||
end
|
||||
|
||||
-- For test purposes, this rebuilds pipe networks without needing to persist them.
|
||||
-- May be useful later for fixing broken stuff.
|
||||
-- Note that this doesn't *remove* pipes that are inappropriately listed in the network,
|
||||
-- that may be tricky.
|
||||
--minetest.register_lbm({
|
||||
-- label = "Validate waterworks pipe networks",
|
||||
-- name = "waterworks:validate_pipe_networks",
|
||||
-- nodenames = {"group:waterworks_pipe"},
|
||||
-- run_at_every_load = true,
|
||||
-- action = function(pos, node)
|
||||
-- local hash_pos = minetest.hash_node_position(pos)
|
||||
-- local found = false
|
||||
-- for i, net in ipairs(waterworks.pipe_networks) do
|
||||
-- if net.pipes[hash_pos] then
|
||||
-- found = true
|
||||
-- break
|
||||
-- end
|
||||
-- end
|
||||
-- if not found then
|
||||
-- local new_net_index = waterworks.place_pipe(pos)
|
||||
-- minetest.log("warning", "[waterworks] detected an unregistered pipe at " ..
|
||||
-- minetest.pos_to_string(pos) .. ", added it to pipe network " ..
|
||||
-- tostring(new_net_index))
|
||||
-- end
|
||||
-- end,
|
||||
--})
|
||||
|
||||
--minetest.register_chatcommand("dump_pipes", {
|
||||
---- params = "pos", -- Short parameter description
|
||||
-- description = "dump pipe network to debug log",
|
||||
-- func = function(name, param)
|
||||
-- --minetest.debug(dump(pipe_networks))
|
||||
--
|
||||
-- for i, net in ipairs(waterworks.pipe_networks) do
|
||||
-- minetest.debug("net #" .. tostring(i) .. " cache valid: " .. dump(net.cache_valid))
|
||||
-- minetest.debug("\tpipes:")
|
||||
-- local count = 0
|
||||
-- for hash_pos, _ in pairs(net.pipes) do
|
||||
-- minetest.debug("\t\t"..minetest.pos_to_string(minetest.get_position_from_hash(hash_pos)))
|
||||
-- count = count + 1
|
||||
-- end
|
||||
-- minetest.debug("\tpipe count: " .. tostring(count))
|
||||
-- minetest.debug("\tconnections:")
|
||||
-- for connection_type, connection_list in pairs(net.connected) do
|
||||
-- minetest.debug("\t\t"..connection_type..":")
|
||||
-- for connection_hash, data in pairs(connection_list) do
|
||||
-- minetest.debug("\t\t\t"..minetest.pos_to_string(minetest.get_position_from_hash(connection_hash))..": "..string.gsub(string.gsub(dump(data), "\t", ""), "\n", " "))
|
||||
-- end
|
||||
-- end
|
||||
-- end
|
||||
--
|
||||
-- end,
|
||||
--})
|
|
@ -0,0 +1,3 @@
|
|||
name = waterworks
|
||||
description = Provides pipes, pumps, and valves for moving water over long distances
|
||||
depends = default
|
|
@ -0,0 +1,386 @@
|
|||
local pipe_networks = waterworks.pipe_networks
|
||||
|
||||
local invalidate_cache = function(pipe_network)
|
||||
pipe_network.cache_valid = false
|
||||
waterworks.dirty_data = true
|
||||
end
|
||||
|
||||
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},
|
||||
}
|
||||
-- Mapping from facedir value to index in cardinal_dirs.
|
||||
local facedir_to_dir_map = {
|
||||
[0]=1, 2, 3, 4,
|
||||
5, 2, 6, 4,
|
||||
6, 2, 5, 4,
|
||||
1, 5, 3, 6,
|
||||
1, 6, 3, 5,
|
||||
1, 4, 3, 2,
|
||||
}
|
||||
|
||||
-- Turn the cardinal directions into a set of integers you can add to a hash to step in that direction.
|
||||
local cardinal_dirs_hash = {}
|
||||
for i, dir in ipairs(cardinal_dirs) do
|
||||
cardinal_dirs_hash[i] = minetest.hash_node_position(dir) - minetest.hash_node_position({x=0, y=0, z=0})
|
||||
end
|
||||
|
||||
local facedir_to_dir_index = function(param2)
|
||||
return facedir_to_dir_map[param2 % 32]
|
||||
end
|
||||
|
||||
local facedir_to_cardinal_hash = function(dir_index)
|
||||
return cardinal_dirs_hash[dir_index]
|
||||
end
|
||||
|
||||
waterworks.facedir_to_hash = function(param2)
|
||||
return facedir_to_cardinal_hash(facedir_to_dir_index(param2))
|
||||
end
|
||||
|
||||
local init_new_network = function(hash_pos)
|
||||
waterworks.dirty_data = true
|
||||
return {pipes = {[hash_pos] = true}, connected = {}, cache_valid = false}
|
||||
end
|
||||
|
||||
local get_neighbor_pipes = function(pos)
|
||||
local neighbor_pipes = {}
|
||||
local neighbor_connected = {}
|
||||
for _, dir in ipairs(cardinal_dirs) do
|
||||
local potential_pipe_pos = vector.add(pos, dir)
|
||||
local neighbor = minetest.get_node(potential_pipe_pos)
|
||||
if minetest.get_item_group(neighbor.name, "waterworks_pipe") > 0 then
|
||||
table.insert(neighbor_pipes, potential_pipe_pos)
|
||||
elseif minetest.get_item_group(neighbor.name, "waterworks_connected") > 0 then
|
||||
table.insert(neighbor_connected, potential_pipe_pos)
|
||||
end
|
||||
end
|
||||
return neighbor_pipes, neighbor_connected
|
||||
end
|
||||
|
||||
local merge_networks = function(index_list)
|
||||
table.sort(index_list)
|
||||
local first_index = table.remove(index_list, 1)
|
||||
local merged_network = pipe_networks[first_index]
|
||||
-- remove in reverse order so that indices of earlier tables to remove don't get disrupted
|
||||
for i = #index_list, 1, -1 do
|
||||
local index = index_list[i]
|
||||
local net_to_merge = pipe_networks[index]
|
||||
for pipe_hash, _ in pairs(net_to_merge.pipes) do
|
||||
merged_network.pipes[pipe_hash] = true
|
||||
end
|
||||
for item_type, item_list in pairs(net_to_merge.connected) do
|
||||
merged_network.connected[item_type] = merged_network.connected[item_type] or {}
|
||||
for connection_hash, connection_data in pairs(item_list) do
|
||||
merged_network.connected[item_type][connection_hash] = connection_data
|
||||
end
|
||||
end
|
||||
table.remove(pipe_networks, index)
|
||||
end
|
||||
invalidate_cache(merged_network)
|
||||
return first_index
|
||||
end
|
||||
|
||||
local handle_connected = function(connected_positions)
|
||||
for _, pos in ipairs(connected_positions) do
|
||||
local node = minetest.get_node(pos)
|
||||
local node_def = minetest.registered_nodes[node.name]
|
||||
if node_def._waterworks_update_connected then
|
||||
node_def._waterworks_update_connected(pos)
|
||||
else
|
||||
minetest.log("error", "[waterworks] Node def for " .. node.name .. " had no _waterworks_update_connected defined")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- When placing a pipe at pos, identifies what pipe network to add it to and updates the network map.
|
||||
-- Note that this can result in fusing multiple networks together into one network.
|
||||
waterworks.place_pipe = function(pos)
|
||||
local hash_pos = minetest.hash_node_position(pos)
|
||||
local neighbor_pipes, neighbor_connected = get_neighbor_pipes(pos)
|
||||
local neighbor_count = #neighbor_pipes
|
||||
|
||||
if neighbor_count == 0 then
|
||||
-- this newly-placed pipe has no other pipes next to it, so make a new network for it.
|
||||
local new_net = init_new_network(hash_pos)
|
||||
table.insert(pipe_networks, new_net)
|
||||
handle_connected(neighbor_connected)
|
||||
return #pipe_networks
|
||||
elseif neighbor_count == 1 then
|
||||
-- there's only one pipe neighbor. Look up what network it belongs to and add this pipe to it too.
|
||||
local neighbor_pos_hash = minetest.hash_node_position(neighbor_pipes[1])
|
||||
for i, net in ipairs(pipe_networks) do
|
||||
local pipes = net.pipes
|
||||
if pipes[neighbor_pos_hash] then
|
||||
pipes[hash_pos] = true
|
||||
invalidate_cache(net)
|
||||
handle_connected(neighbor_connected)
|
||||
return i
|
||||
end
|
||||
end
|
||||
else
|
||||
local neighbor_index_set = {} -- set of indices for networks that neighbors belong to
|
||||
local neighbor_index_list = {} -- list version of above
|
||||
for _, neighbor_pos in ipairs(neighbor_pipes) do
|
||||
local neighbor_hash = minetest.hash_node_position(neighbor_pos)
|
||||
for i, net in ipairs(pipe_networks) do
|
||||
if net.pipes[neighbor_hash] then
|
||||
if not neighbor_index_set[i] then
|
||||
table.insert(neighbor_index_list, i)
|
||||
neighbor_index_set[i] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if #neighbor_index_list == 1 then -- all neighbors belong to one network. Add this node to that network.
|
||||
local target_network_index = neighbor_index_list[1]
|
||||
pipe_networks[target_network_index]["pipes"][hash_pos] = true
|
||||
invalidate_cache(pipe_networks[target_network_index])
|
||||
handle_connected(neighbor_connected)
|
||||
return target_network_index
|
||||
end
|
||||
|
||||
-- The most complicated case, this new pipe segment bridges multiple networks.
|
||||
if #neighbor_index_list > 1 then
|
||||
local new_index = merge_networks(neighbor_index_list)
|
||||
pipe_networks[new_index]["pipes"][hash_pos] = true
|
||||
handle_connected(neighbor_connected)
|
||||
return new_index
|
||||
end
|
||||
end
|
||||
|
||||
-- if we get here we're in a strange state - there are neighbor pipe nodes but none are registered in a network.
|
||||
-- We could be trying to recover from corruption, so pretend the neighbors don't exist and start a new network.
|
||||
-- The unregistered neighbors may join it soon.
|
||||
local new_net = init_new_network(hash_pos)
|
||||
table.insert(pipe_networks, new_net)
|
||||
handle_connected(neighbor_connected)
|
||||
return #pipe_networks
|
||||
|
||||
end
|
||||
|
||||
waterworks.remove_pipe = function(pos)
|
||||
local hash_pos = minetest.hash_node_position(pos)
|
||||
local neighbor_pipes = get_neighbor_pipes(pos)
|
||||
local neighbor_count = #neighbor_pipes
|
||||
|
||||
if neighbor_count == 0 then
|
||||
-- no neighbors, so this is the last of its network.
|
||||
for i, net in ipairs(pipe_networks) do
|
||||
if net.pipes[hash_pos] then
|
||||
table.remove(pipe_networks, i)
|
||||
waterworks.dirty_data = true
|
||||
return i
|
||||
end
|
||||
end
|
||||
|
||||
minetest.log("error", "[waterworks] pipe removed from pos " .. minetest.pos_to_string(pos) ..
|
||||
" didn't belong to any networks. Something went wrong to get to this state.")
|
||||
return -1
|
||||
elseif neighbor_count == 1 then
|
||||
-- there's only one pipe neighbor. This pipe is at the end of a line, so just remove it.
|
||||
for i, net in ipairs(pipe_networks) do
|
||||
local pipes = net.pipes
|
||||
if pipes[hash_pos] then
|
||||
pipes[hash_pos] = nil
|
||||
invalidate_cache(net)
|
||||
-- If there's anything connected to the pipe here, remove it from the network too
|
||||
for _, connected_items in pairs(net.connected) do
|
||||
connected_items[hash_pos] = nil
|
||||
end
|
||||
return i
|
||||
end
|
||||
end
|
||||
minetest.log("error", "[waterworks] pipe removed from pos " .. minetest.pos_to_string(pos) ..
|
||||
" didn't belong to any networks, despite being neighbor to one at " ..
|
||||
minetest.pos_to_string(neighbor_pipes[1]) ..
|
||||
". Something went wrong to get to this state.")
|
||||
return -1
|
||||
else
|
||||
-- we may be splitting networks. This is complicated.
|
||||
-- find the network we currently belong to. Remove ourselves from it.
|
||||
local old_net
|
||||
local old_pipes
|
||||
local old_connected
|
||||
local old_index
|
||||
for i, net in ipairs(pipe_networks) do
|
||||
local pipes = net.pipes
|
||||
if pipes[hash_pos] then
|
||||
old_connected = net.connected
|
||||
old_net = net
|
||||
old_pipes = pipes
|
||||
old_index = i
|
||||
old_pipes[hash_pos] = nil
|
||||
-- if there's anything connected to the pipe here, remove it
|
||||
for _, connected_items in pairs(old_connected) do
|
||||
connected_items[hash_pos] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
if old_index == nil then
|
||||
minetest.log("error", "[waterworks] pipe removed from pos " .. minetest.pos_to_string(pos) ..
|
||||
" didn't belong to any networks, despite being neighbor to several. Something went wrong to get to this state.")
|
||||
return -1
|
||||
end
|
||||
|
||||
-- get the hashes of the neighbor positions.
|
||||
-- We're maintaining a set as well as a list because they're
|
||||
-- efficient for different purposes. The list is easy to count,
|
||||
-- the set is easy to test membership of.
|
||||
local neighbor_hashes_list = {}
|
||||
local neighbor_hashes_set = {}
|
||||
for i, neighbor_pos in ipairs(neighbor_pipes) do
|
||||
local neighbor_hash = minetest.hash_node_position(neighbor_pos)
|
||||
neighbor_hashes_list[i] = neighbor_hash
|
||||
neighbor_hashes_set[neighbor_hash] = true
|
||||
end
|
||||
|
||||
-- We're going to need to traverse through the old network, starting from each of our neighbors,
|
||||
-- to establish what's still connected.
|
||||
local to_visit = {}
|
||||
local visited = {[hash_pos] = true} -- set of hashes we've visited already. We know the starting point is not valid.
|
||||
local new_nets = {} -- this will be where we put new sets of connected nodes.
|
||||
while #neighbor_hashes_list > 0 do
|
||||
local current_neighbor = table.remove(neighbor_hashes_list) -- pop neighbor hash and push it into the to_visit list.
|
||||
neighbor_hashes_set[current_neighbor] = nil
|
||||
table.insert(to_visit, current_neighbor) -- file that neighbor hash as our starting point.
|
||||
local new_net = init_new_network(current_neighbor) -- we know that hash is in old_net, so initialize the new_net with it.
|
||||
local new_pipes = new_net.pipes
|
||||
while #to_visit > 0 do
|
||||
local current_hash = table.remove(to_visit)
|
||||
for _, cardinal_hash in ipairs(cardinal_dirs_hash) do
|
||||
local test_hash = cardinal_hash + current_hash
|
||||
if not visited[test_hash] then
|
||||
if old_pipes[test_hash] then
|
||||
-- we've traversed to a node that was in the old network
|
||||
old_pipes[test_hash] = nil -- remove from old network
|
||||
new_pipes[test_hash] = true -- add to one we're building
|
||||
table.insert(to_visit, test_hash) -- flag it as next one to traverse from
|
||||
if neighbor_hashes_set[test_hash] then
|
||||
--we've encountered another neighbor while traversing
|
||||
--eliminate it from future consideration as a starting point.
|
||||
neighbor_hashes_set[test_hash] = nil
|
||||
for i, neighbor_hash_in_list in ipairs(neighbor_hashes_list) do
|
||||
if neighbor_hash_in_list == test_hash then
|
||||
table.remove(neighbor_hashes_list, i)
|
||||
break
|
||||
end
|
||||
end
|
||||
if #neighbor_hashes_list == 0 then
|
||||
--Huzzah! We encountered all neighbors. The rest of the nodes in old_net should belong to new_net.
|
||||
--We can skip all remaining pathfinding flood-fill and connected testing
|
||||
for remaining_hash, _ in pairs(old_pipes) do
|
||||
new_pipes[remaining_hash] = true
|
||||
to_visit = {}
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
visited[current_hash] = true
|
||||
end
|
||||
table.insert(new_nets, new_net)
|
||||
end
|
||||
|
||||
-- distribute connected items to the new nets
|
||||
if #new_nets == 1 then
|
||||
-- net didn't split, just keep the old stuff
|
||||
new_nets[1].connected = old_connected
|
||||
else
|
||||
for _, new_net in ipairs(new_nets) do
|
||||
local new_pipes = new_net.pipes
|
||||
for item_type, item_list in pairs(old_connected) do
|
||||
new_net.connected[item_type] = new_net.connected[item_type] or {}
|
||||
for connection_hash, connection_data in pairs(item_list) do
|
||||
if new_pipes[connection_hash] then
|
||||
new_net.connected[item_type][connection_hash] = connection_data
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- replace the old net with one of the new nets
|
||||
pipe_networks[old_index] = table.remove(new_nets)
|
||||
-- if there are any additional nets left, add those as brand new ones.
|
||||
for _, new_net in ipairs(new_nets) do
|
||||
table.insert(pipe_networks, new_net)
|
||||
end
|
||||
return old_index
|
||||
end
|
||||
end
|
||||
|
||||
waterworks.place_connected = function(pos, item_type, data)
|
||||
local node = minetest.get_node(pos)
|
||||
local dir_index = facedir_to_dir_index(node.param2)
|
||||
local dir_hash = facedir_to_cardinal_hash(dir_index)
|
||||
local pos_hash = minetest.hash_node_position(pos)
|
||||
local connection_hash = pos_hash + dir_hash
|
||||
|
||||
for i, net in ipairs(pipe_networks) do
|
||||
if net.pipes[connection_hash] then
|
||||
net.connected[item_type] = net.connected[item_type] or {}
|
||||
net.connected[item_type][connection_hash] = net.connected[item_type][connection_hash] or {}
|
||||
net.connected[item_type][connection_hash][dir_index] = data
|
||||
invalidate_cache(net)
|
||||
return i
|
||||
end
|
||||
end
|
||||
|
||||
return -1
|
||||
end
|
||||
|
||||
waterworks.remove_connected = function(pos, item_type)
|
||||
local node = minetest.get_node(pos)
|
||||
local dir_index = facedir_to_dir_index(node.param2)
|
||||
local dir_hash = facedir_to_cardinal_hash(dir_index)
|
||||
local pos_hash = minetest.hash_node_position(pos)
|
||||
local connection_hash = pos_hash + dir_hash
|
||||
|
||||
for i, net in ipairs(pipe_networks) do
|
||||
if net.pipes[connection_hash] then
|
||||
local item_list = net.connected[item_type]
|
||||
if item_list then
|
||||
if item_list[connection_hash] ~= nil then
|
||||
local connected_items = item_list[connection_hash]
|
||||
connected_items[dir_index] = nil
|
||||
local count = 0
|
||||
for _, data in pairs(connected_items) do
|
||||
count = count + 1
|
||||
end
|
||||
if count == 0 then
|
||||
item_list[connection_hash] = nil
|
||||
end
|
||||
count = 0
|
||||
for _, item in pairs(item_list) do
|
||||
count = count + 1
|
||||
end
|
||||
if count == 0 then
|
||||
net.connected[item_type] = nil
|
||||
end
|
||||
invalidate_cache(net)
|
||||
return i
|
||||
end
|
||||
end
|
||||
break -- If we get here, we didn't find the connected node even though we should have.
|
||||
end
|
||||
end
|
||||
|
||||
return -1
|
||||
end
|
||||
|
||||
waterworks.find_network_for_pipe_hash = function(hash)
|
||||
for i, net in ipairs(pipe_networks) do
|
||||
if net.pipes[hash] then
|
||||
return i
|
||||
end
|
||||
end
|
||||
return -1
|
||||
end
|
|
@ -0,0 +1,279 @@
|
|||
|
||||
minetest.register_node("waterworks:pipe", {
|
||||
description = "Waterworks Pipe",
|
||||
tiles = {
|
||||
{name="waterworks_pipe.png^waterworks_pipe_rivets_offset.png", scale=4, align_style="world"},
|
||||
{name="waterworks_pipe.png^waterworks_pipe_rivets_offset_2.png", scale=4, align_style="world"},
|
||||
{name="waterworks_pipe.png^waterworks_pipe_rivets.png", scale=4, align_style="world"},
|
||||
{name="waterworks_pipe.png^waterworks_pipe_rivets_offset_2.png", scale=4, align_style="world"},
|
||||
{name="waterworks_pipe.png^waterworks_pipe_rivets.png", scale=4, align_style="world"},
|
||||
{name="waterworks_pipe.png^waterworks_pipe_rivets_offset_2.png", scale=4, align_style="world"},
|
||||
},
|
||||
connects_to = {"group:waterworks_pipe", "group:waterworks_connected", "group:waterworks_inert"},
|
||||
connect_sides = { "top", "bottom", "front", "left", "back", "right" },
|
||||
drawtype = "nodebox",
|
||||
node_box = {
|
||||
type = "connected",
|
||||
fixed = {-0.25,-0.25,-0.25,0.25,0.25,0.25},
|
||||
connect_top = {-0.375, 0, -0.375, 0.375, 0.5, 0.375},
|
||||
connect_bottom = {-0.375, -0.5, -0.375, 0.375, 0, 0.375},
|
||||
connect_back = {-0.375, -0.375, 0, 0.375, 0.375, 0.5},
|
||||
connect_right = {0, -0.375, -0.375, 0.5, 0.375, 0.375},
|
||||
connect_front = {-0.375, -0.375, -0.5, 0.375, 0.375, 0},
|
||||
connect_left = {-0.5, -0.375, -0.375, 0, 0.375, 0.375},
|
||||
disconnected = {-0.375,-0.375,-0.375,0.375,0.375,0.375},
|
||||
},
|
||||
paramtype = "light",
|
||||
|
||||
is_ground_content = false,
|
||||
groups = {oddly_breakable_by_hand = 1, waterworks_pipe = 1},
|
||||
sounds = default.node_sound_metal_defaults(),
|
||||
on_construct = function(pos)
|
||||
waterworks.place_pipe(pos)
|
||||
end,
|
||||
on_destruct = function(pos)
|
||||
waterworks.remove_pipe(pos)
|
||||
end,
|
||||
})
|
||||
|
||||
-----------------------------------------------------------------
|
||||
|
||||
minetest.register_node("waterworks:valve_on", {
|
||||
description = "Waterworks Valve (open)",
|
||||
tiles = {"waterworks_metal.png^waterworks_valve_seam.png^waterworks_valve_on.png",},
|
||||
connects_to = {"group:waterworks_pipe", "group:waterworks_connected", "group:waterworks_inert"},
|
||||
connect_sides = { "top", "bottom", "front", "left", "back", "right" },
|
||||
drawtype = "nodebox",
|
||||
node_box = {
|
||||
type = "connected",
|
||||
fixed = {-0.4375,-0.4375,-0.4375,0.4375,0.4375,0.4375},
|
||||
connect_top = {-0.375, 0.4375, -0.375, 0.375, 0.5, 0.375},
|
||||
connect_bottom = {-0.375, -0.5, -0.375, 0.375, -0.4375, 0.375},
|
||||
connect_back = {-0.375, -0.375, 0.4375, 0.375, 0.375, 0.5},
|
||||
connect_right = {0.4375, -0.375, -0.375, 0.5, 0.375, 0.375},
|
||||
connect_front = {-0.375, -0.375, -0.5, 0.375, 0.375, -0.4375},
|
||||
connect_left = {-0.5, -0.375, -0.375, -0.4375, 0.375, 0.375},
|
||||
},
|
||||
paramtype = "light",
|
||||
|
||||
is_ground_content = false,
|
||||
groups = {oddly_breakable_by_hand = 1, waterworks_pipe = 1},
|
||||
sounds = default.node_sound_metal_defaults(),
|
||||
on_construct = function(pos)
|
||||
waterworks.place_pipe(pos)
|
||||
end,
|
||||
on_destruct = function(pos)
|
||||
waterworks.remove_pipe(pos)
|
||||
end,
|
||||
on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
|
||||
node.name = "waterworks:valve_off"
|
||||
minetest.set_node(pos, node)
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_node("waterworks:valve_off", {
|
||||
description = "Waterworks Valve (closed)",
|
||||
tiles = {"waterworks_metal.png^waterworks_valve_seam.png^waterworks_valve_off.png",},
|
||||
connects_to = {"group:waterworks_pipe", "group:waterworks_connected", "group:waterworks_inert"},
|
||||
connect_sides = { "top", "bottom", "front", "left", "back", "right" },
|
||||
drawtype = "nodebox",
|
||||
node_box = {
|
||||
type = "connected",
|
||||
fixed = {-0.4375,-0.4375,-0.4375,0.4375,0.4375,0.4375},
|
||||
connect_top = {-0.375, 0.4375, -0.375, 0.375, 0.5, 0.375},
|
||||
connect_bottom = {-0.375, -0.5, -0.375, 0.375, -0.4375, 0.375},
|
||||
connect_back = {-0.375, -0.375, 0.4375, 0.375, 0.375, 0.5},
|
||||
connect_right = {0.4375, -0.375, -0.375, 0.5, 0.375, 0.375},
|
||||
connect_front = {-0.375, -0.375, -0.5, 0.375, 0.375, -0.4375},
|
||||
connect_left = {-0.5, -0.375, -0.375, -0.4375, 0.375, 0.375},
|
||||
},
|
||||
paramtype = "light",
|
||||
|
||||
is_ground_content = false,
|
||||
groups = {oddly_breakable_by_hand = 1, waterworks_inert = 1},
|
||||
sounds = default.node_sound_metal_defaults(),
|
||||
on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
|
||||
node.name = "waterworks:valve_on"
|
||||
minetest.set_node(pos, node)
|
||||
end,
|
||||
})
|
||||
|
||||
-----------------------------------------------------------------
|
||||
|
||||
local place_inlet = function(pos)
|
||||
local node = minetest.get_node(pos)
|
||||
local dir = minetest.facedir_to_dir(node.param2)
|
||||
local target = vector.subtract(pos, dir)
|
||||
waterworks.place_connected(pos, "inlet", {pos = pos, target = target, pressure = target.y})
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("infotext", "Inlet elevation " .. tostring(target.y))
|
||||
end
|
||||
minetest.register_node("waterworks:inlet", {
|
||||
description = "Waterworks Inlet",
|
||||
tiles = {
|
||||
"waterworks_metal.png",
|
||||
"waterworks_metal.png",
|
||||
"waterworks_metal.png",
|
||||
"waterworks_metal.png",
|
||||
"waterworks_metal.png^waterworks_connected_back.png",
|
||||
"waterworks_metal.png^waterworks_inlet.png",
|
||||
},
|
||||
paramtype2 = "facedir",
|
||||
is_ground_content = false,
|
||||
groups = {oddly_breakable_by_hand = 1, waterworks_connected = 1},
|
||||
sounds = default.node_sound_metal_defaults(),
|
||||
paramtype = "light",
|
||||
drawtype = "nodebox",
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = {{-0.375, -0.375, -0.375, 0.375, 0.375, 0.5}, {-0.5, -0.5, -0.5, 0.5, 0.5, -0.375}},
|
||||
},
|
||||
_waterworks_update_connected = place_inlet,
|
||||
on_construct = function(pos)
|
||||
place_inlet(pos)
|
||||
end,
|
||||
on_destruct = function(pos)
|
||||
waterworks.remove_connected(pos, "inlet")
|
||||
end,
|
||||
on_rotate = function(pos, node, user, mode, new_param2)
|
||||
waterworks.remove_connected(pos, "inlet")
|
||||
node.param2 = new_param2
|
||||
minetest.swap_node(pos, node)
|
||||
place_inlet(pos)
|
||||
return true
|
||||
end,
|
||||
})
|
||||
|
||||
local place_pumped_inlet = function(pos)
|
||||
local node = minetest.get_node(pos)
|
||||
local dir = minetest.facedir_to_dir(node.param2)
|
||||
local target = vector.subtract(pos, dir)
|
||||
waterworks.place_connected(pos, "inlet", {pos = pos, target = target, pressure = target.y + 100})
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("infotext", "Pump effective elevation " .. tostring(target.y + 100))
|
||||
end
|
||||
minetest.register_node("waterworks:pumped_inlet", {
|
||||
description = "Waterworks Pumped Inlet",
|
||||
tiles = {
|
||||
"waterworks_turbine_base.png",
|
||||
"waterworks_turbine_base.png",
|
||||
"waterworks_turbine_side.png^[transformFX",
|
||||
"waterworks_turbine_side.png",
|
||||
"waterworks_metal.png^waterworks_connected_back.png",
|
||||
"waterworks_turbine_base.png^waterworks_turbine.png",
|
||||
},
|
||||
paramtype2 = "facedir",
|
||||
is_ground_content = false,
|
||||
groups = {oddly_breakable_by_hand = 1, waterworks_connected = 1},
|
||||
sounds = default.node_sound_metal_defaults(),
|
||||
paramtype = "light",
|
||||
drawtype = "normal",
|
||||
_waterworks_update_connected = place_pumped_inlet,
|
||||
on_construct = function(pos)
|
||||
place_pumped_inlet(pos)
|
||||
end,
|
||||
on_destruct = function(pos)
|
||||
waterworks.remove_connected(pos, "inlet")
|
||||
end,
|
||||
on_rotate = function(pos, node, user, mode, new_param2)
|
||||
waterworks.remove_connected(pos, "inlet")
|
||||
node.param2 = new_param2
|
||||
minetest.swap_node(pos, node)
|
||||
place_pumped_inlet(pos)
|
||||
return true
|
||||
end,
|
||||
})
|
||||
|
||||
local place_outlet = function(pos)
|
||||
local node = minetest.get_node(pos)
|
||||
local dir = minetest.facedir_to_dir(node.param2)
|
||||
local target = vector.subtract(pos, dir)
|
||||
waterworks.place_connected(pos, "outlet", {pos = pos, target = target, pressure = target.y})
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("infotext", "Outlet elevation " .. tostring(target.y))
|
||||
end
|
||||
minetest.register_node("waterworks:outlet", {
|
||||
description = "Waterworks Outlet",
|
||||
tiles = {
|
||||
"waterworks_metal.png",
|
||||
"waterworks_metal.png",
|
||||
"waterworks_metal.png",
|
||||
"waterworks_metal.png",
|
||||
"waterworks_metal.png^waterworks_connected_back.png",
|
||||
"waterworks_metal.png^waterworks_outlet.png",
|
||||
},
|
||||
paramtype2 = "facedir",
|
||||
is_ground_content = false,
|
||||
groups = {oddly_breakable_by_hand = 1, waterworks_connected = 1},
|
||||
|
||||
paramtype = "light",
|
||||
drawtype = "nodebox",
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = {{-0.375, -0.375, -0.375, 0.375, 0.375, 0.5}, {-0.5, -0.5, -0.5, 0.5, 0.5, -0.375}},
|
||||
},
|
||||
|
||||
sounds = default.node_sound_metal_defaults(),
|
||||
_waterworks_update_connected = place_outlet,
|
||||
on_construct = function(pos)
|
||||
place_outlet(pos)
|
||||
end,
|
||||
on_destruct = function(pos)
|
||||
waterworks.remove_connected(pos, "outlet")
|
||||
end,
|
||||
on_rotate = function(pos, node, user, mode, new_param2)
|
||||
waterworks.remove_connected(pos, "outlet")
|
||||
node.param2 = new_param2
|
||||
minetest.swap_node(pos, node)
|
||||
place_outlet(pos)
|
||||
return true
|
||||
end,
|
||||
})
|
||||
|
||||
local place_grate = function(pos)
|
||||
local node = minetest.get_node(pos)
|
||||
local dir = minetest.facedir_to_dir(node.param2)
|
||||
local target = vector.subtract(pos, dir)
|
||||
waterworks.place_connected(pos, "outlet", {pos = pos, target = target, pressure = target.y})
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("infotext", "Grate elevation " .. tostring(target.y))
|
||||
end
|
||||
minetest.register_node("waterworks:grate", {
|
||||
description = "Waterworks Grate",
|
||||
tiles = {
|
||||
"waterworks_metal.png",
|
||||
"waterworks_metal.png",
|
||||
"waterworks_metal.png",
|
||||
"waterworks_metal.png",
|
||||
"waterworks_metal.png^waterworks_connected_back.png",
|
||||
"waterworks_metal.png^waterworks_grate.png",
|
||||
},
|
||||
paramtype2 = "facedir",
|
||||
is_ground_content = false,
|
||||
groups = {oddly_breakable_by_hand = 1, waterworks_connected = 1},
|
||||
|
||||
paramtype = "light",
|
||||
drawtype = "nodebox",
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = {{-0.375, -0.375, -0.375, 0.375, 0.375, 0.5}, {-0.5, -0.5, -0.5, 0.5, 0.5, -0.375}},
|
||||
},
|
||||
|
||||
sounds = default.node_sound_metal_defaults(),
|
||||
_waterworks_update_connected = place_outlet,
|
||||
on_construct = function(pos)
|
||||
place_outlet(pos)
|
||||
end,
|
||||
on_destruct = function(pos)
|
||||
waterworks.remove_connected(pos, "outlet")
|
||||
waterworks.remove_connected(pos, "inlet")
|
||||
end,
|
||||
on_rotate = function(pos, node, user, mode, new_param2)
|
||||
waterworks.remove_connected(pos, "outlet")
|
||||
waterworks.remove_connected(pos, "inlet")
|
||||
node.param2 = new_param2
|
||||
minetest.swap_node(pos, node)
|
||||
place_outlet(pos)
|
||||
return true
|
||||
end,
|
||||
})
|
After Width: | Height: | Size: 61 KiB |
After Width: | Height: | Size: 29 KiB |
After Width: | Height: | Size: 32 KiB |
|
@ -0,0 +1 @@
|
|||
waterworks_make_default_water_non_renewable (Disable self-replication of default water) bool false
|
|
@ -0,0 +1,17 @@
|
|||
The following textures are licensed under the MIT and CC0 licenses by FaceDeer:
|
||||
|
||||
waterworks_connected_back
|
||||
waterworks_grate
|
||||
waterworks_inlet
|
||||
waterworks_metal
|
||||
waterworks_outlet
|
||||
waterworks_pipe
|
||||
waterworks_pipe_rivets
|
||||
waterworks_pipe_rivets_offset
|
||||
waterworks_pipe_rivets_offset_2
|
||||
waterworks_turbine
|
||||
waterworks_turbine_base
|
||||
waterworks_turbine_side
|
||||
waterworks_valve_off
|
||||
waterworks_valve_on
|
||||
waterworks_valve_seam
|
After Width: | Height: | Size: 719 B |
After Width: | Height: | Size: 942 B |
After Width: | Height: | Size: 909 B |
After Width: | Height: | Size: 611 B |
After Width: | Height: | Size: 925 B |
After Width: | Height: | Size: 4.1 KiB |
After Width: | Height: | Size: 672 B |
After Width: | Height: | Size: 675 B |
After Width: | Height: | Size: 672 B |
After Width: | Height: | Size: 846 B |
After Width: | Height: | Size: 643 B |
After Width: | Height: | Size: 821 B |
After Width: | Height: | Size: 768 B |
After Width: | Height: | Size: 761 B |
After Width: | Height: | Size: 187 B |