634 lines
46 KiB
Markdown
Executable File
634 lines
46 KiB
Markdown
Executable File
# Living ecosystems
|
||
|
||
The Last Hearth's original succession model takes ecological inspiration from
|
||
[Dynamic Life](https://www.planetminecraft.com/data-pack/dynamic-life-1-21-5-and-above/).
|
||
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. Lifespan endpoints
|
||
never automatically kill calendar plants or turn them into 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 browse. Grazing debits one partial
|
||
canopy ration without removing the perennial; the shared per-root browse receipt
|
||
rests seven real days. No stubble rendering is installed by this change. 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.
|
||
|
||
### Calendar biology versus bounded loaded work
|
||
|
||
| 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 |
|
||
| Named seedling to young tree | Absolute juvenile-age endpoint from its lifecycle profile (default 5 real years) |
|
||
| Named young to mature tree | Absolute species maturity endpoint, not an additional stage-local duration |
|
||
| Generic unidentified tree | Explicit fallback: young after 1 real year, mature after 10; lifespan unknown |
|
||
| Living calendar plant death | Never inferred from lifespan endpoints or unknown age |
|
||
| Mature tree seed opportunity | At most one present eligible attempt per mean real year; no missed-event replay |
|
||
| Named understory seed opportunity | Requires known maturity and seed season; at most one attempt per 30 days |
|
||
| Dung / generic fungi decay | Existing game policies: 10 / 40 loaded minutes |
|
||
| Litter / deadwood decay | Conservative game fallbacks: 90 calendar days / one mean real year |
|
||
|
||
New tree generations take actual calendar years, not compressed sessions. One
|
||
day is 86,400 seconds, a mean year 365.2425 days. Every named plant uses
|
||
[the lifecycle policy](lifecycles.md), including explicitly unverified group
|
||
fallbacks; sourced fields are not proof that all timing fields were researched.
|
||
Stage swaps preserve one root age. Cold below 3°C/dormancy suppresses visible
|
||
stage transitions and seeding, **not calendar aging**. No exact historical
|
||
growing-season integration or mortality model is claimed. 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 generic saplings now require one real year before their bounded
|
||
young block-tree establishment, using an explicitly unidentified family fallback,
|
||
not an assumed birch. Generic trees have a ten-year maturity policy and no
|
||
automatic death. Player provenance is retained on every newly written timber cell.
|
||
Existing mature player/mapgen timber is not retroactively made killable.
|
||
|
||
Generic game grain has an explicit **120 warm-day approximate parameter**, not a
|
||
species/cultivar research claim. Stages require 24/72/120 equivalent warm days;
|
||
temperature below 3°C contributes zero, 3–10°C half, warmer one. At most 366
|
||
midpoint samples cover the entire planting-to-now interval, with no silent
|
||
30-day age cutoff. Long gaps coarsen quadrature, not discard age. This is bounded
|
||
climate estimation, not exact historic degree-day integration; stage high-water
|
||
prevents display regression. Invalid/future planting clocks and rollback pause,
|
||
never reset birthdays. Berry regrowth and food-patch harvest cooldowns remain
|
||
game resource policies, not organism lifespans. Legacy unidentified berry bushes
|
||
retain their initial standing crop, but picking locks them for one mean real year;
|
||
bare bushes then return only in an eligible Summer. This is a conservative annual
|
||
game policy, not researched species phenology. Rejected inventory transfer
|
||
restores original readiness; partial transfer spends the batch. Node swaps
|
||
preserve natural/ownership metadata. Seasonal greens/mushrooms retain their daily
|
||
game harvest policy, with reservation before inventory callbacks.
|
||
|
||
#### Calendar root persistence and migration (version 1)
|
||
|
||
See [the full metadata contract](botany.md#root-persistence-and-migration).
|
||
`succ_birth_at` is the UTC recruitment/adoption epoch, `succ_initial_age_seconds`
|
||
is a separate documented initial-stage assumption, and
|
||
`succ_last_observed_at` is a durable clock high-water mark.
|
||
`succ_lifecycle_version=1`, exact `succ_lifecycle_profile`, position-derived
|
||
`succ_lifecycle_seed`, `succ_lifecycle_origin` and policy label persist on roots.
|
||
New recruits use `natural_seed` and zero initial age. Existing natural legacy
|
||
plants migrate once at NOW with `legacy_established`, preserving `succ_age`
|
||
unchanged; it remains old active seconds, never millennia or a birthday.
|
||
Generated known botanical stands/soil-supported block log roots adopt
|
||
`worldgen_established` without resetting mature canopies into seeds or claiming
|
||
historically observed ages. Initial stage ages come from known profile endpoints;
|
||
unknown maturity is zero, not invented history. No negative/pre-epoch birthdays.
|
||
Malformed metadata, unsupported versions and clock rollback fail closed. No
|
||
offline mortality, forced terrain loads, whole-world scans or queued seed replay.
|
||
|
||
## Species diversity and understory
|
||
|
||
Shared tree identities are exactly **oak, birch, pine, willow, alder, beech, aspen,
|
||
rowan, hazel, spruce, crab_apple, wild_cherry, field_maple**, matching the thirteen trees in [botany.md](botany.md). Each has
|
||
canonical `hearth:<species>_seedling`, `hearth:<species>_young`, and
|
||
`hearth:<species>_mature` nodes. Growth and seed dispersal preserve the parent's
|
||
species and calendar anchor; old accelerated stage/death durations are removed.
|
||
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.
|
||
Crab apple has a broad round crown, wild cherry has horizontal branch tiers,
|
||
and field maple has a broad lobed crown. All thirteen have distinct mature block
|
||
silhouettes, species bark/end-grain/leaves/planks 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 consumers; habitat reads never change berry timers or grant extra berries.
|
||
Transactional nectar debit below is separate from this observation.
|
||
`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 **one mean calendar year**, releasing three nutrients
|
||
(soil cap eight); subsequent litter decay releases one more, once. Adoption is
|
||
idempotent and grants no invented age. Organic decay uses durable
|
||
`succ_decay_at`, `succ_decay_initial`, `succ_decay_seen_at` high-water and bounded
|
||
`succ_decay_bonus`; elapsed absence changes present eligibility, never queues
|
||
missed decomposition or payouts. Litter returns only its unconsumed mineral
|
||
budget, not another full unit after earthworm feeding. 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 visible growth, loaded stress and reproduction,
|
||
not calendar age; they do not kill perennials at first frost. Spring observes the
|
||
same root anchor plus the full elapsed calendar interval. Deadwood,
|
||
litter, dung and decomposer fungi continue returning nutrients in winter; mature
|
||
trees can continue shedding litter. Initial legacy berry crops remain available;
|
||
spent crops no longer refill every real day.
|
||
|
||
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](../mods/hearth/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
|
||
|
||
Calendar-v1 update: nitrogen eligibility below is now **90 calendar days**, not
|
||
the historical 900 loaded seconds, retaining the finite two-unit support cap.
|
||
New red clover, birdsfoot trefoil, gorse and broom share it. Their tags are not
|
||
independently reviewed species-rate measurements. Separately owned read-only
|
||
effect descriptions may still describe old acceleration; runtime policy here
|
||
and field-level lifecycle confidence labels take precedence.
|
||
|
||
| 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](https://en.wikipedia.org/wiki/Alnus_glutinosa#Ecology) describes *Frankia* nodules and fertility; [white clover, Forage](https://en.wikipedia.org/wiki/Trifolium_repens#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](https://en.wikipedia.org/wiki/Peltigera_canina#Description) identifies *Nostoc*; [Peltigera, Description](https://en.wikipedia.org/wiki/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](https://en.wikipedia.org/wiki/Sphagnum_palustre) describes water absorption; [Sphagnum](https://en.wikipedia.org/wiki/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](https://en.wikipedia.org/wiki/Lemna_minor#Cultivation) describes floating carpets; [Lemnoideae, Duckweed in natural environments](https://en.wikipedia.org/wiki/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*) | A compatible natural fungus can transfer/debit **at most one existing soil nutrient** when a tree independently reaches its calendar juvenile threshold. Multiple fungi select a maximum allowance, not a sum. There is **no time reduction**; the former 16,200/21,600-second boost is removed. | [Fly agaric ecology](https://en.wikipedia.org/wiki/Amanita_muscaria#Distribution_and_ecology) describes associations, not a measured growth acceleration. This is not nutrient creation or a full carbon/water ledger. |
|
||
| Saprotrophic wood fungi, including birch polypore (*Fomitopsis betulina*) | On suitable natural deadwood/litter only, advance its calendar decay bonus by at most **60 seconds per eligible evaluation**, capped at the one-year / 90-day substrate lifetime. The ordinary transition consumes deadwood to litter to air, paying **three then the unconsumed portion of one** soil nutrient once, subject to cap eight. Removing the support removes its natural fungal occupant. The fungus itself has no extra payout. Soil-growing saprotrophs receive no special nutrient effect. | [Birch polypore, Distribution, habitat and ecology](https://en.wikipedia.org/wiki/Fomitopsis_betulina) describes birch-specific brown rot. 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](../tests/species_effects_spec.lua)
|
||
and [succession_spec.lua](../tests/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_nil` reads are used. Missing/ignore nodes fail closed. There
|
||
is no emerge, force-load, mapgen edit, ABM, per-node timer or offline replay.
|
||
- Legacy active counters accrue from consecutive observations. Calendar plant
|
||
ages and organic decay use durable absolute anchors, not these counters.
|
||
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 cannot accelerate or
|
||
reset birthdays, organic clocks or resource receipts. Calendar eligibility
|
||
survives absence; only bounded present work occurs on return. Legacy active
|
||
processes remain sampled approximations, not per-tick accounting.
|
||
- 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 are
|
||
`owner`, `planter`, `placed_by`, `hearth_placed`, `hearth_player_placed` and
|
||
`planted_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 arbitrary
|
||
`buildable_to` nodes. 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`: debit one partial canopy ration from one nearby
|
||
active natural browse plant on safe support. The plant remains rooted;
|
||
`succ_browse_ready_at = now + 7 days` is shared across animals and restarts.
|
||
Seven days is an unverified conservative game fallback, not measured biomass
|
||
recovery. No dormant flora, nonbrowse flowers, crops or player plants are eaten.
|
||
- `pollinate(pos) -> boolean`: exact source, not a new scan. Require botanical
|
||
active flowering (`B.pollinator`), known calendar maturity, natural root and
|
||
permitted loaded source/support. Atomically reserve
|
||
`succ_nectar_ready_at = now + 3600` before any effects. Competing bees share
|
||
this receipt. One non-additive boolean `succ_pollinated_until = now + 86400`
|
||
is written only when the old credit has expired; repeated visits do not stack
|
||
yield or extend the existing credit. One hour replenishment and one day credit
|
||
are game approximations, not measured species rates.
|
||
- `frugivore(pos, species_id?) -> boolean`: verify exact identity if supplied,
|
||
known mature shrub and actual botanical fruiting season. Consume one feeding
|
||
unit from the existing batch by spending the **whole two-fruit patch**; discard
|
||
the remainder rather than duplicate it. Reserve the human harvest field
|
||
`botany_ready_at = now + one mean year`; no items or animal-only fruit account.
|
||
- `granivore(pos, species_id?) -> boolean, optional_cache_id`: only mature
|
||
oak/hazel rooted trees (including adopted soil-level block roots), in Autumn
|
||
and their botanical seeding window. `succ_nuts_year` (UTC calendar year) and
|
||
`succ_nuts_remaining` persist a maximum two-nut current crop per root/year.
|
||
One nut funds a meal. If a second exists it is **separately debited** and the
|
||
second return is the plain species ID expected by wildlife. One remaining
|
||
nut returns `true, nil`; no old-year crop replay, reconnect refill or immature
|
||
root production. Two nuts is a coarse budget, not a measured yield.
|
||
- `disperse_seed(pos, species_id) -> boolean`: plain IDs alone confer no
|
||
authority. One in-memory handoff slot records the immediately preceding paid
|
||
granivore cache nut, with its source/species/UTC second. Any cache attempt
|
||
consumes the slot **before** destination checks; other mutating wildlife
|
||
hooks or another granivore call supersede it. Restart loses it, never refunds
|
||
nuts. Source identity/protection is rechecked; destination must be nearby
|
||
(within two horizontal nodes, same root height), loaded, permitted air on
|
||
viable natural support, with valid cooldown metadata. Existing establishment
|
||
preflight/niche rules apply. Success creates exactly one calendar-zero
|
||
same-species seedling and a one-day destination receipt. This matches the
|
||
synchronous squirrel caller; no opaque token or saved animal seed is required.
|
||
It is an internal trusted-call contract, not authentication against hostile
|
||
mods that can call hooks between them.
|
||
- `detritivore(pos) -> boolean`: exact natural litter source; consume 0.1 of
|
||
its normalized one-unit `succ_litter_remaining` mass and transfer exactly
|
||
0.1 existing mineral budget to permitted soil. At most ten meals per patch,
|
||
shared across restart. Keep the patch until the last unit; final removal
|
||
preflights any dependent fungus. Full soil refuses without spending material.
|
||
Ordinary decay returns only the remainder. No extra fertilizer aura, items
|
||
or renewal timer. Normalized units are game accounting, not measured digestion.
|
||
- `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 counts `grass`, `flowers`, `cover`, `water`,
|
||
`edible`, `pollination`, plus `ground`, `nutrients` (0..8), biome `heat`, `canopy`,
|
||
estimated `light`, `moisture` and `loaded`. 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.
|
||
|
||
### Integration handoff: botanical fruit effect remains separately owned
|
||
|
||
Current [botany.lua](../mods/hearth/botany.lua) human harvest still has a fixed
|
||
two-berry output and a one-day refill. This owned change does **not** pretend
|
||
that `succ_pollinated_until` already increases botanical fruit yield. The botany
|
||
owner must apply at most **one extra berry to an existing eligible fruit batch**
|
||
when its unexpired boolean credit is present, spend that credit with the shared
|
||
harvest transaction, and never give fruit merely for a pollen visit. It must also
|
||
replace daily human botanical refill with the next annual fruiting-season policy
|
||
and coordinate the same `botany_ready_at` lock used here. Animal fruit consumption
|
||
already uses a conservative one-year lock, but a human-first harvest still uses
|
||
the separately owned old daily policy until that integration lands. Flowering
|
||
may precede fruiting by months: the requested one-day credit is explicitly a
|
||
limited game approximation, not an annual pollination/fruit-set simulation.
|
||
|
||
Runtime signatures were checked against [wildlife.lua](../mods/hearth/wildlife.lua)
|
||
and [fauna.md](fauna.md): it consumes literal `true`, calls fruit/nut hooks with
|
||
the plain species ID, compares the optional cache return to that ID, and calls
|
||
dispersal synchronously only for oak/hazel squirrels. The older fauna contract's
|
||
one-day fruit deadline is superseded here by the conservative annual animal
|
||
receipt; its owner should align that documentation. No wildlife edits were made.
|
||
All resource failures leave node/resource metadata unchanged; paid cache authority
|
||
is the intentional exception, lost even on failed caching. No secrets, network
|
||
requests, serialization evaluation or cryptographic authorization were introduced.
|
||
|
||
## 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`, `leaves` retain `_spring`,
|
||
`_autumn`, `_winter` IDs for save compatibility (12 variants). The content
|
||
registrar removes old mesh/atlas references from every legacy stage variant:
|
||
seedlings use `hearth_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.png` on both ends and `hearth_log_side.png` on
|
||
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 existing `hearth_leaves.png`.
|
||
- `meadow_grass_winter`, `fern_winter`, `wildflowers_autumn`, `wildflowers_winter`
|
||
use `hearth_meadow_grass_dormant.png`, `hearth_fern_dormant.png` and
|
||
`hearth_wildflowers_dormant.png`. Other seasons use existing canonical nodes/art.
|
||
- `spring_greens_patch` and `autumn_mushrooms_patch` are dormant canonical nodes;
|
||
`spring_greens_patch_spring`, `autumn_mushrooms_patch_autumn` are 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`;
|
||
icons `hearth_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 calendar expansion owns only the six requested flora/ecology modules, their
|
||
six focused tests and the two botany/ecosystem documents. Bootstrap, lifecycle
|
||
policy, content, schema and generated assets remain other owners' responsibility.
|
||
Exact integrated order (main owns wiring):
|
||
|
||
0. Assign `hearth.lifecycle = dofile(modpath .. "/lifecycle.lua")` (plain namespace,
|
||
not a factory) **before** botany/content.
|
||
|
||
1. Create/install `hearth.seasons = dofile(modpath .. "/seasons.lua")(hearth, api)`.
|
||
2. Create/install `hearth.botany = dofile(modpath .. "/botany.lua")(hearth, api)`.
|
||
3. **Before succession setup**, create/install
|
||
`hearth.diversity = dofile(modpath .. "/diversity.lua")(hearth, api)`.
|
||
This factory immediately extends `seasons.add_variants` with species policy.
|
||
4. Instantiate succession using its factory and assign `hearth.succession`.
|
||
5. After normal content, register/install biomes (thirteen species' logs/leaves/planks).
|
||
6. Call `hearth.diversity.register_nodes(register_node)` with the bare-name wrapper:
|
||
39 species stage nodes, eight existing understory nodes and the bootstrap LBM.
|
||
7. Call `hearth.botany.register_nodes(register_node)`: exactly 79 new non-tree taxa,
|
||
with all explicit tiles and inventory/wield images preserved. The thirteen trees
|
||
and eight existing understory nodes are not registered twice.
|
||
8. Call `seasons.register()` after every canonical node, then instantiate wildlife.
|
||
|
||
The returned diversity table exposes:
|
||
|
||
- `species_order`, `plants`: ordered thirteen/eight canonical short names (the
|
||
latter are legacy registrar-owned understory, not the whole botanical catalogue).
|
||
- `grounds`, `rocks`: exact short-name membership sets; `is_ground(node_or_name)`,
|
||
`is_rock(node_or_name)` and `supports_ground(kind, support)` are boolean policies.
|
||
- `tree_info(kind_or_node) -> species, stage`: two Lua return values, or nil;
|
||
stages are `seedling`, `young`, `mature`. Seasonal names canonicalize first.
|
||
- `family(kind_or_node) -> canonical_family`: species stages map to legacy
|
||
`tree_seedling`, `young_tree`, `mature_tree`; species leaves/logs map to
|
||
`leaves`/`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 supply
|
||
`ground` as a short name and normalized `moisture`, `light`, `loaded`,
|
||
`nutrients`, `canopy` and optionally biome `heat`.
|
||
- `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 thirteen 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 thirteen** species in `species_order`:
|
||
|
||
- Seedlings use `hearth_botany_<species>.png` on a plantlike sprite. Young/mature
|
||
roots and species timber use `hearth_<species>_log_top.png` on both ends and
|
||
`hearth_<species>_log_side.png` elsewhere. Planks use `hearth_<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 use `hearth_<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 **187 nodes**: 47 canonical nodes,
|
||
99 deciduous stage variants, 33 deciduous full-block leaf variants and eight
|
||
understory variants. Pine and spruce add no seasonal variants. Botany independently
|
||
adds 79 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 thirteen-species ordering,
|
||
distinct mature schematics, bounded young canopies, shared rooted geometry,
|
||
metadata-safe deadwood migration, axe-only harvesting, unique material recipes,
|
||
79 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.
|