forked from minetest-mods/nether
513 lines
19 KiB
Lua
513 lines
19 KiB
Lua
--[[
|
|
|
|
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 S = nether.get_translator
|
|
|
|
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 = {}
|
|
|
|
-- 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
|
|
|
|
local cavePerlin = mapgen.getCavePointPerlin()
|
|
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:get_3d({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 values less than 4 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 radiusSquared = 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 = radiusSquared - (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 easier 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 _, cavern_limit_distance = mapgen.find_nearest_lava_sealevel(mantle_y)
|
|
local _, centerRegionLimit_adj = mapgen.get_mapgenblend_adjustments(mantle_y)
|
|
|
|
-- 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
|
|
local cavern_noise_adj =
|
|
mapgen.CENTER_REGION_LIMIT * (cavern_limit_distance * cavern_limit_distance * cavern_limit_distance) -
|
|
centerRegionLimit_adj
|
|
|
|
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
|
|
|
|
|
|
-- an enumerated list of the different regions in the nether
|
|
mapgen.RegionEnum = {
|
|
OVERWORLD = {name = "overworld", desc = S("The Overworld") }, -- Outside the Nether / none of the regions in the Nether
|
|
POSITIVE = {name = "positive", desc = S("Positive nether") }, -- The classic nether caverns are here - where cavePerlin > 0.6
|
|
POSITIVESHELL = {name = "positive shell", desc = S("Shell between positive nether and center region") }, -- the nether side of the wall/buffer area separating classic nether from the mantle
|
|
CENTER = {name = "center", desc = S("Center/Mantle, inside cavern") },
|
|
CENTERSHELL = {name = "center shell", desc = S("Center/Mantle, but outside the caverns") }, -- the mantle side of the wall/buffer area separating the positive and negative regions from the center region
|
|
NEGATIVE = {name = "negative", desc = S("Negative nether") }, -- Secondary/spare region - where cavePerlin < -0.6
|
|
NEGATIVESHELL = {name = "negative shell", desc = S("Shell between negative nether and center region") } -- the spare region side of the wall/buffer area separating the negative region from the mantle
|
|
}
|
|
|
|
|
|
-- Returns (region, noise) where region is a value from mapgen.RegionEnum
|
|
-- and noise is the unadjusted cave perlin value
|
|
mapgen.getRegion = function(pos)
|
|
|
|
if pos.y > nether.DEPTH_CEILING or pos.y < nether.DEPTH_FLOOR then
|
|
return mapgen.RegionEnum.OVERWORLD, nil
|
|
end
|
|
|
|
local caveNoise = mapgen.getCavePerlinAt(pos)
|
|
local sealevel, 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
|
|
|
|
-- 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
|
|
local cavern_noise_adj =
|
|
mapgen.CENTER_REGION_LIMIT * (cavern_limit_distance * cavern_limit_distance * cavern_limit_distance) -
|
|
centerRegionLimit_adj
|
|
|
|
local region
|
|
if caveNoise > tcave then
|
|
region = mapgen.RegionEnum.POSITIVE
|
|
elseif -caveNoise > tcave then
|
|
region = mapgen.RegionEnum.NEGATIVE
|
|
elseif math_abs(caveNoise) < tmantle then
|
|
|
|
if math_abs(caveNoise) + cavern_noise_adj < mapgen.CENTER_CAVERN_LIMIT then
|
|
region = mapgen.RegionEnum.CENTER
|
|
else
|
|
region = mapgen.RegionEnum.CENTERSHELL
|
|
end
|
|
|
|
elseif caveNoise > 0 then
|
|
region = mapgen.RegionEnum.POSITIVESHELL
|
|
else
|
|
region = mapgen.RegionEnum.NEGATIVESHELL
|
|
end
|
|
|
|
return region, caveNoise
|
|
end
|
|
|
|
|
|
minetest.register_chatcommand("nether_whereami",
|
|
{
|
|
description = S("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, S("Unknown player position") end
|
|
local playerPos = vector.round(player:get_pos())
|
|
|
|
local region, caveNoise = mapgen.getRegion(playerPos)
|
|
local seaLevel, cavernLimitDistance = mapgen.find_nearest_lava_sealevel(playerPos.y)
|
|
local tcave_adj, centerRegionLimit_adj = mapgen.get_mapgenblend_adjustments(playerPos.y)
|
|
|
|
local seaDesc = ""
|
|
local boundaryDesc = ""
|
|
local perlinDesc = ""
|
|
|
|
if region ~= mapgen.RegionEnum.OVERWORLD then
|
|
|
|
local seaPos = playerPos.y - seaLevel
|
|
if seaPos > 0 then
|
|
seaDesc = S(", @1m above lava-sea level", seaPos)
|
|
else
|
|
seaDesc = S(", @1m below lava-sea level", seaPos)
|
|
end
|
|
|
|
if tcave_adj > 0 then
|
|
boundaryDesc = S(", approaching y boundary of Nether")
|
|
end
|
|
|
|
perlinDesc = S("[Perlin @1] ", (math_floor(caveNoise * 1000) / 1000))
|
|
end
|
|
|
|
return true, S("@1@2@3@4", perlinDesc, region.desc, seaDesc, boundaryDesc)
|
|
end
|
|
}
|
|
)
|