From 66c222e93b6b48d26ba1be9ade9387313ca9ffe3 Mon Sep 17 00:00:00 2001 From: HybridDog Date: Sat, 10 Oct 2020 17:46:06 +0200 Subject: [PATCH] Fix liquid flowing down ledges --- README.md | 3 -- init.lua | 113 +++++++++++++++++++++++++++++++----------------------- 2 files changed, 65 insertions(+), 51 deletions(-) diff --git a/README.md b/README.md index 63735b5..110f5c5 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,2 @@ For a description of this Minetest mod, see https://forum.minetest.net/viewtopic.php?f=9&t=10271. - -TODO: -* Support more fluids which push items, e.g. river water diff --git a/init.lua b/init.lua index cf9d5ad..17a89ce 100644 --- a/init.lua +++ b/init.lua @@ -2,68 +2,85 @@ -- The setting may change in-game but for simplicity we don't support this. local movement_gravity = tonumber(core.settings:get("movement_gravity")) or 9.81 --- get_flow_target_raw determines position to which the water flows, or returns --- nothing if no target position was found +local function is_flowing_liquid(nodename) + local def = minetest.registered_nodes[nodename] + return def and def.liquidtype == "flowing" +end + +-- get_flow_raw determines the fluid flow vector, or returns nothing if +-- the flow is zero local neighbour_offsets = { {x=-1, y=0, z=0}, {x=1, y=0, z=0}, {x=0, y=0, z=-1}, {x=0, y=0, z=1} } -local function get_flow_target_raw(pos) +local function get_flow_raw(pos) + -- FIXME: Do we need to treat nodes with special liquid_range differently? local param2 = minetest.get_node(pos).param2 - if param2 > 7 then - -- The liquid flows downwards + if param2 == 15 then + -- The liquid has full height and flows downwards return end local neighbours = {} for n = 1, 4 do local p = vector.add(pos, neighbour_offsets[n]) - neighbours[n] = p - neighbours[n + 4] = minetest.get_node(p) + neighbours[n] = minetest.get_node(p) end - for i = 1, 4 do - -- If a neighbour has a lower height, flow to it - local node = neighbours[i + 4] - if node.name == "default:water_flowing" - and node.param2 < param2 then - return neighbours[i] - end - end - for i = 1, 4 do - -- Flow to a downwards-flowing neighbour if its height is not too small - local node = neighbours[i + 4] - if node.name == "default:water_flowing" - and node.param2 >= 11 then - return neighbours[i] - end - end - for i = 1, 4 do - -- Flow to a neighbouring unsolid node - local node = neighbours[i + 4] - if node.name ~= "default:water_flowing" then + if param2 < 8 then + -- The liquid does not flow downwards + for i = 1, 4 do + -- Flow to a neighbouring free space if possible + local node = neighbours[i] local def = minetest.registered_nodes[node.name] - if def and not def.walkable then - return neighbours[i] + if def and not def.walkable and def.liquidtype == "none" then + return neighbour_offsets[i] end end + -- Find a liquid neighbour with lowest height + local min_height_neig = param2 + local min_neighbour + for i = 1, 4 do + local node = neighbours[i] + local height_neigh = node.param2 % 8 + if height_neigh < min_height_neig + and is_flowing_liquid(node.name) then + min_height_neig = height_neigh + min_neighbour = neighbour_offsets[i] + end + end + if min_neighbour then + return min_neighbour + end + -- No free neighbour and no flowing liquid neighbour end - -- No neighbour found + -- The flowing liquid ends here in the XZ plane, so determine the + -- flow from higher neighbour nodes + local flow = {x = 0, y = 0, z = 0} + for i = 1, 4 do + local node = neighbours[i] + local height_neigh = node.param2 % 8 + if is_flowing_liquid(node.name) then + -- A higher liquid is coming from there + flow = vector.subtract(flow, neighbour_offsets[i]) + end + end + return not vector.equals(flow, {x = 0, y = 0, z = 0}) and flow or nil end --- get_flow_target caches the results from get_flow_target_raw for 10 s -local flow_target_cache = {} -setmetatable(flow_target_cache, {__mode = "kv"}) -local function get_flow_target(pos) +-- get_flow caches the results from get_flow_raw for 10 s +local flow_cache = {} +setmetatable(flow_cache, {__mode = "kv"}) +local function get_flow(pos) local vi = minetest.hash_node_position(pos) local t = minetest.get_us_time() - if flow_target_cache[vi] - and t - flow_target_cache[vi][1] < 10 * 1000000 then - return flow_target_cache[vi][2] + if flow_cache[vi] + and t - flow_cache[vi][1] < 10 * 1000000 then + return flow_cache[vi][2] end - local flow_target = get_flow_target_raw(pos) - flow_target_cache[vi] = {t, flow_target} - return flow_target + local flow = get_flow_raw(pos) + flow_cache[vi] = {t, flow} + return flow end local item_entity = minetest.registered_entities["__builtin:item"] @@ -112,17 +129,17 @@ item_entity.on_step = function(self, dtime, ...) if not def or not def.liquidtype or def.liquidtype ~= "flowing" then return end - local pos_next = get_flow_target(pos) - if not pos_next then + local flow = get_flow(pos) + if not flow then return end local vel_current = vel_desired or self.object:get_velocity() - local acc = vector.multiply(vector.subtract(pos_next, pos), 2.0) - if math.abs(vel_current.x) > 1.0 then - acc.x = 0 - end - if math.abs(vel_current.z) > 1.0 then - acc.z = 0 + local acc + if vector.dot(vel_current, flow) < 1.0 then + acc = vector.multiply(flow, 2.0) + else + -- The item is already accelerated by the fluids + acc = {x = 0, y = 0, z = 0} end acc.y = -movement_gravity self.object:set_acceleration(acc)