Files
last-hearth/docs/world-generation.md
T
2026-11-03 12:41:09 -05:00

18 KiB
Executable File
Raw Blame History

World generation: watersheds, woodland, and bedrock

Integration boundary

biomes.lua is a lazy factory:

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.

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.

The following is a source-grounded recommendation, not an applied or engine-playtested preset. Main should place it in game defaults and its launcher new-world creation path, preserving existing saved mapgen params. The current launcher writes mg_name=v7 and seed 731104, but does not yet emit these tuned noise values. Merely changing game defaults is not sufficient to prove that managed-launcher new worlds received them.

mg_name = v7
water_level = 1
mapgen_limit = 512
mg_flags = caves,nodungeons,light,decorations,biomes,ores
mgv7_spflags = mountains,ridges,nofloatlands,caverns
mgv7_mount_zero_level = 0
mgv7_np_terrain_base = 12, 65, (384, 384, 384), 82341, 5, 0.6, 2.0
mgv7_np_terrain_alt = 12, 28, (384, 384, 384), 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 = -8, 16, (320, 320, 320), 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 = 230, 100, (700, 700, 700), 72449, 3, 0.6, 2.0
mgv7_np_mountain = -0.6, 1, (180, 280, 180), 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

Why these parameters:

  • Base/alternate terrain is lifted from stock offset 4 to 12, while retaining an accessible low-relief alternate surface. This reduces ocean dominance without pretending noise can guarantee a dry, safe spawn.
  • Broad 384-node hills and 700-node mountain/river structure fit the current small bounded world. Mountain density retains stock −0.6 bias; its longer vertical than horizontal spread favors grounded massifs over spiky rubble. Mount-height noise controls a density gradient, not an absolute peak cap.
  • Ridges retain v7's native water-level channels and canyon walls. Neither arbitrary block decorations nor exaggerated height offsets manufacture cliffs. The river-width threshold remains the stock C++ value.
  • 256-node climate fields make multiple coherent communities plausible inside the ±512 limit; stock 1000-node climate fields can hide most variety there. Existing small-scale heat/humidity blend noises can remain stock.
  • Floatlands are explicitly disabled; native mountain noise can still produce small overhangs/disconnected remnants. This is stock v7, not a guarantee of mathematically erosion-correct terrain. Dungeons are disabled so unknown mapgen dungeon aliases do not create unrelated structures.

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 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.

No full engine was run for this owner task. Main's remaining native gate is a fresh disposable world with explicit saved preset verification, multiple seed starts (including 731104), resource reachability, actual tree anchoring/media loading and representative valley/forest/alpine views. Do not test by opening the user's persistent world. Neither performance nor safe spawning is certified 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.