Add the Mantle
Adds a magma oceans region to the nether outside the existing nether caverns, which can be reached via tunnels. Other misc changes: * chatcomment nether_whereami, a debug aid for knowing which perlin-noise region you are in * Nether ores no longer obtainable on the ceiling * Move crafts into crafts.lua * Add steam to lava cooling, and play bubbling lava upon death by lava * Add cracked netherbrick a decorative node which can only be obtained from dungeons or structures I encourage someone to improve or replace the cracked netherbrick texture. For copyright purposes it's currently a derivative work (by me, 2020) from nether_brick.png, which is licensed under CC BY-SA 3.0 by PilzAdam, so it can fall under the "All other media" PilzAdam's credit in readme.md rather than need its own entry.
@ -36,17 +36,20 @@ SOFTWARE.
|
||||
* `nether_portal_ignite.ogg` is a derivative of "Flame Ignition" by [hykenfreak](https://freesound.org/people/hykenfreak), used under CC BY 3.0. "Nether Portal ignite" is licensed under CC BY 3.0 by Treer.
|
||||
|
||||
### [Attribution-ShareAlike 4.0 International (CC BY-SA 4.0)](https://creativecommons.org/licenses/by-sa/4.0/)
|
||||
* `nether_basalt`* (files starting with "nether_basalt"): Treer, 2020
|
||||
* `nether_book_`* (files starting with "nether_book"): Treer, 2019-2020
|
||||
* `nether_fumarole.ogg`: Treer, 2020
|
||||
* `nether_lava_bubble`* (files starting with "nether_lava_bubble"): Treer, 2020
|
||||
* `nether_lava_crust_animated.png`: Treer, 2019-2020
|
||||
* `nether_particle_anim`* (files starting with "nether_particle_anim"): Treer, 2019
|
||||
* `nether_portal_ignition_failure.ogg`: Treer, 2019
|
||||
* `nether_smoke_puff.png`: Treer, 2020
|
||||
|
||||
### [Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0)](http://creativecommons.org/licenses/by-sa/3.0/)
|
||||
* `nether_glowstone.png`: BlockMen
|
||||
* `nether_glowstone`* (files starting with "nether_glowstone"): BlockMen
|
||||
* `nether_nether_ingot.png` & `nether_nether_lump.png`: color adjusted versions from "[default](https://github.com/minetest/minetest_game/tree/master/mods/default)" mod, originally by Gambit
|
||||
* `nether_portal.png`: [Extex101](https://github.com/Extex101), 2020
|
||||
* `nether_rack.png`: Zeg9
|
||||
* `nether_rack`* (files starting with "nether_rack"): Zeg9
|
||||
* `nether_tool_`* (files starting with "nether_tool_"): color adjusted versions from "[default](https://github.com/minetest/minetest_game/tree/master/mods/default)" mod, originals by BlockMen
|
||||
|
||||
All other media: Copyright © 2013 PilzAdam, licensed under CC BY-SA 3.0 by PilzAdam.
|
64
crafts.lua
Normal file
@ -0,0 +1,64 @@
|
||||
--[[
|
||||
|
||||
Copyright (C) 2013 PilzAdam
|
||||
Copyright (C) 2020 lortas
|
||||
Copyright (C) 2020 Treer
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for
|
||||
any purpose with or without fee is hereby granted, provided that the
|
||||
above copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
|
||||
BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
|
||||
OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
|
||||
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
|
||||
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
|
||||
SOFTWARE.
|
||||
|
||||
]]--
|
||||
|
||||
minetest.register_craft({
|
||||
output = "nether:brick 4",
|
||||
recipe = {
|
||||
{"nether:rack", "nether:rack"},
|
||||
{"nether:rack", "nether:rack"},
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "nether:fence_nether_brick 6",
|
||||
recipe = {
|
||||
{"nether:brick", "nether:brick", "nether:brick"},
|
||||
{"nether:brick", "nether:brick", "nether:brick"},
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "nether:brick_compressed",
|
||||
recipe = {
|
||||
{"nether:brick","nether:brick","nether:brick"},
|
||||
{"nether:brick","nether:brick","nether:brick"},
|
||||
{"nether:brick","nether:brick","nether:brick"},
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "nether:basalt_hewn",
|
||||
type = "shapeless",
|
||||
recipe = {
|
||||
"nether:basalt",
|
||||
"nether:basalt",
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "nether:basalt_chiselled 4",
|
||||
recipe = {
|
||||
{"nether:basalt_hewn", "nether:basalt_hewn"},
|
||||
{"nether:basalt_hewn", "nether:basalt_hewn"}
|
||||
}
|
||||
})
|
||||
|
||||
-- See tools.lua for tools related crafting
|
10
depends.txt
@ -1,8 +1,8 @@
|
||||
stairs
|
||||
default
|
||||
moreblocks?
|
||||
mesecons?
|
||||
loot?
|
||||
dungeon_loot?
|
||||
stairs
|
||||
doc_basics?
|
||||
dungeon_loot?
|
||||
fire?
|
||||
loot?
|
||||
mesecons?
|
||||
moreblocks?
|
||||
|
16
init.lua
@ -116,6 +116,7 @@ end
|
||||
dofile(nether.path .. "/portal_api.lua")
|
||||
dofile(nether.path .. "/nodes.lua")
|
||||
dofile(nether.path .. "/tools.lua")
|
||||
dofile(nether.path .. "/crafts.lua")
|
||||
if nether.NETHER_REALM_ENABLED then
|
||||
if nether.useBiomes then
|
||||
dofile(nether.path .. "/mapgen.lua")
|
||||
@ -227,3 +228,18 @@ The expedition parties have found no diamonds or gold, and after an experienced
|
||||
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
-- Play bubbling lava sounds if player killed by lava
|
||||
minetest.register_on_dieplayer(
|
||||
function(player, reason)
|
||||
if reason.node ~= nil and minetest.get_node_group(reason.node, "lava") > 0 or reason.node == "nether:lava_crust" then
|
||||
minetest.sound_play(
|
||||
"nether_lava_bubble",
|
||||
-- this sample was encoded at 3x speed to reduce .ogg file size
|
||||
-- at the expense of higher frequencies, so pitch it down ~3x
|
||||
{to_player = player:get_player_name(), pitch = 0.3, gain = 0.8}
|
||||
)
|
||||
end
|
||||
end
|
||||
)
|
260
mapgen.lua
@ -2,6 +2,12 @@
|
||||
|
||||
Nether mod for minetest
|
||||
|
||||
"mapgen.lua" is the modern biomes-based Nether mapgen, which
|
||||
requires Minetest v5.1 or greater
|
||||
"mapgen_nobiomes.lua" is the legacy version of the mapgen, only used
|
||||
in older versions of Minetest or in v6 worlds.
|
||||
|
||||
|
||||
Copyright (C) 2013 PilzAdam
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for
|
||||
@ -27,10 +33,32 @@ local NETHER_FLOOR = nether.DEPTH_FLOOR
|
||||
local TCAVE = 0.6
|
||||
local BLEND = 128
|
||||
|
||||
-- parameters for central region
|
||||
local REGION_BUFFER_THICKNESS = 0.2
|
||||
local CENTER_REGION_LIMIT = TCAVE - REGION_BUFFER_THICKNESS -- Netherrack gives way to Deep-Netherrack here
|
||||
local CENTER_CAVERN_LIMIT = CENTER_REGION_LIMIT - 0.1 -- Deep-Netherrack gives way to air here
|
||||
local SURFACE_CRUST_LIMIT = CENTER_CAVERN_LIMIT * 0.25 -- Crusted-lava at the surface of the lava ocean gives way to liquid lava here
|
||||
local CRUST_LIMIT = CENTER_CAVERN_LIMIT * 0.85 -- Crusted-lava under the surface of the lava ocean gives way to liquid lava here
|
||||
local BASALT_COLUMN_UPPER_LIMIT = CENTER_CAVERN_LIMIT * 0.9 -- Basalt columns may appear between these upper and lower limits
|
||||
local BASALT_COLUMN_LOWER_LIMIT = CENTER_CAVERN_LIMIT * 0.25 -- This value is close to SURFACE_CRUST_LIMIT so basalt columns give way to "flowing" lava rivers
|
||||
|
||||
-- Stuff
|
||||
|
||||
local math_max, math_min = math.max, math.min -- avoid needing table lookups each time a common math function is invoked
|
||||
-- Shared Nether mapgen namespace
|
||||
-- For mapgen files to share functions and constants
|
||||
nether.mapgen = {}
|
||||
local mapgen = nether.mapgen
|
||||
|
||||
mapgen.TCAVE = TCAVE -- const needed in mapgen_mantle.lua
|
||||
mapgen.BLEND = BLEND -- const needed in mapgen_mantle.lua
|
||||
mapgen.CENTER_REGION_LIMIT = CENTER_REGION_LIMIT -- const needed in mapgen_mantle.lua
|
||||
mapgen.CENTER_CAVERN_LIMIT = CENTER_CAVERN_LIMIT -- const needed in mapgen_mantle.lua
|
||||
mapgen.BASALT_COLUMN_UPPER_LIMIT = BASALT_COLUMN_UPPER_LIMIT -- const needed in mapgen_mantle.lua
|
||||
mapgen.BASALT_COLUMN_LOWER_LIMIT = BASALT_COLUMN_LOWER_LIMIT -- const needed in mapgen_mantle.lua
|
||||
|
||||
mapgen.ore_ceiling = NETHER_CEILING - BLEND -- leave a solid 128 node cap of netherrack before introducing ores
|
||||
mapgen.ore_floor = NETHER_FLOOR + BLEND
|
||||
|
||||
local debugf = nether.debug
|
||||
|
||||
if minetest.read_schematic == nil then
|
||||
-- Using biomes to create the Nether requires the ability for biomes to set "node_cave_liquid = air".
|
||||
@ -41,6 +69,18 @@ if minetest.read_schematic == nil then
|
||||
error("This " .. nether.modname .. " mapgen requires Minetest v5.1 or greater, use mapgen_nobiomes.lua instead.", 0)
|
||||
end
|
||||
|
||||
-- Load helper functions for generating the mantle / center region
|
||||
dofile(nether.path .. "/mapgen_mantle.lua")
|
||||
|
||||
|
||||
-- Misc math functions
|
||||
|
||||
-- avoid needing table lookups each time a common math function is invoked
|
||||
local math_max, math_min, math_abs, math_floor = math.max, math.min, math.abs, math.floor
|
||||
|
||||
|
||||
-- Inject nether_caverns biome
|
||||
|
||||
local function override_underground_biomes()
|
||||
-- https://forum.minetest.net/viewtopic.php?p=257522#p257522
|
||||
-- Q: Is there a way to override an already-registered biome so I can get it out of the
|
||||
@ -123,7 +163,7 @@ minetest.register_biome({
|
||||
node_stone = "nether:native_mapgen", -- nether:native_mapgen is used here to prevent the native mapgen from placing ores and decorations.
|
||||
node_filler = "nether:native_mapgen", -- The lua on_generate will transform nether:rack_native into nether:rack then decorate and add ores.
|
||||
node_dungeon = "nether:brick",
|
||||
--node_dungeon_alt = "default:mossycobble",
|
||||
node_dungeon_alt = "nether:brick_cracked",
|
||||
node_dungeon_stair = "stairs:stair_nether_brick",
|
||||
-- Setting node_cave_liquid to "air" avoids the need to filter lava and water out of the mapchunk and
|
||||
-- surrounding shell (overdraw nodes beyond the mapchunk).
|
||||
@ -152,19 +192,30 @@ minetest.register_ore({
|
||||
clust_scarcity = 11 * 11 * 11,
|
||||
clust_num_ores = 3,
|
||||
clust_size = 2,
|
||||
y_max = NETHER_CEILING,
|
||||
y_min = NETHER_FLOOR,
|
||||
y_max = mapgen.ore_ceiling,
|
||||
y_min = mapgen.ore_floor
|
||||
})
|
||||
|
||||
minetest.register_ore({
|
||||
ore_type = "scatter",
|
||||
ore = "nether:lava_crust", -- crusted lava replaces of scattered glowstone in the mantle
|
||||
wherein = "nether:rack_deep",
|
||||
clust_scarcity = 16 * 16 * 16,
|
||||
clust_num_ores = 4,
|
||||
clust_size = 2,
|
||||
y_max = mapgen.ore_ceiling,
|
||||
y_min = mapgen.ore_floor
|
||||
})
|
||||
|
||||
minetest.register_ore({
|
||||
ore_type = "scatter",
|
||||
ore = "default:lava_source",
|
||||
wherein = "nether:rack",
|
||||
wherein = {"nether:rack", "nether:rack_deep"},
|
||||
clust_scarcity = 36 * 36 * 36,
|
||||
clust_num_ores = 4,
|
||||
clust_size = 2,
|
||||
y_max = NETHER_CEILING,
|
||||
y_min = NETHER_FLOOR,
|
||||
y_max = mapgen.ore_ceiling,
|
||||
y_min = mapgen.ore_floor
|
||||
})
|
||||
|
||||
minetest.register_ore({
|
||||
@ -173,8 +224,8 @@ minetest.register_ore({
|
||||
wherein = "nether:rack",
|
||||
clust_scarcity = 14 * 14 * 14,
|
||||
clust_size = 8,
|
||||
y_max = NETHER_CEILING,
|
||||
y_min = NETHER_FLOOR
|
||||
y_max = mapgen.ore_ceiling,
|
||||
y_min = mapgen.ore_floor
|
||||
})
|
||||
|
||||
|
||||
@ -182,7 +233,7 @@ minetest.register_ore({
|
||||
|
||||
-- 3D noise
|
||||
|
||||
local np_cave = {
|
||||
mapgen.np_cave = {
|
||||
offset = 0,
|
||||
scale = 1,
|
||||
spread = {x = 384, y = 128, z = 384}, -- squashed 3:1
|
||||
@ -199,23 +250,29 @@ local nobj_cave = nil
|
||||
local nbuf_cave = {}
|
||||
local dbuf = {}
|
||||
|
||||
local yblmin = NETHER_FLOOR + BLEND * 2
|
||||
local yblmax = NETHER_CEILING - BLEND * 2
|
||||
|
||||
-- Content ids
|
||||
|
||||
local c_air = minetest.get_content_id("air")
|
||||
local c_netherrack = minetest.get_content_id("nether:rack")
|
||||
local c_netherbrick = minetest.get_content_id("nether:brick")
|
||||
local c_netherrack_deep = minetest.get_content_id("nether:rack_deep")
|
||||
local c_dungeonbrick = minetest.get_content_id("nether:brick")
|
||||
local c_dungeonbrick_alt = minetest.get_content_id("nether:brick_cracked")
|
||||
local c_netherbrick_slab = minetest.get_content_id("stairs:slab_nether_brick")
|
||||
local c_netherfence = minetest.get_content_id("nether:fence_nether_brick")
|
||||
local c_glowstone = minetest.get_content_id("nether:glowstone")
|
||||
local c_lava_source = minetest.get_content_id("default:lava_source")
|
||||
local c_lavasea_source = minetest.get_content_id("nether:lava_source") -- same as lava but with staggered animation to look better as an ocean
|
||||
local c_lava_crust = minetest.get_content_id("nether:lava_crust")
|
||||
local c_native_mapgen = minetest.get_content_id("nether:native_mapgen")
|
||||
|
||||
|
||||
-- Dungeon excavation functions
|
||||
|
||||
function is_dungeon_brick(node_id)
|
||||
return node_id == c_dungeonbrick or node_id == c_dungeonbrick_alt
|
||||
end
|
||||
|
||||
function build_dungeon_room_list(data, area)
|
||||
|
||||
local result = {}
|
||||
@ -310,6 +367,8 @@ end
|
||||
-- (Corridors and the parts of rooms which extend beyond the emerge boundary will remain filled)
|
||||
function excavate_dungeons(data, area, rooms)
|
||||
|
||||
local vi, node_id
|
||||
|
||||
-- any air from the native mapgen has been replaced by netherrack, but
|
||||
-- we don't want this inside dungeons, so fill dungeon rooms with air
|
||||
for _, roomInfo in ipairs(rooms) do
|
||||
@ -319,9 +378,10 @@ function excavate_dungeons(data, area, rooms)
|
||||
|
||||
for z = room_min.z, room_max.z do
|
||||
for y = room_min.y, room_max.y do
|
||||
local vi = area:index(room_min.x, y, z)
|
||||
vi = area:index(room_min.x, y, z)
|
||||
for x = room_min.x, room_max.x do
|
||||
if data[vi] == c_netherrack then data[vi] = c_air end
|
||||
node_id = data[vi]
|
||||
if node_id == c_netherrack or node_id == c_netherrack_deep then data[vi] = c_air end
|
||||
vi = vi + 1
|
||||
end
|
||||
end
|
||||
@ -346,20 +406,22 @@ function decorate_dungeons(data, area, rooms)
|
||||
local window_y = roomInfo.y + math_min(2, room_max.y - roomInfo.y - 1)
|
||||
|
||||
if room_seed % 3 == 0 and room_max.y < maxEdge.y then
|
||||
-- Glowstone chandelier
|
||||
-- Glowstone chandelier (feel free to replace with a fancy schematic)
|
||||
local vi = area:index(roomInfo.x, room_max.y + 1, roomInfo.z)
|
||||
if data[vi] == c_netherbrick then data[vi] = c_glowstone end
|
||||
if is_dungeon_brick(data[vi]) then data[vi] = c_glowstone end
|
||||
|
||||
elseif room_seed % 4 == 0 and room_min.y > minEdge.y
|
||||
and room_min.x > minEdge.x and room_max.x < maxEdge.x
|
||||
and room_min.z > minEdge.z and room_max.z < maxEdge.z then
|
||||
-- lava well (feel free to replace with a fancy schematic)
|
||||
local vi = area:index(roomInfo.x, room_min.y, roomInfo.z)
|
||||
if data[vi - yStride] == c_netherbrick then data[vi - yStride] = c_lava_source end
|
||||
if data[vi - zStride] == c_air then data[vi - zStride] = c_netherbrick_slab end
|
||||
if data[vi + zStride] == c_air then data[vi + zStride] = c_netherbrick_slab end
|
||||
if data[vi - xStride] == c_air then data[vi - xStride] = c_netherbrick_slab end
|
||||
if data[vi + xStride] == c_air then data[vi + xStride] = c_netherbrick_slab end
|
||||
if is_dungeon_brick(data[vi - yStride]) then
|
||||
data[vi - yStride] = c_lava_source
|
||||
if data[vi - zStride] == c_air then data[vi - zStride] = c_netherbrick_slab end
|
||||
if data[vi + zStride] == c_air then data[vi + zStride] = c_netherbrick_slab end
|
||||
if data[vi - xStride] == c_air then data[vi - xStride] = c_netherbrick_slab end
|
||||
if data[vi + xStride] == c_air then data[vi + xStride] = c_netherbrick_slab end
|
||||
end
|
||||
end
|
||||
|
||||
-- Barred windows
|
||||
@ -377,15 +439,15 @@ function decorate_dungeons(data, area, rooms)
|
||||
local vi_max = area:index(room_max.x + 1, window_y, roomInfo.z)
|
||||
local locations = {-zStride, zStride, -zStride + yStride, zStride + yStride}
|
||||
for _, offset in ipairs(locations) do
|
||||
if data[vi_min + offset] == c_netherbrick then data[vi_min + offset] = window_node end
|
||||
if data[vi_max + offset] == c_netherbrick then data[vi_max + offset] = window_node end
|
||||
if is_dungeon_brick(data[vi_min + offset]) then data[vi_min + offset] = window_node end
|
||||
if is_dungeon_brick(data[vi_max + offset]) then data[vi_max + offset] = window_node end
|
||||
end
|
||||
vi_min = area:index(roomInfo.x, window_y, room_min.z - 1)
|
||||
vi_max = area:index(roomInfo.x, window_y, room_max.z + 1)
|
||||
locations = {-xStride, xStride, -xStride + yStride, xStride + yStride}
|
||||
for _, offset in ipairs(locations) do
|
||||
if data[vi_min + offset] == c_netherbrick then data[vi_min + offset] = window_node end
|
||||
if data[vi_max + offset] == c_netherbrick then data[vi_max + offset] = window_node end
|
||||
if is_dungeon_brick(data[vi_min + offset]) then data[vi_min + offset] = window_node end
|
||||
if is_dungeon_brick(data[vi_max + offset]) then data[vi_max + offset] = window_node end
|
||||
end
|
||||
end
|
||||
|
||||
@ -395,8 +457,43 @@ function decorate_dungeons(data, area, rooms)
|
||||
end
|
||||
|
||||
|
||||
local yblmin = NETHER_FLOOR + BLEND * 2
|
||||
local yblmax = NETHER_CEILING - BLEND * 2
|
||||
-- At both the top and bottom of the Nether, as set by NETHER_CEILING and NETHER_FLOOR,
|
||||
-- there is a 128 deep cap of solid netherrack, followed by a 128-deep blending zone
|
||||
-- where Nether caverns may start to appear.
|
||||
-- The solid zones and blending zones are achieved by adjusting the np_cave noise to be
|
||||
-- outside the range where caverns form, this function returns that adjustment.
|
||||
--
|
||||
-- Returns two values: the noise limit adjustment for nether caverns, and the
|
||||
-- noise limit adjustment for the central region / mantle caverns
|
||||
mapgen.get_mapgenblend_adjustments = function(y)
|
||||
|
||||
-- floorAndCeilingBlend will normally be 0, but shifts toward 1 in the
|
||||
-- blending zone, and goes higher than 1 in the solid zone between the
|
||||
-- blending zone and the end of the nether.
|
||||
local floorAndCeilingBlend = 0
|
||||
if y > yblmax then floorAndCeilingBlend = ((y - yblmax) / BLEND) ^ 2 end
|
||||
if y < yblmin then floorAndCeilingBlend = ((yblmin - y) / BLEND) ^ 2 end
|
||||
|
||||
-- the nether caverns exist when np_cave noise is greater than TCAVE, so
|
||||
-- to fade out the nether caverns, adjust TCAVE upward.
|
||||
local tcave_adj = floorAndCeilingBlend
|
||||
|
||||
-- the central regions exists when np_cave noise is below CENTER_REGION_LIMIT,
|
||||
-- so to fade out the mantle caverns adjust CENTER_REGION_LIMIT downward.
|
||||
local centerRegionLimit_adj = -(CENTER_REGION_LIMIT * floorAndCeilingBlend)
|
||||
|
||||
return tcave_adj, centerRegionLimit_adj
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- On-generated function
|
||||
|
||||
local tunnelCandidate_count = 0
|
||||
local tunnel_count = 0
|
||||
local total_chunk_count = 0
|
||||
local function on_generated(minp, maxp, seed)
|
||||
|
||||
if minp.y > NETHER_CEILING or maxp.y < NETHER_FLOOR then
|
||||
@ -414,38 +511,120 @@ local function on_generated(minp, maxp, seed)
|
||||
local zCaveStride = yCaveStride * yCaveStride
|
||||
local chulens = {x = yCaveStride, y = yCaveStride, z = yCaveStride}
|
||||
|
||||
nobj_cave = nobj_cave or minetest.get_perlin_map(np_cave, chulens)
|
||||
nobj_cave = nobj_cave or minetest.get_perlin_map(mapgen.np_cave, chulens)
|
||||
local nvals_cave = nobj_cave:get_3d_map_flat(minp, nbuf_cave)
|
||||
|
||||
|
||||
local dungeonRooms = build_dungeon_room_list(data, area)
|
||||
local abs_cave_noise, abs_cave_noise_adjusted
|
||||
|
||||
for y = y0, y1 do -- Y loop first to minimise tcave calculations
|
||||
local contains_nether = false
|
||||
local contains_shell = false
|
||||
local contains_mantle = false
|
||||
local contains_ocean = false
|
||||
|
||||
local tcave = TCAVE
|
||||
if y > yblmax then tcave = TCAVE + ((y - yblmax) / BLEND) ^ 2 end
|
||||
if y < yblmin then tcave = TCAVE + ((yblmin - y) / BLEND) ^ 2 end
|
||||
|
||||
for y = y0, y1 do -- Y loop first to minimise tcave & lava-sea calculations
|
||||
|
||||
local sea_level, cavern_limit_distance = mapgen.find_nearest_lava_sealevel(y)
|
||||
local above_lavasea = y > sea_level
|
||||
local below_lavasea = y < sea_level
|
||||
|
||||
local tcave_adj, centerRegionLimit_adj = mapgen.get_mapgenblend_adjustments(y)
|
||||
local tcave = TCAVE + tcave_adj
|
||||
local tmantle = CENTER_REGION_LIMIT + centerRegionLimit_adj -- cavern_noise_adj already contains central_region_limit_adj, so tmantle is only for comparisons when cavern_noise_adj hasn't been added to the noise value
|
||||
local cavern_noise_adj =
|
||||
CENTER_REGION_LIMIT * (cavern_limit_distance * cavern_limit_distance * cavern_limit_distance) -
|
||||
centerRegionLimit_adj -- cavern_noise_adj gets added to noise value instead of added to the limit np_noise is compared against, so subtract centerRegionLimit_adj so subtract centerRegionLimit_adj instead of adding
|
||||
|
||||
for z = z0, z1 do
|
||||
local vi = area:index(x0, y, z) -- Initial voxelmanip index
|
||||
local ni = (z - z0) * zCaveStride + (y - y0) * yCaveStride + 1
|
||||
local noise2di = 1 + (z - z0) * yCaveStride
|
||||
|
||||
for x = x0, x1 do
|
||||
|
||||
local id = data[vi] -- Existing node
|
||||
local cave_noise = nvals_cave[ni]
|
||||
|
||||
if nvals_cave[ni] > tcave then
|
||||
if cave_noise > tcave then
|
||||
-- Prime region
|
||||
-- This was the only region in initial versions of the Nether mod.
|
||||
-- It is the only region that portals from the surface will open into.
|
||||
data[vi] = c_air
|
||||
elseif id == c_air or id == c_native_mapgen then
|
||||
data[vi] = c_netherrack -- excavate_dungeons() will mostly reverse this inside dungeons
|
||||
contains_nether = true
|
||||
|
||||
elseif -cave_noise > tcave then
|
||||
-- Secondary/spare region
|
||||
-- This secondary region is unused until someone decides to do something cool or novel with it.
|
||||
-- Reaching here would require the player to first find and journey through the central region,
|
||||
-- as it's always separated from the Prime region by the central region.
|
||||
|
||||
data[vi] = c_netherrack -- For now I've just left this region as solid netherrack instead of air.
|
||||
|
||||
-- Only set contains_nether to true here if you want tunnels created between the secondary region
|
||||
-- and the central region.
|
||||
--contains_nether = true
|
||||
--data[vi] = c_air
|
||||
else
|
||||
-- netherrack walls and/or center region/mantle
|
||||
abs_cave_noise = math_abs(cave_noise)
|
||||
|
||||
-- abs_cave_noise_adjusted makes the center region smaller as distance from the lava ocean
|
||||
-- increases, we do this by pretending the abs_cave_noise value is higher.
|
||||
abs_cave_noise_adjusted = abs_cave_noise + cavern_noise_adj
|
||||
|
||||
if abs_cave_noise_adjusted >= CENTER_CAVERN_LIMIT then
|
||||
|
||||
local id = data[vi] -- Check existing node to avoid removing dungeons
|
||||
if id == c_air or id == c_native_mapgen then
|
||||
if abs_cave_noise < tmantle then
|
||||
data[vi] = c_netherrack_deep
|
||||
else
|
||||
-- the shell seperating the mantle from the rest of the nether...
|
||||
data[vi] = c_netherrack -- excavate_dungeons() will mostly reverse this inside dungeons
|
||||
contains_shell = true
|
||||
end
|
||||
end
|
||||
|
||||
elseif above_lavasea then
|
||||
data[vi] = c_air
|
||||
contains_mantle = true
|
||||
elseif abs_cave_noise_adjusted < SURFACE_CRUST_LIMIT or (below_lavasea and abs_cave_noise_adjusted < CRUST_LIMIT) then
|
||||
data[vi] = c_lavasea_source
|
||||
contains_ocean = true
|
||||
else
|
||||
data[vi] = c_lava_crust
|
||||
contains_ocean = true
|
||||
end
|
||||
end
|
||||
|
||||
ni = ni + 1
|
||||
vi = vi + 1
|
||||
noise2di = noise2di + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if contains_mantle or contains_ocean then
|
||||
mapgen.add_basalt_columns(data, area, minp, maxp) -- function provided by mapgen_mantle.lua
|
||||
end
|
||||
|
||||
if contains_nether and contains_mantle then
|
||||
tunnelCandidate_count = tunnelCandidate_count + 1
|
||||
local success = mapgen.excavate_tunnel_to_center_of_the_nether(data, area, nvals_cave, minp, maxp) -- function provided by mapgen_mantle.lua
|
||||
if success then tunnel_count = tunnel_count + 1 end
|
||||
end
|
||||
total_chunk_count = total_chunk_count + 1
|
||||
if total_chunk_count % 50 == 0 then
|
||||
debugf(
|
||||
"%s of %s chunks contain both nether and lava-sea (%s%%), %s chunks generated a pathway (%s%%)",
|
||||
tunnelCandidate_count,
|
||||
total_chunk_count,
|
||||
math_floor(tunnelCandidate_count * 100 / total_chunk_count),
|
||||
tunnel_count,
|
||||
math_floor(tunnel_count * 100 / total_chunk_count)
|
||||
)
|
||||
end
|
||||
|
||||
-- any air from the native mapgen has been replaced by netherrack, but we
|
||||
-- don't want netherrack inside dungeons, so fill known dungeon rooms with air.
|
||||
excavate_dungeons(data, area, dungeonRooms)
|
||||
@ -453,10 +632,9 @@ local function on_generated(minp, maxp, seed)
|
||||
|
||||
vm:set_data(data)
|
||||
|
||||
-- avoid generating decorations on the underside of the bottom of the nether
|
||||
if minp.y > NETHER_FLOOR and maxp.y < NETHER_CEILING then minetest.generate_decorations(vm) end
|
||||
|
||||
minetest.generate_ores(vm)
|
||||
minetest.generate_decorations(vm)
|
||||
|
||||
vm:set_lighting({day = 0, night = 0}, minp, maxp)
|
||||
vm:calc_lighting()
|
||||
vm:update_liquids()
|
||||
@ -467,7 +645,7 @@ end
|
||||
-- use knowledge of the nether mapgen algorithm to return a suitable ground level for placing a portal.
|
||||
-- player_name is optional, allowing a player to spawn a remote portal in their own protected areas.
|
||||
function nether.find_nether_ground_y(target_x, target_z, start_y, player_name)
|
||||
local nobj_cave_point = minetest.get_perlin(np_cave)
|
||||
local nobj_cave_point = minetest.get_perlin(mapgen.np_cave)
|
||||
local air = 0 -- Consecutive air nodes found
|
||||
|
||||
local minp_schem, maxp_schem = nether.get_schematic_volume({x = target_x, y = 0, z = target_z}, nil, "nether_portal")
|
||||
|
@ -25,10 +25,16 @@
|
||||
-- emerged or not before the decoration was placed.
|
||||
local allow_lava_decorations = nether.useBiomes
|
||||
|
||||
-- Keep compatibility with mapgen_nobiomes.lua, so hardcoding 128
|
||||
-- instead of using nether.mapgen.BLEND
|
||||
local decoration_ceiling = nether.DEPTH_CEILING - 128
|
||||
local decoration_floor = nether.DEPTH_FLOOR + 128
|
||||
|
||||
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 D = {name = "nether:rack_deep", 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}
|
||||
@ -125,8 +131,8 @@ minetest.register_decoration({
|
||||
sidelen = 80,
|
||||
fill_ratio = 0.0003,
|
||||
biomes = {"nether_caverns"},
|
||||
y_max = nether.DEPTH_CEILING, -- keep compatibility with mapgen_nobiomes.lua
|
||||
y_min = nether.DEPTH_FLOOR,
|
||||
y_max = decoration_ceiling,
|
||||
y_min = decoration_floor,
|
||||
schematic = schematic_GlowstoneStalactite,
|
||||
flags = "place_center_x,place_center_z,force_placement,all_ceilings",
|
||||
place_offset_y=-3
|
||||
@ -139,8 +145,8 @@ minetest.register_decoration({
|
||||
sidelen = 80,
|
||||
fill_ratio = 0.0008,
|
||||
biomes = {"nether_caverns"},
|
||||
y_max = nether.DEPTH_CEILING, -- keep compatibility with mapgen_nobiomes.lua
|
||||
y_min = nether.DEPTH_FLOOR,
|
||||
y_max = decoration_ceiling,
|
||||
y_min = decoration_floor,
|
||||
schematic = schematic_GlowstoneStalactite,
|
||||
replacements = {["nether:glowstone"] = "nether:rack"},
|
||||
flags = "place_center_x,place_center_z,all_ceilings",
|
||||
@ -148,6 +154,193 @@ minetest.register_decoration({
|
||||
})
|
||||
|
||||
|
||||
local schematic_GreaterStalactite = {
|
||||
size = {x = 3, y = 23, z = 3},
|
||||
data = { -- note that data is upside down
|
||||
|
||||
_, _, _,
|
||||
_, _, _,
|
||||
_, _, _,
|
||||
_, _, _,
|
||||
_, _, _,
|
||||
_, _, _,
|
||||
_, _, _,
|
||||
_, _, _,
|
||||
_, _, _,
|
||||
_, _, _,
|
||||
_, _, _,
|
||||
_, _, _,
|
||||
_, _, _,
|
||||
_, D, _,
|
||||
_, D, _,
|
||||
_, D, _,
|
||||
_, D, _,
|
||||
D, D, D,
|
||||
D, D, D,
|
||||
D, D, D,
|
||||
_, D, _,
|
||||
_, _, _,
|
||||
_, _, _,
|
||||
|
||||
_, D, _, -- ypos 0, prob 85% (218/255)
|
||||
_, D, _, -- ypos 1, prob 85% (218/255)
|
||||
_, D, _, -- ypos 2, prob 85% (218/255)
|
||||
_, D, _, -- ypos 3, prob 85% (218/255)
|
||||
_, D, _, -- ypos 4, prob 85% (218/255)
|
||||
_, D, _, -- ypos 5, prob 85% (218/255)
|
||||
_, D, _, -- ypos 6, prob 85% (218/255)
|
||||
_, D, _, -- ypos 7, prob 85% (218/255)
|
||||
_, D, _, -- ypos 8, prob 85% (218/255)
|
||||
_, D, D, -- ypos 9, prob 50% (128/256) to make half of stalactites asymmetric
|
||||
_, D, D, -- ypos 10, prob 50% (128/256) to make half of stalactites asymmetric
|
||||
_, D, D, -- ypos 11, prob 50% (128/256) to make half of stalactites asymmetric
|
||||
_, D, D, -- ypos 12, prob 50% (128/256) to make half of stalactites asymmetric
|
||||
D, D, D, -- ypos 13, prob 75% (192/256)
|
||||
D, D, D, -- ypos 14, prob 75% (192/256)
|
||||
D, D, D, -- ypos 15, prob 100%
|
||||
D, D, D, -- ypos 16, prob 100%
|
||||
D, D, D, -- ypos 17, prob 100%
|
||||
D, D, D, -- ypos 18, prob 100%
|
||||
D, D, D, -- ypos 19, prob 75% (192/256)
|
||||
D, D, D, -- ypos 20, prob 85% (218/255)
|
||||
_, D, D, -- ypos 21, prob 50% (128/256) to make half of stalactites asymmetric
|
||||
_, D, _, -- ypos 22, prob 100%
|
||||
|
||||
_, _, _,
|
||||
_, _, _,
|
||||
_, _, _,
|
||||
_, _, _,
|
||||
_, _, _,
|
||||
_, _, _,
|
||||
_, _, _,
|
||||
_, _, _,
|
||||
_, _, _,
|
||||
_, _, _,
|
||||
_, _, _,
|
||||
_, _, _,
|
||||
_, D, _,
|
||||
_, D, _,
|
||||
_, D, _,
|
||||
_, D, _,
|
||||
_, D, _,
|
||||
D, D, D,
|
||||
D, D, D,
|
||||
D, D, D,
|
||||
_, D, _,
|
||||
_, D, _,
|
||||
_, _, _,
|
||||
|
||||
},
|
||||
-- Y-slice probabilities do not function correctly for ceiling schematic
|
||||
-- decorations because they are inverted, so ypos numbers have been inverted
|
||||
-- to match, and a larger offset in place_offset_y should be used (e.g. -3).
|
||||
yslice_prob = {
|
||||
{ypos = 21, prob = 128},
|
||||
{ypos = 20, prob = 218},
|
||||
{ypos = 19, prob = 192},
|
||||
{ypos = 14, prob = 192},
|
||||
{ypos = 13, prob = 192},
|
||||
{ypos = 12, prob = 128},
|
||||
{ypos = 11, prob = 128},
|
||||
{ypos = 10, prob = 128},
|
||||
{ypos = 9, prob = 128},
|
||||
{ypos = 8, prob = 218},
|
||||
{ypos = 7, prob = 218},
|
||||
{ypos = 6, prob = 218},
|
||||
{ypos = 5, prob = 218},
|
||||
{ypos = 4, prob = 218},
|
||||
{ypos = 3, prob = 218},
|
||||
{ypos = 2, prob = 218},
|
||||
{ypos = 1, prob = 218},
|
||||
{ypos = 0, prob = 218}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
-- A stalagmite is an upsidedown stalactite, so
|
||||
-- use the GreaterStalactite to create a ToweringStalagmite schematic
|
||||
local schematic_ToweringStalagmite = {
|
||||
size = schematic_GreaterStalactite.size,
|
||||
data = {},
|
||||
yslice_prob = {}
|
||||
}
|
||||
local array_length = #schematic_GreaterStalactite.data + 1
|
||||
for i, node in ipairs(schematic_GreaterStalactite.data) do
|
||||
schematic_ToweringStalagmite.data[array_length - i] = node
|
||||
end
|
||||
y_size = schematic_GreaterStalactite.size.y
|
||||
for i, node in ipairs(schematic_GreaterStalactite.yslice_prob) do
|
||||
schematic_ToweringStalagmite.yslice_prob[i] = {
|
||||
-- we can safely lower the prob. to gain more variance because floor based schematics
|
||||
-- don't have the bug where missing lines moves them away from the surface
|
||||
prob = schematic_GreaterStalactite.yslice_prob[i].prob - 20,
|
||||
ypos = y_size - 1 - schematic_GreaterStalactite.yslice_prob[i].ypos
|
||||
}
|
||||
end
|
||||
|
||||
minetest.register_decoration({
|
||||
name = "Deep-glowstone stalactite",
|
||||
deco_type = "schematic",
|
||||
place_on = "nether:rack_deep",
|
||||
sidelen = 80,
|
||||
fill_ratio = 0.0003,
|
||||
biomes = {"nether_caverns"},
|
||||
y_max = decoration_ceiling,
|
||||
y_min = decoration_floor,
|
||||
schematic = schematic_GlowstoneStalactite,
|
||||
replacements = {["nether:rack"] = "nether:rack_deep", ["nether:glowstone"] = "nether:glowstone_deep"},
|
||||
flags = "place_center_x,place_center_z,force_placement,all_ceilings",
|
||||
place_offset_y=-3
|
||||
})
|
||||
|
||||
minetest.register_decoration({
|
||||
name = "Deep-glowstone stalactite outgrowth",
|
||||
deco_type = "schematic",
|
||||
place_on = "nether:glowstone_deep",
|
||||
sidelen = 40,
|
||||
fill_ratio = 0.15,
|
||||
biomes = {"nether_caverns"},
|
||||
y_max = decoration_ceiling,
|
||||
y_min = decoration_floor,
|
||||
schematic = {
|
||||
size = {x = 1, y = 4, z = 1},
|
||||
data = { G, G, G, G }
|
||||
},
|
||||
replacements = {["nether:glowstone"] = "nether:glowstone_deep"},
|
||||
flags = "place_center_x,place_center_z,all_ceilings",
|
||||
})
|
||||
|
||||
minetest.register_decoration({
|
||||
name = "Deep-netherrack stalactite",
|
||||
deco_type = "schematic",
|
||||
place_on = "nether:rack_deep",
|
||||
sidelen = 80,
|
||||
fill_ratio = 0.0003,
|
||||
biomes = {"nether_caverns"},
|
||||
y_max = decoration_ceiling,
|
||||
y_min = decoration_floor,
|
||||
schematic = schematic_GlowstoneStalactite,
|
||||
replacements = {["nether:rack"] = "nether:rack_deep", ["nether:glowstone"] = "nether:rack_deep"},
|
||||
flags = "place_center_x,place_center_z,force_placement,all_ceilings",
|
||||
place_offset_y=-3
|
||||
})
|
||||
|
||||
minetest.register_decoration({
|
||||
name = "Deep-netherrack towering stalagmite",
|
||||
deco_type = "schematic",
|
||||
place_on = "nether:rack_deep",
|
||||
sidelen = 80,
|
||||
fill_ratio = 0.001,
|
||||
biomes = {"nether_caverns"},
|
||||
y_max = decoration_ceiling,
|
||||
y_min = decoration_floor,
|
||||
schematic = schematic_ToweringStalagmite,
|
||||
replacements = {["nether:basalt"] = "nether:rack_deep"},
|
||||
flags = "place_center_x,place_center_z,force_placement,all_floors",
|
||||
place_offset_y=-2
|
||||
})
|
||||
|
||||
-- =======================================
|
||||
-- Concealed crevice / Lava sinkhole
|
||||
-- =======================================
|
||||
@ -161,8 +354,8 @@ if allow_lava_decorations then
|
||||
sidelen = 80,
|
||||
fill_ratio = 0.002,
|
||||
biomes = {"nether_caverns"},
|
||||
y_max = nether.DEPTH, -- keep compatibility with mapgen_nobiomes.lua
|
||||
y_min = nether.DEPTH_FLOOR,
|
||||
y_max = decoration_ceiling,
|
||||
y_min = decoration_floor,
|
||||
schematic = {
|
||||
size = {x = 4, y = 7, z = 4},
|
||||
data = { -- note that data is upside down
|
||||
@ -263,8 +456,8 @@ minetest.register_decoration({
|
||||
sidelen = 80,
|
||||
fill_ratio = 0.005,
|
||||
biomes = {"nether_caverns"},
|
||||
y_max = nether.DEPTH,
|
||||
y_min = nether.DEPTH_FLOOR,
|
||||
y_max = decoration_ceiling,
|
||||
y_min = decoration_floor,
|
||||
schematic = schematic_fumarole,
|
||||
replacements = replacements_full,
|
||||
flags = "place_center_x,place_center_z,all_floors",
|
||||
@ -292,8 +485,8 @@ minetest.register_decoration({
|
||||
sidelen = 8,
|
||||
noise_params = fumarole_clump_noise,
|
||||
biomes = {"nether_caverns"},
|
||||
y_max = nether.DEPTH,
|
||||
y_min = nether.DEPTH_FLOOR,
|
||||
y_max = decoration_ceiling,
|
||||
y_min = decoration_floor,
|
||||
schematic = schematic_fumarole,
|
||||
replacements = replacements_full,
|
||||
flags = "place_center_x,place_center_z,all_floors",
|
||||
@ -308,8 +501,8 @@ minetest.register_decoration({
|
||||
sidelen = 8,
|
||||
noise_params = fumarole_clump_noise,
|
||||
biomes = {"nether_caverns"},
|
||||
y_max = nether.DEPTH,
|
||||
y_min = nether.DEPTH_FLOOR,
|
||||
y_max = decoration_ceiling,
|
||||
y_min = decoration_floor,
|
||||
schematic = schematic_fumarole,
|
||||
replacements = replacements_slab,
|
||||
flags = "place_center_x,place_center_z,all_floors",
|
||||
@ -324,8 +517,8 @@ minetest.register_decoration({
|
||||
sidelen = 8,
|
||||
noise_params = fumarole_clump_noise,
|
||||
biomes = {"nether_caverns"},
|
||||
y_max = nether.DEPTH,
|
||||
y_min = nether.DEPTH_FLOOR,
|
||||
y_max = decoration_ceiling,
|
||||
y_min = decoration_floor,
|
||||
schematic = {
|
||||
size = {x = 4, y = 4, z = 4},
|
||||
data = { -- note that data is upside down
|
||||
@ -363,8 +556,8 @@ minetest.register_decoration({
|
||||
sidelen = 8,
|
||||
noise_params = fumarole_clump_noise,
|
||||
biomes = {"nether_caverns"},
|
||||
y_max = nether.DEPTH,
|
||||
y_min = nether.DEPTH_FLOOR,
|
||||
y_max = decoration_ceiling,
|
||||
y_min = decoration_floor,
|
||||
schematic = {
|
||||
size = {x = 4, y = 5, z = 4},
|
||||
data = { -- note that data is upside down
|
||||
|
476
mapgen_mantle.lua
Normal file
@ -0,0 +1,476 @@
|
||||
--[[
|
||||
|
||||
Nether mod for minetest
|
||||
|
||||
This file contains helper functions for generating the Mantle
|
||||
(AKA center region), which are moved into a separate file to keep the
|
||||
size of mapgen.lua manageable.
|
||||
|
||||
|
||||
Copyright (C) 2021 Treer
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for
|
||||
any purpose with or without fee is hereby granted, provided that the
|
||||
above copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
|
||||
BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
|
||||
OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
|
||||
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
|
||||
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
|
||||
SOFTWARE.
|
||||
|
||||
]]--
|
||||
|
||||
|
||||
local debugf = nether.debug
|
||||
local mapgen = nether.mapgen
|
||||
|
||||
local BASALT_COLUMN_UPPER_LIMIT = mapgen.BASALT_COLUMN_UPPER_LIMIT
|
||||
local BASALT_COLUMN_LOWER_LIMIT = mapgen.BASALT_COLUMN_LOWER_LIMIT
|
||||
|
||||
|
||||
-- 2D noise for basalt formations
|
||||
local np_basalt = {
|
||||
offset =-0.85,
|
||||
scale = 1,
|
||||
spread = {x = 46, y = 46, z = 46},
|
||||
seed = 1000,
|
||||
octaves = 5,
|
||||
persistence = 0.5,
|
||||
lacunarity = 2.6,
|
||||
flags = "eased"
|
||||
}
|
||||
|
||||
|
||||
-- Buffers and objects we shouldn't recreate every on_generate
|
||||
|
||||
local nobj_basalt = nil
|
||||
local nbuf_basalt = {}
|
||||
local cavePerlin = nil
|
||||
|
||||
-- Content ids
|
||||
|
||||
local c_air = minetest.get_content_id("air")
|
||||
local c_netherrack_deep = minetest.get_content_id("nether:rack_deep")
|
||||
local c_glowstone = minetest.get_content_id("nether:glowstone")
|
||||
local c_lavasea_source = minetest.get_content_id("nether:lava_source") -- same as lava but with staggered animation to look better as an ocean
|
||||
local c_lava_crust = minetest.get_content_id("nether:lava_crust")
|
||||
local c_basalt = minetest.get_content_id("nether:basalt")
|
||||
|
||||
|
||||
-- Math funcs
|
||||
local math_max, math_min, math_abs, math_floor = math.max, math.min, math.abs, math.floor -- avoid needing table lookups each time a common math function is invoked
|
||||
|
||||
function random_unit_vector()
|
||||
return vector.normalize({
|
||||
x = math.random() - 0.5,
|
||||
y = math.random() - 0.5,
|
||||
z = math.random() - 0.5
|
||||
})
|
||||
end
|
||||
|
||||
-- returns the smallest component in the vector
|
||||
function vector_min(v)
|
||||
return math_min(v.x, math_min(v.y, v.z))
|
||||
end
|
||||
|
||||
|
||||
-- Mantle mapgen functions (AKA Center region)
|
||||
|
||||
-- Returns (absolute height, fractional distance from ceiling or sea floor)
|
||||
-- the fractional distance from ceiling or sea floor is a value between 0 and 1 (inclusive)
|
||||
-- Note it may find the most relevent sea-level - not necesssarily the one you are closest
|
||||
-- to, since the space above the sea reaches much higher than the depth below the sea.
|
||||
mapgen.find_nearest_lava_sealevel = function(y)
|
||||
-- todo: put oceans near the bottom of chunks to improve ability to generate tunnels to the center
|
||||
-- todo: constrain y to be not near the bounds of the nether
|
||||
-- todo: add some random adj at each level, seeded only by the level height
|
||||
local sealevel = math.floor((y + 100) / 200) * 200
|
||||
--local sealevel = math.floor((y + 80) / 160) * 160
|
||||
--local sealevel = math.floor((y + 120) / 240) * 240
|
||||
|
||||
local cavern_limits_fraction
|
||||
local height_above_sea = y - sealevel
|
||||
if height_above_sea >= 0 then
|
||||
cavern_limits_fraction = math_min(1, height_above_sea / 95)
|
||||
else
|
||||
-- approaches 1 much faster as the lava sea is shallower than the cavern above it
|
||||
cavern_limits_fraction = math_min(1, -height_above_sea / 40)
|
||||
end
|
||||
|
||||
return sealevel, cavern_limits_fraction
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
mapgen.add_basalt_columns = function(data, area, minp, maxp)
|
||||
-- Basalt columns are structures found in lava oceans, and the only way to obtain
|
||||
-- nether basalt.
|
||||
-- Their x, z position is determined by a 2d noise map and a 2d slice of the cave
|
||||
-- noise (taken at lava-sealevel).
|
||||
|
||||
local x0, y0, z0 = minp.x, math_max(minp.y, nether.DEPTH_FLOOR), minp.z
|
||||
local x1, y1, z1 = maxp.x, math_min(maxp.y, nether.DEPTH_CEILING), maxp.z
|
||||
|
||||
local yStride = area.ystride
|
||||
local yCaveStride = x1 - x0 + 1
|
||||
|
||||
cavePerlin = cavePerlin or minetest.get_perlin(mapgen.np_cave)
|
||||
nobj_basalt = nobj_basalt or minetest.get_perlin_map(np_basalt, {x = yCaveStride, y = yCaveStride})
|
||||
local nvals_basalt = nobj_basalt:get_2d_map_flat({x=minp.x, y=minp.z}, {x=yCaveStride, y=yCaveStride}, nbuf_basalt)
|
||||
|
||||
local nearest_sea_level, _ = mapgen.find_nearest_lava_sealevel(math_floor((y0 + y1) / 2))
|
||||
|
||||
local leeway = mapgen.CENTER_CAVERN_LIMIT * 0.18
|
||||
|
||||
for z = z0, z1 do
|
||||
local noise2di = 1 + (z - z0) * yCaveStride
|
||||
|
||||
for x = x0, x1 do
|
||||
|
||||
local basaltNoise = nvals_basalt[noise2di]
|
||||
if basaltNoise > 0 then
|
||||
-- a basalt column is here
|
||||
|
||||
local abs_sealevel_cave_noise = math_abs(cavePerlin:get3d({x = x, y = nearest_sea_level, z = z}))
|
||||
|
||||
-- Add Some quick deterministic noise to the column heights
|
||||
-- This is probably not good noise, but it doesn't have to be.
|
||||
local fastNoise = 17
|
||||
fastNoise = 37 * fastNoise + y0
|
||||
fastNoise = 37 * fastNoise + z
|
||||
fastNoise = 37 * fastNoise + x
|
||||
fastNoise = 37 * fastNoise + math_floor(basaltNoise * 32)
|
||||
|
||||
local columnHeight = basaltNoise * 18 + ((fastNoise % 3) - 1)
|
||||
|
||||
-- columns should drop below sealevel where lava rivers are flowing
|
||||
-- i.e. anywhere abs_sealevel_cave_noise < BASALT_COLUMN_LOWER_LIMIT
|
||||
-- And we'll also have it drop off near the edges of the lava ocean so that
|
||||
-- basalt columns can only be found by the player reaching a lava ocean.
|
||||
local lowerClip = (math_min(math_max(abs_sealevel_cave_noise, BASALT_COLUMN_LOWER_LIMIT - leeway), BASALT_COLUMN_LOWER_LIMIT + leeway) - BASALT_COLUMN_LOWER_LIMIT) / leeway
|
||||
local upperClip = (math_min(math_max(abs_sealevel_cave_noise, BASALT_COLUMN_UPPER_LIMIT - leeway), BASALT_COLUMN_UPPER_LIMIT + leeway) - BASALT_COLUMN_UPPER_LIMIT) / leeway
|
||||
local columnHeightAdj = lowerClip * -upperClip -- all are values between 1 and -1
|
||||
|
||||
columnHeight = columnHeight + math_floor(columnHeightAdj * 12 - 12)
|
||||
|
||||
local vi = area:index(x, y0, z) -- Initial voxelmanip index
|
||||
|
||||
for y = y0, y1 do -- Y loop first to minimise tcave & lava-sea calculations
|
||||
|
||||
if y < nearest_sea_level + columnHeight then
|
||||
|
||||
local id = data[vi] -- Existing node
|
||||
if id == c_lava_crust or id == c_lavasea_source or (id == c_air and y > nearest_sea_level) then
|
||||
-- Avoid letting columns extend beyond the central region.
|
||||
-- (checking node ids saves having to calculate abs_cave_noise_adjusted here
|
||||
-- to test it against CENTER_CAVERN_LIMIT)
|
||||
data[vi] = c_basalt
|
||||
end
|
||||
end
|
||||
|
||||
vi = vi + yStride
|
||||
end
|
||||
end
|
||||
|
||||
noise2di = noise2di + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- returns an array of points from pos1 and pos2 which deviate from a straight line
|
||||
-- but which don't venture too close to a chunk boundary
|
||||
function generate_waypoints(pos1, pos2, minp, maxp)
|
||||
|
||||
local segSize = 10
|
||||
local maxDeviation = 7
|
||||
local minDistanceFromChunkWall = 5
|
||||
|
||||
local pathVec = vector.subtract(pos2, pos1)
|
||||
local pathVecNorm = vector.normalize(pathVec)
|
||||
local pathLength = vector.distance(pos1, pos2)
|
||||
local minBound = vector.add(minp, minDistanceFromChunkWall)
|
||||
local maxBound = vector.subtract(maxp, minDistanceFromChunkWall)
|
||||
|
||||
local result = {}
|
||||
result[1] = pos1
|
||||
|
||||
local segmentCount = math_floor(pathLength / segSize)
|
||||
for i = 1, segmentCount do
|
||||
local waypoint = vector.add(pos1, vector.multiply(pathVec, i / (segmentCount + 1)))
|
||||
|
||||
-- shift waypoint a few blocks in a random direction orthogonally to the pathVec, to make the path crooked.
|
||||
local crossProduct
|
||||
repeat
|
||||
crossProduct = vector.normalize(vector.cross(pathVecNorm, random_unit_vector()))
|
||||
until vector.length(crossProduct) > 0
|
||||
local deviation = vector.multiply(crossProduct, math.random(1, maxDeviation))
|
||||
waypoint = vector.add(waypoint, deviation)
|
||||
waypoint = {
|
||||
x = math_min(maxBound.x, math_max(minBound.x, waypoint.x)),
|
||||
y = math_min(maxBound.y, math_max(minBound.y, waypoint.y)),
|
||||
z = math_min(maxBound.z, math_max(minBound.z, waypoint.z))
|
||||
}
|
||||
|
||||
result[#result + 1] = waypoint
|
||||
end
|
||||
|
||||
result[#result + 1] = pos2
|
||||
return result
|
||||
end
|
||||
|
||||
|
||||
function excavate_pathway(data, area, nether_pos, center_pos, minp, maxp)
|
||||
|
||||
local ystride = area.ystride
|
||||
local zstride = area.zstride
|
||||
|
||||
math.randomseed(nether_pos.x + 10 * nether_pos.y + 100 * nether_pos.z) -- so each tunnel generates deterministically (this doesn't have to be a quality seed)
|
||||
local dist = math_floor(vector.distance(nether_pos, center_pos))
|
||||
local waypoints = generate_waypoints(nether_pos, center_pos, minp, maxp)
|
||||
|
||||
-- First pass: record path details
|
||||
local linedata = {}
|
||||
local last_pos = {}
|
||||
local line_index = 1
|
||||
local first_filled_index, boundary_index, last_filled_index
|
||||
for i = 0, dist do
|
||||
-- Bresenham's line would be good here, but too much lua code
|
||||
local waypointProgress = (#waypoints - 1) * i / dist
|
||||
local segmentIndex = math_min(math_floor(waypointProgress) + 1, #waypoints - 1) -- from the integer portion of waypointProgress
|
||||
local segmentInterp = waypointProgress - (segmentIndex - 1) -- the remaining fractional portion
|
||||
local segmentStart = waypoints[segmentIndex]
|
||||
local segmentVector = vector.subtract(waypoints[segmentIndex + 1], segmentStart)
|
||||
local pos = vector.round(vector.add(segmentStart, vector.multiply(segmentVector, segmentInterp)))
|
||||
|
||||
if not vector.equals(pos, last_pos) then
|
||||
local vi = area:indexp(pos)
|
||||
local node_id = data[vi]
|
||||
linedata[line_index] = {
|
||||
pos = pos,
|
||||
vi = vi,
|
||||
node_id = node_id
|
||||
}
|
||||
if boundary_index == nil and node_id == c_netherrack_deep then
|
||||
boundary_index = line_index
|
||||
end
|
||||
if node_id == c_air then
|
||||
if boundary_index ~= nil and last_filled_index == nil then
|
||||
last_filled_index = line_index
|
||||
end
|
||||
else
|
||||
if first_filled_index == nil then
|
||||
first_filled_index = line_index
|
||||
end
|
||||
end
|
||||
line_index = line_index + 1
|
||||
last_pos = pos
|
||||
end
|
||||
end
|
||||
first_filled_index = first_filled_index or 1
|
||||
last_filled_index = last_filled_index or #linedata
|
||||
boundary_index = boundary_index or last_filled_index
|
||||
|
||||
|
||||
-- limit tunnel radius to roughly the closest that startPos or stopPos comes to minp-maxp, so we
|
||||
-- don't end up exceeding minp-maxp and having excavation filled in when the next chunk is generated.
|
||||
local startPos, stopPos = linedata[first_filled_index].pos, linedata[last_filled_index].pos
|
||||
local radiusLimit = vector_min(vector.subtract(startPos, minp))
|
||||
radiusLimit = math_min(radiusLimit, vector_min(vector.subtract(stopPos, minp)))
|
||||
radiusLimit = math_min(radiusLimit, vector_min(vector.subtract(maxp, startPos)))
|
||||
radiusLimit = math_min(radiusLimit, vector_min(vector.subtract(maxp, stopPos)))
|
||||
|
||||
if radiusLimit < 4 then -- This is a logic check, ignore it. It could be commented out
|
||||
-- 4 is (79 - 75), and shouldn't be possible if sampling-skip was 10
|
||||
-- i.e. if sampling-skip was 10 then {5, 15, 25, 35, 45, 55, 65, 75} should be sampled from possible positions 0 to 79
|
||||
debugf("Error: radiusLimit %s is smaller then half the sampling distance. min %s, max %s, start %s, stop %s", radiusLimit, minp, maxp, startPos, stopPos)
|
||||
end
|
||||
radiusLimit = radiusLimit + 1 -- chunk walls wont be visibly flat if the radius only exceeds it a little ;)
|
||||
|
||||
-- Second pass: excavate
|
||||
local start_index, stop_index = math_max(1, first_filled_index - 2), math_min(#linedata, last_filled_index + 3)
|
||||
for i = start_index, stop_index, 3 do
|
||||
|
||||
-- Adjust radius so that tunnels start wide but thin out in the middle
|
||||
local distFromEnds = 1 - math_abs(((start_index + stop_index) / 2) - i) / ((stop_index - start_index) / 2) -- from 0 to 1, with 0 at ends and 1 in the middle
|
||||
-- Have it more flaired at the ends, rather than linear.
|
||||
-- i.e. sizeAdj approaches 1 quickly as distFromEnds increases
|
||||
local distFromMiddle = 1 - distFromEnds
|
||||
local sizeAdj = 1 - (distFromMiddle * distFromMiddle * distFromMiddle)
|
||||
|
||||
local radius = math_min(radiusLimit, math.random(50 - (25 * sizeAdj), 80 - (45 * sizeAdj)) / 10)
|
||||
local radiusCubed = radius * radius
|
||||
local radiusCeil = math_floor(radius + 0.5)
|
||||
|
||||
linedata[i].radius = radius -- Needed in third pass
|
||||
linedata[i].distFromEnds = distFromEnds -- Needed in third pass
|
||||
|
||||
local vi = linedata[i].vi
|
||||
for z = -radiusCeil, radiusCeil do
|
||||
local vi_z = vi + z * zstride
|
||||
for y = -radiusCeil, radiusCeil do
|
||||
local vi_zy = vi_z + y * ystride
|
||||
local xSquaredLimit = radiusCubed - (z * z + y * y)
|
||||
for x = -radiusCeil, radiusCeil do
|
||||
if x * x < xSquaredLimit then
|
||||
data[vi_zy + x] = c_air
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- Third pass: decorate
|
||||
-- Add glowstones to make tunnels to the mantle easyier to find
|
||||
-- https://i.imgur.com/sRA28x7.jpg
|
||||
for i = start_index, stop_index, 3 do
|
||||
if linedata[i].distFromEnds < 0.3 then
|
||||
local glowcount = 0
|
||||
local radius = linedata[i].radius
|
||||
for _ = 1, 20 do
|
||||
local testPos = vector.round(vector.add(linedata[i].pos, vector.multiply(random_unit_vector(), radius + 0.5)))
|
||||
local vi = area:indexp(testPos)
|
||||
if data[vi] ~= c_air then
|
||||
data[vi] = c_glowstone
|
||||
glowcount = glowcount + 1
|
||||
--else
|
||||
-- data[vi] = c_debug
|
||||
end
|
||||
if glowcount >= 2 then break end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
-- excavates a tunnel connecting the Primary or Secondary region with the mantle / central region
|
||||
-- if a suitable path is found.
|
||||
-- Returns true if successful
|
||||
mapgen.excavate_tunnel_to_center_of_the_nether = function(data, area, nvals_cave, minp, maxp)
|
||||
|
||||
local result = false
|
||||
local extent = vector.subtract(maxp, minp)
|
||||
local skip = 10 -- sampling rate of 1 in 10
|
||||
|
||||
local highest = -1000
|
||||
local lowest = 1000
|
||||
local lowest_vi
|
||||
local highest_vi
|
||||
|
||||
local yCaveStride = maxp.x - minp.x + 1
|
||||
local zCaveStride = yCaveStride * yCaveStride
|
||||
|
||||
local vi_offset = area:indexp(vector.add(minp, math_floor(skip / 2))) -- start half the sampling distance away from minp
|
||||
local vi, ni
|
||||
|
||||
for y = 0, extent.y - 1, skip do
|
||||
local sealevel = mapgen.find_nearest_lava_sealevel(minp.y + y)
|
||||
|
||||
if minp.y + y > sealevel then -- only create tunnels above sea level
|
||||
for z = 0, extent.z - 1, skip do
|
||||
|
||||
vi = vi_offset + y * area.ystride + z * area.zstride
|
||||
ni = z * zCaveStride + y * yCaveStride + 1
|
||||
for x = 0, extent.x - 1, skip do
|
||||
|
||||
local noise = math_abs(nvals_cave[ni])
|
||||
if noise < lowest then
|
||||
lowest = noise
|
||||
lowest_vi = vi
|
||||
end
|
||||
if noise > highest then
|
||||
highest = noise
|
||||
highest_vi = vi
|
||||
end
|
||||
ni = ni + skip
|
||||
vi = vi + skip
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if lowest < mapgen.CENTER_CAVERN_LIMIT and highest > mapgen.TCAVE + 0.03 then
|
||||
|
||||
local mantle_y = area:position(lowest_vi).y
|
||||
local sealevel, cavern_limit_distance = mapgen.find_nearest_lava_sealevel(mantle_y)
|
||||
local _, centerRegionLimit_adj = mapgen.get_mapgenblend_adjustments(mantle_y)
|
||||
local cavern_noise_adj =
|
||||
mapgen.CENTER_REGION_LIMIT * (cavern_limit_distance * cavern_limit_distance * cavern_limit_distance) -
|
||||
centerRegionLimit_adj -- cavern_noise_adj gets added to noise value instead of added to the limit np_noise is compared against, so subtract centerRegionLimit_adj instead of adding
|
||||
|
||||
if lowest + cavern_noise_adj < mapgen.CENTER_CAVERN_LIMIT then
|
||||
excavate_pathway(data, area, area:position(highest_vi), area:position(lowest_vi), minp, maxp)
|
||||
result = true
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
|
||||
minetest.register_chatcommand("nether_whereami",
|
||||
{
|
||||
description = "Describes which region of the nether the player is in",
|
||||
privs = {debug = true},
|
||||
func = function(name, param)
|
||||
|
||||
local player = minetest.get_player_by_name(name)
|
||||
if player == nil then return false, "Unknown player position" end
|
||||
|
||||
local pos = vector.round(player:get_pos())
|
||||
if pos.y > nether.DEPTH_CEILING or pos.y < nether.DEPTH_FLOOR then
|
||||
return true, "The Overworld"
|
||||
end
|
||||
|
||||
cavePerlin = cavePerlin or minetest.get_perlin(mapgen.np_cave)
|
||||
local densityNoise = cavePerlin:get_3d(pos)
|
||||
local sea_level, cavern_limit_distance = mapgen.find_nearest_lava_sealevel(pos.y)
|
||||
local tcave_adj, centerRegionLimit_adj = mapgen.get_mapgenblend_adjustments(pos.y)
|
||||
local tcave = mapgen.TCAVE + tcave_adj
|
||||
local tmantle = mapgen.CENTER_REGION_LIMIT + centerRegionLimit_adj
|
||||
local cavern_noise_adj =
|
||||
mapgen.CENTER_REGION_LIMIT * (cavern_limit_distance * cavern_limit_distance * cavern_limit_distance) -
|
||||
centerRegionLimit_adj -- cavern_noise_adj gets added to noise value instead of added to the limit np_noise is compared against, so subtract centerRegionLimit_adj so subtract centerRegionLimit_adj instead of adding
|
||||
|
||||
local desc
|
||||
|
||||
if densityNoise > tcave then
|
||||
desc = "Positive nether"
|
||||
elseif -densityNoise > tcave then
|
||||
desc = "Negative nether"
|
||||
elseif math_abs(densityNoise) < tmantle then
|
||||
desc = "Mantle"
|
||||
|
||||
if math_abs(densityNoise) + cavern_noise_adj < mapgen.CENTER_CAVERN_LIMIT then
|
||||
desc = desc .. " inside cavern"
|
||||
else
|
||||
desc = desc .. " but outside cavern"
|
||||
end
|
||||
|
||||
elseif densityNoise > 0 then
|
||||
desc = "Shell between positive nether and center region"
|
||||
else
|
||||
desc = "Shell between negative nether and center region"
|
||||
end
|
||||
|
||||
local sea_pos = pos.y - sea_level
|
||||
if sea_pos > 0 then
|
||||
desc = desc .. ", " .. sea_pos .. "m above lava-sea level"
|
||||
else
|
||||
desc = desc .. ", " .. sea_pos .. "m below lava-sea level"
|
||||
end
|
||||
|
||||
if tcave_adj > 0 then
|
||||
desc = desc .. ", approaching y boundary of Nether"
|
||||
end
|
||||
|
||||
return true, "[Perlin " .. (math_floor(densityNoise * 1000) / 1000) .. "] " .. desc
|
||||
end
|
||||
}
|
||||
)
|
@ -2,6 +2,12 @@
|
||||
|
||||
Nether mod for minetest
|
||||
|
||||
"mapgen_nobiomes.lua" is the legacy version of the mapgen, only used
|
||||
in older versions of Minetest or in v6 worlds.
|
||||
"mapgen.lua" is the modern biomes-based Nether mapgen, which
|
||||
requires Minetest v5.1 or greater
|
||||
|
||||
|
||||
Copyright (C) 2013 PilzAdam
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for
|
||||
@ -190,8 +196,7 @@ minetest.register_on_generated(function(minp, maxp, seed)
|
||||
|
||||
vm:set_data(data)
|
||||
|
||||
-- avoid generating decorations on the underside of the bottom of the nether
|
||||
if minp.y > NETHER_FLOOR and maxp.y < NETHER_CEILING then minetest.generate_decorations(vm) end
|
||||
minetest.generate_decorations(vm)
|
||||
|
||||
vm:set_lighting({day = 0, night = 0}, minp, maxp)
|
||||
vm:calc_lighting()
|
||||
|
396
nodes.lua
@ -75,6 +75,15 @@ minetest.register_node("nether:rack", {
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
})
|
||||
|
||||
-- Deep Netherrack, found in the mantle / central magma layers
|
||||
minetest.register_node("nether:rack_deep", {
|
||||
description = S("Deep-Netherrack"),
|
||||
tiles = {"nether_rack_deep.png"},
|
||||
is_ground_content = true,
|
||||
groups = {cracky = 3, level = 2},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
})
|
||||
|
||||
minetest.register_node("nether:sand", {
|
||||
description = S("Nethersand"),
|
||||
tiles = {"nether_sand.png"},
|
||||
@ -95,6 +104,17 @@ minetest.register_node("nether:glowstone", {
|
||||
sounds = default.node_sound_glass_defaults(),
|
||||
})
|
||||
|
||||
-- Deep glowstone, found in the mantle / central magma layers
|
||||
minetest.register_node("nether:glowstone_deep", {
|
||||
description = S("Deep-Glowstone"),
|
||||
tiles = {"nether_glowstone_deep.png"},
|
||||
is_ground_content = true,
|
||||
light_source = 14,
|
||||
paramtype = "light",
|
||||
groups = {cracky = 3, oddly_breakable_by_hand = 3},
|
||||
sounds = default.node_sound_glass_defaults(),
|
||||
})
|
||||
|
||||
minetest.register_node("nether:brick", {
|
||||
description = S("Nether Brick"),
|
||||
tiles = {"nether_brick.png"},
|
||||
@ -111,6 +131,15 @@ minetest.register_node("nether:brick_compressed", {
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
})
|
||||
|
||||
-- A decorative node which can only be obtained from dungeons or structures
|
||||
minetest.register_node("nether:brick_cracked", {
|
||||
description = S("Cracked Nether Brick"),
|
||||
tiles = {"nether_brick_cracked.png"},
|
||||
is_ground_content = false,
|
||||
groups = {cracky = 2, level = 2},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
})
|
||||
|
||||
local fence_texture =
|
||||
"default_fence_overlay.png^nether_brick.png^default_fence_overlay.png^[makealpha:255,126,126"
|
||||
|
||||
@ -169,6 +198,345 @@ if minetest.get_modpath("moreblocks") then
|
||||
end
|
||||
|
||||
|
||||
-- Mantle nodes
|
||||
|
||||
-- Nether basalt is intended as a valuable material and possible portalstone - an alternative to
|
||||
-- obsidian that's available for other mods to use.
|
||||
-- It cannot be found in the regions of the nether where Nether portals link to, so requires a journey to obtain.
|
||||
minetest.register_node("nether:basalt", {
|
||||
description = S("Blue Basalt"),
|
||||
tiles = {
|
||||
"nether_basalt.png",
|
||||
"nether_basalt.png",
|
||||
"nether_basalt_side.png",
|
||||
"nether_basalt_side.png",
|
||||
"nether_basalt_side.png",
|
||||
"nether_basalt_side.png"
|
||||
},
|
||||
is_ground_content = true,
|
||||
groups = {cracky = 1, level = 3}, -- set proper digging times and uses, and maybe explosion immune if api handles that
|
||||
on_blast = function() --[[blast proof]] end,
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
})
|
||||
|
||||
-- Potentially a portalstone, but will also be a stepping stone between basalt
|
||||
-- and chiseled basalt.
|
||||
-- It can only be introduced by the biomes-based mapgen, since it requires the
|
||||
-- MT 5.0 world-align texture features.
|
||||
minetest.register_node("nether:basalt_hewn", {
|
||||
description = S("Hewn Basalt"),
|
||||
tiles = {{
|
||||
name = "nether_basalt_hewn.png",
|
||||
align_style = "world",
|
||||
scale = 2
|
||||
}},
|
||||
inventory_image = minetest.inventorycube(
|
||||
"nether_basalt_hewn.png^[sheet:2x2:0,0",
|
||||
"nether_basalt_hewn.png^[sheet:2x2:0,1",
|
||||
"nether_basalt_hewn.png^[sheet:2x2:1,1"
|
||||
),
|
||||
is_ground_content = false,
|
||||
groups = {cracky = 1, level = 2},
|
||||
on_blast = function() --[[blast proof]] end,
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
})
|
||||
|
||||
-- Chiselled basalt is intended as a portalstone - an alternative to obsidian that's
|
||||
-- available for other mods to use. It is crafted from Hewn Basalt.
|
||||
-- It should only be introduced by the biomes-based mapgen, since in future it may
|
||||
-- require the MT 5.0 world-align texture features.
|
||||
minetest.register_node("nether:basalt_chiselled", {
|
||||
description = S("Chiselled Basalt"),
|
||||
tiles = {
|
||||
"nether_basalt_chiselled_top.png",
|
||||
"nether_basalt_chiselled_top.png" .. "^[transformFY",
|
||||
"nether_basalt_chiselled_side.png",
|
||||
"nether_basalt_chiselled_side.png",
|
||||
"nether_basalt_chiselled_side.png",
|
||||
"nether_basalt_chiselled_side.png"
|
||||
},
|
||||
inventory_image = minetest.inventorycube(
|
||||
"nether_basalt_chiselled_top.png",
|
||||
"nether_basalt_chiselled_side.png",
|
||||
"nether_basalt_chiselled_side.png"
|
||||
),
|
||||
paramtype2 = "facedir",
|
||||
is_ground_content = false,
|
||||
groups = {cracky = 1, level = 2},
|
||||
on_blast = function() --[[blast proof]] end,
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
})
|
||||
|
||||
|
||||
-- Lava-sea source
|
||||
-- This is a lava source using a different animated texture so that each node
|
||||
-- is out of phase in its animation from its neighbor. This prevents the magma
|
||||
-- ocean from visually clumping together into a patchwork of 16x16 squares.
|
||||
-- It can only be used by the biomes-based mapgen, since it requires the MT 5.0
|
||||
-- world-align texture features.
|
||||
local lavasea_source = {}
|
||||
local lava_source = minetest.registered_nodes["default:lava_source"]
|
||||
for key, value in pairs(lava_source) do lavasea_source[key] = value end
|
||||
lavasea_source.name = nil
|
||||
lavasea_source.tiles = {
|
||||
{
|
||||
name = "nether_lava_source_animated.png",
|
||||
backface_culling = false,
|
||||
align_style = "world",
|
||||
scale = 2,
|
||||
animation = {
|
||||
type = "vertical_frames",
|
||||
aspect_w = 32,
|
||||
aspect_h = 32,
|
||||
length = 3.0,
|
||||
},
|
||||
},
|
||||
{
|
||||
name = "nether_lava_source_animated.png",
|
||||
backface_culling = true,
|
||||
align_style = "world",
|
||||
scale = 2,
|
||||
animation = {
|
||||
type = "vertical_frames",
|
||||
aspect_w = 32,
|
||||
aspect_h = 32,
|
||||
length = 3.0,
|
||||
},
|
||||
},
|
||||
}
|
||||
lavasea_source.liquid_alternative_source = "nether:lava_source"
|
||||
lavasea_source.inventory_image = minetest.inventorycube(
|
||||
"nether_lava_source_animated.png^[sheet:2x16:0,0",
|
||||
"nether_lava_source_animated.png^[sheet:2x16:0,1",
|
||||
"nether_lava_source_animated.png^[sheet:2x16:1,1"
|
||||
)
|
||||
minetest.register_node("nether:lava_source", lavasea_source)
|
||||
|
||||
|
||||
-- a place to store the original ABM function so nether.cool_lava() can call it
|
||||
local original_cool_lava_action
|
||||
|
||||
nether.cool_lava = function(pos, node)
|
||||
|
||||
local pos_above = {x = pos.x, y = pos.y + 1, z = pos.z}
|
||||
local node_above = minetest.get_node(pos_above)
|
||||
|
||||
-- Evaporate water sitting above lava, if it's in the Nether.
|
||||
-- (we don't want Nether mod to affect overworld lava mechanics)
|
||||
if minetest.get_node_group(node_above.name, "water") > 0 and
|
||||
pos.y < nether.DEPTH_CEILING and pos.y > nether.DEPTH_FLOOR then
|
||||
-- cools_lava might be a better group to check for, but perhaps there's
|
||||
-- something in that group that isn't a liquid and shouldn't be evaporated?
|
||||
minetest.swap_node(pos_above, {name="air"})
|
||||
end
|
||||
|
||||
-- add steam to cooling lava
|
||||
minetest.add_particlespawner({
|
||||
amount = 20,
|
||||
time = 0.15,
|
||||
minpos = {x=pos.x - 0.4, y=pos.y - 0, z=pos.z - 0.4},
|
||||
maxpos = {x=pos.x + 0.4, y=pos.y + 0.5, z=pos.z + 0.4},
|
||||
minvel = {x = -0.5, y = 0.5, z = -0.5},
|
||||
maxvel = {x = 0.5, y = 1.5, z = 0.5},
|
||||
minacc = {x = 0, y = 0.1, z = 0},
|
||||
maxacc = {x = 0, y = 0.2, z = 0},
|
||||
minexptime = 0.5,
|
||||
maxexptime = 1.3,
|
||||
minsize = 1.5,
|
||||
maxsize = 3.5,
|
||||
texture = "nether_particle_anim4.png",
|
||||
animation = {
|
||||
type = "vertical_frames",
|
||||
aspect_w = 7,
|
||||
aspect_h = 7,
|
||||
length = 1.4,
|
||||
}
|
||||
})
|
||||
|
||||
if node.name == "nether:lava_source" or node.name == "nether:lava_crust" then
|
||||
-- use swap_node to avoid triggering the lava_crust's after_destruct
|
||||
minetest.swap_node(pos, {name = "nether:basalt"})
|
||||
|
||||
minetest.sound_play("default_cool_lava",
|
||||
{pos = pos, max_hear_distance = 16, gain = 0.25}, true)
|
||||
else
|
||||
-- chain the original ABM action to handle conventional lava
|
||||
original_cool_lava_action(pos, node)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
minetest.register_on_mods_loaded(function()
|
||||
|
||||
-- register a bucket of Lava-sea source - but make it just the same bucket as default lava.
|
||||
-- (by doing this in register_on_mods_loaded we don't need to declare a soft dependency)
|
||||
if minetest.get_modpath("bucket") and minetest.global_exists("bucket") then
|
||||
local lava_bucket = bucket.liquids["default:lava_source"]
|
||||
if lava_bucket ~= nil then
|
||||
local lavasea_bucket = {}
|
||||
for key, value in pairs(lava_bucket) do lavasea_bucket[key] = value end
|
||||
lavasea_bucket.source = "nether:lava_source"
|
||||
bucket.liquids[lavasea_bucket.source] = lavasea_bucket
|
||||
end
|
||||
end
|
||||
|
||||
-- include "nether:lava_source" in any "default:lava_source" ABMs
|
||||
local function include_nether_lava(set_of_nodes)
|
||||
if (type(set_of_nodes) == "table") then
|
||||
for _, nodename in pairs(set_of_nodes) do
|
||||
if nodename == "default:lava_source" then
|
||||
-- I'm amazed this works, but it does
|
||||
table.insert(set_of_nodes, "nether:lava_source")
|
||||
break;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for _, abm in pairs(minetest.registered_abms) do
|
||||
include_nether_lava(abm.nodenames)
|
||||
include_nether_lava(abm.neighbors)
|
||||
if abm.label == "Lava cooling" and abm.action ~= nil then
|
||||
-- lets have lava_crust cool as well
|
||||
original_cool_lava_action = abm.action
|
||||
abm.action = nether.cool_lava
|
||||
table.insert(abm.nodenames, "nether:lava_crust")
|
||||
end
|
||||
end
|
||||
for _, lbm in pairs(minetest.registered_lbms) do
|
||||
include_nether_lava(lbm.nodenames)
|
||||
end
|
||||
--minetest.log("minetest.registered_abms" .. dump(minetest.registered_abms))
|
||||
--minetest.log("minetest.registered_lbms" .. dump(minetest.registered_lbms))
|
||||
end)
|
||||
|
||||
-- creates a lava splash, and leaves lava_source in place of the lava_crust
|
||||
local function smash_lava_crust(pos, playsound)
|
||||
|
||||
local lava_particlespawn_def = {
|
||||
amount = 6,
|
||||
time = 0.1,
|
||||
minpos = {x=pos.x - 0.5, y=pos.y + 0.3, z=pos.z - 0.5},
|
||||
maxpos = {x=pos.x + 0.5, y=pos.y + 0.5, z=pos.z + 0.5},
|
||||
minvel = {x = -1.5, y = 1.5, z = -1.5},
|
||||
maxvel = {x = 1.5, y = 5, z = 1.5},
|
||||
minacc = {x = 0, y = -10, z = 0},
|
||||
maxacc = {x = 0, y = -10, z = 0},
|
||||
minexptime = 1,
|
||||
maxexptime = 1,
|
||||
minsize = .2,
|
||||
maxsize = .8,
|
||||
texture = "^[colorize:#A00:255",
|
||||
glow = 8
|
||||
}
|
||||
minetest.add_particlespawner(lava_particlespawn_def)
|
||||
lava_particlespawn_def.texture = "^[colorize:#FB0:255"
|
||||
lava_particlespawn_def.maxvel.y = 3
|
||||
lava_particlespawn_def.glow = 12
|
||||
minetest.add_particlespawner(lava_particlespawn_def)
|
||||
|
||||
minetest.set_node(pos, {name = "default:lava_source"})
|
||||
|
||||
if math.random(1, 3) == 1 and minetest.registered_nodes["fire:basic_flame"] ~= nil then
|
||||
-- occasionally brief flames will be seen when breaking lava crust
|
||||
local posAbove = {x = pos.x, y = pos.y + 1, z = pos.z}
|
||||
if minetest.get_node(posAbove).name == "air" then
|
||||
minetest.set_node(posAbove, {name = "fire:basic_flame"})
|
||||
minetest.get_node_timer(posAbove):set(math.random(7, 15) / 10, 0)
|
||||
--[[ commented out because the flame sound plays for too long
|
||||
if minetest.global_exists("fire") and fire.update_player_sound ~= nil then
|
||||
-- The fire mod only updates its sound every 3 seconds, these flames will be
|
||||
-- out by then, so start the sound immediately
|
||||
local players = minetest.get_connected_players()
|
||||
for n = 1, #players do fire.update_player_sound(players[n]) end
|
||||
end]]
|
||||
end
|
||||
end
|
||||
|
||||
if playsound then
|
||||
minetest.sound_play(
|
||||
"nether_lava_bubble",
|
||||
-- this sample was encoded at 3x speed to reduce .ogg file size
|
||||
-- at the expense of higher frequencies, so pitch it down ~3x
|
||||
{pos = pos, pitch = 0.3, max_hear_distance = 8, gain = 0.4}
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- lava_crust nodes can only be used in the biomes-based mapgen, since they require
|
||||
-- the MT 5.0 world-align texture features.
|
||||
minetest.register_node("nether:lava_crust", {
|
||||
description = "Lava crust",
|
||||
tiles = {
|
||||
{
|
||||
name="nether_lava_crust_animated.png",
|
||||
backface_culling=true,
|
||||
tileable_vertical=true,
|
||||
tileable_horizontal=true,
|
||||
align_style="world",
|
||||
scale=2,
|
||||
animation = {
|
||||
type = "vertical_frames",
|
||||
aspect_w = 32,
|
||||
aspect_h = 32,
|
||||
length = 1.8,
|
||||
},
|
||||
}
|
||||
},
|
||||
inventory_image = minetest.inventorycube(
|
||||
"nether_lava_crust_animated.png^[sheet:2x48:0,0",
|
||||
"nether_lava_crust_animated.png^[sheet:2x48:0,1",
|
||||
"nether_lava_crust_animated.png^[sheet:2x48:1,1"
|
||||
),
|
||||
collision_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
-- Damage is calculated "starting 0.1 above feet
|
||||
-- and progressing upwards in 1 node intervals", so
|
||||
-- lower this node's collision box by more than 0.1
|
||||
-- to ensure damage will be taken when standing on
|
||||
-- the node.
|
||||
{-0.5, -0.5, -0.5, 0.5, 0.39, 0.5}
|
||||
},
|
||||
},
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
-- Keep the selection box matching the visual node,
|
||||
-- rather than the collision_box.
|
||||
{-0.5, -0.5, -0.5, 0.5, 0.5, 0.5}
|
||||
},
|
||||
},
|
||||
|
||||
after_destruct = function(pos, oldnode)
|
||||
smash_lava_crust(pos, true)
|
||||
end,
|
||||
after_dig_node = function(pos, oldnode, oldmetadata, digger)
|
||||
end,
|
||||
on_blast = function(pos, intensity)
|
||||
smash_lava_crust(pos, false)
|
||||
end,
|
||||
|
||||
paramtype = "light",
|
||||
light_source = default.LIGHT_MAX - 3,
|
||||
buildable_to = false,
|
||||
walkable_to = true,
|
||||
is_ground_content = true,
|
||||
drop = {
|
||||
items = {{
|
||||
-- Allow SilkTouch-esque "pickaxes of preservation" to mine the lava crust intact, if PR #10141 gets merged.
|
||||
tools = {"this line will block early MT versions which don't respect the tool_groups restrictions"},
|
||||
tool_groups = {{"pickaxe", "preservation"}},
|
||||
items = {"nether:lava_crust"}
|
||||
}}
|
||||
},
|
||||
--liquid_viscosity = 7,
|
||||
damage_per_second = 2,
|
||||
groups = {oddly_breakable_by_hand = 3, cracky = 3, explody = 1, igniter = 1},
|
||||
})
|
||||
|
||||
|
||||
-- Fumaroles (Chimney's)
|
||||
|
||||
local function fumarole_startTimer(pos, timeout_factor)
|
||||
@ -393,31 +761,3 @@ 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({
|
||||
output = "nether:brick 4",
|
||||
recipe = {
|
||||
{"nether:rack", "nether:rack"},
|
||||
{"nether:rack", "nether:rack"},
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "nether:fence_nether_brick 6",
|
||||
recipe = {
|
||||
{"nether:brick", "nether:brick", "nether:brick"},
|
||||
{"nether:brick", "nether:brick", "nether:brick"},
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "nether:brick_compressed",
|
||||
recipe = {
|
||||
{"nether:brick","nether:brick","nether:brick"},
|
||||
{"nether:brick","nether:brick","nether:brick"},
|
||||
{"nether:brick","nether:brick","nether:brick"},
|
||||
}
|
||||
})
|
||||
|
BIN
sounds/nether_lava_bubble.0.ogg
Normal file
BIN
sounds/nether_lava_bubble.ogg
Normal file
BIN
textures/nether_basalt.png
Normal file
After Width: | Height: | Size: 678 B |
BIN
textures/nether_basalt_chiselled_side.png
Normal file
After Width: | Height: | Size: 597 B |
BIN
textures/nether_basalt_chiselled_top.png
Normal file
After Width: | Height: | Size: 594 B |
BIN
textures/nether_basalt_hewn.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
textures/nether_basalt_side.png
Normal file
After Width: | Height: | Size: 518 B |
BIN
textures/nether_brick_cracked.png
Normal file
After Width: | Height: | Size: 290 B |
BIN
textures/nether_glowstone_deep.png
Normal file
After Width: | Height: | Size: 666 B |
BIN
textures/nether_lava_crust_animated.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
textures/nether_lava_source_animated.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
textures/nether_particle_anim4.png
Normal file
After Width: | Height: | Size: 288 B |
BIN
textures/nether_rack_deep.png
Normal file
After Width: | Height: | Size: 353 B |
19
tools.lua
@ -1,3 +1,22 @@
|
||||
--[[
|
||||
|
||||
Copyright (C) 2020 lortas
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for
|
||||
any purpose with or without fee is hereby granted, provided that the
|
||||
above copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
|
||||
BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
|
||||
OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
|
||||
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
|
||||
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
|
||||
SOFTWARE.
|
||||
|
||||
]]--
|
||||
|
||||
local S = nether.get_translator
|
||||
|
||||
minetest.register_tool("nether:pick_nether", {
|
||||
|