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