diff --git a/README.md b/README.md index aaa3a9a..e6e712d 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,9 @@ SOFTWARE. * `nether_book_`* (files starting with "nether_book"): Treer, 2019-2020 * `nether_portal_ignition_failure.ogg`: Treer, 2019 * `nether_particle_anim`* (files starting with "nether_particle_anim"): Treer, 2019 + * `nether_fumarole.ogg`: Treer, 2020 + * `nether_smoke_puff.png` is a derivative work of gleamyRock.gif by Spring (https://opengameart.org/users/spring), used + under CC0 1.0. "Nether smoke puff" is licenced under CC BY-SA 4.0 Treer, 2020 ### [Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0)](http://creativecommons.org/licenses/by-sa/3.0/) * `nether_rack.png`: Zeg9 diff --git a/mapgen_decorations.lua b/mapgen_decorations.lua index fbca05e..7005da4 100644 --- a/mapgen_decorations.lua +++ b/mapgen_decorations.lua @@ -25,12 +25,22 @@ -- emerged or not before the decoration was placed. local allow_lava_decorations = nether.useBiomes -local _ = {name = "air", prob = 0} -local A = {name = "air", prob = 255, force_place = true} -local G = {name = "nether:glowstone", prob = 255, force_place = true} -local N = {name = "nether:rack", prob = 255} -local S = {name = "nether:sand", prob = 255, force_place = true} -local L = {name = "default:lava_source", prob = 255, force_place = true} +local _ = {name = "air", prob = 0} +local A = {name = "air", prob = 255, force_place = true} +local G = {name = "nether:glowstone", prob = 255, force_place = true} +local N = {name = "nether:rack", prob = 255} +local S = {name = "nether:sand", prob = 255, force_place = true} +local L = {name = "default:lava_source", prob = 255, force_place = true} +local F = {name = "nether:fumarole", prob = 255, force_place = true} +local FS = {name = "nether:fumarole_slab", prob = 255, force_place = true} +local F1 = {name = "nether:fumarole_corner", prob = 255, force_place = true, param2 = 0} +local F2 = {name = "nether:fumarole_corner", prob = 255, force_place = true, param2 = 1} +local F3 = {name = "nether:fumarole_corner", prob = 255, force_place = true, param2 = 2} +local F4 = {name = "nether:fumarole_corner", prob = 255, force_place = true, param2 = 3} +local S1 = {name = "stairs:stair_netherrack", prob = 255, force_place = true, param2 = 5} +local S2 = {name = "stairs:stair_netherrack", prob = 255, force_place = true, param2 = 7} +local S3 = {name = "stairs:stair_netherrack", prob = 255, force_place = true, param2 = 12} +local S4 = {name = "stairs:stair_netherrack", prob = 255, force_place = true, param2 = 16} -- ================= @@ -39,7 +49,7 @@ local L = {name = "default:lava_source", prob = 255, force_place = true} local schematic_GlowstoneStalactite = { size = {x = 5, y = 10, z = 5}, - data = { + data = { -- note that data is upside down _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, @@ -143,7 +153,7 @@ minetest.register_decoration({ -- ======================================= -- if player places a torch/block on this sand or digs it while standing on it, it sinks into lava -if allow_lava_decorations then +if allow_lava_decorations then minetest.register_decoration({ name = "Weak trap", deco_type = "schematic", @@ -151,11 +161,11 @@ if allow_lava_decorations then sidelen = 80, fill_ratio = 0.002, biomes = {"nether_caverns"}, - y_max = NETHER_CEILING, -- keep compatibility with mapgen_nobiomes.lua - y_min = NETHER_FLOOR, + y_max = nether.DEPTH, -- keep compatibility with mapgen_nobiomes.lua + y_min = nether.DEPTH_FLOOR, schematic = { size = {x = 4, y = 7, z = 4}, - data = { + data = { -- note that data is upside down _, _, _, _, _, _, _, _, _, N, _, _, @@ -165,7 +175,7 @@ if allow_lava_decorations then _, _, _, _, _, N, _, _, -- make it look like a stalactite if it protrudes out the bottom of a landform - _, N, _, _, + _, N, _, _, N, L, N, _, N, L, L, N, N, L, L, N, @@ -195,3 +205,195 @@ if allow_lava_decorations then rotation = "random" }) end + + +-- ========================== +-- Fumaroles (Chimneys) +-- ========================== + + +local replacements_slab = {} +local replacements_full = {["nether:fumarole_slab"] = "nether:fumarole"} + +if allow_lava_decorations then + -- Minetest engine limitations mean any mesh or nodebox node (like nether:fumarole) + -- will light up if it has lava below it, so replace the air node over the lava with + -- a node that prevents light propagation. + -- (Unfortunately this also means if a player digs down to get the lava block it'll + -- look like the lighting wasn't set in the block above the lava) + replacements_slab["air"] = "nether:airlike_darkness" + replacements_full["air"] = "nether:airlike_darkness" +else + -- Lava is frequently removed by the old mapgen, so put sand at the bottom + -- of fumaroles. + replacements_slab["default:lava_source"] = "nether:sand" + replacements_full["default:lava_source"] = "nether:sand" +end + + +local schematic_fumarole = { + size = {x = 3, y = 5, z = 3}, + data = { -- note that data is upside down + _, _, _, + _, N, _, + _, N, _, + _, _, _, + _, _, _, + + _, N, _, + N, L, N, + N, A, N, + _, F, _, + _,FS, _, + + _, _, _, + _, N, _, + _, N, _, + _, _, _, + _, _, _, + }, +} + +-- Common fumarole decoration that's flush with the floor and spawns everywhere + +minetest.register_decoration({ + name = "Sunken nether fumarole", + deco_type = "schematic", + place_on = {"nether:rack"}, + sidelen = 80, + fill_ratio = 0.005, + biomes = {"nether_caverns"}, + y_max = nether.DEPTH, + y_min = nether.DEPTH_FLOOR, + schematic = schematic_fumarole, + replacements = replacements_full, + flags = "place_center_x,place_center_z,all_floors", + place_offset_y=-4 +}) + + +-- Rarer formations of raised fumaroles in clumps + +local fumarole_clump_noise_offset = -0.58 +local fumarole_clump_noise = { + offset = fumarole_clump_noise_offset, + scale = 0.5, + spread = {x = 40, y = 40, z = 15}, + octaves = 4, + persist = 0.65, + lacunarity = 2.0, +} + +fumarole_clump_noise.offset = fumarole_clump_noise_offset - 0.035 +minetest.register_decoration({ + name = "Raised Nether fumarole", + deco_type = "schematic", + place_on = {"nether:rack"}, + sidelen = 8, + noise_params = fumarole_clump_noise, + biomes = {"nether_caverns"}, + y_max = nether.DEPTH, + y_min = nether.DEPTH_FLOOR, + schematic = schematic_fumarole, + replacements = replacements_full, + flags = "place_center_x,place_center_z,all_floors", + place_offset_y=-3 +}) + +fumarole_clump_noise.offset = fumarole_clump_noise_offset +minetest.register_decoration({ + name = "Half-raised Nether fumarole", + deco_type = "schematic", + place_on = {"nether:rack"}, + sidelen = 8, + noise_params = fumarole_clump_noise, + biomes = {"nether_caverns"}, + y_max = nether.DEPTH, + y_min = nether.DEPTH_FLOOR, + schematic = schematic_fumarole, + replacements = replacements_slab, + flags = "place_center_x,place_center_z,all_floors", + place_offset_y=-3 +}) + +fumarole_clump_noise.offset = fumarole_clump_noise_offset - 0.035 +minetest.register_decoration({ + name = "Nether fumarole mound", + deco_type = "schematic", + place_on = {"nether:rack"}, + sidelen = 8, + noise_params = fumarole_clump_noise, + biomes = {"nether_caverns"}, + y_max = nether.DEPTH, + y_min = nether.DEPTH_FLOOR, + schematic = { + size = {x = 4, y = 4, z = 4}, + data = { -- note that data is upside down + _, _, _, _, + _, N, N, _, + _, _, _, _, + _, _, _, _, + + _, S, S, _, + N, A, A, N, + _, S2, S1, _, + _, F2, F1, _, + + _, S, S, _, + N, A, A, N, + _, S3, S4, _, + _, F3, F4, _, + + _, _, _, _, + _, N, N, _, + _, _, _, _, + _, _, _, _ + }, + yslice_prob = {{ypos = 3, prob = 192}} -- occasionally leave the fumarole cap off + }, + flags = "place_center_x,place_center_z,all_floors", + place_offset_y = -2 +}) + +fumarole_clump_noise.offset = fumarole_clump_noise_offset - 0.01 +minetest.register_decoration({ + name = "Double Nether fumarole", + deco_type = "schematic", + place_on = {"nether:rack"}, + sidelen = 8, + noise_params = fumarole_clump_noise, + biomes = {"nether_caverns"}, + y_max = nether.DEPTH, + y_min = nether.DEPTH_FLOOR, + schematic = { + size = {x = 4, y = 5, z = 4}, + data = { -- note that data is upside down + _, _, _, _, + _, N, N, _, + _, _, _, _, + _, _, _, _, + _, _, _, _, + + _, S, S, _, + N, A, A, N, + _, S2, S1, _, + _, F2, F, _, + _, _, FS, _, + + _, S, S, _, + F, A, A, N, -- the F may add slight variance in landforms where it gets exposed + _, S3, S4, _, + _, F3, F4, _, + _, _, _, _, + + _, _, _, _, + _, N, N, _, + _, _, _, _, + _, _, _, _, + _, _, _, _ + } + }, + flags = "place_center_x,place_center_z,all_floors", + place_offset_y = -2, + rotation = "random" +}) \ No newline at end of file diff --git a/nodes.lua b/nodes.lua index ba27164..770ae0c 100644 --- a/nodes.lua +++ b/nodes.lua @@ -139,6 +139,15 @@ stairs.register_stair_and_slab( S("Outer Nether Stair") ) +stairs.register_stair( + "netherrack", + "nether:rack", + {cracky = 2, level = 2}, + {"nether_rack.png"}, + S("Netherrack stair"), + default.node_sound_stone_defaults() +) + -- StairsPlus if minetest.get_modpath("moreblocks") then @@ -152,6 +161,232 @@ if minetest.get_modpath("moreblocks") then end +-- Fumaroles (Chimney's) + +local function fumarole_startTimer(pos, timeout_factor) + + if timeout_factor == nil then timeout_factor = 1 end + local next_timeout = (math.random(50, 900) / 10) * timeout_factor + + minetest.get_meta(pos):set_float("expected_timeout", next_timeout) + minetest.get_node_timer(pos):start(next_timeout) +end + +-- Create an LBM to start fumarole node timers +minetest.register_lbm({ + label = "Start fumarole smoke", + name = "nether:start_fumarole", + nodenames = {"nether:fumarole"}, + run_at_every_load = true, + action = function(pos, node) + local node_above = minetest.get_node({x = pos.x, y = pos.y + 1, z = pos.z}) + if node_above.name == "air" then --and node.param2 % 4 == 0 then + fumarole_startTimer(pos) + end + end +}) + +local function set_fire(pos, extinguish) + local posBelow = {x = pos.x, y = pos.y - 1, z = pos.z} + + if extinguish then + if minetest.get_node(pos).name == "fire:permanent_flame" then minetest.set_node(pos, {name="air"}) end + if minetest.get_node(posBelow).name == "fire:permanent_flame" then minetest.set_node(posBelow, {name="air"}) end + + elseif minetest.get_node(posBelow).name == "air" then + minetest.set_node(posBelow, {name="fire:permanent_flame"}) + elseif minetest.get_node(pos).name == "air" then + minetest.set_node(pos, {name="fire:permanent_flame"}) + end +end + +local function fumarole_onTimer(pos, elapsed) + + local expected_timeout = minetest.get_meta(pos):get_float("expected_timeout") + if elapsed > expected_timeout + 10 then + -- The timer didn't fire when it was supposed to, so the chunk was probably inactive and has + -- just been approached again, meaning *every* fumarole's on_timer is about to go off. + -- Skip this event and restart the clock for a future random interval. + fumarole_startTimer(pos, 1) + return false + end + + -- Fumaroles in the Nether can catch fire. + -- (if taken to the surface and used as cottage chimneys, they don't catch fire) + local inNether = pos.y <= nether.DEPTH and pos.y >= nether.DEPTH_FLOOR + local canCatchFire = inNether and minetest.registered_nodes["fire:permanent_flame"] ~= nil + local smoke_offset = 0 + local timeout_factor = 1 + local smoke_time_adj = 1 + + local posAbove = {x = pos.x, y = pos.y + 1, z = pos.z} + local extinguish = minetest.get_node(posAbove).name ~= "air" + + if extinguish or (canCatchFire and math.floor(elapsed) % 7 == 0) then + + if not extinguish then + -- fumarole gasses are igniting + smoke_offset = 1 + timeout_factor = 0.22 -- reduce burning time + end + + set_fire(posAbove, extinguish) + set_fire({x = pos.x + 1, y = pos.y + 1, z = pos.z}, extinguish) + set_fire({x = pos.x - 1, y = pos.y + 1, z = pos.z}, extinguish) + set_fire({x = pos.x, y = pos.y + 1, z = pos.z + 1}, extinguish) + set_fire({x = pos.x, y = pos.y + 1, z = pos.z - 1}, extinguish) + + elseif inNether then + + if math.floor(elapsed) % 3 == 1 then + -- throw up some embers / lava splash + local embers_particlespawn_def = { + amount = 6, + time = 0.1, + minpos = {x=pos.x - 0.1, y=pos.y + 0.0, z=pos.z - 0.1}, + maxpos = {x=pos.x + 0.1, y=pos.y + 0.2, z=pos.z + 0.1}, + minvel = {x = -.5, y = 4.5, z = -.5}, + maxvel = {x = .5, y = 7, z = .5}, + minacc = {x = 0, y = -10, z = 0}, + maxacc = {x = 0, y = -10, z = 0}, + minexptime = 1.4, + maxexptime = 1.4, + minsize = .2, + maxsize = .8, + texture = "^[colorize:#A00:255", + glow = 8 + } + minetest.add_particlespawner(embers_particlespawn_def) + embers_particlespawn_def.texture = "^[colorize:#A50:255" + embers_particlespawn_def.maxvel.y = 3 + embers_particlespawn_def.glow = 12 + minetest.add_particlespawner(embers_particlespawn_def) + + else + -- gas noises + minetest.sound_play("nether_fumarole", { + pos = pos, + max_hear_distance = 60, + gain = 0.24, + pitch = math.random(35, 95) / 100 + }) + end + + else + -- we're not in the Nether, so can afford to be a bit more smokey + timeout_factor = 0.4 + smoke_time_adj = 1.3 + end + + -- let out some smoke + minetest.add_particlespawner({ + amount = 12 * smoke_time_adj, + time = math.random(40, 60) / 10 * smoke_time_adj, + minpos = {x=pos.x - 0.2, y=pos.y + smoke_offset, z=pos.z - 0.2}, + maxpos = {x=pos.x + 0.2, y=pos.y + smoke_offset, z=pos.z + 0.2}, + minvel = {x=0, y=0.7, z=-0}, + maxvel = {x=0, y=0.8, z=-0}, + minacc = {x=0.0,y=0.0,z=-0}, + maxacc = {x=0.0,y=0.1,z=-0}, + minexptime = 5, + maxexptime = 5.5, + minsize = 1.5, + maxsize = 7, + texture = "nether_smoke_puff.png", + }) + + fumarole_startTimer(pos, timeout_factor) + return false +end + + +minetest.register_node("nether:fumarole", { + description="Fumarolic Chimney", + tiles = {"nether_rack.png"}, + on_timer = fumarole_onTimer, + after_place_node = function(pos, placer, itemstack, pointed_thing) + fumarole_onTimer(pos, 1) + return false + end, + is_ground_content = true, + groups = {cracky = 3, level = 2, fumarole=1}, + paramtype = "light", + drawtype = "nodebox", + node_box = { + type = "fixed", + fixed = { + {-0.5000, -0.5000, -0.5000, -0.2500, 0.5000, 0.5000}, + {-0.5000, -0.5000, -0.5000, 0.5000, 0.5000, -0.2500}, + {-0.5000, -0.5000, 0.2500, 0.5000, 0.5000, 0.5000}, + {0.2500, -0.5000, -0.5000, 0.5000, 0.5000, 0.5000} + } + }, + selection_box = {type = 'fixed', fixed = {-.5, -.5, -.5, .5, .5, .5}} +}) + +minetest.register_node("nether:fumarole_slab", { + description="Fumarolic Chimney Slab", + tiles = {"nether_rack.png"}, + is_ground_content = true, + on_timer = fumarole_onTimer, + after_place_node = function(pos, placer, itemstack, pointed_thing) + fumarole_onTimer(pos, 1) + return false + end, + groups = {cracky = 3, level = 2, fumarole=1}, + paramtype = "light", + drawtype = "nodebox", + node_box = { + type = "fixed", + fixed = { + {-0.5000, -0.5000, -0.5000, -0.2500, 0.000, 0.5000}, + {-0.5000, -0.5000, -0.5000, 0.5000, 0.000, -0.2500}, + {-0.5000, -0.5000, 0.2500, 0.5000, 0.000, 0.5000}, + {0.2500, -0.5000, -0.5000, 0.5000, 0.000, 0.5000} + } + }, + selection_box = {type = 'fixed', fixed = {-.5, -.5, -.5, .5, 0, .5}}, + collision_box = {type = 'fixed', fixed = {-.5, -.5, -.5, .5, 0, .5}} +}) + +minetest.register_node("nether:fumarole_corner", { + description="Fumarolic Chimney Corner", + tiles = {"nether_rack.png"}, + is_ground_content = true, + groups = {cracky = 3, level = 2, fumarole=1}, + paramtype = "light", + paramtype2 = "facedir", + drawtype = "nodebox", + node_box = { + type = "fixed", + fixed = { + {-0.2500, -0.5000, 0.5000, 0.000, 0.5000, 0.000}, + {-0.5000, -0.5000, 0.2500, 0.000, 0.5000, 0.000}, + {-0.5000, -0.5000, 0.2500, 0.000, 0.000, -0.5000}, + {0.000, -0.5000, -0.5000, 0.5000, 0.000, 0.5000} + } + }, + selection_box = { + type = 'fixed', + fixed = { + {-.5, -.5, -.5, .5, 0, .5}, + {0, 0, .5, -.5, .5, 0}, + } + } + +}) + +-- nether:airlike_darkness is an air node through which light does not propagate. +-- Use of it should be avoided when possible as it has the appearance of a lighting bug. +-- Fumarole decorations use it to stop the propagation of light from the lava below, +-- since engine limitations mean any mesh or nodebox node will light up if it has lava +-- below it. +local airlike_darkness = {} +for k,v in pairs(minetest.registered_nodes["air"]) do airlike_darkness[k] = v end +airlike_darkness.paramtype = "none" +minetest.register_node("nether:airlike_darkness", airlike_darkness) + + -- Crafting minetest.register_craft({ diff --git a/sounds/nether_fumarole.ogg b/sounds/nether_fumarole.ogg new file mode 100644 index 0000000..e02c464 Binary files /dev/null and b/sounds/nether_fumarole.ogg differ diff --git a/textures/nether_smoke_puff.png b/textures/nether_smoke_puff.png new file mode 100644 index 0000000..10cb1db Binary files /dev/null and b/textures/nether_smoke_puff.png differ