302 lines
18 KiB
Markdown
Executable File
302 lines
18 KiB
Markdown
Executable File
# World generation: watersheds, woodland, and bedrock
|
||
|
||
## Integration boundary
|
||
|
||
[biomes.lua](../mods/hearth/biomes.lua) is a lazy factory:
|
||
|
||
```lua
|
||
local worldgen = dofile(modpath .. "/biomes.lua")(hearth, api, register_node, construction)
|
||
hearth.biomes = worldgen.register()
|
||
```
|
||
|
||
The main content owner must replace the **entire old mapgen section**, from its
|
||
mapgen alias registrations through its old tree decoration, with this call.
|
||
Keep the subsequent wildlife load. Call after base nodes, resources, ecological
|
||
flora, and seasonal patches have registered, using content's **private**
|
||
`register_node` helper. Do not retain duplicate old biomes/decorations/iron seams.
|
||
This module does not load itself or edit the content/bootstrap/launcher/config.
|
||
|
||
`register()` returns the same module and is idempotent on that instance. Create
|
||
one instance per mod load, not one per player. Duplicate terrain node names fail
|
||
rather than override existing definitions. Nothing clears global registrations;
|
||
existing mapgen aliases/items retain precedence. Coexisting mods may therefore
|
||
alter the final landscape. Alias precedence is coexistence, not a promise that a
|
||
foreign mapgen-stone binding is compatible with this game's geology.
|
||
|
||
### Shared ecosystem API
|
||
|
||
- `species_order`: exactly `oak`, `birch`, `pine`, `willow`, `alder`, `beech`,
|
||
`aspen`, `rowan`, `hazel`, `spruce` (ten species).
|
||
- `profiles[species]`: height, width, crown, approximate heat/humidity preferences,
|
||
altitude limits, soils, biome names, shade and litter hints. Alder also has
|
||
`nitrogen_fixer=true`; willow and alder have `riparian=true`.
|
||
- `surface(short_name | qualified_name | {name=qualified_name})`: habitat table
|
||
or `nil`. Moisture, fertility and drainage are normalized 0–1 **preferences**,
|
||
not actual sampled soil water or mutable nutrient stores.
|
||
- `supports_species(species, surface)`: soil compatibility only; `false` for
|
||
unknown inputs. The ecology owner must also check climate, light, water,
|
||
claims/protection, loadedness and player-placed status before any growth.
|
||
- `tree_schematic(species [, canonical])`: fresh bounded table schematic.
|
||
`canonical=true` uses existing generic logs/leaves (legacy meadow resource
|
||
probes); normal calls use species blocks. Unknown species raises an error.
|
||
- `biome_profiles`, `surfaces`, `rocks`: declarative read-only-by-convention data.
|
||
|
||
This owner registers only species **log/leaves/planks**, not species seedling,
|
||
young, mature, seed, seasonal-variant or decay nodes. Ecological growth and
|
||
seasonal behavior belong to the ecosystem owner. Mapgen places full-sized trees,
|
||
not oversized plant sprites; existing understory nodes provide habitats, litter,
|
||
deadwood, fungi and seasonal forage for the ecological simulation. Registering a
|
||
canopy group alone does not make an older ecology module recognize species:
|
||
that owner must consume `profiles`/`hearth_canopy` and recognize these soils.
|
||
Seasonal retinting/leaf shedding of species canopies is likewise not implemented
|
||
by this registration module.
|
||
|
||
## Ten connected landscape communities
|
||
|
||
Heat and humidity below are **Voronoi centers**, not hard rectangular ranges.
|
||
The nearest eligible center wins; altitude limits are hard. These heat numbers
|
||
are mapgen climate coordinates, not Celsius or geographic latitude. Runtime
|
||
weather/seasons remain owned by the climate system.
|
||
|
||
| Registered biome | Y | Heat / humidity | Surface / bedrock | Community |
|
||
|---|---:|---:|---|---|
|
||
| `hearth_temperate` | 2–110 | 52 / 45 | Grass / limestone | Open meadow, grain, flowers, greens, sparse oak and canonical timber |
|
||
| `hearth_oak_woodland` | 3–110 | 65 / 62 | Forest floor / limestone | Oak, domed beech, coppice hazel, fern, litter, fungi, fallen wood |
|
||
| `hearth_birch_forest` | 6–150 | 36 / 58 | Forest floor / granite | Birch, columnar aspen, rowan and spruce; woodland forage |
|
||
| `hearth_pine_taiga` | 2–180 | 18 / 38 | Podzol / granite | Pine, spired spruce and rowan; acidic nutrient-poor litter, mushrooms |
|
||
| `hearth_wet_forest` | 2–65 | 42 / 90 | Peat, alluvium / slate | Moist fern/decomposer community; alder only at actual water edges |
|
||
| `hearth_floodplain` | 2–8 | 68 / 90 | Alluvium / sandstone | Fertile flood terraces, grain, greens, willow/alder beside water |
|
||
| `hearth_dry_scrub` | 2–135 | 83 / 18 | Grass, shallow earth / sandstone | Open grass/flower scrub, berries; no artificial woodland or springs |
|
||
| `hearth_alpine_meadow` | 111–210 | 30 / 48 | Alpine turf, scree / slate | Short grass/flowers above most woodland; occasional rowan through Y=180 |
|
||
| `hearth_rocky_peaks` | 211–31000 | 25 / 45 | Permanent snow, scree / granite | Snowline across all climates, exposed structurally unsupported rock |
|
||
| `hearth_riverbank` | −31000–1 | 50 / 50 | Sand, gravel / sandstone | Shore/channel substrate, riparian gathering and rare old weirs |
|
||
|
||
The last biome is also the underground altitude fallback, not a claim that the
|
||
deep world is literally a river. Explicit geological beds below correct that.
|
||
There is an eligible biome at every altitude. Woodland-to-alpine overlap allows
|
||
climate-dependent treelines through Y=180; above 210 there is only the peak
|
||
biome, with zero vertical blend. Snow is a stable full-block cap, **not** seasonal
|
||
snowfall or a falling node. Surface/altitude alone cannot distinguish every
|
||
steep slope: stock v7's structural-support checks expose bedrock where the node
|
||
below is air/water; this is not a custom slope/erosion simulator.
|
||
|
||
### Full-block trees
|
||
|
||
| Species | Schematic width × height × depth | Shape |
|
||
|---|---|---|
|
||
| Oak | 7 × 7 × 7 | Broad layered crown with lateral log branches |
|
||
| Birch | 5 × 8 × 5 | Narrow elevated diamond crown and slender trunk |
|
||
| Pine | 7 × 9 × 7 | Wide low branches narrowing to a single leafy tip |
|
||
| Willow | 9 × 7 × 9 | Spreading branches, hanging curtains down to the lower trunk |
|
||
| Alder | 5 × 6 × 5 | Short riparian trunk and compact upright crown |
|
||
| Beech | 9 × 8 × 9 | Broad domed crown |
|
||
| Aspen | 5 × 9 × 5 | Tall columnar crown |
|
||
| Rowan | 7 × 6 × 7 | Open crown |
|
||
| Hazel | 7 × 5 × 7 | Low coppice form |
|
||
| Spruce | 5 × 10 × 5 | Narrow spired evergreen crown |
|
||
|
||
All occupied cells use probability 255; empty cells use zero. Schematics are
|
||
x-fastest, then y, then z, centered horizontally with base offset +1 above the
|
||
support. They do not force placement or cut into terrain. Canopies register
|
||
before ground flora, preventing grass from blocking the base trunk. These are
|
||
distinct authored forms, not random blocks. Trees at steep/obstructed boundaries
|
||
can still be clipped by native non-forcing schematic placement; there is no
|
||
Lua post-generation scan/rebuild of partial crowns.
|
||
|
||
Fallen deadwood is an ordinary sideways full cube, not a plant sprite or a mesh.
|
||
It requires an axe and drops one canonical `hearth:log`; hands do not yield
|
||
sticks. Its existing metadata-preserving orientation fix and ecological
|
||
substrate identity remain separate from this documentation/process/UI pass.
|
||
The named botanical catalogue adds 80 species in eight categories of ten;
|
||
`/botany [search]` reads their real common/scientific names, habitat windows,
|
||
seasons, game toxicity flags and conditional roles. See the
|
||
[complete category list and caveats](content-expansion.md#eighty-species-botanical-field-guide).
|
||
|
||
Reeds, clay and weirs require nearby source/flowing water (at least one of the
|
||
native eight neighbors at support Y or Y−1). Willow/alder require the same
|
||
check as well as biome, altitude and soil compatibility. Wet climate alone is
|
||
not proof of a river. Floodplain selection itself is climate/altitude based,
|
||
not hydrological flood simulation.
|
||
|
||
## Geology and resource progression
|
||
|
||
Surface bedrock follows biome geology, then registered ores run in this order:
|
||
|
||
1. Replace sandstone/canonical stone with **granite basement below −80**,
|
||
**slate from −79 to −24**, and **limestone from −23 to −8**. Above that,
|
||
shallow sandstone and biome-specific rocks remain.
|
||
2. Rare coherent 24-node basalt intrusions in bedrock below −24 (48³ scarcity;
|
||
correlated 3D noise, not scattered basalt confetti).
|
||
3. Nested scatter clusters in the resulting actual host stones:
|
||
|
||
| Resource node | Allowed hosts | Y maximum | Cluster / spacing |
|
||
|---|---|---:|---|
|
||
| `hearth:quartz_vein` | Granite, slate | 160 | 6 attempts in 3³, scarcity 11³ |
|
||
| `hearth:copper_ore_vein` | Basalt, limestone, slate | 32 | 5 in 3³, scarcity 13³ |
|
||
| `hearth:coal_ore_vein` | Sandstone, slate, limestone | 70 | 9 in 3³, scarcity 10³ |
|
||
| Existing `hearth:iron_ore_vein` | Basalt, granite, limestone, canonical stone | −8 | 6 in 3³, scarcity 11³ |
|
||
| Same iron, sparse exposed/shallow seams | Same hosts | −7 through 85 | 3 in 2³, scarcity 19³ |
|
||
|
||
All deep ranges start at −31000 (actual mapgen limit still bounds the world).
|
||
Scarcity and cluster counts are expectations, not guaranteed yields. Ores
|
||
replace their host, never air/soil/leaves. They can be exposed by terrain/caves,
|
||
but are not fake simple decorations placed on cavern floors.
|
||
|
||
All ten species logs drop themselves; **one log crafts four matching species planks**.
|
||
Each is accepted by exact name as **180 seconds** of direct hearth fuel.
|
||
Planks, buildings and arbitrary `tree` groups are not direct fuels.
|
||
For canonical timber, place **two logs of the same species in separate crafting
|
||
slots**, shapelessly, to produce **two canonical `hearth:log`**. There is no
|
||
single-log canonical conversion. Species planks convert 1:1 to `hearth:planks`. This
|
||
preserves axe requirements and the existing charcoal-charge, sticks, wall,
|
||
storage and other exact-name recipes without destructive node aliases. There
|
||
is no reverse conversion that duplicates timber.
|
||
|
||
Every raw rock drops itself; **one rock produces four matching rock-brick
|
||
building blocks**. Place **two rocks of the same type in separate crafting
|
||
slots**, shapelessly, to produce **two canonical `hearth:stone`** for existing
|
||
foundation recipes. There is no single-rock canonical conversion. Both pair
|
||
conversions preserve mass (2:2), and cannot shadow the single-input plank/brick
|
||
recipes. Mixed species or rock types do not convert together.
|
||
Masonry uses `hearth_building=1` plus construction placement/ownership hooks,
|
||
so the private registration helper retains room invalidation and protection.
|
||
There are no new stair/wall variants or alternate protection callbacks here.
|
||
Loose gravel/scree drops canonical loose flint. New soils drop canonical soil;
|
||
hand placing that soil does not restore an intact woodland ecosystem.
|
||
|
||
Quartz/copper/coal ore nodes each drop two `hearth:quartz`, `hearth:copper_ore`,
|
||
or `hearth:coal` craftitems. Downstream integration supplies three-quartz windows,
|
||
copper charges (45-second smelt with 30 seconds reserved) and 240-second coal
|
||
fuel under the 600-second reserve cap. These are process/content contracts, not
|
||
additional biome-factory recipes. Existing iron definitions are reused.
|
||
|
||
Loose flint registers on **all** listed surfaces, and dry sticks on those
|
||
surfaces through Y=210. Meadow canonical grass/logs remain for old resource
|
||
probes; woodland berries, river reeds/clay and limited upland seeps retain
|
||
early-survival routes. This is coverage, not a verified guarantee for seed
|
||
731104 or every start. Main must update species/soil-aware spawn checks and
|
||
validate water, flint, sticks, timber, forage, reeds and clay after integration.
|
||
Do not silently hand players supplies or claim native seed validation from
|
||
mocked registration tests.
|
||
|
||
## Terrain material art contract: 64 PNGs
|
||
|
||
Every file has prefix **hearth_** and suffix **.png**. No downloads, texture
|
||
aliases, temporary inventory silhouettes or engine-default art are required.
|
||
|
||
**61 world tiles** (expand each listed brace alternative):
|
||
|
||
- `hearth_{granite,basalt,limestone,slate,sandstone}.png` — 5 opaque rock faces.
|
||
- `hearth_{granite,basalt,limestone,slate,sandstone}_bricks.png` — 5 opaque,
|
||
coursed masonry faces, one material per full block.
|
||
- `hearth_{forest_floor,podzol,alluvium,peat,alpine_turf}.png` — 5 opaque soil
|
||
**top** faces. Bottom and four sides reuse existing `hearth_soil.png`.
|
||
- `hearth_{gravel,scree,snow}.png` — 3 opaque full-block faces.
|
||
- `hearth_{quartz_vein,copper_ore_vein,coal_ore_vein}.png` — 3 complete opaque
|
||
ore-bearing rock faces (not transparent overlays).
|
||
- For **each** oak, birch, pine, willow, alder, beech, aspen, rowan, hazel, spruce: `hearth_<species>_log_top.png`,
|
||
`hearth_<species>_log_side.png`, `hearth_<species>_leaves.png`,
|
||
`hearth_<species>_planks.png` — 40 tiles total. Logs use top/top/side;
|
||
leaves use clipped alpha and full-block `allfaces_optional` rendering.
|
||
|
||
**3 inventory images:** `hearth_item_quartz.png`,
|
||
`hearth_item_copper_ore.png`, `hearth_item_coal.png`.
|
||
|
||
Canonical grass/log/leaves, iron ore, water, flora, seasonal forage, sand, dirt,
|
||
loose flint and weir textures are **existing** art. This module owns no art
|
||
generation scripts/assets. Species ecological-stage textures/meshes remain
|
||
the ecosystem/art owners' separate contract. Dynamic Lua tile names must be
|
||
explicitly enumerated by the art owner if its scanner cannot expand loops.
|
||
|
||
## Epic-scale landscape generator — NEW WORLDS ONLY
|
||
|
||
New Last Hearth worlds use stock v7 as a cave/ore substrate and then the original
|
||
`epic_terrain` Lua mapgen mod reshapes each freshly generated mapchunk with
|
||
seeded, world-coordinate noise. The mod lives in the separate
|
||
`luanti-mapgenerator-epic` workspace repository and is bundled into the managed
|
||
Last Hearth runtime package. It does not copy Epic Terrain code, JSON density
|
||
functions, textures, or other assets. Its feature mapping is inspired by the
|
||
public Epic Terrain description/source, not a byte-for-byte or mathematical
|
||
port. The launcher applies the generator preset only when a world is first
|
||
created; it never rewrites saved map metadata or generated mapblocks.
|
||
|
||
```conf
|
||
mg_name = v7
|
||
water_level = 32
|
||
mapgen_limit = 4096
|
||
mg_flags = caves,nodungeons,light,biomes,ores
|
||
mgv7_spflags = mountains,ridges,nofloatlands,caverns
|
||
mgv7_mount_zero_level = 0
|
||
mgv7_np_terrain_base = 12, 45, (900, 384, 900), 82341, 5, 0.6, 2.0
|
||
mgv7_np_terrain_alt = -8, 24, (900, 384, 900), 5934, 5, 0.6, 2.0
|
||
mgv7_np_terrain_persist = 0.6, 0.1, (1200, 1200, 1200), 539, 3, 0.6, 2.0
|
||
mgv7_np_height_select = -4, 14, (1400, 320, 1400), 4213, 6, 0.7, 2.0
|
||
mgv7_np_filler_depth = 0, 1.2, (150, 150, 150), 261, 3, 0.7, 2.0
|
||
mgv7_np_mount_height = 250, 100, (700, 700, 700), 72449, 3, 0.6, 2.0
|
||
mgv7_np_mountain = -0.6, 1, (320, 280, 320), 5333, 5, 0.63, 2.0
|
||
mgv7_np_ridge_uwater = 0, 1, (700, 700, 700), 85039, 5, 0.6, 2.0
|
||
mgv7_np_ridge = 0, 1, (100, 100, 100), 6467, 4, 0.75, 2.0
|
||
mg_biome_np_heat = 50, 50, (256, 256, 256), 5349, 3, 0.5, 2.0
|
||
mg_biome_np_humidity = 50, 50, (256, 256, 256), 842, 3, 0.5, 2.0
|
||
```
|
||
|
||
Design and source-grounded limits:
|
||
|
||
- Continentalness is shaped by 5100-node plate and 1750-node warped-shelf fields;
|
||
a protected 620-node radius around world origin gives first arrival a dry
|
||
meadow, while distant islands, peninsulas and ocean shelves continue to vary
|
||
by seed.
|
||
- Broad uplift plus ridged mountain fields creates plains, alpine massifs and
|
||
high snow surfaces. A separate coast band uses a sharper elevation step for
|
||
sea cliffs and indented shore profiles.
|
||
- Major and minor river corridors are carved using separate warped ridge fields.
|
||
Their water levels descend in short quantized steps, creating waterfalls;
|
||
lakes form in closed noise basins. This is intentionally stylized flow, not a
|
||
global watershed/drainage solver, so a river need not reach the ocean in every
|
||
seed.
|
||
- `epic_terrain` keeps native v7 cave voids and ore nodes where possible, then
|
||
writes its own soil, water, block-cube trees and understory. Native v7
|
||
decorations are disabled for newly created worlds so flora does not float
|
||
above the reshaped land.
|
||
- Epic Terrain's Minecraft source replaces a density-function graph and custom
|
||
surface/rivers rules. Luanti does not expose that graph, so this is a feature-
|
||
level parody implemented with the public `register_on_generated`/VoxelManip
|
||
path, not a direct port. It is not a physical erosion simulation.
|
||
|
||
Never call `settings:set_default`, change mapgen at runtime, rewrite existing
|
||
map metadata, delete chunks, or automatically regenerate a player's world.
|
||
New biome registrations can still change **future ungenerated** chunks in an
|
||
existing world; they do not retrofit old terrain and may create seams. Offer a
|
||
backed-up new world for the coherent preset and explicitly disclose seams for
|
||
existing saves. Keep existing world identity and backup protections intact.
|
||
|
||
## Validation and limitations
|
||
|
||
The standalone [biome tests](../tests/biomes_spec.lua) exercise an allowlisted
|
||
registration-only API mock, all material references and drops, canonical
|
||
crafting paths, non-forcing aliases, repeat registration, altitude coverage,
|
||
ordered geology, species silhouettes, riparian checks and art names. They do
|
||
not assert that mocked noise produced terrain.
|
||
|
||
Engine source/API reviewed locally: `MapgenV7Params::readParams`,
|
||
`MapgenV7::baseTerrainLevelAtPoint`, `getMountainTerrainAtPoint`,
|
||
`getRiverChannelFromMap`, `makeChunk`, `MapgenBasic::generateBiomes`,
|
||
`OreScatter::generate`, `OreStratum::generate`, and the documented biome/ore/
|
||
decoration/schematic definitions. In particular v7 generates biomes, caves,
|
||
ordered ores, then decorations; biome transitions must not be assumed to
|
||
extrude surface rock unchanged to the bottom of the map.
|
||
|
||
The native `test:seeds` gate creates eight disposable worlds, including seed
|
||
731104, and checks startup resources plus safe arrival; it is not a terrain
|
||
silhouette or biome-traversal test, and it does not require every seed to start
|
||
on dry land at the origin. A representative coastal, valley, river and alpine
|
||
visual survey is still needed before claiming those aesthetics. Never test by
|
||
opening the user's persistent world. Neither performance nor universal spawn
|
||
safety is certified by this preset or by the standalone checks.
|
||
|
||
Security review: no credentials, cryptography, certificates, network access,
|
||
filesystem writes, deserialization or player-supplied executable input are
|
||
introduced. Schematics are fixed-size bounded data; private node registration
|
||
preserves the caller's protection/construction hooks. Existing player worlds
|
||
and engine-global API functions are not mutated.
|