forked from Minetest/dynamic_liquid
redo lava/water interaction
parent
7ce992363b
commit
08989ef513
|
@ -0,0 +1,220 @@
|
|||
if not minetest.get_modpath("default") then return end
|
||||
|
||||
local new_lava_cooling = minetest.setting_getbool("dynamic_liquid_new_lava_cooling")
|
||||
new_lava_cooling = new_lava_cooling or new_lava_cooling == nil -- default true
|
||||
|
||||
if not new_lava_cooling then return end
|
||||
|
||||
-- The existing cool_lava ABM is hard-coded to respond to water nodes
|
||||
-- and overriding node groups doesn't appear to work:
|
||||
-- https://github.com/minetest/minetest/issues/5518
|
||||
-- So modifying the lava-cooling system requires some unfortunate
|
||||
-- workarounds. Most notable is the "dynamic_cools_lava" group;
|
||||
-- the "cools_lava" group can't be removed from flowing water for ABM
|
||||
-- purposes so this redundancy is required.
|
||||
|
||||
-- Mods can hook into this system by adding the group
|
||||
-- "dynamic_cools_lava_flowing" and/or "dynamic_cools_lava_source"
|
||||
-- to nodes that should cool lava and
|
||||
-- "dynamic_lava_flowing_destroys" and/or "dynamic_lava_source_destroys"
|
||||
-- to nodes that should be destroyed by proximity to lava.
|
||||
|
||||
local particles = minetest.setting_getbool("enable_particles")
|
||||
particles = particles or particles == nil -- default true
|
||||
|
||||
local steam = function(pos)
|
||||
if particles then
|
||||
minetest.add_particlespawner({
|
||||
amount = 6,
|
||||
time = 1,
|
||||
minpos = pos,
|
||||
maxpos = pos,
|
||||
minvel = {x=-2, y=0, z=-2},
|
||||
maxvel = {x=2, y=1, z=2},
|
||||
minacc = {x=0, y=2, z=0},
|
||||
maxacc = {x=0, y=2, z=0},
|
||||
minexptime = 1,
|
||||
maxexptime = 4,
|
||||
minsize = 16,
|
||||
maxsize = 16,
|
||||
collisiondetection = false,
|
||||
vertical = false,
|
||||
texture = "smoke_puff.png",
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
default.cool_lava = function(pos, node)
|
||||
-- no-op disables default cooling ABM
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------
|
||||
|
||||
local dynamic_cools_lava_flowing = {"group:dynamic_cools_lava_flowing", "group:cools_lava"}
|
||||
|
||||
-- Flowing lava will turn these blocks into steam.
|
||||
local dynamic_lava_flowing_destroys = {
|
||||
"group:dynamic_lava_flowing_destroys",
|
||||
"default:water_flowing",
|
||||
"default:river_water_flowing",
|
||||
"default:snow",
|
||||
"default:snowblock"
|
||||
}
|
||||
|
||||
local all_flowing_nodes = {unpack(dynamic_cools_lava_flowing)}
|
||||
for i = 1, #dynamic_lava_flowing_destroys do
|
||||
all_flowing_nodes[#dynamic_cools_lava_flowing + i] = dynamic_lava_flowing_destroys[i]
|
||||
end
|
||||
|
||||
local cool_lava_flowing = function(pos, node)
|
||||
local cooler_adjacent = minetest.find_node_near(pos, 1, dynamic_cools_lava_flowing)
|
||||
|
||||
if cooler_adjacent ~= nil then
|
||||
-- pulling nearby sources into position is necessary to break certain classes of
|
||||
-- flow "deadlock". Weird, but what're you gonna do.
|
||||
local nearby_source = minetest.find_node_near(pos, 1, "default:lava_source")
|
||||
if nearby_source then
|
||||
minetest.set_node(pos, {name="default:lava_source"})
|
||||
minetest.set_node(nearby_source, {name="air"})
|
||||
steam(nearby_source)
|
||||
else
|
||||
minetest.set_node(pos, {name = "air"})
|
||||
steam(pos)
|
||||
end
|
||||
end
|
||||
|
||||
local evaporate_list = minetest.find_nodes_in_area(
|
||||
vector.add(pos,{x=-1, y=-1, z=-1}),
|
||||
vector.add(pos,{x=1, y=1, z=1}),
|
||||
dynamic_lava_flowing_destroys
|
||||
)
|
||||
for _, loc in pairs(evaporate_list) do
|
||||
minetest.set_node(loc, {name="air"})
|
||||
steam(loc)
|
||||
end
|
||||
|
||||
minetest.sound_play("default_cool_lava",
|
||||
{pos = pos, max_hear_distance = 16, gain = 0.25})
|
||||
end
|
||||
|
||||
minetest.register_abm({
|
||||
label = "Lava flowing cooling",
|
||||
nodenames = {"default:lava_flowing"},
|
||||
neighbors = all_flowing_nodes,
|
||||
interval = 1,
|
||||
chance = 1,
|
||||
catch_up = false,
|
||||
action = function(...)
|
||||
cool_lava_flowing(...)
|
||||
end,
|
||||
})
|
||||
|
||||
-------------------------------------------------------------------------------------------------
|
||||
|
||||
local dynamic_cools_lava_source = {"group:dynamic_cools_lava_source"}
|
||||
for name, node_def in pairs(minetest.registered_nodes) do
|
||||
-- We don't want "flowing" nodes to cool lava source blocks, otherwise when water falls onto a large pool of lava there's
|
||||
-- way too many blocks turned to obsidian.
|
||||
if minetest.get_item_group(name, "cools_lava") > 0 and name ~= "default:water_flowing" and name ~= "default:river_water_flowing" then
|
||||
table.insert(dynamic_cools_lava_source, name)
|
||||
end
|
||||
end
|
||||
|
||||
-- lava source blocks will turn these blocks into steam.
|
||||
local dynamic_lava_source_destroys = {
|
||||
"group:dynamic_lava_source_destroys",
|
||||
"default:water_source",
|
||||
"default:river_water_source",
|
||||
"default:water_flowing",
|
||||
"default:river_water_flowing",
|
||||
"default:ice",
|
||||
"default:snow",
|
||||
"default:snowblock"
|
||||
}
|
||||
|
||||
local all_source_nodes = {unpack(dynamic_cools_lava_source)}
|
||||
for i = 1, #dynamic_lava_source_destroys do
|
||||
all_source_nodes[#dynamic_cools_lava_source + i] = dynamic_lava_source_destroys[i]
|
||||
end
|
||||
|
||||
local is_pos_in_list = function(pos, list)
|
||||
for _, loc in pairs(list) do
|
||||
if vector.equals(pos, loc) then return true end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function shuffle_array(a)
|
||||
local rand = math.random
|
||||
local iterations = #a
|
||||
local j
|
||||
|
||||
for i = iterations, 2, -1 do
|
||||
j = rand(i)
|
||||
a[i], a[j] = a[j], a[i]
|
||||
end
|
||||
end
|
||||
|
||||
local cool_lava_source = function(pos, node)
|
||||
local cooler_list = minetest.find_nodes_in_area(
|
||||
vector.add(pos,{x=-1, y=-1, z=-1}),
|
||||
vector.add(pos,{x=1, y=1, z=1}),
|
||||
dynamic_cools_lava_source)
|
||||
local evaporate_list = minetest.find_nodes_in_area(
|
||||
vector.add(pos,{x=-1, y=-1, z=-1}),
|
||||
vector.add(pos,{x=1, y=1, z=1}),
|
||||
dynamic_lava_source_destroys
|
||||
)
|
||||
|
||||
shuffle_array(cooler_list)
|
||||
|
||||
local obsidian_location = nil
|
||||
for _, loc in pairs(cooler_list) do
|
||||
if is_pos_in_list(loc, evaporate_list) then
|
||||
if loc.y < pos.y then
|
||||
if loc.z == pos.z and loc.x == pos.x then
|
||||
obsidian_location = loc -- best outcome, directly below us. End loop immediately.
|
||||
break
|
||||
else
|
||||
obsidian_location = loc -- next-best outcome, in the tier below us.
|
||||
end
|
||||
elseif loc.y == pos.y and obsidian_location == nil then
|
||||
obsidian_location = loc -- On the same level as us, acceptable if nothing else comes along.
|
||||
end
|
||||
end
|
||||
end
|
||||
if obsidian_location == nil and #cooler_list > 0 then
|
||||
obsidian_location = pos -- there's an adjacent cooler node, but it's above us. Turn into obsidian in place.
|
||||
end
|
||||
|
||||
for _, loc in pairs(evaporate_list) do
|
||||
minetest.set_node(loc, {name="air"})
|
||||
steam(loc)
|
||||
end
|
||||
|
||||
if obsidian_location ~= nil then
|
||||
minetest.set_node(pos, {name = "air"})
|
||||
minetest.set_node(obsidian_location, {name = "default:obsidian"})
|
||||
elseif #evaporate_list > 0 then
|
||||
-- Again, this weird bit is necessary for breaking certain types of flow deadlock
|
||||
local loc = evaporate_list[math.random(1,#evaporate_list)]
|
||||
minetest.set_node(pos, {name = "air"})
|
||||
minetest.set_node(loc, {name = "default:lava_source"})
|
||||
end
|
||||
|
||||
minetest.sound_play("default_cool_lava",
|
||||
{pos = pos, max_hear_distance = 16, gain = 0.25})
|
||||
end
|
||||
|
||||
|
||||
minetest.register_abm({
|
||||
label = "Lava source cooling",
|
||||
nodenames = {"default:lava_source"},
|
||||
neighbors = all_source_nodes,
|
||||
interval = 1,
|
||||
chance = 1,
|
||||
catch_up = false,
|
||||
action = function(...)
|
||||
cool_lava_source(...)
|
||||
end,
|
||||
})
|
2
init.lua
2
init.lua
|
@ -7,6 +7,8 @@ dynamic_liquid.registered_liquid_neighbors = {}
|
|||
local MP = minetest.get_modpath(minetest.get_current_modname())
|
||||
local S, NS = dofile(MP.."/intllib.lua")
|
||||
|
||||
dofile(MP.."/cooling_lava.lua")
|
||||
|
||||
-- By making this giant table of all possible permutations of horizontal direction we can avoid
|
||||
-- lots of redundant calculations.
|
||||
|
||||
|
|
|
@ -33,6 +33,12 @@ dynamic_liquid_flow_through (Flow-through) bool true
|
|||
#block modifier in these situations.
|
||||
dynamic_liquid_mapgen_prefill (Mapgen water prefill) bool true
|
||||
|
||||
#Dynamic liquid movement causes trouble with the default lava cooling system.
|
||||
#When set to true, this replaces the default lava cooling system with one
|
||||
#that will not result in oceans being paved over by a single wandering
|
||||
#lava block or lava seas being paved by a single wandering water block.
|
||||
dynamic_liquid_new_lava_cooling (Revised lava cooling) bool true
|
||||
|
||||
[Flow Rates]
|
||||
|
||||
#Sets the probability of water flow per block per second.
|
||||
|
|
Loading…
Reference in New Issue