2025-01-05 18:19:30 +00:00
|
|
|
-- Copyright (C) 2025 snoutie
|
|
|
|
-- Authors: snoutie (copyright@achtarmig.org)
|
|
|
|
-- This program is free software: you can redistribute it and/or modify
|
|
|
|
-- it under the terms of the GNU Affero General Public License as published
|
|
|
|
-- by the Free Software Foundation, either version 3 of the License, or
|
|
|
|
-- (at your option) any later version.
|
|
|
|
|
|
|
|
-- This program is distributed in the hope that it will be useful,
|
|
|
|
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
-- GNU Affero General Public License for more details.
|
|
|
|
|
|
|
|
-- You should have received a copy of the GNU Affero General Public License
|
|
|
|
-- along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
|
2025-01-06 21:18:15 +00:00
|
|
|
local modpath = core.get_modpath(core.get_current_modname())
|
|
|
|
local internal = dofile(modpath .. "/internal.lua")
|
|
|
|
|
|
|
|
-- A table of offsets for ease of use
|
2025-01-05 17:11:50 +00:00
|
|
|
local offsets = {
|
|
|
|
{ x = 0, z = 1, },
|
|
|
|
{ x = 0, z = -1, },
|
|
|
|
{ x = 1, z = 0, },
|
|
|
|
{ x = -1, z = 0, }
|
|
|
|
}
|
2025-01-06 21:18:15 +00:00
|
|
|
-- Store number of offsets for less calculations
|
2025-01-05 17:11:50 +00:00
|
|
|
local number_of_offsets = table.getn(offsets)
|
|
|
|
|
2025-01-06 21:18:15 +00:00
|
|
|
-- Adds offset to pos
|
|
|
|
-- @param pos table Position
|
|
|
|
-- @param offset table Offset
|
|
|
|
-- @return table Position with added offset
|
2025-01-05 17:11:50 +00:00
|
|
|
local function add_offset_to_pos(pos, offset)
|
|
|
|
return {
|
|
|
|
x = pos.x + offset.x,
|
|
|
|
z = pos.z + offset.z,
|
|
|
|
y = pos.y
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
2025-01-06 21:18:15 +00:00
|
|
|
-- Get a LPN from either the buffer or create a new one
|
|
|
|
-- and add it to the buffer
|
|
|
|
-- @param b table Buffered LPN
|
|
|
|
-- @param pos table Positon of the node
|
|
|
|
-- @return table LPN
|
2025-01-05 17:11:50 +00:00
|
|
|
local function get_lpn_buffered(b, pos)
|
|
|
|
local h = core.hash_node_position(pos)
|
|
|
|
if b[h] ~= nil then
|
|
|
|
return b[h]
|
|
|
|
end
|
2025-01-06 21:18:15 +00:00
|
|
|
b[h] = internal.new_lpn(h, pos)
|
2025-01-05 17:11:50 +00:00
|
|
|
return b[h]
|
|
|
|
end
|
|
|
|
|
2025-01-06 21:18:15 +00:00
|
|
|
-- Checks if liquid_id of curr and lpn are the same.
|
|
|
|
-- Checks if the liquid_level is above -1
|
|
|
|
-- @param curr table The current LPN
|
|
|
|
-- @param lpn table The LPN to compare against
|
|
|
|
-- @return bool Is this LPN relevant
|
2025-01-05 17:11:50 +00:00
|
|
|
local function is_lpn_relevant(curr, lpn)
|
|
|
|
return lpn.liquid_level >= 0 and (lpn.liquid_level == 0 or curr.liquid_id == lpn.liquid_id)
|
|
|
|
end
|
|
|
|
|
2025-01-06 21:18:15 +00:00
|
|
|
-- Gets all adjacent liquid nodes with the same liquid_id as curr
|
|
|
|
-- Gets all adjacent air nodes
|
|
|
|
-- @param b table Buffered LPN
|
|
|
|
-- @param curr table The current LPN
|
|
|
|
-- @return table Table of neighboring LPN
|
|
|
|
-- @return number Number of neighbors
|
2025-01-05 17:11:50 +00:00
|
|
|
local function get_valid_neighbors(b, curr)
|
|
|
|
local neighbors = {}
|
|
|
|
local number_of_neighbors = 0
|
|
|
|
for i = 1, number_of_offsets do
|
|
|
|
local pos = add_offset_to_pos(curr.pos, offsets[i])
|
|
|
|
local lpn = get_lpn_buffered(b, pos)
|
|
|
|
|
|
|
|
if is_lpn_relevant(curr, lpn) then
|
|
|
|
number_of_neighbors = number_of_neighbors + 1
|
|
|
|
table.insert(neighbors, math.random(number_of_neighbors, 1), lpn)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return neighbors, number_of_neighbors
|
|
|
|
end
|
|
|
|
|
2025-01-06 21:18:15 +00:00
|
|
|
-- Gets neighbors of curr and calculates the pressure
|
|
|
|
-- This function combines get_valid_neighbors and get_pressure_straight
|
|
|
|
-- @param b table Buffered LPN
|
|
|
|
-- @param curr table The current LPN
|
|
|
|
-- @return float Pressure of curr calculated from valid neighbors
|
2025-01-05 17:11:50 +00:00
|
|
|
local function get_valid_neigbor_pressure(b, curr)
|
|
|
|
local number_of_neighbors = 0
|
|
|
|
local total_liquid_level = curr.liquid_level
|
|
|
|
for i = 1, number_of_offsets do
|
|
|
|
local pos = add_offset_to_pos(curr.pos, offsets[i])
|
|
|
|
local lpn = get_lpn_buffered(b, pos)
|
|
|
|
|
|
|
|
if is_lpn_relevant(curr, lpn) then
|
|
|
|
total_liquid_level = total_liquid_level + lpn.liquid_level / 2
|
|
|
|
number_of_neighbors = number_of_neighbors + 1
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return total_liquid_level / (number_of_neighbors + 1)
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Gets the pressure of curr from the neighbors given
|
2025-01-06 21:18:15 +00:00
|
|
|
-- @param b table Buffered LPN
|
|
|
|
-- @param neighbors table Table of neighboring LPN
|
|
|
|
-- @param number_of_neighbors number Number of neighbors
|
|
|
|
-- @param curr table The current LPN
|
|
|
|
-- @return float Pressure of curr calculated from neighbors
|
2025-01-05 17:11:50 +00:00
|
|
|
local function get_pressure_straight(b, neighbors, number_of_neighbors, curr)
|
|
|
|
local total_liquid_level = curr.liquid_level
|
|
|
|
|
|
|
|
for i = 1, number_of_neighbors do
|
|
|
|
total_liquid_level = total_liquid_level + neighbors[i].liquid_level / 2
|
|
|
|
end
|
|
|
|
return total_liquid_level / (number_of_neighbors + 1)
|
|
|
|
end
|
|
|
|
|
2025-01-06 21:18:15 +00:00
|
|
|
-- Tries to move amount from "from" to "to"
|
|
|
|
-- Actual amount moved may not be the same as "amount"
|
|
|
|
-- therefore the actual amount moved is returned
|
|
|
|
-- @param from table LPN from which amount is deducted
|
|
|
|
-- @param to table LPN to which amount is added
|
|
|
|
-- @return number Actual amount moved
|
2025-01-05 17:11:50 +00:00
|
|
|
local function try_move(from, to, amount)
|
|
|
|
if to.liquid_level >= 8 then
|
|
|
|
return 0
|
|
|
|
end
|
|
|
|
local max_allowed = amount + 8 - (to.liquid_level + amount)
|
|
|
|
local max_allowed_clamped = math.min(max_allowed, amount, from.liquid_level)
|
|
|
|
|
2025-01-06 21:18:15 +00:00
|
|
|
internal.set_lpn(from.liquid_id, from, from.liquid_level - max_allowed_clamped)
|
|
|
|
internal.set_lpn(from.liquid_id, to, to.liquid_level + max_allowed_clamped)
|
2025-01-05 17:11:50 +00:00
|
|
|
|
|
|
|
if max_allowed_clamped ~= amount then
|
|
|
|
return max_allowed_clamped
|
|
|
|
end
|
|
|
|
return amount
|
|
|
|
end
|
|
|
|
|
2025-01-06 21:18:15 +00:00
|
|
|
-- Calculates how to move curr_pos
|
|
|
|
-- @param b table Buffered LPN
|
|
|
|
-- @param curr_pos table Current position
|
2025-01-05 17:11:50 +00:00
|
|
|
local function move(b, curr_pos)
|
|
|
|
local curr_nbs
|
|
|
|
local curr_nnbs
|
|
|
|
local up_lpn
|
|
|
|
|
|
|
|
--check down first
|
|
|
|
local down_lpn = get_lpn_buffered(b, { x = curr_pos.x, y = curr_pos.y - 1, z = curr_pos.z })
|
|
|
|
local curr_lpn = get_lpn_buffered(b, curr_pos)
|
|
|
|
|
|
|
|
if curr_lpn.liquid_level <= 0 then
|
2025-01-06 21:18:15 +00:00
|
|
|
internal.remove_node_to_check(curr_lpn.pos)
|
2025-01-05 17:11:50 +00:00
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
curr_nbs, curr_nnbs = get_valid_neighbors(b, curr_lpn)
|
|
|
|
|
|
|
|
if is_lpn_relevant(curr_lpn, down_lpn) then
|
|
|
|
local moved = try_move(curr_lpn, down_lpn, curr_lpn.liquid_level)
|
|
|
|
if moved > 0 then
|
|
|
|
up_lpn = get_lpn_buffered(b, { x = curr_pos.x, y = curr_pos.y + 1, z = curr_pos.z })
|
|
|
|
if up_lpn.liquid_level >= 0 then
|
2025-01-06 21:18:15 +00:00
|
|
|
internal.add_node_to_check(up_lpn.pos)
|
2025-01-05 17:11:50 +00:00
|
|
|
end
|
2025-01-06 21:18:15 +00:00
|
|
|
internal.add_node_to_check(down_lpn.pos)
|
2025-01-05 17:11:50 +00:00
|
|
|
end
|
|
|
|
if moved == curr_lpn.liquid_level then
|
2025-01-06 21:18:15 +00:00
|
|
|
internal.remove_node_to_check(curr_lpn.pos)
|
2025-01-05 17:11:50 +00:00
|
|
|
for i = 1, curr_nnbs do
|
2025-01-06 21:18:15 +00:00
|
|
|
internal.add_node_to_check(curr_nbs[i].pos)
|
2025-01-05 17:11:50 +00:00
|
|
|
end
|
|
|
|
return
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2025-01-06 21:18:15 +00:00
|
|
|
-- Every neighbor will be higher (except for air) so processing is done
|
2025-01-05 17:11:50 +00:00
|
|
|
if curr_lpn.liquid_level <= 1 then
|
2025-01-06 21:18:15 +00:00
|
|
|
internal.remove_node_to_check(curr_lpn.pos)
|
2025-01-05 17:11:50 +00:00
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
local curr_prs = get_pressure_straight(b, curr_nbs, curr_nnbs, curr_lpn)
|
|
|
|
|
|
|
|
if curr_prs >= 4.8 then --4.8 == ((8*4/2)+8)/5 -> meaning maximum pressure, implies no way to move
|
2025-01-06 21:18:15 +00:00
|
|
|
internal.remove_node_to_check(curr_lpn.pos)
|
2025-01-05 17:11:50 +00:00
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2025-01-06 21:18:15 +00:00
|
|
|
-- TODO: Understand what is happening here and optimize
|
2025-01-05 17:11:50 +00:00
|
|
|
local number_of_swaps = 0
|
|
|
|
for i = 1, curr_nnbs do
|
|
|
|
local next_lpn = curr_nbs[i]
|
|
|
|
local next_prs = get_valid_neigbor_pressure(b, next_lpn)
|
|
|
|
if curr_prs > next_prs and next_lpn.liquid_level < curr_lpn.liquid_level then
|
|
|
|
if try_move(curr_lpn, next_lpn, 1) > 0 then
|
2025-01-06 21:18:15 +00:00
|
|
|
internal.add_node_to_check(next_lpn.pos)
|
|
|
|
internal.add_node_to_check(
|
|
|
|
get_lpn_buffered(b, { x = curr_pos.x, y = curr_pos.y + 1, z = curr_pos.z }).pos
|
|
|
|
)
|
2025-01-05 17:11:50 +00:00
|
|
|
number_of_swaps = number_of_swaps + 1
|
|
|
|
end
|
|
|
|
elseif curr_lpn.liquid_level < next_lpn.liquid_level then
|
2025-01-06 21:18:15 +00:00
|
|
|
internal.add_node_to_check(next_lpn.pos)
|
2025-01-05 17:11:50 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if number_of_swaps == 0 then
|
2025-01-06 21:18:15 +00:00
|
|
|
internal.remove_node_to_check(curr_lpn.pos)
|
2025-01-05 17:11:50 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
core.register_on_mapblocks_changed(function(modified_blocks, modified_blocks_count)
|
2025-01-06 21:18:15 +00:00
|
|
|
-- Buffered LPN
|
2025-01-05 17:11:50 +00:00
|
|
|
local b = {}
|
2025-01-06 21:18:15 +00:00
|
|
|
for hpos, pos in pairs(liquid_physics._nodes_to_check) do
|
2025-01-05 17:11:50 +00:00
|
|
|
if core.compare_block_status(pos, "active") then
|
|
|
|
move(b, pos)
|
|
|
|
else
|
2025-01-06 21:18:15 +00:00
|
|
|
internal.remove_node_to_check(pos)
|
2025-01-05 17:11:50 +00:00
|
|
|
end
|
|
|
|
end
|
2025-01-06 21:18:15 +00:00
|
|
|
-- Update only changed nodes from buffer
|
2025-01-05 17:11:50 +00:00
|
|
|
for _, lpn in pairs(b) do
|
|
|
|
if lpn.init_liquid_level ~= lpn.liquid_level then
|
2025-01-11 21:57:44 +00:00
|
|
|
internal.set_node(lpn)
|
2025-01-05 17:11:50 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end)
|