From 08989ef513c7499e8054195c698b456f60f93a0d Mon Sep 17 00:00:00 2001 From: FaceDeer Date: Fri, 7 Apr 2017 00:23:34 -0600 Subject: [PATCH] redo lava/water interaction --- cooling_lava.lua | 220 +++++++++++++++++++++++++++++++++++++++++++++++ init.lua | 2 + settingtypes.txt | 6 ++ 3 files changed, 228 insertions(+) create mode 100644 cooling_lava.lua diff --git a/cooling_lava.lua b/cooling_lava.lua new file mode 100644 index 0000000..e15efb9 --- /dev/null +++ b/cooling_lava.lua @@ -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, +}) \ No newline at end of file diff --git a/init.lua b/init.lua index ca08f81..6b70320 100644 --- a/init.lua +++ b/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. diff --git a/settingtypes.txt b/settingtypes.txt index 9bcac89..9830eb2 100644 --- a/settingtypes.txt +++ b/settingtypes.txt @@ -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.