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