Finish initial refactor design
parent
78fea8405c
commit
851cb19cd2
215
init.lua
215
init.lua
|
@ -7,7 +7,7 @@ dofile(modpath.."/lua/timer.lua")
|
||||||
|
|
||||||
-- Local variables used for programming
|
-- Local variables used for programming
|
||||||
local internal =
|
local internal =
|
||||||
{ a = 0
|
{ a = nil
|
||||||
|
|
||||||
-- Average size of a cave biome
|
-- Average size of a cave biome
|
||||||
, biome_size = { x = 50, y = 50, z = 50 }
|
, biome_size = { x = 50, y = 50, z = 50 }
|
||||||
|
@ -18,6 +18,18 @@ local internal =
|
||||||
-- The margin that a voxelmanip gives around the chunk being generated
|
-- The margin that a voxelmanip gives around the chunk being generated
|
||||||
, mapgen_buffer = 16
|
, mapgen_buffer = 16
|
||||||
|
|
||||||
|
-- Enum type used during cave generation for classifying different types
|
||||||
|
-- of cave nodes. Do not touch unless you're changing the code
|
||||||
|
, node_types =
|
||||||
|
{ stick_edge = 0
|
||||||
|
, air = 1
|
||||||
|
, stone = 2
|
||||||
|
, floor = 3
|
||||||
|
, wall = 4
|
||||||
|
, ceiling = 5
|
||||||
|
, floor_deco = 6
|
||||||
|
}
|
||||||
|
|
||||||
-- Point that can be used for generating Voronoi graphs
|
-- Point that can be used for generating Voronoi graphs
|
||||||
-- The number can be anything, as long as it reasonably far away from
|
-- The number can be anything, as long as it reasonably far away from
|
||||||
-- the range 0 - 100
|
-- the range 0 - 100
|
||||||
|
@ -64,14 +76,15 @@ Flat3dArray.__index = Flat3dArray
|
||||||
-- Please see API.md for reference
|
-- Please see API.md for reference
|
||||||
noordstar_caves =
|
noordstar_caves =
|
||||||
{ a = nil
|
{ a = nil
|
||||||
-- The `cave_vastness` is a variable that determines the size of caves.
|
|
||||||
-- The function takes in an x, y and z variable, and returns a number between
|
-- The `cave_vastness` is a variable that determines the size of caves.
|
||||||
-- 0 and 1.
|
-- The function takes in an x, y and z variable, and returns a number
|
||||||
-- Low numbers mean very rare and tiny caves, while high numbers mean massive
|
-- between 0 and 1.
|
||||||
-- caves, with massive open spaces.
|
-- Low numbers mean very rare and tiny caves, while high numbers mean
|
||||||
-- If you wish to overwrite this function, it is good to keep in mind to:
|
-- massive caves, with massive open spaces.
|
||||||
-- - Make sure that the output changes VERY SLOWLY over time
|
-- If you wish to overwrite this function, it is good to keep in mind to:
|
||||||
-- - Keep the function performant as it will be executed many, many times
|
-- - Make sure that the output changes VERY SLOWLY over time
|
||||||
|
-- - Keep the function performant as it will be executed many, many times
|
||||||
, cave_vastness = function(pos)
|
, cave_vastness = function(pos)
|
||||||
return pos.y / internal.world_depth
|
return pos.y / internal.world_depth
|
||||||
end
|
end
|
||||||
|
@ -128,6 +141,7 @@ function noordstar_caves.unregister_shape(name)
|
||||||
noordstar_caves.registered_shapes[name] = nil
|
noordstar_caves.registered_shapes[name] = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
-- Calculate the distance of a given biome to a given point
|
-- Calculate the distance of a given biome to a given point
|
||||||
function internal.biome_def_distance(def, heat, humidity)
|
function internal.biome_def_distance(def, heat, humidity)
|
||||||
local dx = math.abs(humidity - def.humidity_point)
|
local dx = math.abs(humidity - def.humidity_point)
|
||||||
|
@ -452,6 +466,64 @@ function internal.flat_from_cave_bools(minp, maxp)
|
||||||
return bools
|
return bools
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function internal.flat_from_node_types(bools, minp, maxp)
|
||||||
|
local nt = internal.node_types
|
||||||
|
|
||||||
|
-- local bools = internal.flat_from_cave_bools(minp, maxp)
|
||||||
|
|
||||||
|
local node_types = Flat3dArray:from_func(minp, maxp, function (i, pos)
|
||||||
|
-- Simplify calculation by ignoring edges
|
||||||
|
if pos.x == minp.x or pos.x == maxp.x then
|
||||||
|
return nt.stick_edge
|
||||||
|
elseif pos.y == minp.y or pos.y == maxp.y then
|
||||||
|
return nt.stick_edge
|
||||||
|
elseif pos.z == maxp.z or pos.z == minp.z then
|
||||||
|
return nt.stick_edge
|
||||||
|
end
|
||||||
|
|
||||||
|
if bools:get_index(i) then
|
||||||
|
-- PART OF CAVE
|
||||||
|
|
||||||
|
if bools:get_index(i + bools.down) == false then
|
||||||
|
-- Block is right on the floor
|
||||||
|
return nt.floor_deco
|
||||||
|
end
|
||||||
|
|
||||||
|
return nt.air
|
||||||
|
else
|
||||||
|
-- NOT PART OF CAVE
|
||||||
|
|
||||||
|
-- Floor
|
||||||
|
if bools:get_index(i + bools.up) then
|
||||||
|
return nt.floor
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Ceiling
|
||||||
|
if bools:get_index(i + bools.down) then
|
||||||
|
return nt.ceiling
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Walls
|
||||||
|
if bools:get_index(i + bools.north) then
|
||||||
|
return nt.wall
|
||||||
|
end
|
||||||
|
if bools:get_index(i + bools.east) then
|
||||||
|
return nt.wall
|
||||||
|
end
|
||||||
|
if bools:get_index(i + bools.south) then
|
||||||
|
return nt.wall
|
||||||
|
end
|
||||||
|
if bools:get_index(i + bools.west) then
|
||||||
|
return nt.wall
|
||||||
|
end
|
||||||
|
|
||||||
|
return nt.stone
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
return node_types
|
||||||
|
end
|
||||||
|
|
||||||
-- Get a Flat3dArray using noise_params
|
-- Get a Flat3dArray using noise_params
|
||||||
function internal.flat_from_noise_params(noise_params, minp, maxp)
|
function internal.flat_from_noise_params(noise_params, minp, maxp)
|
||||||
local nx = maxp.x - minp.x + 1
|
local nx = maxp.x - minp.x + 1
|
||||||
|
@ -501,6 +573,105 @@ function internal.from_3d_to_flat(dx, dy, dz, nx, ny)
|
||||||
return (nx * ny * dz) + (nx * dy) + dx + 1
|
return (nx * ny * dz) + (nx * dy) + dx + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Generate caves given a voxelmanip array
|
||||||
|
function internal.generate_caves(data, minp, maxp)
|
||||||
|
-- Increased voxelmanip size
|
||||||
|
local vminp =
|
||||||
|
{ x = minp.x - internal.mapgen_buffer
|
||||||
|
, y = minp.y - internal.mapgen_buffer
|
||||||
|
, z = minp.z - internal.mapgen_buffer
|
||||||
|
}
|
||||||
|
local vmaxp =
|
||||||
|
{ x = maxp.x + internal.mapgen_buffer
|
||||||
|
, y = maxp.y + internal.mapgen_buffer
|
||||||
|
, z = maxp.z + internal.mapgen_buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Increased node type size to account for unknown edges
|
||||||
|
local bminp =
|
||||||
|
{ x = minp.x - 1
|
||||||
|
, y = minp.y - 1
|
||||||
|
, z = minp.z - 1
|
||||||
|
}
|
||||||
|
local bmaxp =
|
||||||
|
{ x = maxp.x + 1
|
||||||
|
, y = maxp.y + 1
|
||||||
|
, z = maxp.z + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Load data
|
||||||
|
local vmanip = Flat3dArray:new(vminp, vmaxp, data)
|
||||||
|
|
||||||
|
-- Get cave bools
|
||||||
|
local bools = internal.flat_from_cave_bools(bminp, bmaxp)
|
||||||
|
|
||||||
|
-- Get node types
|
||||||
|
local nts = internal.flat_from_node_types(bools, bminp, bmaxp)
|
||||||
|
|
||||||
|
-- Calculate biome heat & humidity
|
||||||
|
local heat_points = internal.flat_from_noise_params(
|
||||||
|
internal.heat_noise_params(), bminp, bmaxp
|
||||||
|
)
|
||||||
|
local humidity_points = internal.flat_from_noise_params(
|
||||||
|
internal.humidity_noise_params(), bminp, bmaxp
|
||||||
|
)
|
||||||
|
|
||||||
|
-- Place blocks where necessary
|
||||||
|
internal.iter_3d_area(bminp, bmaxp, function (i, pos)
|
||||||
|
local function place(name)
|
||||||
|
if type(name) == "string" then
|
||||||
|
vmanip:set_index(
|
||||||
|
vmanip:pos_to_index(pos),
|
||||||
|
minetest.get_content_id(name)
|
||||||
|
)
|
||||||
|
elseif type(name) == "nil" then
|
||||||
|
else
|
||||||
|
error("Inserted invalid type " .. type(name) .. " into voxelmanip array")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local nt = nts:get_index(i)
|
||||||
|
|
||||||
|
if nt == internal.node_types.stone then
|
||||||
|
elseif nt == internal.node_types.stick_edge then
|
||||||
|
elseif nt == internal.node_types.air then
|
||||||
|
place("air")
|
||||||
|
elseif nt == internal.node_types.floor_deco then
|
||||||
|
-- TODO: Place registered decoration
|
||||||
|
else
|
||||||
|
-- Find appropriate biome
|
||||||
|
local heat = heat_points:get_index(i)
|
||||||
|
local humidity = humidity_points:get_index(i)
|
||||||
|
|
||||||
|
local name = internal.closest_cave_biome(heat, humidity)
|
||||||
|
|
||||||
|
local def = noordstar_caves.registered_biomes[name] or internal.default_biome()
|
||||||
|
|
||||||
|
if nt == internal.node_types.floor then
|
||||||
|
place(def.node_floor)
|
||||||
|
elseif nt == internal.node_types.wall then
|
||||||
|
place(def.node_wall)
|
||||||
|
elseif nt == internal.node_types.ceiling then
|
||||||
|
place(def.node_roof)
|
||||||
|
else
|
||||||
|
error(
|
||||||
|
table.concat(
|
||||||
|
{ "Found unknown node type "
|
||||||
|
, nt
|
||||||
|
, " (type "
|
||||||
|
, type(nt)
|
||||||
|
, ")"
|
||||||
|
}
|
||||||
|
, ""
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
return vmanip.arr
|
||||||
|
end
|
||||||
|
|
||||||
-- Get the noise params for the cave biome temperature.
|
-- Get the noise params for the cave biome temperature.
|
||||||
function internal.heat_noise_params()
|
function internal.heat_noise_params()
|
||||||
return {
|
return {
|
||||||
|
@ -599,6 +770,11 @@ function internal.iter_3d_area(minp, maxp, callback)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- -- Log text to the Minetest chat
|
||||||
|
-- function internal.log(text)
|
||||||
|
-- minetest.chat_send_all(os.time() .. " - " .. text)
|
||||||
|
-- end
|
||||||
|
|
||||||
-- Helper function to convert a set of coordinates to a readable string
|
-- Helper function to convert a set of coordinates to a readable string
|
||||||
function internal.pos_to_str(pos)
|
function internal.pos_to_str(pos)
|
||||||
return "(" .. pos.x .. ", " .. pos.y .. ", " .. pos.z .. " )"
|
return "(" .. pos.x .. ", " .. pos.y .. ", " .. pos.z .. " )"
|
||||||
|
@ -698,6 +874,7 @@ function internal.within_bounds(pos, minp, maxp)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
-- Create a new flat 3d array based on a function callback that gets the index
|
-- Create a new flat 3d array based on a function callback that gets the index
|
||||||
-- and position as inputs
|
-- and position as inputs
|
||||||
function Flat3dArray:from_func(minp, maxp, callback)
|
function Flat3dArray:from_func(minp, maxp, callback)
|
||||||
|
@ -813,6 +990,26 @@ function Flat3dArray:validate_pos(pos)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
minetest.register_on_generated(function(minp, maxp, blockseed)
|
||||||
|
local vm = minetest.get_mapgen_object("voxelmanip")
|
||||||
|
local data = vm:get_data()
|
||||||
|
|
||||||
|
data = internal.generate_caves(data, minp, maxp)
|
||||||
|
|
||||||
|
vm:set_data(data)
|
||||||
|
vm:write_to_map()
|
||||||
|
end)
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
|
|
578
lua/engine.lua
578
lua/engine.lua
|
@ -1,578 +0,0 @@
|
||||||
|
|
||||||
-- Constants and magic numbers
|
|
||||||
local mapgen_buffer = 16
|
|
||||||
|
|
||||||
-- Cave biome range
|
|
||||||
local biome_spread_range = 50
|
|
||||||
|
|
||||||
-- Noise params for heat_point
|
|
||||||
local heat_noise_params =
|
|
||||||
{ offset = 50
|
|
||||||
, scale = 50
|
|
||||||
, spread = { x = biome_spread_range, y = biome_spread_range, z = biome_spread_range }
|
|
||||||
, seed = 320523
|
|
||||||
, octaves = 2
|
|
||||||
, persistence = 0.1
|
|
||||||
, lacunarity = 2.0
|
|
||||||
, flags = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
-- Noise params for humidity_point
|
|
||||||
local humidity_noise_params =
|
|
||||||
{ offset = 50
|
|
||||||
, scale = 50
|
|
||||||
, spread = { x = biome_spread_range, y = biome_spread_range, z = biome_spread_range }
|
|
||||||
, seed = 9923473
|
|
||||||
, octaves = 2
|
|
||||||
, persistence = 0.1
|
|
||||||
, lacunarity = 2.0
|
|
||||||
, flags = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
local default_biome =
|
|
||||||
{ name = "noordstar_caves:none"
|
|
||||||
, heat_point = -1e6
|
|
||||||
, humidity_point = -1e6
|
|
||||||
, minp = { x = -1e5, y = -1e5, z = -1e5 }
|
|
||||||
, maxp = { x = 1e5, y = 1e5, z = 1e5 }
|
|
||||||
}
|
|
||||||
|
|
||||||
local cave_shape_chunk_size = 8
|
|
||||||
|
|
||||||
local cluster_shape_chunks_x = 4
|
|
||||||
local cluster_shape_chunks_y = 6
|
|
||||||
local cluster_shape_chunks_z = 4
|
|
||||||
|
|
||||||
local function reduced_pos(pos)
|
|
||||||
return
|
|
||||||
{ x = math.floor(pos.x / (cave_shape_chunk_size * cluster_shape_chunks_x))
|
|
||||||
, y = math.floor(pos.y / (cave_shape_chunk_size * cluster_shape_chunks_y))
|
|
||||||
, z = math.floor(pos.z / (cave_shape_chunk_size * cluster_shape_chunks_z))
|
|
||||||
}
|
|
||||||
end
|
|
||||||
-- Convert 3d relative coordinates to an index on a flat array
|
|
||||||
local function from_3d_to_flat(dx, dy, dz, nx, ny)
|
|
||||||
return (nx * ny * dz) + (nx * dy) + dx + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Convert an index on a flat array to 3d relative coordinates
|
|
||||||
local function from_flat_to_3d(i, nx, ny)
|
|
||||||
return {
|
|
||||||
dx = (i - 1) % nx,
|
|
||||||
dy = math.floor((i - 1) / nx) % ny,
|
|
||||||
dz = math.floor((i - 1) / (nx * ny))
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Iterate over a 3d area, both indexing by the index and the ansolute position
|
|
||||||
local function iter_3d_area(minp, maxp, callback)
|
|
||||||
local nx = maxp.x - minp.x + 1
|
|
||||||
local ny = maxp.y - minp.y + 1
|
|
||||||
local nz = maxp.z - minp.z + 1
|
|
||||||
|
|
||||||
for i = 1, nx * ny * nz do
|
|
||||||
local dpos = from_flat_to_3d(i, nx, ny)
|
|
||||||
local pos = {
|
|
||||||
x = minp.x + dpos.dx,
|
|
||||||
y = minp.y + dpos.dy,
|
|
||||||
z = minp.z + dpos.dz,
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(i, pos)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Helper function to convert a set of coordinates to a readable string
|
|
||||||
local function pos_to_str(pos)
|
|
||||||
return "(" .. pos.x .. ", " .. pos.y .. ", " .. pos.z .. " )"
|
|
||||||
end
|
|
||||||
|
|
||||||
local Flat3dArray = {}
|
|
||||||
Flat3dArray.__index = Flat3dArray
|
|
||||||
|
|
||||||
function Flat3dArray:new(minp, maxp, arr)
|
|
||||||
local instance = {}
|
|
||||||
setmetatable(instance, Flat3dArray)
|
|
||||||
|
|
||||||
local nx = maxp.x - minp.x + 1
|
|
||||||
local ny = maxp.y - minp.y + 1
|
|
||||||
local nz = maxp.z - minp.z + 1
|
|
||||||
|
|
||||||
if #arr ~= nx * ny * nz then
|
|
||||||
error(
|
|
||||||
"Input array doesn't match dimension lengths: " .. nx .. " x " ..
|
|
||||||
ny .. " x " .. nz .. " = " .. (nx*ny*nz) .. ", but found " .. #arr
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
instance.nx = nx
|
|
||||||
instance.ny = ny
|
|
||||||
instance.nz = nz
|
|
||||||
instance.minp = minp
|
|
||||||
instance.maxp = maxp
|
|
||||||
instance.arr = arr
|
|
||||||
|
|
||||||
return instance
|
|
||||||
end
|
|
||||||
|
|
||||||
function Flat3dArray:from_func(minp, maxp, callback)
|
|
||||||
local arr = {}
|
|
||||||
|
|
||||||
iter_3d_area(minp, maxp, function (i, pos)
|
|
||||||
arr[i] = callback(i, pos)
|
|
||||||
end)
|
|
||||||
|
|
||||||
return self:new(minp, maxp, arr)
|
|
||||||
end
|
|
||||||
|
|
||||||
function Flat3dArray:get_index(i)
|
|
||||||
local out = self.arr[i]
|
|
||||||
if out == nil then
|
|
||||||
error(
|
|
||||||
"Index " .. i .. " not found in array of length " .. #self.arr
|
|
||||||
)
|
|
||||||
end
|
|
||||||
return out
|
|
||||||
end
|
|
||||||
|
|
||||||
function Flat3dArray:get_pos(pos)
|
|
||||||
self:validate_pos(pos)
|
|
||||||
|
|
||||||
local dx = pos.x - self.minp.x
|
|
||||||
local dy = pos.y - self.minp.y
|
|
||||||
local dz = pos.z - self.minp.z
|
|
||||||
|
|
||||||
return self:get_index(from_3d_to_flat(dx, dy, dz, self.nx, self.ny))
|
|
||||||
end
|
|
||||||
|
|
||||||
local function is_valid_pos(pos, minp, maxp)
|
|
||||||
if pos.x < minp.x then
|
|
||||||
return false
|
|
||||||
elseif pos.x > maxp.x then
|
|
||||||
return false
|
|
||||||
elseif pos.y < minp.y then
|
|
||||||
return false
|
|
||||||
elseif pos.y > maxp.y then
|
|
||||||
return false
|
|
||||||
elseif pos.z < minp.z then
|
|
||||||
return false
|
|
||||||
elseif pos.z > maxp.z then
|
|
||||||
return false
|
|
||||||
else
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function Flat3dArray:valid_pos(pos)
|
|
||||||
return is_valid_pos(pos, self.minp, self.maxp)
|
|
||||||
end
|
|
||||||
|
|
||||||
function Flat3dArray:validate_pos(pos)
|
|
||||||
if not self:valid_pos(pos) then
|
|
||||||
error(
|
|
||||||
table.concat(
|
|
||||||
{ "Position "
|
|
||||||
, pos_to_str(pos)
|
|
||||||
, " out of bounds from minp = "
|
|
||||||
, pos_to_str(self.minp)
|
|
||||||
, ", maxp = "
|
|
||||||
, pos_to_str(self.maxp)
|
|
||||||
},
|
|
||||||
""
|
|
||||||
)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get an enhanced function from the def function that warns us when the
|
|
||||||
-- function does not behave properly
|
|
||||||
local function enhanced_func(def)
|
|
||||||
if type(def.name) ~= "string" then
|
|
||||||
error("Invalid nameless shape definition")
|
|
||||||
elseif type(def.func) ~= "function" then
|
|
||||||
error(
|
|
||||||
"Invalid shape definition misses an adjustment function"
|
|
||||||
)
|
|
||||||
else
|
|
||||||
return function(pos, n)
|
|
||||||
local out = def.func(pos, n)
|
|
||||||
|
|
||||||
if type(out) == "number" then
|
|
||||||
return out
|
|
||||||
elseif n == nil then
|
|
||||||
error(
|
|
||||||
"Shape " .. def.name .. " function must return a number. The input `n` was nil. Perhaps your `noise_params` field is invalid?"
|
|
||||||
)
|
|
||||||
else
|
|
||||||
error("Shape " .. def.name .. " function must return a number.")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get a flat array of cave shape noise values from a given cave shape def
|
|
||||||
local function get_flat_from_shape_def(def, minp, maxp)
|
|
||||||
local f = enhanced_func(def)
|
|
||||||
|
|
||||||
local nx = maxp.x - minp.x + 1
|
|
||||||
local ny = maxp.y - minp.y + 1
|
|
||||||
local nz = maxp.z - minp.z + 1
|
|
||||||
|
|
||||||
local noise_flat_map = {}
|
|
||||||
|
|
||||||
-- If noise parameters have been defined, fill the table with noise
|
|
||||||
-- If not, all values remain nil
|
|
||||||
if def.noise_params ~= nil then
|
|
||||||
local p = PerlinNoiseMap(def.noise_params, { x = nx, y = ny, z = nz })
|
|
||||||
|
|
||||||
if nz == 1 then
|
|
||||||
p:get_2d_map_flat(minp, noise_flat_map)
|
|
||||||
else
|
|
||||||
p:get_3d_map_flat(minp, noise_flat_map)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
iter_3d_area(minp, maxp, function(i, pos)
|
|
||||||
noise_flat_map[i] = f(pos, noise_flat_map[i])
|
|
||||||
end)
|
|
||||||
|
|
||||||
return Flat3dArray:new(minp, maxp, noise_flat_map)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function get_flat_from_noise_params(minp, maxp, noise_params)
|
|
||||||
local nx = maxp.x - minp.x + 1
|
|
||||||
local ny = maxp.y - minp.y + 1
|
|
||||||
local nz = maxp.z - minp.z + 1
|
|
||||||
|
|
||||||
local buffer = {}
|
|
||||||
|
|
||||||
local p = PerlinNoiseMap(noise_params, { x = nx, y = ny, z = nz })
|
|
||||||
|
|
||||||
if nz == 1 then
|
|
||||||
p:get_2d_map_flat(minp, buffer)
|
|
||||||
else
|
|
||||||
p:get_3d_map_flat(minp, buffer)
|
|
||||||
end
|
|
||||||
|
|
||||||
return Flat3dArray:new(minp, maxp, buffer)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Based on the number of cave shapes, calculate how quickly connectivity is
|
|
||||||
-- meant to change
|
|
||||||
local function get_connectivity_noise_params(shape_size)
|
|
||||||
local factor = math.max(math.abs(shape_size) ^ 0.5, 1)
|
|
||||||
|
|
||||||
return {
|
|
||||||
offset = 50,
|
|
||||||
scale = 50,
|
|
||||||
spread =
|
|
||||||
reduced_pos(
|
|
||||||
{ x = factor * 250
|
|
||||||
, y = factor * 100
|
|
||||||
, z = factor * 250
|
|
||||||
}
|
|
||||||
),
|
|
||||||
seed = 297948,
|
|
||||||
octaves = 2,
|
|
||||||
persistence = 0.2,
|
|
||||||
lacunarity = 2.0,
|
|
||||||
flags = "eased"
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Based on the number of cave shapes, calculate how quickly verticality is
|
|
||||||
-- meant to change
|
|
||||||
local function get_verticality_noise_params(shape_size)
|
|
||||||
local factor = math.max(math.abs(shape_size) ^ 0.5, 1)
|
|
||||||
|
|
||||||
return {
|
|
||||||
offset = 50,
|
|
||||||
scale = 50,
|
|
||||||
spread =
|
|
||||||
reduced_pos(
|
|
||||||
{ x = factor * 100
|
|
||||||
, y = factor * 250
|
|
||||||
, z = factor * 100
|
|
||||||
}
|
|
||||||
),
|
|
||||||
seed = 35644,
|
|
||||||
octaves = 2,
|
|
||||||
persistence = 0.2,
|
|
||||||
lacunarity = 2.0,
|
|
||||||
flags = "eased"
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get whether a cave shape is within distance.
|
|
||||||
local function shape_within_distance(shape_size, dx, dy)
|
|
||||||
local factor = math.max(math.abs(shape_size) ^ 0.5, 1)
|
|
||||||
local max_distance = 30 / factor
|
|
||||||
|
|
||||||
return dx^2 + dy^2 <= max_distance^2
|
|
||||||
end
|
|
||||||
|
|
||||||
local function shape_def_distance(def, x, y)
|
|
||||||
local dx = math.abs(x - def.connectivity_point)
|
|
||||||
local dy = math.abs(y - def.verticality_point)
|
|
||||||
|
|
||||||
return dx^2 + dy^2
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get a flat map containing all flat threshold values
|
|
||||||
local function get_threshold_flat(minp, maxp)
|
|
||||||
local connectivity = get_flat_from_noise_params(
|
|
||||||
reduced_pos(minp), reduced_pos(maxp),
|
|
||||||
get_connectivity_noise_params(#noordstar_caves.registered_shapes)
|
|
||||||
)
|
|
||||||
|
|
||||||
local verticality = get_flat_from_noise_params(
|
|
||||||
reduced_pos(minp), reduced_pos(maxp),
|
|
||||||
get_verticality_noise_params(#noordstar_caves.registered_shapes)
|
|
||||||
)
|
|
||||||
|
|
||||||
local noise = {}
|
|
||||||
|
|
||||||
-- Get noise for all cave shapes
|
|
||||||
for key, def in pairs(noordstar_caves.registered_shapes) do
|
|
||||||
noise[key] = get_flat_from_shape_def(def, reduced_pos(minp), reduced_pos(maxp))
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Create a (reduced) flat array
|
|
||||||
local reduced = Flat3dArray:from_func(
|
|
||||||
reduced_pos(minp), reduced_pos(maxp),
|
|
||||||
function(i, pos)
|
|
||||||
local x = connectivity:get_pos(pos)
|
|
||||||
local y = verticality:get_pos(pos)
|
|
||||||
|
|
||||||
local value = 0
|
|
||||||
|
|
||||||
for key, def in pairs(noordstar_caves.registered_shapes) do
|
|
||||||
local dx = math.abs(x - def.connectivity_point)
|
|
||||||
local dy = math.abs(y - def.verticality_point)
|
|
||||||
|
|
||||||
if shape_within_distance(#noordstar_caves.registered_shapes, dx, dy) then
|
|
||||||
value = math.max(value, noise[key]:get_pos(pos))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return value
|
|
||||||
end
|
|
||||||
)
|
|
||||||
|
|
||||||
-- Create the flat array
|
|
||||||
local x = Flat3dArray:from_func(minp, maxp, function(i, pos)
|
|
||||||
return reduced:get_pos(reduced_pos(pos))
|
|
||||||
end)
|
|
||||||
|
|
||||||
return x
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- Old minimum height for the overworld
|
|
||||||
local old_overworld_min = mcl_vars.mg_overworld_min
|
|
||||||
|
|
||||||
-- If another mod doesn't override the maximum world depth, we will assume that
|
|
||||||
-- the world depth is the following value.
|
|
||||||
local world_depth = -60
|
|
||||||
|
|
||||||
-- Otherwise, this variable can be changed using the following function,
|
|
||||||
-- which will also update all the other necessary variables
|
|
||||||
function noordstar_caves.set_world_depth(h)
|
|
||||||
-- Set world depth variable
|
|
||||||
world_depth = h
|
|
||||||
end
|
|
||||||
noordstar_caves.set_world_depth(world_depth)
|
|
||||||
|
|
||||||
-- The `cave_vastness` is a variable that determines the size of caves.
|
|
||||||
-- The function takes in an x, y and z variable, and returns a number between
|
|
||||||
-- 0 and 1.
|
|
||||||
-- Low numbers mean very rare and tiny caves, while high numbers mean massive
|
|
||||||
-- caves, with massive open spaces.
|
|
||||||
-- If you wish to overwrite this function, it is good to keep in mind to:
|
|
||||||
-- - Make sure that the output changes VERY SLOWLY over time
|
|
||||||
-- - This function will be run a LOT so it is very performance sensitive
|
|
||||||
noordstar_caves.cave_vastness = function(pos)
|
|
||||||
return pos.y / world_depth
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Secretly, we're using an internal function that also adds a safe layer for
|
|
||||||
-- when we're approaching bedrock levels
|
|
||||||
local function cave_vastness(pos)
|
|
||||||
if world_depth + 20 < pos.y then
|
|
||||||
return noordstar_caves.cave_vastness(pos)
|
|
||||||
elseif world_depth + 5 < pos.y then
|
|
||||||
return noordstar_caves.cave_vastness(pos) * math.abs(pos.y - world_depth - 5) / 15
|
|
||||||
else
|
|
||||||
return 0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get a flat array of nodes that are either in caves or not in caves
|
|
||||||
local function get_flat_cave_bools(minp, maxp)
|
|
||||||
local thresholds = get_threshold_flat(minp, maxp)
|
|
||||||
|
|
||||||
return Flat3dArray:from_func(minp, maxp, function(i, pos)
|
|
||||||
return thresholds:get_pos(pos) >= 1 - cave_vastness(pos)
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
local node_type =
|
|
||||||
{ unknown = 1 -- Edge of a chunk
|
|
||||||
, floor = 2 -- Floor of a cave
|
|
||||||
, wall = 3 -- Side wall of a cave
|
|
||||||
, roof = 4 -- Roof of a cave
|
|
||||||
, content = 5 -- Air in the cave not adjacent to a wall, floor or roof
|
|
||||||
, stone = 6 -- Underground node not adjacent to a cave
|
|
||||||
}
|
|
||||||
|
|
||||||
local function get_flat_cave_node_types(minp, maxp)
|
|
||||||
local bools = get_flat_cave_bools(minp, maxp)
|
|
||||||
|
|
||||||
return Flat3dArray:from_func(minp, maxp, function (i, pos)
|
|
||||||
if bools:get_pos(pos) then
|
|
||||||
return node_type.content
|
|
||||||
-- Floor takes precedence
|
|
||||||
elseif pos.y == maxp.y then -- Could be floor
|
|
||||||
return node_type.unknown
|
|
||||||
elseif bools:get_pos({ x = pos.x, y = pos.y + 1, z = pos.z }) then
|
|
||||||
return node_type.floor
|
|
||||||
-- Then roof takes precedence
|
|
||||||
elseif pos.y == minp.y then -- Could be roof
|
|
||||||
return node_type.unknown
|
|
||||||
elseif bools:get_pos({ x = pos.x, y = pos.y - 1, z = pos.z }) then
|
|
||||||
return node_type.roof
|
|
||||||
else
|
|
||||||
-- Check for walls
|
|
||||||
local left = { x = pos.x - 1, y = pos.y, z = pos.z }
|
|
||||||
local right = { x = pos.x + 1, y = pos.y, z = pos.z }
|
|
||||||
local front = { x = pos.x, y = pos.y, z = pos.z - 1 }
|
|
||||||
local back = { x = pos.x, y = pos.y, z = pos.z + 1 }
|
|
||||||
|
|
||||||
-- Check if the value is near the edge
|
|
||||||
local on_edge = false
|
|
||||||
|
|
||||||
if left.x < minp.x then
|
|
||||||
on_edge = true
|
|
||||||
elseif bools:get_pos(left) then
|
|
||||||
return node_type.wall
|
|
||||||
end
|
|
||||||
if right.x > maxp.x then
|
|
||||||
on_edge = true
|
|
||||||
elseif bools:get_pos(right) then
|
|
||||||
return node_type.wall
|
|
||||||
end
|
|
||||||
if front.z < minp.z then
|
|
||||||
on_edge = true
|
|
||||||
elseif bools:get_pos(front) then
|
|
||||||
return node_type.wall
|
|
||||||
end
|
|
||||||
if back.z > maxp.z then
|
|
||||||
on_edge = true
|
|
||||||
elseif bools:get_pos(back) then
|
|
||||||
return node_type.wall
|
|
||||||
end
|
|
||||||
|
|
||||||
if on_edge then
|
|
||||||
return node_type.unknown
|
|
||||||
else
|
|
||||||
return node_type.stone
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Transform categorized blocks into
|
|
||||||
local function biome_def_to_content_id(def, nt, original_block)
|
|
||||||
local function get_node(name, alt)
|
|
||||||
return function()
|
|
||||||
if name == nil then
|
|
||||||
return alt
|
|
||||||
else
|
|
||||||
return minetest.get_content_id(name)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local node_air = get_node(def.node_air, minetest.get_content_id("air"))
|
|
||||||
local node_floor = get_node(def.node_floor, original_block)
|
|
||||||
local node_stone = get_node(nil, original_block)
|
|
||||||
local node_roof = get_node(def.node_roof, original_block)
|
|
||||||
local node_unknown = get_node(nil, original_block)
|
|
||||||
local node_wall = get_node(def.node_wall, original_block)
|
|
||||||
|
|
||||||
if nt == node_type.unknown then
|
|
||||||
return node_unknown()
|
|
||||||
elseif nt == node_type.floor then
|
|
||||||
return node_floor()
|
|
||||||
elseif nt == node_type.wall then
|
|
||||||
return node_wall()
|
|
||||||
elseif nt == node_type.roof then
|
|
||||||
return node_roof()
|
|
||||||
elseif nt == node_type.content then
|
|
||||||
return node_air()
|
|
||||||
elseif nt == node_type.stone then
|
|
||||||
return node_stone()
|
|
||||||
else
|
|
||||||
return original_block
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function biome_distance(def, heat, humidity)
|
|
||||||
return (def.heat_point - heat)^2 + (def.humidity_point - humidity)^2
|
|
||||||
end
|
|
||||||
|
|
||||||
minetest.register_on_generated(function(minp, maxp, blockseed)
|
|
||||||
local vminp =
|
|
||||||
{ x = minp.x - mapgen_buffer
|
|
||||||
, y = minp.y - mapgen_buffer
|
|
||||||
, z = minp.z - mapgen_buffer
|
|
||||||
}
|
|
||||||
local vmaxp =
|
|
||||||
{ x = maxp.x + mapgen_buffer
|
|
||||||
, y = maxp.y + mapgen_buffer
|
|
||||||
, z = maxp.z + mapgen_buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
-- Get voxelmanip
|
|
||||||
local vm = minetest.get_mapgen_object("voxelmanip")
|
|
||||||
local flat_data = Flat3dArray:new(vminp, vmaxp, vm:get_data())
|
|
||||||
|
|
||||||
-- Get threshold values
|
|
||||||
local node_types = get_flat_cave_node_types(vminp, vmaxp)
|
|
||||||
|
|
||||||
local heat_points = get_flat_from_noise_params(minp, maxp, heat_noise_params)
|
|
||||||
local humidity_points = get_flat_from_noise_params(minp, maxp, humidity_noise_params)
|
|
||||||
|
|
||||||
-- Map block values
|
|
||||||
local nids = Flat3dArray:from_func(vminp, vmaxp, function(i, pos)
|
|
||||||
local nt = node_types:get_pos(pos)
|
|
||||||
|
|
||||||
if not is_valid_pos(pos, minp, maxp) then
|
|
||||||
return flat_data:get_pos(pos)
|
|
||||||
else
|
|
||||||
local biome = default_biome
|
|
||||||
|
|
||||||
local heat = heat_points:get_pos(pos)
|
|
||||||
local humidity = humidity_points:get_pos(pos)
|
|
||||||
|
|
||||||
for _, def in pairs(noordstar_caves.registered_biomes) do
|
|
||||||
if pos.x < def.minp.x then
|
|
||||||
elseif pos.y < def.minp.y then
|
|
||||||
elseif pos.z < def.minp.z then
|
|
||||||
elseif pos.x > def.maxp.x then
|
|
||||||
elseif pos.y > def.maxp.y then
|
|
||||||
elseif pos.z > def.maxp.z then
|
|
||||||
elseif biome_distance(def, heat, humidity) > biome_distance(biome, heat, humidity) then
|
|
||||||
else
|
|
||||||
biome = def
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return biome_def_to_content_id(biome, nt, flat_data:get_pos(pos))
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
-- Write all changes to the Minetest world
|
|
||||||
vm:set_data(nids.arr)
|
|
||||||
vm:write_to_map()
|
|
||||||
end)
|
|
Loading…
Reference in New Issue