Compare commits
5 Commits
851cb19cd2
...
5005ae7d32
Author | SHA1 | Date |
---|---|---|
Bram van den Heuvel | 5005ae7d32 | |
Bram van den Heuvel | 1c74ea3c60 | |
Bram van den Heuvel | 2809346af8 | |
Bram van den Heuvel | d111364b0f | |
Bram van den Heuvel | 588ed956b1 |
111
API.md
111
API.md
|
@ -14,6 +14,9 @@ For shapes, the following functions are available:
|
|||
- `noordstar_caves.unregister_shape(name)` Remove a defined cave shape
|
||||
- `noordstar_caves.clear_registered_shapes()` Remove all known cave shapes
|
||||
|
||||
Generally, it is recommended to keep the number of cave shapes below 100.
|
||||
A good number of shapes is 10 for diversity but performance.
|
||||
|
||||
The shapes are defined as follows:
|
||||
|
||||
```lua
|
||||
|
@ -132,12 +135,6 @@ The biomes are defined as follows:
|
|||
-- 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.
|
||||
|
@ -160,3 +157,105 @@ The biomes are defined as follows:
|
|||
-- but can exceed these values.
|
||||
}
|
||||
```
|
||||
|
||||
## Decorations
|
||||
|
||||
As a final part of generating the caves, decorations can be added to add unique
|
||||
structures to cave biomes.
|
||||
|
||||
For decorations, the following functions are defined:
|
||||
|
||||
- `noordstar_caves.register_decoration(decoration def)` Define a new cave decoration
|
||||
- `noordstar_caves.clear_registered_decorations()` Remove all known cave decorations
|
||||
|
||||
The decorations are defined as follows:
|
||||
|
||||
```lua
|
||||
{
|
||||
deco_type = "simple",
|
||||
-- Type. "simple" or "schematic" supported
|
||||
|
||||
place_on = "floor",
|
||||
-- Side of the cave to place the decoration on. "floor" or "ceiling" supported
|
||||
|
||||
fill_ratio = 0.02,
|
||||
-- Percentage of surface nodes on which this decoration will spawn
|
||||
|
||||
biomes = { "noordstar_caves:tundra", "foo:desert" },
|
||||
-- List of (cave!) biomes that this decoration will spawn in. Occurs in all
|
||||
-- biomes if this is omitted.
|
||||
|
||||
y_min = -31000,
|
||||
y_max = 31000,
|
||||
-- Lower and upper limits for decoration (inclusive).
|
||||
-- These parameters refer to the Y-coordinate of the node where it is
|
||||
-- originally chosen to be placed.
|
||||
|
||||
----- Simple-type parameters
|
||||
|
||||
decoration = "foo:grass",
|
||||
-- The node name used as the decoration.
|
||||
-- If instead a list of strings, a randomly selected node from the list
|
||||
-- is placed as the decoration.
|
||||
|
||||
height = 1,
|
||||
-- Decoration height in nodes.
|
||||
-- If height_max is not 0, this is the lower limit of a randomly selected
|
||||
-- height.
|
||||
-- Height can not be over 16.
|
||||
|
||||
height_max = 0,
|
||||
-- Upper limit of the randomly selected height.
|
||||
-- If absent, the parameter `height` is used as a constant.
|
||||
-- Max height will be capped at 16.
|
||||
|
||||
place_offset_y = 0,
|
||||
-- Y offset of the decoration base node relative to the standard base node
|
||||
-- position.
|
||||
-- Can be positive or negative. Default is 0.
|
||||
|
||||
----- Schematic-type parameters
|
||||
|
||||
schematic = "foobar.mts",
|
||||
-- If schematic is a string, it is the filepath relative to the correct
|
||||
-- working directory of the specified Minetest schematic file.
|
||||
-- Could also be the ID of a previously registered schematic.
|
||||
|
||||
schematic = {
|
||||
size = {x = 4, y = 6, z = 4},
|
||||
data = {
|
||||
{name = "default:cobble", param1 = 255, param2 = 0},
|
||||
{name = "default:dirt_with_grass", param1 = 255, param2 = 0},
|
||||
{name = "air", param1 = 255, param2 = 0},
|
||||
...
|
||||
},
|
||||
yslice_prob = {
|
||||
{ypos = 2, prob = 128},
|
||||
{ypos = 5, prob = 64},
|
||||
...
|
||||
},
|
||||
},
|
||||
-- Alternative schematic specification by supplying a table. The fields
|
||||
-- size and data are mandatory whereas yslice_prob is optional.
|
||||
-- See 'schematic specifier' in the Minetest Lua API documentation.
|
||||
|
||||
|
||||
replacements = {["oldname"] = "convert_to", ...},
|
||||
-- Map of node names to replace in the schematic after reading it.
|
||||
|
||||
flags = "place_center_x, place_center_y, place_center_z",
|
||||
-- Flags for schematic decorations. See 'Schematic attributes'.
|
||||
|
||||
rotation = "90",
|
||||
-- Rotation can be "0", "90", "180", "270", or "random"
|
||||
|
||||
place_offset_y = 0,
|
||||
-- If the flag 'place_center_y' is set this parameter is ignored.
|
||||
-- Y offset of the schematic base node layer relative to the 'place_on'
|
||||
-- node.
|
||||
-- Can be positive or negative. Default is 0.
|
||||
-- Effect is inverted for decorations on the ceiling.
|
||||
-- Ignored by 'y_min' and 'y_max' checks, which always refer to the
|
||||
-- 'place_on' node.
|
||||
}
|
||||
```
|
||||
|
|
146
init.lua
146
init.lua
|
@ -92,6 +92,9 @@ noordstar_caves =
|
|||
-- A public list of all registered biomes
|
||||
, registered_biomes = {}
|
||||
|
||||
-- A public list of all registered decorations
|
||||
, registered_decorations = {}
|
||||
|
||||
-- A public list of all registered shapes
|
||||
, registered_shapes = {}
|
||||
|
||||
|
@ -102,6 +105,11 @@ function noordstar_caves.clear_registered_biomes()
|
|||
noordstar_caves.registered_biomes = {}
|
||||
end
|
||||
|
||||
-- Remove all registered decorations and start with a clean slate
|
||||
function noordstar_caves.clear_registered_decoration()
|
||||
noordstar_caves.registered_decorations = {}
|
||||
end
|
||||
|
||||
-- Remove all registered shapes and start with a clean slate
|
||||
function noordstar_caves.clear_registered_shapes()
|
||||
noordstar_caves.registered_shapes = {}
|
||||
|
@ -116,6 +124,15 @@ function noordstar_caves.register_biome(def)
|
|||
end
|
||||
end
|
||||
|
||||
-- Register a new cave decoration
|
||||
function noordstar_caves.register_decoration(def)
|
||||
local d = internal.clean_deco_def(def)
|
||||
|
||||
if d then
|
||||
table.insert(noordstar_caves.registered_decorations, d)
|
||||
end
|
||||
end
|
||||
|
||||
-- Register a new cave shape
|
||||
function noordstar_caves.register_shape(def)
|
||||
local d = internal.clean_shape_def(def)
|
||||
|
@ -257,6 +274,63 @@ function internal.clean_biome_def(def)
|
|||
return d
|
||||
end
|
||||
|
||||
-- Clean the user input on a decoration definition before inserting it.
|
||||
function internal.clean_deco_def(def)
|
||||
if def.deco_type ~= "simple" and def.deco_type ~= "schematic" then
|
||||
return nil
|
||||
end
|
||||
if def.deco_type == "simple" and type(def.decoration) ~= "string" then
|
||||
return nil
|
||||
end
|
||||
if def.deco_type == "schematic" then
|
||||
if type(def.schematic) == "string" then
|
||||
elseif type(def.schematic) == "table" then
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
if type(def.fill_ratio) ~= "number" then
|
||||
return nil
|
||||
end
|
||||
|
||||
local d = {
|
||||
deco_type = def.deco_type,
|
||||
decoration = def.decoration,
|
||||
schematic = def.schematic,
|
||||
fill_ratio = def.fill_ratio,
|
||||
}
|
||||
|
||||
if def.place_on == "floor" or def.place_on == "ceiling" then
|
||||
d.place_on = def.place_on
|
||||
else
|
||||
d.place_on = "floor"
|
||||
end
|
||||
|
||||
local function place(key, t, default)
|
||||
if type(def[key]) == t then
|
||||
d[key] = def[key]
|
||||
else
|
||||
d[key] = default
|
||||
end
|
||||
end
|
||||
|
||||
place("y_min", "number", internal.world_minp.y)
|
||||
place("y_max", "number", internal.world_maxp.y)
|
||||
place("height_max", "number", 0)
|
||||
place("height", "number", 1)
|
||||
place("place_offset_y", "number", 0)
|
||||
place("replacements", "table", {})
|
||||
place("flags", "string", "")
|
||||
place("rotation", "string", "0")
|
||||
|
||||
d.height = math.max(d.height , 0)
|
||||
d.height = math.min(d.height , 16)
|
||||
d.height_max = math.max(d.height_max, 0)
|
||||
d.height_max = math.min(d.height_max, 16)
|
||||
|
||||
return d
|
||||
end
|
||||
|
||||
-- Clean the user input on a shape definition before inserting it.
|
||||
function internal.clean_shape_def(def)
|
||||
if type(def.name) ~= "string" then
|
||||
|
@ -291,34 +365,38 @@ function internal.clean_shape_def(def)
|
|||
return d
|
||||
end
|
||||
|
||||
-- Get the most nearby cave shape
|
||||
function internal.closest_cave_biome(heat, humidity)
|
||||
-- Get the most nearby cave biome
|
||||
function internal.closest_cave_biome(heat, humidity, pos)
|
||||
local biome = internal.default_biome()
|
||||
local d = internal.biome_def_distance(biome, heat, humidity)
|
||||
|
||||
for key, def in pairs(noordstar_caves.registered_biomes) do
|
||||
local new_d = internal.biome_def_distance(def, heat, humidity)
|
||||
|
||||
if new_d <= d then
|
||||
biome = def
|
||||
d = new_d
|
||||
for _, def in pairs(noordstar_caves.registered_biomes) do
|
||||
if internal.is_valid_pos(pos, def.minp, def.maxp) then
|
||||
local new_d = internal.biome_def_distance(def, heat, humidity)
|
||||
|
||||
if new_d <= d then
|
||||
biome = def
|
||||
d = new_d
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return shape
|
||||
return biome
|
||||
end
|
||||
|
||||
-- Get the most nearby cave shape
|
||||
function internal.closest_cave_shape(cnct, vrtcl)
|
||||
function internal.closest_cave_shape(cnct, vrtcl, y)
|
||||
local shape = internal.default_shape()
|
||||
local d = internal.shape_def_distance(shape, cnct, vrtcl)
|
||||
|
||||
for key, def in pairs(noordstar_caves.registered_shapes) do
|
||||
local new_d = internal.shape_def_distance(def, cnct, vrtcl)
|
||||
|
||||
if new_d <= d then
|
||||
shape = def
|
||||
d = new_d
|
||||
if def.y_min <= y and y <= def.y_max then
|
||||
local new_d = internal.shape_def_distance(def, cnct, vrtcl)
|
||||
|
||||
if new_d <= d then
|
||||
shape = def
|
||||
d = new_d
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -442,7 +520,7 @@ function internal.flat_from_cave_bools(minp, maxp)
|
|||
local cnct = connectivity:get_index(i)
|
||||
local vrtcl = verticality:get_index(i)
|
||||
|
||||
local def = internal.closest_cave_shape(cnct, vrtcl)
|
||||
local def = internal.closest_cave_shape(cnct, vrtcl, pos.y)
|
||||
|
||||
return def.name
|
||||
end)
|
||||
|
@ -453,10 +531,17 @@ function internal.flat_from_cave_bools(minp, maxp)
|
|||
noise[key] = internal.flat_from_shape_def(shape, minp, maxp)
|
||||
end
|
||||
|
||||
local default = internal.default_shape()
|
||||
noise[default.name] = internal.flat_from_shape_def(default, minp, maxp)
|
||||
|
||||
-- Create a flat array of bools
|
||||
local bools = Flat3dArray:from_func(minp, maxp, function (i, pos)
|
||||
local key = reduced:get_pos(internal.reduced_shape_pos(pos))
|
||||
|
||||
if noise[key] == nil then
|
||||
error("Key " .. key .. " gave no value on noise")
|
||||
end
|
||||
|
||||
local n = noise[key]:get_index(i)
|
||||
local v = internal.cave_vastness(pos)
|
||||
|
||||
|
@ -566,6 +651,8 @@ function internal.flat_from_shape_def(def, minp, maxp)
|
|||
internal.iter_3d_area(minp, maxp, function (i, pos)
|
||||
noise_flat_map[i] = def.func(pos, noise_flat_map[i])
|
||||
end)
|
||||
|
||||
return Flat3dArray:new(minp, maxp, noise_flat_map)
|
||||
end
|
||||
|
||||
-- Convert 3d relative coordinates to an index on a flat array
|
||||
|
@ -616,14 +703,17 @@ function internal.generate_caves(data, minp, maxp)
|
|||
internal.humidity_noise_params(), bminp, bmaxp
|
||||
)
|
||||
|
||||
local air = minetest.get_content_id("air")
|
||||
|
||||
-- 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)
|
||||
)
|
||||
local vi = vmanip:pos_to_index(pos)
|
||||
|
||||
if vmanip:get_index(vi) == air then
|
||||
elseif type(name) == "string" then
|
||||
vmanip:set_index(vi, minetest.get_content_id(name))
|
||||
elseif type(name) == "nil" then
|
||||
else
|
||||
error("Inserted invalid type " .. type(name) .. " into voxelmanip array")
|
||||
|
@ -637,15 +727,19 @@ function internal.generate_caves(data, minp, maxp)
|
|||
elseif nt == internal.node_types.air then
|
||||
place("air")
|
||||
elseif nt == internal.node_types.floor_deco then
|
||||
-- TODO: Place registered decoration
|
||||
for _, deco in ipairs(noordstar_caves.registered_decorations) do
|
||||
if deco.place_on ~= "floor" then
|
||||
elseif math.random() > deco.fill_ratio then
|
||||
else
|
||||
-- TODO: Check for biome
|
||||
end
|
||||
end
|
||||
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()
|
||||
local def = internal.closest_cave_biome(heat, humidity, pos)
|
||||
|
||||
if nt == internal.node_types.floor then
|
||||
place(def.node_floor)
|
||||
|
@ -674,7 +768,7 @@ end
|
|||
|
||||
-- Get the noise params for the cave biome temperature.
|
||||
function internal.heat_noise_params()
|
||||
return {
|
||||
return {
|
||||
offset = 50,
|
||||
scale = 50,
|
||||
spread = internal.biome_size,
|
||||
|
|
|
@ -39,7 +39,7 @@ function timer.stop()
|
|||
internal.stats[name] = stop - start
|
||||
end
|
||||
|
||||
name_len = math.max(name_len, name)
|
||||
name_len = math.max(name_len, string.len(name))
|
||||
end
|
||||
|
||||
local h1 = "Task"
|
||||
|
@ -60,29 +60,22 @@ function timer.stop()
|
|||
)
|
||||
)
|
||||
|
||||
internal.log(string.rep("-", name_len + 3 + 8 + 3 + 8))
|
||||
internal.log_hr(name_len)
|
||||
|
||||
for _, t in ipairs(internal.waypoints) do
|
||||
local name = t[1]
|
||||
local duration = tostring(math.round(1e3 * (t[3] - t[2])))
|
||||
local avg_duration = tostring(math.round(1e3 * internal.stats[name] / internal.sessions))
|
||||
|
||||
internal.log(
|
||||
table.concat(
|
||||
{ name
|
||||
, string.rep(" ", name_len - string.len(name))
|
||||
, " | "
|
||||
, string.rep(" ", 5 - string.len(duration))
|
||||
, duration
|
||||
, " ms | "
|
||||
, string.rep(" ", 5 - string.len(avg_duration))
|
||||
, avg_duration
|
||||
, " ms"
|
||||
}
|
||||
, ""
|
||||
)
|
||||
internal.log_time(
|
||||
name,
|
||||
internal.to_ms(t[3] - t[2]),
|
||||
internal.to_ms(internal.stats[name] / internal.sessions),
|
||||
name_len
|
||||
)
|
||||
end
|
||||
|
||||
internal.log_hr(name_len)
|
||||
|
||||
internal.log_time("Total", internal.sum(), internal.sum_avg(), name_len)
|
||||
end
|
||||
|
||||
-- Log text to the Minetest chat
|
||||
|
@ -90,8 +83,57 @@ function internal.log(text)
|
|||
minetest.chat_send_all(os.time() .. " - " .. text)
|
||||
end
|
||||
|
||||
function internal.log_hr(name_len)
|
||||
internal.log(string.rep("-", name_len + 3 + 8 + 3 + 8))
|
||||
end
|
||||
|
||||
function internal.log_time(header, time, time_avg, header_max_len)
|
||||
local duration = tostring(time)
|
||||
local duration_avg = tostring(time_avg)
|
||||
|
||||
internal.log(
|
||||
table.concat(
|
||||
{ header
|
||||
, string.rep(" ", header_max_len - string.len(header))
|
||||
, " | "
|
||||
, string.rep(" ", 5 - string.len(duration))
|
||||
, duration
|
||||
, " ms | "
|
||||
, string.rep(" ", 5 - string.len(duration_avg))
|
||||
, duration_avg
|
||||
, " ms"
|
||||
}
|
||||
, ""
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
function internal.now()
|
||||
internal.last = os.clock()
|
||||
end
|
||||
|
||||
function internal.sum()
|
||||
local s = 0
|
||||
|
||||
for _, t in ipairs(internal.waypoints) do
|
||||
s = s + (t[3] - t[2])
|
||||
end
|
||||
|
||||
return internal.to_ms(s)
|
||||
end
|
||||
|
||||
function internal.sum_avg()
|
||||
local s = 0
|
||||
|
||||
for _, t in pairs(internal.stats) do
|
||||
s = s + t
|
||||
end
|
||||
|
||||
return internal.to_ms(s / internal.sessions)
|
||||
end
|
||||
|
||||
function internal.to_ms(s)
|
||||
return math.round(s * 1e3)
|
||||
end
|
||||
|
||||
internal.now()
|
||||
|
|
Loading…
Reference in New Issue