36 KiB
Executable File
Living ecosystems
The Last Hearth's original succession model takes ecological inspiration from Dynamic Life. No datapack code, species implementation, textures or other assets were copied. This is a runtime simulation, not extra random map decorations or world regeneration.
What changes
Unclaimed, unmodified ecological ground automatically recruits niche-appropriate groundcover, including the eight understory plants described below. Sunny, moderately grassy sites support wildflowers; dense grass competes with flowers. Moist shaded sites support ferns. Trees reduce local light and retain moisture; prolonged shade kills sun-loving ground cover. Rivers and biome humidity also influence moisture. Established sparse meadow can recruit a tree seedling; seedlings become young trees, then mature seed-bearing trees and finally deadwood. Living plants disperse to one adjacent suitable gap per eligible attempt. Mature trees shed litter and disperse seedlings. Competition and occupation reject seeds; seedlings never replace flowers, crops, another tree, or even existing meadow grass.
Wildlife can consume active natural grass, ferns, clover, sedge and nettles and
leave dung. Dormant winter plants cannot supply food or breeding habitat. Grazing opens gaps
but imposes a five-minute recolonization cooldown. Dung, litter and deadwood
decompose; moist, shaded organic patches seed fungi. Decomposition releases finite
nutrients into the supporting soil (capped at eight). Fertile soil recruits ground
cover sooner and successful establishment consumes one nutrient. Fungi are
not edible forage; generic fungi decomposes, while botanical fungi have
host-specific root associations or saprotrophic substrate effects below.
These feedbacks permit shifting meadow/flower/
woodland patches rather than a single guaranteed final biome.
Fallen logs (hearth:deadwood) are ordinary full wood cubes laid sideways, not
irregular branch meshes. Their label is Fallen log (axe; yields timber). Each
requires an axe and drops one hearth:log, exactly like standing timber: tree=1,
choppy=2, no hand-diggable snappy group. Default full-cube collision and selection
apply. Normal reach, claim/external protection and construction support-removal
checks remain; there is no right-click bypass. Loose hearth:sticks remain
hand-gatherable, preserving the first-tool path. Stone still needs a mining tool.
New woodland mapgen places sparse three-cube fallen logs, never force-placing them.
paramtype2="facedir", param2=12 points end grain along +X; a random schematic
rotation can align the line along Z (param2=4 is +Z). The name hearth:deadwood
is retained for decomposition and species metadata. The hearth:block_deadwood
load callback rotates legacy vertical deadwood once, marks succ_block_deadwood=1,
and leaves already-horizontal orientation intact. It rechecks exact loaded node
identity, respects external protection, and uses swap_node to retain owner/player
markers, natural flags, age, metadata inventories and timers. It does not convert
other timber, rewrite surrounding nodes, or regenerate existing terrain.
Active-time scales (minimums, not wall-clock promises)
| Process | Observed active time |
|---|---|
| Bare soil recruits ground cover | 3 minutes; 90 seconds with nutrients |
| Grass/flower/fern dispersal eligibility | 15 minutes; attempt every third evaluation |
| Severe niche stress to litter/deadwood | 10 minutes |
| Old meadow recruits a seedling | 2 hours; 4 in dense unfertilized grass, only suitable spatial niches |
| Seedling to young tree | 6 hours |
| Young to mature tree | 24 additional hours |
| Mature tree death | 72 additional hours |
| Mature seed dispersal | After 30 minutes; every tenth evaluation |
| Wildflower / grass and fern senescence | 4 / 6 hours |
| Dung / litter / fungi / deadwood decay | 10 / 30 / 40 / 120 minutes |
New tree generations therefore take sessions, not seconds. Each stage resets its
own age. Cold weather below 3°C suppresses establishment and living reproduction/
stage transitions. Decomposition and stress remain possible. This is a practical
niche model, not a botanical or hydrological solver. Young/mature identities are
ordinary full-cube log roots; a mature tree needs an actual multi-node block trunk
and leaf canopy, not one bare root cube. The read-only diversity.tree_plan
interface below shares the same geometry used by mapgen. Applying it, validating
every occupied destination and support, and tracking generated components belong
to the ecological growth owner. This content change installs no geometry-writing
scheduler or automatic load-time expansion. Do not retain the old 5×5×6 mesh-only
clearance assumption: mature profiles reach 9×10×9 and must be preflighted in full.
Unknown/player plants, water, unloaded nodes, claims and markers must block growth;
the plan's omitted air cells are never permission to clear or replace anything.
Existing mapgen log/leaves trees contribute shade but are not aged or destroyed.
Player-planted saplings, crops and berry bushes retain their existing time contract.
Species diversity and understory
Shared tree identities are exactly oak, birch, pine, willow, alder, beech, aspen,
rowan, hazel, spruce, matching the ten trees in botany.md. Each has
canonical hearth:<species>_seedling, hearth:<species>_young, and
hearth:<species>_mature nodes. The six-hour/24-hour/72-hour stage durations above
remain unchanged. Growth and seed dispersal preserve the parent's species, and
death records succ_species on generic deadwood (retained through its litter
transition). Each young/mature root drops one of its species logs, never three
logs from one cube. Additional generated trunk cubes are separately harvestable.
Roots are hidden from creative inventory, use species log top/end and bark side
textures, drawtype="normal", tree=1, choppy=2, and default cube collision.
Seedlings are conventional plantlike sprites. Mapgen full-block species
logs/leaves/planks belong to the biome module;
logs/leaves contribute canopy without being adopted as killable succession trees.
Recruitment is deterministic, not an all-oak fallback:
- Pine favors cold podzol and alpine turf. Warm podzol permits pioneer birch.
- Birch pioneers dry/open grass, earth and woodland gaps.
- Oak favors stable forest floor with an existing canopy.
- Beech has a broad domed crown and avoids saturation; aspen is tall and columnar; rowan has an open crown; hazel is low coppice; spruce has a narrow evergreen spire. All ten have distinct mature block silhouettes and deterministic recruitment.
- Willow favors moist alluvium/floodplain; alder favors peat and wet woodland.
Riparian recruitment requires moisture ≥0.65. Species soil support is delegated
to
hearth.biomes.supports_species(species, surface)when present; its decision is authoritative. A standalone soil policy works without the biome module. - Ecological soils are grass, soil, forest floor, podzol, alluvium, peat and alpine
turf. Biome
surface(node)moisture hints blend with humidity/water/canopy. This is not an expansion of the separate crop-planting soil policy.
| Understory | Niche and feedback |
|---|---|
| Clover | Sunny, moderately moist gaps; edible; flowering pollinator habitat; fixes bounded nitrogen |
| Nettle | Moist, already fertile ground; edible natural browse, not an inventory food item |
| Heather | Dry, open acidic podzol/alpine turf; autumn pollinator habitat |
| Bluebell | Moist shade; blooms only in Spring, occupied and dormant otherwise |
| Sedge | Saturated, lit riparian soil; edible when active |
| Bulrush | Saturated, lit riparian soil; tall wetland cover, not edible |
| Moss | Moist shade; persistent through Winter, not edible |
| Lichen | Dry exposed rock/open ground; persistent through Winter, not edible |
These are active succession species: they establish, compete, spread into air, undergo stress, and senesce. They do not merely decorate a fixed map. Suitable understory appears on the first ordinary 90/180-second establishment cycle, rather than waiting for tree maturation. Wet herbs can recruit riparian trees after two hours; heather can recruit pine. Saturated sites cannot fall back to meadow grass, and meadow grass cannot disperse into them. Exposed rock admits lichen only; neither recruitment nor decay replaces rock with soil or fertilizes it.
Clover can add at most two nitrogen units per supporting soil node, recorded
as succ_fixed_nitrogen across restart, grazing and recolonization; additions stop
at local fertility four. The universal nutrient cap remains eight, and successful
establishment consumes one. Clover has no harvest/item drop or timer, and cannot
create an endless fertilizer-item loop. Habitat reads clamp corrupt/excessive
nutrient metadata to eight without rewriting it.
Natural blooming clover, wildflowers, spring bluebells and autumn heather increase
the read-only habitat(pos).pollination count. This exposes a bounded habitat input
for berry-regrowth consumers; this module does not change berry timers or grant
extra berries. edible is a separate active-natural-forage count. Pine and spruce
are evergreen in the catalogue and seasonal node policy; deciduous trees
retain only 15% canopy influence.
Known unmarked worldgen diversity herbs, species stage roots and generic deadwood are naturalized by
diversity.bootstrap(pos, node?) from a load callback or the existing column probe.
Bootstrap checks the plant and support for ownership, ambiguous claims, external
protection and placement/unknown metadata. Hand placement marks player_placed=1.
It never adopts species/generic logs, leaves, planks, crops or berry bushes. There
is no general adoption of arbitrary flora, generic litter or fungi, and no new
scanning scheduler. Adopted deadwood needs loaded natural ecological soil and
decays to litter after two observed active hours, releasing three nutrients
(soil cap eight); subsequent litter decay releases one more, once. Adoption is
idempotent and grants no age or absence catch-up. As with
legacy unmarked soil, provenance cannot distinguish third-party placements that
deliberately omit every marker; those integrations must mark or claim their flora.
Seasonal flora and food
Appearance and availability use hearth.climate_at(os.time()).season: the same
actual-epoch/latitude-aware climate as ecology, not the machine's local month
and not a replay of an optional preview clock. Southern latitude reverses the
calendar through the existing solar model. Explicit test climates can preview it.
| Flora | Spring | Summer | Autumn | Winter |
|---|---|---|---|---|
| Deciduous block leaves | Spring foliage | Original foliage | Autumn colours | Twig canopy texture |
| Young/mature block trunks | Bark/end grain | Bark/end grain | Bark/end grain | Same full cubes; no mesh swap |
| Pine/spruce leaves | Evergreen | Evergreen | Evergreen | Evergreen |
| Clover / nettle / heather / sedge / bulrush | Active | Active | Active | Dormant |
| Bluebell | Bloom | Dormant | Dormant | Dormant |
| Moss / lichen | Active | Active | Active | Active |
| Meadow grass / fern | Living | Living | Living | Dormant perennial |
| Wildflowers | Bloom | Bloom | Dormant | Dormant |
| Spring greens patch | Pickable | Dormant | Dormant | Dormant |
| Autumn mushroom patch | Dormant | Dormant | Pickable | Dormant |
Frost and seasonal dormancy pause living age, stress and reproduction; they do not kill perennials at first frost. Spring resumes the same ecological age. Deadwood, litter, dung and decomposer fungi continue returning nutrients in winter; mature trees can continue shedding litter. First-night berry availability stays year-round.
Spring greens recruit and spread into moist (≥0.55), light ≥9 gaps in Spring.
Autumn mushrooms recruit and spread into moist (≥0.6), shaded (light ≤10) gaps in
Autumn. Deterministic sparse recruitment niches prevent every gap becoming food.
Dormant patches remain occupied: neither seeds nor normal placement replace them.
The existing decomposer fungi is still not edible.
Right-click a patch (or dig to pick) within six nodes with normal construction
use authorization. One picked greens item restores 12 food when eaten.
One picked mushroom can be crafted shapelessly into existing raw_food, then
cooked at a lit, fuelled hearth. Raw mushrooms have no eat callback. These are
original game foods, not advice about identifying or eating real wild plants.
Picking never removes a patch. An empty inventory slot is required; one successful
item transfer writes succ_harvest_ready_at = now + 86400 (one real day). Rejected
delivery grants nothing and writes no cooldown. The durable deadline survives
restart, unload and every seasonal toggle; returning to Spring/Autumn cannot refill
it. Harvest writes no age, owner or placement metadata and bypasses no claim.
hearth.seasons.kind(node_or_name) canonicalizes only registered controlled
variants; succession, grazing and habitat see the original kind and natural flag.
name_for(kind, epoch) selects appearance. reconcile(pos, node?, epoch?) swaps
only a loaded controlled node, preserves param1/param2 and all metadata, and does
not call construction/destruction callbacks or reset timers. Appearance-only
swaps may recolour claimed/hand-placed plants without changing permission or
provenance; external protection still blocks them. Ecological mutations retain
the stricter claim/placement exclusions below. No save schema/version change.
Reviewed species effects and evidence
species_effects.lua is an offline policy
factory, effects(hearth, api). It never fetches Wikipedia, interprets article
prose, registers a scheduler, or writes nodes/metadata. Succession uses an injected
hearth.species_effects, or loads this fixed sibling in the engine before its
existing globalstep is registered. Minimal standalone fixtures may omit it.
Content/bootstrap integration can initialize it before succession.
E.describe(record_or_id) returns an ordered list of
{title, status, game, source_species, source_url, ...}. Status is exactly
simulated or descriptive: simulated means the named bounded game mechanism
exists, not that all real-world consequences or field rates are simulated.
E.source(id) and E.sources() expose identity references for all 80 catalogue
IDs. Bundled Wikipedia page URLs/revision IDs are used when available; without
the bundle, Latin-title links are only identity pointers, not verified evidence.
Unreviewed records explicitly say that no species-effect evidence was reviewed
here. identity_revision / identity_revision_url refer to the bundled
introduction snapshot, not a claim that its excerpt contains the ecological
body section reviewed below. Effect review date: 2026-11-03. No invented
revision IDs, biological rates, or undisclosed effect generation from tags.
Implemented mechanics, not just catalogue descriptions
| Species / relationship | Exact game effect | Evidence and limits |
|---|---|---|
| Common alder (Alnus glutinosa), white clover (Trifolium repens) | After 900 warm active seconds, add at most one nutrient per eligible evaluation, never above local fertility four. Two units total per supporting soil node, recorded in succ_fixed_nitrogen, shared across species, recolonization and restart; soil's universal cap remains eight. Alder seedling, young and mature roots resolve through the same species ID; generated logs/leaves do not earn another contribution. |
Alder, Ecology describes Frankia nodules and fertility; white clover, Forage describes symbiotic fixation. The finite lifetime cap is game balance, not an annual nitrogen flux or physiological exhaustion claim. |
| Dog lichen (Peltigera canina) | Uses the same existing finite nitrogen budget, never a fertilizer item or independent annual allowance. The pre-existing lungwort-lichen nitrogen tag retains that shared policy; its species-effect evidence is not independently reviewed here. | Dog lichen, Description identifies Nostoc; Peltigera, Description explains nitrogen fixation. Explicit species identity plus genus-level mechanism, not a measured species rate. |
| Blunt-leaved bog-moss (Sphagnum palustre) | A natural, active mat on loaded, permitted peat/alluvium at the sample's ground level adds 0.10 moisture and subtracts 0.20 pH from the local land/bank habitat estimate, capped at two mats (+0.20 / -0.40); moisture clamps to 0..1, pH to 3.5..8.5. This changes actual recruitment/stress niche tests. | S. palustre describes water absorption; Sphagnum explains water retention and acidification by cation exchange. Acidification evidence is genus-level. Coefficients are game approximations, not measured pH changes per plant. No permanent chemistry, peat creation, terrain conversion or global hydrology. |
| Common duckweed (Lemna minor) | Each natural active mat immediately above conserved source water at the sample's stand level reduces nearby submerged freshwater habitat light by 1.5, capped at four mats / six light units. It never brightens a dark site, and never lowers light below two. Surface plants and terrestrial sites are not shaded by this rule. | L. minor, Cultivation describes floating carpets; Lemnoideae, Duckweed in natural environments describes shade. This is a local group-level shading approximation, not an algae, oxygen, contaminant-removal or nutrient-purification simulation. |
| Host-associated fungi, including fly agaric (Amanita muscaria) | An active compatible nearby fungus permits a tree seedling's young-stage transition at 16,200 rather than 21,600 active seconds. Only a successful transition consumes at most one existing soil nutrient. Multiple fungi select the maximum allowance, not a sum; zero nutrients means no early growth. | Fly agaric, Distribution and ecology describes ectomycorrhizal associations. Game host lists are narrower than the article. The existing growth debit is not nutrient creation, a full carbon-for-mineral ledger, or simulated water transfer. |
| Saprotrophic wood fungi, including birch polypore (Fomitopsis betulina) | On suitable natural deadwood/litter only, advance its existing decay age by at most 60 seconds per eligible evaluation, capped at 7,200 / 1,800. The ordinary substrate transition consumes deadwood to litter to air, paying three then one soil nutrients once, subject to cap eight. Removing the support removes its natural fungal occupant. The fungus itself has no extra nutrient payout. Soil-growing saprotrophs receive no special nutrient effect. | Birch polypore, Distribution, habitat and ecology describes birch-specific brown rot. Existing host suitability remains authoritative. Natural parasitism on weakened living birches is not simulated. No attacks on living/player trees. |
The sampler collects species_counts (including canonical alder roots, moss and
duckweed) within its existing 5x5x5 stencil. Mat feedback uses only the
sample's stand level, with natural/protection/placement checks on the mat and its
immediate support; claimed, planted, protected, unsupported or unloaded sources
do not contribute. A partially unloaded sample grants no derived mat effects.
E.habitat(base, observations) returns a copy and exposes source_moisture,
source_ph, and source_light. Succession calls it once after deriving fresh
base moisture, support-class pH and aquatic classification. Repeated habitat
reads cannot accumulate acidification or retention and never change metadata.
Consumers must not feed an already-derived habitat back as a new base sample.
All aquatic stands still occupy air above water: no rule clears, replaces,
refills or converts a water-source node. These habitat estimates drive botanical
niches, not the renderer's actual node light or a global pH map.
Existing season-gated browse, flowering/pollination and nursery counts are also reported, as is canopy shade/moisture (pine/spruce evergreen; deciduous Winter influence 15%). Nursery cover reduces stress accrual to 75%; pollination is an input, not a new berry-yield promise. Engineer tags on other species remain descriptive / not simulated: no automatic bank stabilization, erosion or rock weathering. Resin, allelopathy, toxicity attacks and living-tree parasitism are explicitly not simulated by this module. Article food/medicinal statements never grant food permission, poison attacks, healing or recipes.
Focused evidence is in species_effects_spec.lua and succession_spec.lua: all-80 source mapping, descriptor honesty, quantitative mat caps and changed real niche eligibility, read-only/repeated sampling, source/support protection, shared durable nitrogen, no duplicate fungal nutrients, and source-water preservation. These are standalone Lua tests, not a claim of full-engine or multiplayer validation.
Safety and execution contract
- One scheduler runs once per second, doing at most eight column probes total, shared round-robin across connected players (not eight per player). Columns scan a 25×25 square and twelve vertical nodes (+6..−5) around a player, seeking existing natural ground. No connected players means no ecological work.
- Each probe checks at most twelve heights and one site. Encountered foliage, including old unmarked mapgen leaves, is reconciled on that same probe: at most twelve appearance swaps per column, 96 per second total, not a second player radius scan. An LBM reconciles each controlled flora node on load without emergence or a world registry; that load-driven work is one swap per callback, separate from the scheduler's per-second budget. Habitat uses a fixed 5×5×5 stencil; site safety uses a fixed 3×3×3 stencil. Mutations per evaluated site are at most two (mature seed plus litter); other stages write at most one. Wildlife calls scan at most nine candidate sites and mutate at most one.
- Only
get_node_or_nilreads are used. Missing/ignore nodes fail closed. There is no emerge, force-load, mapgen edit, ABM, per-node timer or offline replay. - Ages accrue from consecutive observations, not from absolute planting dates. First observation after restart, load, cache eviction, disconnect, clock rewind, or a gap longer than 1,200 seconds only establishes a baseline. The load callback invalidates observations when vegetation or its natural support reloads. A stalled server step gets one fixed budget, never a backlog of simulation steps.
- A bounded 8,192-entry in-memory observation cache is disposable (enough for ten disjoint stationary-player scan squares). Age, stress, next-attempt timestamp, natural-origin flag and attempt serial live in node metadata; soil stores only its nutrient count. There is no global save schema. Durable attempt timestamps/serials prevent repeated evaluation or rerolling a blocked seed by reconnecting/restarting. Restarting pauses growth; it cannot accelerate it. Ages are sampled approximations between visits, not per-tick accounting; very busy/many-player worlds may deliberately progress more slowly.
- Source, destination and natural supports must all be unclaimed, externally
unprotected and free of placement metadata. All normal content placement marks
player_placed=1, including hand-placed soil/turf and soil left by crop harvest. Recognized legacy markers areowner,planter,placed_by,hearth_placed,hearth_player_placedandplanted_at. Unknown third-party non-succession metadata also opts out conservatively. Any nonempty/nonzero marker opts out. Nearby markers/builds/farms also block succession conservatively. Only explicitly natural simulation plants may be consumed, replaced or decayed. Never replace water or arbitrarybuildable_tonodes. Old unmarked player earth cannot be distinguished from mapgen earth: claim it to keep nature out.
Wildlife interface
hearth.succession is installed by the content registrar:
forage(pos) -> boolean: consume exactly one nearby active naturalmeadow_grass,fern,clover,sedgeornettleon safe natural support. Does not eat dormant flora, flowers, crops or player plants.deposit(pos) -> boolean: place exactly one dung nutrient patch in nearby air over safe natural support. Never replaces existing plants or water. Nutrients arrive through active decomposition, not immediately on deposit.habitat(pos) -> table: read-only countsgrass,flowers,cover,water,edible,pollination, plusground,nutrients(0..8), biomeheat,canopy, estimatedlight,moistureandloaded. Counts use the fixed local stencil; wildlife should reject an incomplete (loaded=false) sample.step(dtime) -> number: registered scheduler; returns columns processed. Exposed constants document the scheduler's fixed production limits, not settings.
Positions must be finite near-world-range vectors; APIs round to node coordinates. Callers supply animal ground-cover height (the air/plant cell above soil), not the supporting soil or the animal's eye height. Calls return false when no safe target exists. Habitat has no writes, timers or protection-violation reports.
Assets and verification
Exact registered node / art integration contract
All names below have the hearth: namespace. Existing canonical nodes retain their
names and callbacks/groups/drops. Summer always uses the existing canonical tree
and leaves node; no redundant summer variant is registered.
- Legacy
tree_seedling,young_tree,mature_tree,leavesretain_spring,_autumn,_winterIDs for save compatibility (12 variants). The content registrar removes old mesh/atlas references from every legacy stage variant: seedlings usehearth_sapling.png; young/mature trees use log top/side textures and remain normal cubes. Seasonal appearance belongs to their separate leaves. - Deadwood uses
hearth_log_top.pngon both ends andhearth_log_side.pngon the remaining faces, with no mesh, custom collision/selection or seasonal variant. - Mapgen leaf variants use
hearth_leaves_spring.png,hearth_leaves_autumn.png,hearth_leaves_winter.png; canonical Summer uses existinghearth_leaves.png. meadow_grass_winter,fern_winter,wildflowers_autumn,wildflowers_winterusehearth_meadow_grass_dormant.png,hearth_fern_dormant.pngandhearth_wildflowers_dormant.png. Other seasons use existing canonical nodes/art.spring_greens_patchandautumn_mushrooms_patchare dormant canonical nodes;spring_greens_patch_spring,autumn_mushrooms_patch_autumnare active variants. Textures:hearth_spring_greens.png,hearth_spring_greens_dormant.png,hearth_autumn_mushrooms.png,hearth_autumn_mushrooms_dormant.png.- Picked craftitems (not nodes):
hearth:spring_greens,hearth:autumn_mushrooms; iconshearth_item_spring_greens.png,hearth_item_autumn_mushrooms.png.
The legacy seasonal set adds 20 nodes total, including both canonical food patches. All variants
are hidden from creative inventory, retain base callbacks and drop definitions,
and cannot become duplicate harvest sources by toggling. The registrar honours
explicit mesh tiles directly; it does not require override_item in engine mocks.
Main-agent wiring and exact diversity interface
The blockshape change owns only diversity, content, biomes, their three focused tests and this document. Succession, ecology, seasons, botany and generated assets remain other owners' responsibility. Exact integrated order:
- Create/install
hearth.seasons = dofile(modpath .. "/seasons.lua")(hearth, api). - Create/install
hearth.botany = dofile(modpath .. "/botany.lua")(hearth, api). - Before succession setup, create/install
hearth.diversity = dofile(modpath .. "/diversity.lua")(hearth, api). This factory immediately extendsseasons.add_variantswith species policy. - Instantiate succession using its factory and assign
hearth.succession. - After normal content, register/install biomes (ten species' logs/leaves/planks).
- Call
hearth.diversity.register_nodes(register_node)with the bare-name wrapper: 30 species stage nodes, eight existing understory nodes and the bootstrap LBM. - Call
hearth.botany.register_nodes(register_node): exactly 62 new non-tree taxa, with all explicit tiles and inventory/wield images preserved. The ten trees and eight existing understory nodes are not registered twice. - Call
seasons.register()after every canonical node, then instantiate wildlife.
The returned diversity table exposes:
species_order,plants: ordered ten/eight canonical short names.grounds,rocks: exact short-name membership sets;is_ground(node_or_name),is_rock(node_or_name)andsupports_ground(kind, support)are boolean policies.tree_info(kind_or_node) -> species, stage: two Lua return values, or nil; stages areseedling,young,mature. Seasonal names canonicalize first.family(kind_or_node) -> canonical_family: species stages map to legacytree_seedling,young_tree,mature_tree; species leaves/logs map toleaves/log; herbs retain their own canonical kind.supports_species(species, surface),suitable(kind, habitat, season?): support/niche policy with optional biome integration.choose_recruit(pos, habitat, season) -> understory_kind_or_nil;choose_tree(pos, habitat) -> species_seedling_kind_or_nil. Habitats supplygroundas a short name and normalizedmoisture,light,loaded,nutrients,canopyand optionally biomeheat.edible(kind, epoch?),pollinator(kind, season);is_worldgen_flora(kind),bootstrap(pos, node?) -> boolean;register_nodes(register_node), idempotent node registration.
Geometry handoff to ecological growth
diversity.tree_plan(species, stage?) -> plan | nil is read-only. Species is one
of the ten IDs or "generic" for legacy trees; stage is "young" or "mature"
(default mature). Invalid IDs/stages or missing biome integration return nil.
plan is a fresh dense array of {x, y, z, name, param2} records: integer offsets
relative to the stage root at (0,0,0), fully qualified node names, and facedir.
plan.size={x,y,z} gives the bounding schematic dimensions. There are no air
records. The root is hearth:<species>_<stage>; remaining trunk/branch and canopy
records use hearth:<species>_log and hearth:<species>_leaves. Generic plans use
oak geometry with young_tree/mature_tree roots and canonical log/leaves.
Every non-root occupied cell exactly matches
biomes.tree_schematic(species, canonical?, stage?) (x-fastest, then y, then z).
Young plans are at most 3×5×3; mature plans at most 9×10×9. Mutating a returned
plan cannot change later plans or mapgen geometry.
Ecology owner: preflight the complete bounded plan, loaded supports and all
destination claims/protection/provenance before writing. Only the exact existing
natural root and verifiably owned tree components may be reused on stage growth;
never overwrite water, player nodes, farms or unrelated flora. Track component
ownership/root association and stale callbacks; reject partial growth, coordinate
overflow, unloaded cells and blocked transitions. Preserve species identity and
handle component removal/death within the existing scheduler budget. This API
neither applies geometry nor migrates/expands old roots on load. Mature roots
must not be treated as finished trees without their real block canopies.
When ecological death creates fresh hearth:deadwood, supply param2=12 (or 4
for a Z-oriented log) immediately; the load migration is for legacy saves, not
a substitute for orienting newly created fallen timber.
seasons.add_variants(kind, definitions, active_seasons?) is available immediately
after factory creation and before registration. Definitions map Spring, Summer,
Autumn, Winter to {texture=..., mesh=...}. An optional active-season boolean
set controls availability. Missing base nodes in mocks/optional integrations are
skipped without an assertion; a later registration call can register their variants.
No original 12-month climate calculation or preview-clock semantics changed.
Exact species/understory art contract
For all ten species in species_order:
- Seedlings use
hearth_botany_<species>.pngon a plantlike sprite. Young/mature roots and species timber usehearth_<species>_log_top.pngon both ends andhearth_<species>_log_side.pngelsewhere. Planks usehearth_<species>_planks.png. No ecological tree definition references a mesh or atlas. - Biome-owned full-block leaves use
hearth_<species>_leaves.png. Deciduous seasonal variants usehearth_<species>_leaves_spring.png,hearth_<species>_leaves_autumn.png,hearth_<species>_leaves_winter.png. Pine and spruce leaves retain the base texture throughout the year. - Deciduous stage/leaf variants append
_spring,_autumn,_winter; Summer restores the canonical node. Seedling variants keep the botany tile. Young/mature seasonal IDs are retained for existing saves but have the same bark cubes, never foliage atlases. Their availability still pauses in Winter for deciduous species. Diversity registers those multi-face cube variants before the seasonal foliage copier; seasons remains responsible for name selection and swapping.
Understory base textures are hearth_clover.png, hearth_nettle.png,
hearth_heather.png, hearth_bluebell.png, hearth_sedge.png,
hearth_bulrush.png, hearth_moss.png, hearth_lichen.png. Clover, nettle,
heather, bluebell, sedge and bulrush additionally use
hearth_<plant>_dormant.png in Winter (nodes append _winter). Bluebell also
has _summer and _autumn node variants using its same dormant texture.
Moss and lichen have no dormant art. No art is generated or modified here.
With all biome leaves installed, diversity adds 142 nodes: 38 canonical nodes, 72 deciduous stage variants, 24 deciduous full-block leaf variants and eight understory variants. Pine and spruce add no seasonal variants. Botany independently adds 62 nodes. Species provenance on generic deadwood remains metadata.
The standalone succession fixture tests growth, niches, shade, competition, seedlings, seeds, nutrient feedback, dung/fungi/decay, grazing, source/destination/ support protection, placement markers, unloaded nodes, work/read budgets, cold, restart cooldowns and no-catchup behavior. Content tests check node registration, named textures, wildlife API wiring and placement marking. Seasonal fixtures add all four seasons, northern/southern calendars, seasonal foliage/restoration, metadata/age conservation, forage cooldowns/restart, niches/occupation, protected and unloaded no-write cases, target-volume clearance and the 96-swap bound. The blockshape-focused standalone suites are diversity, biomes and content; botany has its separate catalogue suite. They check exact ten-species ordering, distinct mature schematics, bounded young canopies, shared rooted geometry, metadata-safe deadwood migration, axe-only harvesting, all 78 unique recipes, 62 additional botany nodes and preserved aquatic icons/placement guards. Engine visuals, multiplayer ecology balance and long-session woodland density still require in-engine playtesting; unit fixtures are not a performance benchmark.
Security review: no credentials, cryptography, certificates, network requests, deserialization or unbounded external inputs were introduced. Mutation authority is least-privilege nature (no player identity); loaded/protection/marker checks fail closed before writes.