Compare commits
5 Commits
01921b398a
...
67f780b2b3
Author | SHA1 | Date |
---|---|---|
|
67f780b2b3 | |
|
acfdea349f | |
|
b9e18f70eb | |
|
1250abe7d8 | |
|
36b755725d |
61
API.md
61
API.md
|
@ -100,4 +100,63 @@ The shapes are defined as follows:
|
||||||
Just like the surface world, the underground world uses biomes to decorate their
|
Just like the surface world, the underground world uses biomes to decorate their
|
||||||
caves. The cave biomes are independent of the cave shapes.
|
caves. The cave biomes are independent of the cave shapes.
|
||||||
|
|
||||||
**Under development.**
|
For shapes, the following functions are available:
|
||||||
|
|
||||||
|
- `noordstar_caves.register_biome(biome def)` Define a new cave biome
|
||||||
|
- `noordstar_caves.unregister_biome(name)` Remove a defined cave biome
|
||||||
|
- `noordstar_caves.clear_registered_biomes()` Remove all known cave biomes
|
||||||
|
|
||||||
|
The biomes are defined as follows:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
{
|
||||||
|
name = "noordstar_caves:tundra",
|
||||||
|
-- Unique name identifying the biome
|
||||||
|
-- Namespacing is not required but recommended
|
||||||
|
|
||||||
|
node_dust = "foo:snow",
|
||||||
|
-- Node dropped onto floor after all else is generated
|
||||||
|
|
||||||
|
node_floor = "foo:dirt_with_snow",
|
||||||
|
-- Node forming the floor that the player walks on
|
||||||
|
|
||||||
|
node_wall = "foo:ice",
|
||||||
|
-- Node forming the side walls of the cave
|
||||||
|
|
||||||
|
node_roof = "foo:bluestone",
|
||||||
|
-- Node forming the ceiling of the cave
|
||||||
|
|
||||||
|
node_air = "foo:air",
|
||||||
|
-- Nodes filling the inside of the cave. By default, this is air.
|
||||||
|
-- You can replace it with e.g. water to make the entire cave a water cave.
|
||||||
|
-- Keep in mind that cave biomes can blend, so it might not always look
|
||||||
|
-- very smooth.
|
||||||
|
|
||||||
|
node_shell = "foo:permafrost",
|
||||||
|
depth_shell = 3,
|
||||||
|
-- Node forming a layer around the entire cave and thickness of this layer
|
||||||
|
-- You can make the depth as high as you want, but raising it past 16
|
||||||
|
-- might cause hard cut-offs at chunk edges.
|
||||||
|
|
||||||
|
y_max = -100,
|
||||||
|
y_min = -31000,
|
||||||
|
-- Upper and lower limits of the cave biome.
|
||||||
|
-- Alternatively you can use xyz limits as shown below.
|
||||||
|
|
||||||
|
max_pos = { x = 31000, y = -100, z = 31000 }
|
||||||
|
min_pos = { x = -31000, y = -500, z = -31000 }
|
||||||
|
-- xyz limits for biome, an alternative to using `y_min` and `y_max`.
|
||||||
|
-- Cave biome is limited to a cuboid defined by these positions.
|
||||||
|
-- Any x, y or z field left undefined defaults to -31000 in `min_pos` or
|
||||||
|
-- 31000 in `max_pos`.
|
||||||
|
|
||||||
|
heat_point = 0,
|
||||||
|
humidity_point = 50,
|
||||||
|
-- Characteristic temperature and humidity for the biome.
|
||||||
|
-- Just like the Minetest Lua API for biomes, these values create
|
||||||
|
-- 'biome points' on a voronoi diagram with heat and humidity as axes.
|
||||||
|
-- The resulting voronoi cells determine the distribution of the biomes.
|
||||||
|
-- Heat and humidity have an average of 50, vary mostly between 0 and 100
|
||||||
|
-- but can exceed these values.
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
3
init.lua
3
init.lua
|
@ -11,6 +11,9 @@ noordstar_caves = {}
|
||||||
-- Load features to influence cave shapes
|
-- Load features to influence cave shapes
|
||||||
load("shape")
|
load("shape")
|
||||||
|
|
||||||
|
-- Load features to influence cave biomes
|
||||||
|
load("biome")
|
||||||
|
|
||||||
-- Start engine to generate caves
|
-- Start engine to generate caves
|
||||||
load("engine")
|
load("engine")
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,91 @@
|
||||||
|
-- This file takes care of cave biomes
|
||||||
|
|
||||||
|
noordstar_caves.registered_biomes = {}
|
||||||
|
|
||||||
|
-- Clean the input and return a valid shape def
|
||||||
|
-- If the input is invalid, return nil
|
||||||
|
local function clean_def(def)
|
||||||
|
if type(def.name) ~= "string" then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
if type(def.heat_point) ~= "number" then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
if type(def.humidity_point) ~= "number" then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local d = {
|
||||||
|
name = def.name,
|
||||||
|
heat_point = def.heat_point,
|
||||||
|
humidity_point = def.humidity_point,
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Position
|
||||||
|
if type(def.min_pos) == "table" then
|
||||||
|
d.minp = {
|
||||||
|
x = def.min_pos.x or -1e5,
|
||||||
|
y = def.min_pos.y or -1e5,
|
||||||
|
z = def.min_pos.z or -1e5,
|
||||||
|
}
|
||||||
|
elseif type(def.y_min) == "number" then
|
||||||
|
d.minp = { x = -1e5, y = def.y_min, z = -1e5 }
|
||||||
|
else
|
||||||
|
d.minp = { x = -1e5, y = -1e5, z = -1e5 }
|
||||||
|
end
|
||||||
|
|
||||||
|
if type(def.max_pos) == "table" then
|
||||||
|
d.maxp = {
|
||||||
|
x = def.max_pos.x or 1e5,
|
||||||
|
y = def.max_pos.y or 1e5,
|
||||||
|
z = def.max_pos.z or 1e5,
|
||||||
|
}
|
||||||
|
elseif type(def.y_max) == "number" then
|
||||||
|
d.maxp = { x = 1e5, y = def.y_max, z = 1e5 }
|
||||||
|
else
|
||||||
|
d.maxp = { x = 1e5, y = 1e5, z = 1e5 }
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Optional nodes
|
||||||
|
if type(def.node_dust) == "string" then
|
||||||
|
d.node_dust = def.node_dust
|
||||||
|
end
|
||||||
|
if type(def.node_floor) == "string" then
|
||||||
|
d.node_floor = def.node_floor
|
||||||
|
end
|
||||||
|
if type(def.node_wall) == "string" then
|
||||||
|
d.node_wall = def.node_wall
|
||||||
|
end
|
||||||
|
if type(def.node_roof) == "string" then
|
||||||
|
d.node_roof = def.node_roof
|
||||||
|
end
|
||||||
|
if type(def.node_air) == "string" then
|
||||||
|
d.node_air = def.node_air
|
||||||
|
end
|
||||||
|
if type(def.node_shell) == "string" then
|
||||||
|
d.node_shell = def.node_shell
|
||||||
|
end
|
||||||
|
if type(def.depth_shell) == "number" then
|
||||||
|
d.depth_shell = def.depth_shell
|
||||||
|
else
|
||||||
|
d.depth_shell = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
return d
|
||||||
|
end
|
||||||
|
|
||||||
|
function noordstar_caves.register_biome(def)
|
||||||
|
local d = clean_def(def)
|
||||||
|
|
||||||
|
if d ~= nil then
|
||||||
|
noordstar_caves.registered_biomes[d.name] = d
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function noordstar_caves.unregister_biome(name)
|
||||||
|
noordstar_caves.registered_biomes[name] = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function noordstar_caves.clear_registered_biomes()
|
||||||
|
noordstar_caves.registered_biomes = {}
|
||||||
|
end
|
115
lua/engine.lua
115
lua/engine.lua
|
@ -2,6 +2,41 @@
|
||||||
-- Constants and magic numbers
|
-- Constants and magic numbers
|
||||||
local mapgen_buffer = 16
|
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 }
|
||||||
|
}
|
||||||
|
|
||||||
-- Convert 3d relative coordinates to an index on a flat array
|
-- Convert 3d relative coordinates to an index on a flat array
|
||||||
local function from_3d_to_flat(dx, dy, dz, nx, ny)
|
local function 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
|
||||||
|
@ -421,6 +456,46 @@ local function get_flat_cave_node_types(minp, maxp)
|
||||||
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 = original_block
|
||||||
|
local node_roof = get_node(def.node_roof, original_block)
|
||||||
|
local node_unknown = 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)
|
minetest.register_on_generated(function(minp, maxp, blockseed)
|
||||||
local vminp =
|
local vminp =
|
||||||
{ x = minp.x - mapgen_buffer
|
{ x = minp.x - mapgen_buffer
|
||||||
|
@ -440,29 +515,35 @@ minetest.register_on_generated(function(minp, maxp, blockseed)
|
||||||
-- Get threshold values
|
-- Get threshold values
|
||||||
local node_types = get_flat_cave_node_types(vminp, vmaxp)
|
local node_types = get_flat_cave_node_types(vminp, vmaxp)
|
||||||
|
|
||||||
local node_air = minetest.get_content_id("air")
|
local heat_points = get_flat_from_noise_params(minp, maxp, heat_noise_params)
|
||||||
local node_floor = minetest.get_content_id("mcl_core:glass_green")
|
local humidity_points = get_flat_from_noise_params(minp, maxp, humidity_noise_params)
|
||||||
local node_wall = minetest.get_content_id("mcl_core:glass_purple")
|
|
||||||
local node_roof = minetest.get_content_id("mcl_core:glass_red")
|
|
||||||
local node_other = minetest.get_content_id("mcl_core:glass")
|
|
||||||
|
|
||||||
|
-- Map block values
|
||||||
local nids = Flat3dArray:from_func(vminp, vmaxp, function(i, pos)
|
local nids = Flat3dArray:from_func(vminp, vmaxp, function(i, pos)
|
||||||
local nt = node_types:get_pos(pos)
|
local nt = node_types:get_pos(pos)
|
||||||
|
|
||||||
if not is_valid_pos(pos, minp, maxp) then
|
if not is_valid_pos(pos, minp, maxp) then
|
||||||
return flat_data:get_pos(pos)
|
return flat_data:get_pos(pos)
|
||||||
elseif nt == node_type.unknown then
|
else
|
||||||
return node_other
|
local biome = default_biome
|
||||||
elseif nt == node_type.floor then
|
|
||||||
return node_floor
|
local heat = heat_points:get_pos(pos)
|
||||||
elseif nt == node_type.wall then
|
local humidity = humidity_points:get_pos(pos)
|
||||||
return node_wall
|
|
||||||
elseif nt == node_type.roof then
|
for _, def in pairs(noordstar_caves.registered_biomes) do
|
||||||
return node_roof
|
if pos.x < def.minp.x then
|
||||||
elseif nt == node_type.content then
|
elseif pos.y < def.minp.y then
|
||||||
return node_air
|
elseif pos.z < def.minp.z then
|
||||||
elseif nt == node_type.stone then
|
elseif pos.x > def.maxp.x then
|
||||||
return flat_data:get_pos(pos)
|
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
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
|
|
@ -47,6 +47,10 @@ function noordstar_caves.register_shape(def)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function noordstar_caves.unregister_shape(name)
|
||||||
|
noordstar_caves.registered_shapes[name] = nil
|
||||||
|
end
|
||||||
|
|
||||||
function noordstar_caves.clear_registered_shapes()
|
function noordstar_caves.clear_registered_shapes()
|
||||||
noordstar_caves.registered_shapes = {}
|
noordstar_caves.registered_shapes = {}
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue