VoxeLibre/mods/CORE/flowlib/init.lua

303 lines
8.6 KiB
Lua

local math = math
local get_node = minetest.get_node
local get_item_group = minetest.get_item_group
local registered_nodes = minetest.registered_nodes
flowlib = {}
--sum of direction vectors must match an array index
--(sum,root)
--(0,1), (1,1+0=1), (2,1+1=2), (3,1+2^2=5), (4,2^2+2^2=8)
local inv_roots = {
[0] = 1,
[1] = 1,
[2] = 0.70710678118655,
[4] = 0.5,
[5] = 0.44721359549996,
[8] = 0.35355339059327,
}
local function to_unit_vector(dir_vector)
local sum = dir_vector.x * dir_vector.x + dir_vector.z * dir_vector.z
return {x = dir_vector.x * inv_roots[sum], y = dir_vector.y, z = dir_vector.z * inv_roots[sum]}
end
local function is_touching(realpos,nodepos,radius)
local boarder = 0.5 - radius
return math.abs(realpos - nodepos) > (boarder)
end
flowlib.is_touching = is_touching
local function is_water(pos)
return get_item_group(get_node(pos).name, "water") ~= 0
end
flowlib.is_water = is_water
local function node_is_water(node)
return get_item_group(node.name, "water") ~= 0
end
flowlib.node_is_water = node_is_water
local function is_lava(pos)
return get_item_group(get_node(pos).name, "lava") ~= 0
end
flowlib.is_lava = is_lava
local function node_is_lava(node)
return get_item_group(node.name, "lava") ~= 0
end
flowlib.node_is_lava = node_is_lava
local function is_liquid(pos)
return get_item_group(get_node(pos).name, "liquid") ~= 0
end
flowlib.is_liquid = is_liquid
local function node_is_liquid(node)
return get_item_group(node.name, "liquid") ~= 0
end
flowlib.node_is_liquid = node_is_liquid
--This code is more efficient
local function quick_flow_logic(node, pos_testing, direction)
local name = node.name
if not registered_nodes[name] then
return 0
end
if registered_nodes[name].liquidtype == "source" then
local node_testing = get_node(pos_testing)
if not registered_nodes[node_testing.name] then
return 0
end
if registered_nodes[node_testing.name].liquidtype ~= "flowing" then
return 0
else
return direction
end
elseif registered_nodes[name].liquidtype == "flowing" then
local node_testing = get_node(pos_testing)
local param2_testing = node_testing.param2
if not registered_nodes[node_testing.name] then
return 0
end
if registered_nodes[node_testing.name].liquidtype == "source" then
return -direction
elseif registered_nodes[node_testing.name].liquidtype == "flowing" then
if param2_testing < node.param2 then
if (node.param2 - param2_testing) > 6 then
return -direction
else
return direction
end
elseif param2_testing > node.param2 then
if (param2_testing - node.param2) > 6 then
return direction
else
return -direction
end
end
end
end
return 0
end
local function quick_flow(pos, node)
if not node_is_liquid(node) then
return {x = 0, y = 0, z = 0}
end
local x = quick_flow_logic(node,{x = pos.x-1, y = pos.y, z = pos.z},-1) + quick_flow_logic(node,{x = pos.x+1, y = pos.y, z = pos.z}, 1)
local z = quick_flow_logic(node,{x = pos.x, y = pos.y, z = pos.z-1},-1) + quick_flow_logic(node,{x = pos.x, y = pos.y, z = pos.z+1}, 1)
return to_unit_vector({x = x, y = 0, z = z})
end
flowlib.quick_flow = quick_flow
--if not in water but touching, move centre to touching block
--x has higher precedence than z
--if pos changes with x, it affects z
local function move_centre(pos, realpos, node, radius)
if is_touching(realpos.x, pos.x, radius) then
if is_liquid({x = pos.x-1, y = pos.y, z = pos.z}) then
node = get_node({x=pos.x-1, y = pos.y, z = pos.z})
pos = {x = pos.x-1, y = pos.y, z = pos.z}
elseif is_liquid({x = pos.x+1, y = pos.y, z = pos.z}) then
node = get_node({x = pos.x+1, y = pos.y, z = pos.z})
pos = {x = pos.x+1, y = pos.y, z = pos.z}
end
end
if is_touching(realpos.z, pos.z, radius) then
if is_liquid({x = pos.x, y = pos.y, z = pos.z - 1}) then
node = get_node({x = pos.x, y = pos.y, z = pos.z - 1})
pos = {x = pos.x, y = pos.y, z = pos.z - 1}
elseif is_liquid({x = pos.x, y = pos.y, z = pos.z + 1}) then
node = get_node({x = pos.x, y = pos.y, z = pos.z + 1})
pos = {x = pos.x, y = pos.y, z = pos.z + 1}
end
end
return pos, node
end
flowlib.move_centre = move_centre
--BEGIN getCornerLevel
local function get_corner_level(x, z, c_flowing, c_source, neighbors)
local sum = 0
local count = 0
local air_count = 0
for dz=0,1 do
for dx=0,1 do
local neighbor_data = neighbors[z + dz][x + dx]
if neighbor_data.top_is_same_liquid then
return 0.5 --- REPLACE returns
end
if neighbor_data.content == c_source then
return 0.5
end
if neighbor_data.content == c_flowing then
sum = sum + neighbor_data.level
count = count + 1
elseif neighbor_data.content == "air" then
air_count = air_count + 1
end
end
end
if air_count >= 2 then
return -0.5 + (0.2/10)
end
if count > 0 then
return sum/count
end
return 0
end
--END getCornerLevel
-- Based on https://github.com/minetest/minetest/blob/master/src/client/content_mapblock.cpp#L536-L813
-- If minetest ever changes liquid flowing rendering this must be changed accordingly
local function get_liquid_corner_heights(pos)
local cur_liquid = {}
local node = get_node(pos)
if not (get_item_group(node.name, "liquid") > 0) then return end
local ndef = registered_nodes[node.name]
local node_above = get_node({x=pos.x, y=pos.y+1, z=pos.z})
-- BEGIN prepareLiquidNodeDrawing
cur_liquid.content = node.name
local c_flowing = ndef.liquid_alternative_flowing
local c_source = ndef.liquid_alternative_source
cur_liquid.top_is_same_liquid = (node_above.name == c_flowing or node_above.name == c_source)
-- quick return if the liquid above is the same
if cur_liquid.top_is_same_liquid then return {0.5, 0.5, 0.5, 0.5} end
-- END prepareLiquidNodeDrawing
local c_flowing_ndef = registered_nodes[c_flowing]
--BEGIN getLiquidNeighborhood
local range = math.max(1, math.min(c_flowing_ndef.liquid_range, 8))
local neighbors = {
{{}, {}, {}},
{{}, {}, {}},
{{}, {}, {}},
}
for w = -1,1 do
for u = -1,1 do
local neighbor = neighbors[w+2][u+2]
local neighbor_node = get_node({x=pos.x + u, y=pos.y, z=pos.z + w})
neighbor.content = neighbor_node.name
neighbor.level = -0.5
if neighbor.content == c_source then
neighbor.is_same_liquid = true
neighbor.level = 0.5
elseif neighbor.content == c_flowing then
neighbor.is_same_liquid = true
local liquid_level = bit.band(neighbor_node.param2, 7)
liquid_level = math.max(liquid_level - (8 - range), 0)
neighbor.level = (-0.5 + (liquid_level + 0.5) / range)
end
if neighbor.content ~= "ignore" then
local above_neighbor_node = get_node({x=pos.x + u, y=pos.y + 1, z=pos.z + w})
if above_neighbor_node.name == c_source or above_neighbor_node.name == c_flowing then
neighbor.top_is_same_liquid = true
end
end
end
end
--END getLiquidNeighborhood
--BEGIN drawLiquidTop calculateCornerLevels
local vertices = {
get_corner_level(1, 2, c_flowing, c_source, neighbors),
get_corner_level(2, 2, c_flowing, c_source, neighbors),
get_corner_level(2, 1, c_flowing, c_source, neighbors),
get_corner_level(1, 1, c_flowing, c_source, neighbors),
}
--END drawLiquidTop calculateCornerLevels
return vertices
end
local function get_liquid_height(pos, pos_offset)
local pos_origin
if pos_offset == nil then
pos_origin = vector.round(pos)
pos_offset = pos - pos_origin
else
pos_origin = pos
end
local corner_heights = get_liquid_corner_heights(pos_origin)
if corner_heights == nil then return end
-- order: -+ ++ +- --
--first 3 vertices are connected, so are last 2 and first
local height_centre, height_x, height_z
if pos_offset.x + pos_offset.z > 0 then
-- first triangle
pos_offset = vector.subtract({x=0.5, y=0, z=0.5}, pos_offset)
height_centre = corner_heights[2]
height_x = corner_heights[1]
height_z = corner_heights[3]
else
-- second triangle
pos_offset = vector.add({x=0.5, y=0, z=0.5}, pos_offset)
height_centre = corner_heights[4]
height_x = corner_heights[3]
height_z = corner_heights[1]
end
-- interpolate based on position in triangle
local height = height_centre + (height_x - height_centre) * pos_offset.x + (height_z - height_centre) * pos_offset.z
return height + pos_origin.y
end
local function liquid_at_exact(pos, pos_offset)
local pos_origin
if pos_offset == nil then
pos_origin = vector.round(pos)
pos_offset = pos - pos_origin
else
pos_origin = pos
end
local liq_height = get_liquid_height(pos_origin, pos_offset)
if liq_height == nil then return nil end
if liq_height > (pos_offset.y + pos_origin.y) then
return get_node(pos_origin).name
else
return nil
end
end
flowlib.get_liquid_height = get_liquid_height
flowlib.liquid_at_exact = liquid_at_exact