123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265 |
- local modname = minetest.get_current_modname()
- local minetest, nodeupdate, vector = minetest, nodeupdate, vector
- local random, pairs = math.random, pairs
- local cools_to, melt_densities, random_melt_product = ...
- local function melt(pos, node)
- local node_name = node and node.name or minetest.get_node(pos).name
- minetest.set_node(pos, {name=random_melt_product(node_name)})
- return true
- end
- local function cool_down(pos, node)
- local node_name = node and node.name or minetest.get_node(pos).name
- local cold_name = cools_to[node_name]
- if not cold_name then
- return false
- end
- minetest.set_node(pos, {name = cold_name})
- minetest.spawn_falling_node(pos)-- so cooled ores fall to the bottom of a pool of water
- return true
- end
- local function swap_nodes(pos1, node1, pos2, node2)
- -- swap_node is faster than set_node, avoiding node destructors/constructors
- -- also metadata is not reset, which two molten_ores don't have anyway
- minetest.swap_node(pos1, node2 or minetest.get_node(pos2))
- minetest.swap_node(pos2, node1 or minetest.get_node(pos1))
- end
- -- fluid dynamics
- minetest.register_abm({
- nodenames = {"group:molten_ore"},
- interval = 1,
- chance = 1,
- action = function(pos, node)
- if minetest.get_item_group(node.name, "molten_ore") < 3 then
- return
- end
- local flow_name = node.name.."_flowing"
- -- look below
- local flow_nodes = minetest.find_nodes_in_area(
- {x=pos.x , y=pos.y - 1, z=pos.z},
- {x=pos.x , y=pos.y - 1, z=pos.z},
- "group:molten_ore_flowing"
- )
- for _,fp in pairs(flow_nodes) do
- swap_nodes(pos, node, fp)
- return
- end
- -- look one node out
- flow_nodes = minetest.find_nodes_in_area(
- {x=pos.x - 1, y=pos.y - 1, z=pos.z - 1},
- {x=pos.x + 1, y=pos.y - 1, z=pos.z + 1},
- "group:molten_ore_flowing"
- )
- for _,fp in pairs(flow_nodes) do
- -- check above to make sure it can get here
- local na = minetest.get_node({x=fp.x, y=fp.y+1, z=fp.z})
- local g = minetest.get_item_group(na.name, "molten_ore")
- if g > 0 then
- swap_nodes(pos, node, fp)
- return
- end
- end
- -- look two nodes out
- flow_nodes = minetest.find_nodes_in_area(
- {x=pos.x - 2, y=pos.y - 1, z=pos.z - 2},
- {x=pos.x + 2, y=pos.y - 1, z=pos.z + 2},
- "group:molten_ore_flowing"
- )
- for _,fp in pairs(flow_nodes) do
- -- check above
- local na = minetest.get_node({x=fp.x, y=fp.y+1, z=fp.z})
- local ga = minetest.get_item_group(na.name, "molten_ore")
- if ga > 0 then
- -- check between above and node
- local nb = minetest.get_node({x=(fp.x + pos.x) / 2, y=pos.y, z=(fp.z + pos.z) / 2})
- local gb = minetest.get_item_group(nb.name, "molten_ore")
- if gb > 0 then
- swap_nodes(pos, node, fp)
- return
- end
- end
- end
- end,
- })
- -- dense metals sink to the bottom
- minetest.register_abm({
- nodenames = {"group:molten_ore_source"},
- neightbors = {"group:molten_ore_source"},
- interval = 4,
- chance = 2,
- action = function(pos, node)
- -- look one node out
- local light_nodes = minetest.find_nodes_in_area(
- {x=pos.x - 1, y=pos.y - 1, z=pos.z - 1},
- {x=pos.x + 1, y=pos.y - 1, z=pos.z + 1},
- "group:molten_ore_source"
- )
- for _,fp in pairs(light_nodes) do
- local n = minetest.get_node(fp)
- local sd = melt_densities[node.name]
- local dd = melt_densities[n.name]
- if dd and sd and dd < sd then
- swap_nodes(pos, node, fp, n)
- return
- end
- end
- end,
- })
- local function is_heated(pos, node)
- -- don't cool near active electrodes
- if minetest.find_node_near(pos, 4, {modname..":electrode_on"}) then
- return true
- end
- -- BUG: should only heat above it
- if minetest.find_node_near(pos, 4, {modname..":burner_on"}) then
- return true
- end
- -- BUG: should only heat above it
- if minetest.find_node_near(pos, 5, {modname..":oil_burner_on"}) then
- return true
- end
- -- don't cool near heater bricks
- if minetest.find_node_near(pos, 2, {modname..":furnace_heater"}) then
- return true
- end
- return false
- end
- -- air cooling
- minetest.register_abm({
- nodenames = {"group:molten_ore"},
- interval = 10,
- chance = 15,
- action = function(pos, node)
- if is_heated(pos, node) then
- return
- end
- -- let ore fall before cooling
- local below = minetest.get_node_or_nil({x=pos.x, y=pos.y-1, z=pos.z})
- if below then
- if 0 ~= minetest.get_item_group(below.name, "molten_ore_flowing") then
- return
- end
- -- melt cools 3 times more slowly over refractory materials
- -- helps prevent clogs in structures
- if 0 ~= minetest.get_item_group(below.name, "refractory") then
- if random(3) >= 2 then
- return
- end
- end
- end
- if cool_down(pos, node) then
- minetest.sound_play("default_cool_lava",
- {pos = pos, max_hear_distance = 16, gain = 0.25})
- end
- end,
- })
- local function spawnSteam(pos)
- pos.y = pos.y+1
- minetest.add_particlespawner({
- amount = 20,
- time = 3,
- minpos = vector.subtract(pos, 2 / 2),
- maxpos = vector.add(pos, 2 / 2),
- minvel = {x=-0.1, y=0, z=-0.1},
- maxvel = {x=0.1, y=0.5, z=0.1},
- minacc = {x=-0.1, y=0.1, z=-0.1},
- maxacc = {x=0.1, y=0.3, z=0.1},
- minexptime = 1,
- maxexptime = 3,
- minsize = 10,
- maxsize = 20,
- texture = modname.."_steam.png^[colorize:white:120",
- })
- end
- -- water cooling
- minetest.register_abm({
- nodenames = {"group:molten_ore"},
- neighbors = {
- "default:water_source",
- "default:water_flowing",
- "default:river_water_source",
- "default:river_water_flowing"
- },
- interval = 2,
- chance = 2,
- action = function(pos, node)
- if cool_down(pos, node) then
- spawnSteam(pos)
- minetest.sound_play("default_cool_lava",
- {pos = pos, max_hear_distance = 16, gain = 0.25})
- end
- end,
- })
- local function try_conduct(pos, node)
- local node = minetest.get_node_or_nil(pos)
- if node and 0 == minetest.get_item_group(node.name, "refractory") and
- 0 == minetest.get_item_group(node.name, "molten_ore") then
- melt(pos, node)
- -- avoid conduction loops around heaters
- if not is_heated(pos, node) then
- -- but conserve energy if possible, which also protects from
- -- melting entire mountains from a single molten ore
- cool_down(pos, node)
- end
- return true
- end
- return false
- end
- local heat_conduct_dirs = {
- {1, 0}, {-1, 0}, {0, 1}, {0, -1},
- }
- -- molten ore conducts heat, either remelting other things or destroying them
- minetest.register_abm({
- nodenames = {"group:molten_ore_source"},
- interval = 5,
- chance = 40,
- action = function(pos, node)
- -- prefer the node below
- if try_conduct({x=pos.x, y=pos.y - 1, z=pos.z }) then
- return
- end
- -- then start with a random direction and rotate
- local start = random(4)
- for i=0, 3 do
- local dir = heat_conduct_dirs[(start + i) % 4 + 1]
- if try_conduct({x=pos.x + dir[1], y=pos.y, z=pos.z + dir[2]}) then
- return
- end
- end
- -- above is not destroyed
- end,
- })
|