242 lines
32 KiB
Markdown
Executable File
242 lines
32 KiB
Markdown
Executable File
# Botany: 80 temperate taxa and integration contract
|
||
|
||
## Scope and evidence
|
||
|
||
The catalogue in [mods/hearth/botany.lua](../mods/hearth/botany.lua) has **80 distinct real species**, partitioned into **eight ordered groups of ten**. It represents a north-west/central European temperate landscape mosaic: deciduous woods, conifer woods, hedgerows, acid heath, wet meadow, bog and freshwater margins. It does **not** imply that all species grow together in one patch, or that all are native to every British locality. Norway spruce is native to continental northern/central Europe and widely planted in Britain. Hazel is counted only among trees; heather only among herbs; rushes and reedmace only among grasses/sedges. Lichens are lichen-forming fungi but have their own disjoint gameplay category, not a second count in fungi.
|
||
|
||
Taxonomic/reference authorities for maintaining these names and broad habitats:
|
||
|
||
- **P**: [BSBI/UKCEH Plant Atlas 2020](https://plantatlas2020.org/) for vascular-plant regional distributions, habitat and nomenclature; [Kew Plants of the World Online](https://powo.science.kew.org/) for name/synonym checks. Kew blocked automated access during this implementation; no claim of an automated 80-record authority audit is made.
|
||
- **T**: [Woodland Trust British tree accounts](https://www.woodlandtrust.org.uk/trees-woods-and-wildlife/british-trees/a-z-of-british-trees/) and [plant accounts](https://www.woodlandtrust.org.uk/trees-woods-and-wildlife/plants/), complementary common names and woodland niches.
|
||
- **W**: [The Wildlife Trusts wildflower accounts](https://www.wildlifetrusts.org/wildlife-explorer/wildflowers), including freshwater plants.
|
||
- **M**: British Bryological Society, *Mosses and Liverworts of Britain and Ireland: A Field Guide* (2010), and *Atlas of British & Irish Bryophytes* (2014), for the five moss names/niches. Online BBS access also blocked automated retrieval; these are bibliographic authorities, not downloaded records.
|
||
- **F**: [British Mycological Society English names](https://www.britmycolsoc.org.uk/resources/english-names), [Index Fungorum](https://www.indexfungorum.org/), and [First Nature fungal accounts](https://www.first-nature.com/fungi/). Particularly [fly agaric](https://www.first-nature.com/fungi/amanita-muscaria.php) for root-host associations, [birch polypore](https://www.first-nature.com/fungi/piptoporus-betulinus.php) for birch/deadwood associations and its modern name, and [wood blewit](https://www.first-nature.com/fungi/lepista-nuda.php) for litter decomposition and late fruiting.
|
||
- **L**: [British Lichen Society species accounts and maps](https://britishlichensociety.org.uk/resources/species-accounts) and its [Lichens of Great Britain and Ireland](https://britishlichensociety.org.uk/identification/lgbi3). Names and species boundaries evolve; common lichen names are not universally standardized.
|
||
|
||
The numeric light/moisture/pH/depth windows below are **explicit game approximations**, not measured ecological tolerances, identification keys, conservation advice or a scientific simulation. The catalogue selects named species rather than inventing generic “magic herbs”. *Rubus plicatus* is one real blackberry species, not the *Rubus fruticosus* aggregate counted as a species. Meadow fescue *Schedonorus pratensis* is also encountered as *Festuca pratensis* / *Lolium pratense*. Wood blewit *Collybia nuda* is widely recorded as *Lepista nuda* / *Clitocybe nuda*. Birch polypore *Fomitopsis betulina* replaces the familiar *Piptoporus betulinus*. These synonyms are not extra entries.
|
||
|
||
**No real-world foraging, medicinal or consumption claims are provided.** Only three fictional bush outputs use the existing game item `hearth:berries`. This is a gameplay simplification, not a statement that a wild plant or lookalike is safe. Every fungus, herb, tree and lichen has `food=false`; toxic/stinging examples are flagged but `toxic=false` is **not** a safety certification. `browse` describes wildlife relationships, not player edibility. No new food items, recipes, poisoning system or damage to player plants are registered.
|
||
|
||
## Exact ID manifest (art, trees and ecology handoff)
|
||
|
||
`id` is the canonical catalogue key. Every non-tree node is exactly `hearth:<id>`. Every tree's initial node is `hearth:<id>_seedling`; tree stages/resources belong to the diversity/resources owner. **Do not create a second generic berry bush or alias a generic patch into this count.** Existing generic `berry_bush`, `fern`, `wildflowers` and `meadow_grass` remain outside the 80.
|
||
|
||
Owner flags: **E** = existing node, never registered here; **D** = tree diversity owner, never registered here; **B** = new botany-owned node. The 13 existing mappings are retained verbatim: five trees plus clover, nettle, heather, bluebell, sedge, bulrush, moss and lichen. “Sedge”, “moss” and “lichen” receive a specific catalogue interpretation; their old IDs do not change.
|
||
|
||
Art contract for **all 80**: `hearth_botany_<id>.png`. This module uses that exact tile for each of its **62 new understory nodes**. Trees use species bark cubes and full-block leaf crowns supplied by diversity/terrain; no seasonal tree mesh is introduced on a cube. Seasonal copying preserves bark/end-grain faces, `facedir`, callbacks and drops; foliage gets its own species-season texture. Assets and real-engine visual validation remain the art/main integration owners' responsibility.
|
||
|
||
### trees (10; P/T)
|
||
|
||
| id | Common name | Scientific name | Owner | Habitat emphasis |
|
||
|---|---|---|---|---|
|
||
| oak | Pedunculate oak | Quercus robur | E/D | Mesic woodland; ectomycorrhizal roots |
|
||
| birch | Silver birch | Betula pendula | E/D | Light acid pioneer woods |
|
||
| pine | Scots pine | Pinus sylvestris | E/D | Open acid woods; resin |
|
||
| willow | White willow | Salix alba | E/D | Wet alluvial woods; spring flowers |
|
||
| alder | Common alder | Alnus glutinosa | E/D | Wet woods; Frankia-associated nitrogen fixation |
|
||
| beech | European beech | Fagus sylvatica | D | Well-drained woodland, avoiding saturation |
|
||
| aspen | European aspen | Populus tremula | D | Light woodland gaps |
|
||
| rowan | Rowan | Sorbus aucuparia | D | Cool acid woodland/edges; flowers |
|
||
| hazel | Common hazel | Corylus avellana | D | Woodland understorey; ectomycorrhiza |
|
||
| spruce | Norway spruce | Picea abies | D | Moist acid conifer woods; resin |
|
||
|
||
### shrubs (10; P/T)
|
||
|
||
| id | Common name | Scientific name | Owner | Habitat emphasis |
|
||
|---|---|---|---|---|
|
||
| bilberry | Bilberry | Vaccinium myrtillus | B | Acid heath/woodland; ericoid, not ectomycorrhizal |
|
||
| blackberry | Pleated blackberry | Rubus plicatus | B | Hedgerows/wood edges |
|
||
| raspberry | Red raspberry | Rubus idaeus | B | Woodland clearings |
|
||
| elder | Black elder | Sambucus nigra | B | Hedgerows; no game food |
|
||
| blackthorn | Blackthorn | Prunus spinosa | B | Thorny hedges; spring flowers |
|
||
| hawthorn | Common hawthorn | Crataegus monogyna | B | Hedgerows; spring flowers |
|
||
| dog_rose | Dog rose | Rosa canina | B | Open scrub; summer pollen |
|
||
| holly | European holly | Ilex aquifolium | B | Evergreen woodland cover; no game food |
|
||
| juniper | Common juniper | Juniperus communis | B | Open dry heath/scrub; resin |
|
||
| guelder_rose | Guelder rose | Viburnum opulus | B | Damp hedges/wet edges; no game food |
|
||
|
||
### grasses (10; includes sedges, rushes and reedmace; P)
|
||
|
||
| id | Common name | Scientific name | Owner | Habitat emphasis |
|
||
|---|---|---|---|---|
|
||
| sedge | Greater tussock-sedge | Carex paniculata | E | Wet bank tussocks |
|
||
| bulrush | Bulrush (great reedmace) | Typha latifolia | E | Reedbed margin; bank-cell approximation |
|
||
| meadow_fescue | Meadow fescue | Schedonorus pratensis | B | Moist neutral grassland |
|
||
| cocksfoot | Cock's-foot | Dactylis glomerata | B | Mesic meadow/edge |
|
||
| timothy | Timothy | Phleum pratense | B | Moist meadow |
|
||
| sweet_vernal_grass | Sweet vernal-grass | Anthoxanthum odoratum | B | Acid grassland |
|
||
| yorkshire_fog | Yorkshire-fog | Holcus lanatus | B | Mesic grassland |
|
||
| tufted_hairgrass | Tufted hair-grass | Deschampsia cespitosa | B | Wet meadow tussocks |
|
||
| common_reed | Common reed | Phragmites australis | B | Wet banks/reedbed edges |
|
||
| soft_rush | Soft rush | Juncus effusus | B | Wet pasture/bog margin |
|
||
|
||
### herbs (10; P/T/W)
|
||
|
||
| id | Common name | Scientific name | Owner | Habitat emphasis |
|
||
|---|---|---|---|---|
|
||
| clover | White clover | Trifolium repens | E | Open grassland; rhizobial nitrogen fixation |
|
||
| nettle | Common nettle | Urtica dioica | E | Nutrient-rich damp edges; larval host, not nectar |
|
||
| heather | Heather | Calluna vulgaris | E | Acid heath; late-season flowers |
|
||
| bluebell | English bluebell | Hyacinthoides non-scripta | E | Spring woodland floor |
|
||
| yarrow | Yarrow | Achillea millefolium | B | Meadow; summer/autumn flowers |
|
||
| foxglove | Common foxglove | Digitalis purpurea | B | Acid clearings; no game food |
|
||
| meadowsweet | Meadowsweet | Filipendula ulmaria | B | Wet meadow; pollen resource |
|
||
| wild_garlic | Wild garlic | Allium ursinum | B | Spring moist woodland; no game food |
|
||
| cowslip | Cowslip | Primula veris | B | Base-rich open grassland |
|
||
| wood_anemone | Wood anemone | Anemone nemorosa | B | Spring woodland floor; no game food |
|
||
|
||
### ferns_mosses (10; five ferns P/T, five mosses M)
|
||
|
||
| id | Common name | Scientific name | Owner | Habitat emphasis |
|
||
|---|---|---|---|---|
|
||
| bracken | Bracken | Pteridium aquilinum | B | Acid slopes/clearings; competition descriptor |
|
||
| lady_fern | Lady-fern | Athyrium filix-femina | B | Damp shaded woods |
|
||
| male_fern | Male-fern | Dryopteris filix-mas | B | Shaded woods |
|
||
| harts_tongue | Hart's-tongue fern | Asplenium scolopendrium | B | Damp base-rich shade |
|
||
| polypody | Common polypody | Polypodium vulgare | B | Acid rock/bark/woodland |
|
||
| sphagnum | Blunt-leaved bog-moss | Sphagnum palustre | B | Saturated acid bog/wet woodland |
|
||
| haircap_moss | Common haircap | Polytrichum commune | B | Damp acid soils |
|
||
| moss | Cypress-leaved plait-moss | Hypnum cupressiforme | E | Shady soil, wood, bark and rock |
|
||
| fork_moss | Broom fork-moss | Dicranum scoparium | B | Acid woodland/heath |
|
||
| star_moss | Swan's-neck thyme-moss | Mnium hornum | B | Damp woodland banks |
|
||
|
||
### fungi (10; F)
|
||
|
||
| id | Common name | Scientific name | Owner | Habitat emphasis |
|
||
|---|---|---|---|---|
|
||
| fly_agaric | Fly agaric | Amanita muscaria | B | Ectomycorrhizal: birch/pine/spruce |
|
||
| birch_bolete | Brown birch bolete | Leccinum scabrum | B | Ectomycorrhizal: birch only in this model |
|
||
| bay_bolete | Bay bolete | Imleria badia | B | Ectomycorrhizal: conifers/beech/oak |
|
||
| chanterelle | Chanterelle | Cantharellus cibarius | B | Ectomycorrhizal woodland roots |
|
||
| wood_blewit | Wood blewit | Collybia nuda | B | Litter saprotroph; autumn/winter |
|
||
| shaggy_inkcap | Shaggy inkcap | Coprinus comatus | B | Grassland-soil saprotroph |
|
||
| turkey_tail | Turkey tail | Trametes versicolor | B | Broadleaf deadwood saprotroph |
|
||
| oyster_fungus | Oyster mushroom | Pleurotus ostreatus | B | Broadleaf deadwood; late-season fruiting |
|
||
| honey_fungus | Honey fungus | Armillaria mellea | B | Deadwood here; parasitism descriptive only |
|
||
| birch_polypore | Birch polypore | Fomitopsis betulina | B | Birch deadwood here; parasitism descriptive only |
|
||
|
||
### aquatic (10; P/W)
|
||
|
||
| id | Common name | Scientific name | Owner | Placement niche |
|
||
|---|---|---|---|---|
|
||
| duckweed | Common duckweed | Lemna minor | B | Surface; 1–6 depth units |
|
||
| yellow_waterlily | Yellow water-lily | Nuphar lutea | B | Surface; 1–3, mud anchor |
|
||
| white_waterlily | White water-lily | Nymphaea alba | B | Surface; 1–3, mud anchor |
|
||
| water_crowfoot | Common water-crowfoot | Ranunculus aquatilis | B | Surface abstraction; 1–3, mud anchor |
|
||
| water_mint | Water mint | Mentha aquatica | B | Wet bank, dry destination |
|
||
| water_forgetmenot | Water forget-me-not | Myosotis scorpioides | B | Wet bank, dry destination |
|
||
| brooklime | Brooklime | Veronica beccabunga | B | Wet bank, dry destination |
|
||
| water_starwort | Common water-starwort | Callitriche stagnalis | B | Submerged abstraction; 1–4, mud anchor |
|
||
| hornwort | Rigid hornwort | Ceratophyllum demersum | B | Submerged/rootless; 1–4, water substrate |
|
||
| pondweed | Broad-leaved pondweed | Potamogeton natans | B | Surface; 1–3, mud anchor |
|
||
|
||
### lichens (10; L)
|
||
|
||
| id | Common name | Scientific name | Owner | Habitat emphasis |
|
||
|---|---|---|---|---|
|
||
| reindeer_lichen | Reindeer lichen | Cladonia rangiferina | B | Open acid heath; wildlife browse |
|
||
| oakmoss | Oakmoss | Evernia prunastri | B | Bark in light woodland |
|
||
| old_mans_beard | Old man's beard | Usnea subfloridana | B | Humid bark/branches |
|
||
| lichen | Map lichen | Rhizocarpon geographicum | E | Acid siliceous rock, not generic soil |
|
||
| dog_lichen | Dog lichen | Peltigera canina | B | Mossy soil; cyanobacterial fixation |
|
||
| lungwort_lichen | Lungwort lichen | Lobaria pulmonaria | B | Humid woodland bark; cyanobacterial cephalodia |
|
||
| sunburst_lichen | Common orange lichen | Xanthoria parietina | B | Well-lit nutrient-enriched bark/base-rich stone |
|
||
| hammered_shield | Hammered shield lichen | Parmelia sulcata | B | Woodland bark |
|
||
| cup_lichen | Mealy pixie-cup | Cladonia chlorophaea | B | Acid soil/humus, distinct species below |
|
||
| pixie_cup | Pebbled pixie-cup | Cladonia pyxidata | B | Soil/humus; not a duplicate of chlorophaea |
|
||
|
||
## Exact public interface
|
||
|
||
Loading the file returns **`factory(hearth, api) -> B`**. The factory has no registration side effects and publishes no globals. Main assigns `hearth.botany` before loading succession. Botany installs no scheduler, globalstep, ABM, LBM, mapgen callback, timer or liquid transformation. [mods/hearth/succession.lua](../mods/hearth/succession.lua) consumes this policy in its existing eight-probe loop.
|
||
|
||
### Catalogue/query APIs
|
||
|
||
- `B.groups`, `B.group_order`: ordered arrays `trees, shrubs, grasses, herbs, ferns_mosses, fungi, aquatic, lichens`.
|
||
- `B.catalogue`: ordered 80-record snapshot, group order then manifest row order.
|
||
- `B.get(value) -> record|nil`; `B.list(group_or_nil) -> ordered records`; `B.group_of(value) -> group|nil`; `B.kind(value) -> id|nil`.
|
||
- `value` may be a bare ID, full `hearth:` node name or `{name=...}`. Trees also resolve canonical `_seedling`, `_young`, `_mature`, `_leaves`, `_log`, `_planks`. Seasonal names resolve through `hearth.seasons.kind` when available. Generic patches and foreign namespaces do not resolve.
|
||
- Every returned record is a detached copy. Editing a snapshot cannot change internal policy.
|
||
- Record fields: `id`, `common`, `latin`, `group`, `node`, `texture`, `existing`, `food`, `toxic`, `seasons` (boolean set), `tags` (boolean set), `niche`; flowering taxa also have `flowering`, the three game-fruit taxa have `fruiting` (both boolean sets).
|
||
- `niche` fields: `substrates` set, inclusive `light={min,max}` on 0–15, `moisture={min,max}` on 0–1, `ph={min,max}` in pH units, `water={min,max}` in game node-depth units, `aquatic` string. Some also require `hosts` set or `nutrients` minimum.
|
||
|
||
### Read-only habitat selection
|
||
|
||
`B.suitable(value, h, season) -> boolean` and `B.choose(p, h, season) -> bare_node_kind, record | nil` use only supplied observations. `p` must contain finite integer `x,y,z` within ±30990; `h` must have `loaded=true` and finite `light`, `moisture`, `ph`. Missing/invalid facts do not receive optimistic defaults. `season` is exactly `Spring`, `Summer`, `Autumn` or `Winter`.
|
||
|
||
`h.ground` is a canonical bare/full support node or node table. `h.substrate`, when supplied, overrides the direct substrate for land/bark/deadwood niches. Bark and deadwood are **semantic observations**, not assertions that arbitrary planks/logs are safe recruitment sites. The ecological owner must derive those observations from unmodified natural substrate and enforce owner/protection checks. Do not treat crafted timber or planted trees as free fungal substrate.
|
||
|
||
`h.host` is a nearby identified tree ID/stage/root association, mandatory for host-specific fungi. Its search radius and finite sampling budget belong to the existing ecological scheduler. Presence of an unrelated tree does not satisfy a birch specialist. `h.nutrients` must be at least one for nettle. pH is a game support-class observation; callers lacking it should explicitly derive a documented approximation from the substrate, not silently infer that all plants fit.
|
||
|
||
`h.aquatic` is `land`, `bank`, `surface` or `submerged`; land may omit it. `h.water` is **numeric column depth**, not a boolean or permission to replace a water node; land/bank may omit it (zero). The selection represents a candidate only:
|
||
|
||
| Mode | Required observations | Safe external placement rule |
|
||
|---|---|---|
|
||
| land | Depth zero; accepted substrate | Loaded air destination above accepted natural substrate |
|
||
| bank | Explicit `bank`, depth zero, wet support | Loaded air destination beside real freshwater, never inside/over it |
|
||
| surface | Explicit `surface`, `freshwater=true`, allowed positive column depth; accepted mud bottom | Flat pad in a distinct air cell above the actual surface; source node below stays unchanged |
|
||
| submerged | Explicit `submerged`, `freshwater=true`, positive column depth; rooted starwort needs mud bottom, rootless hornwort needs `substrate=water` | Succession places a **qualitative shallow-water stand sprite in the separate air cell above conserved source water**. It is not literally rendered inside the liquid. No source replacement, fake liquid, water clearing/refilling or flood-producing liquid flora |
|
||
|
||
Surface depth ranges/anchors are intentionally coarse: even floating duckweed uses a valid freshwater bottom observation rather than an unbounded “water somewhere” fallback. Succession checks up to six consecutive loaded `hearth:water_source` cells and an accepted bottom, with authority/markers checked on every cell. Flowing water, unknown bottoms, over-depth or unloaded columns fail closed. Surface/submerged mode for a new stand is position-deterministic; established stands retain their species mode. Thus all ten aquatic taxa have establishment/spread/age pathways, but **hornwort/starwort use above-water stand markers, not scientifically exact submerged geometry**. `register_nodes` keeps direct source-water item placement disabled. Bank flora occupies dry cells beside actual water.
|
||
|
||
`choose` considers all 80 records in a deterministic position-hashed order, skipping trees unless `h.include_trees=true`. It returns an unprefixed actual node kind, e.g. `bluebell`, `water_mint` or `beech_seedling`, plus a detached descriptor. It never writes the map, chooses an occupied cell, checks claim ownership itself, consumes nutrients or rescans the world. The caller must independently verify loaded, empty, unmodified destination/support and authorization **immediately before any write**, using its existing bounded scheduler. No plant replaces another, spreads water or creates nutrients here.
|
||
|
||
### Interactions and finite effects
|
||
|
||
- `B.has_tag(value, tag) -> boolean`.
|
||
- `B.interactions(value) -> tag-keyed descriptor copies` with a `description`, and where applicable `cap` or `enabled=false`.
|
||
- `B.active(value, season) -> boolean`: establishment/visible-season gate, **not** an instruction to destroy winter-dormant perennial plants. Four spring woodland herbs gate to Spring; evergreen trees, mosses/lichens and persistent bracket fungi have broader windows.
|
||
- `B.pollinator(value, season) -> boolean`: actual flowering tag **and** flowering season. Oak, wind-pollinated grasses, nettle, mosses and fungi do not become nectar sources. Meadowsweet and dog rose represent pollen as well as nectar relations.
|
||
- `B.edible(value, season) -> boolean`: **game berry permission only**, independent of wildlife browse; only bilberry/raspberry in Summer and blackberry in Summer/Autumn return true.
|
||
- `B.browse(value, season) -> boolean`: active wildlife relationship; not a universal diet rule. Wildlife owner must still choose an appropriate animal diet.
|
||
- `B.resin(value) -> boolean`: only the catalogue's pine/spruce/juniper resin tag. It grants no item or extraction callback.
|
||
- `B.nitrogen(value, current) -> suggested_delta`: only nitrogen-fixer-tagged taxa; `min(1,max(0,2-current))`, zero for invalid/negative current. Alder/Frankia, clover/rhizobia and cyanobacterial lichen partners are distinct biological associations. **Ecology owner must apply a per-site contribution/cooldown and metadata cap of two; repeatedly calling this pure query is not permission for unlimited fertilizer.**
|
||
- `B.root_fungi(value, tree, budget) -> transfer_ceiling`: only ectomycorrhizal **fungi** matching the supplied host, at most one and at most the finite positive existing budget. A nearby host can receive a bounded establishment advantage through that owner's already-budgeted resource transfer. Caller debits the same budget; the function creates nothing and never independently raises soil nitrogen. Tree ectomycorrhizal tags alone are not free fungal boosts. Saprotrophs receive no root-fungus boost.
|
||
|
||
`saprotroph` describes recycling existing litter/wood: any later benefit must consume existing substrate, not mint nutrients. `engineer` describes physical substrate/water retention or aquatic cover; it does not grant terraforming. `nursery` means conditional cover/microhabitat. `parasite` and `allelopathy` are **disabled descriptive interactions**: honey fungus/birch polypore do not infect player trees and bracken never kills neighboring plants. The module mutates no ecological state.
|
||
|
||
### Registration and harvest
|
||
|
||
`B.register_nodes(register_node)` accepts the content registrar **taking bare names**. It is idempotent and skips known existing IDs, all ten trees and any already present `api.registered_nodes[full_name]`. In an otherwise empty registry it registers **62** nodes (70 non-tree taxa minus eight existing understory IDs). It registers no recipes, aliases, items or tree stages/resources. Existing flora definitions are never overridden. Main should load this factory after seasons and call registration after legacy diversity registration.
|
||
|
||
New terrestrial plants/fungi/shrubs are conventional crossed `plantlike` sprites with no meshes/sculptures. Surface aquatics are thin nodeboxes wholly inside ±0.5 of the node. They are not water; submerged sprites are not given liquid behavior or water groups. All are non-walkable and `buildable_to=false`. Ordinary herbs, mosses, fungi and lichens dig to their own node item. **All new shrubs dig to `hearth:sticks`**, so digging/replanting cannot erase a berry cooldown and mint a fresh fruit-bearing bush. Placement marks `player_placed=1`; normal construction/protection handling is supplied by the existing registrar. There is no new growth timer.
|
||
|
||
`B.harvest(pos, player) -> success_boolean, count_or_reason` is the only resource mutation API. Berry callbacks return the original wielded stack. It:
|
||
|
||
1. Validates finite integer bounded coordinates and a real player; looks up **current** `hearth.construction.allowed(pos, player, "use")` at callback time. Missing policy denies access; no permissive fallback. Existing policy owns reach, claim and protection checks.
|
||
2. Re-reads the loaded node and requires its exact canonical three-bush ID, then current `hearth.climate_at().season` and game fruiting season.
|
||
3. Checks persisted string metadata `botany_ready_at`, rejecting malformed/negative/nonfinite values. Missing metadata means initial fruit stock; future timestamps deny until ready.
|
||
4. Requires inventory room for `hearth:berries 2`; full inventory neither changes stock nor starts cooldown.
|
||
5. Locks the bush by writing a finite timestamp **one real day** ahead before inserting at most two berries. This blocks callback reentrancy. A completely rejected insertion restores the old timestamp; partial delivery consumes that cycle, never exceeds two or drops overflow into the world. Unexpected insertion results/errors leave the lock in place rather than duplicate stock.
|
||
|
||
The factory may precede construction publication, but harvest cannot. Time uses optional injected `api.now()` (numeric Unix seconds, for tests), otherwise `os.time()`. No offline catch-up or accumulating fruit: one ready bush gives at most two per cooldown even after months away, and cooldown metadata survives module reload. New callbacks are installed only on `bilberry`, `blackberry`, `raspberry`; generic existing berry-bush behavior is untouched.
|
||
|
||
Reasons: `invalid`, `denied`, `species`, `season`, `clock`, `metadata`, `cooldown`, `inventory`; success returns delivered count 1 or 2. No player health changes, medicinal effects, wild-consumption guidance or automatic fertilizer is added.
|
||
|
||
## Ecology80 integration and bounds
|
||
|
||
The single succession loop owns recruitment, local spread, dormancy, senescence and decomposition for all 80 catalogue entries. It uses the **actual node kind returned by `B.choose`**, including species seedling suffixes, and rechecks `B.suitable` against destination facts before writes. Trees enter older gaps only after 7,200 active seconds; other recruits establish after 180 active seconds (90 with existing nutrients). No second scheduler, unbounded radius, map emergence or offline replay is introduced. Existing eight column probes/second, radius 12, twelve sampled heights, 8,192-entry age cache, 60-second durable attempt cooldown and 1,200-second maximum continuity gap remain.
|
||
|
||
`S.habitat(pos)` is read-only. Its 5×5×5 stencil now exposes exact catalogue dimensions: finite `light`, `moisture`, `ph`, `nutrients`, depth-valued `water`, separate `near_water`, `ground`, semantic `substrate`, `freshwater`, `aquatic`, `host`, active browse count (`edible`, retained for wildlife compatibility), seasonal `pollination`, `nursery` and host-fungus IDs. pH is an explicit **game support-class approximation**: peat 4.5; podzol/granite 5; alpine turf/slate/sandstone/scree/stone/forest floor/bark/deadwood/litter 5.5; grass/soil/alluvium/sand/basalt 6.5; limestone 7.5. Missing support observations fail closed. Nursery-tagged natural cover slows accumulated habitat stress by 25%; it creates neither water nor nutrients. Fungal host observations use only nearby identified natural trees or recorded natural wood species, never player timber.
|
||
|
||
Non-tree active lifetimes (seconds): shrubs/lichens 86,400; grasses/aquatic 21,600; herbs 14,400; ferns/mosses 43,200; fungal fruit bodies 2,400. Dormant perennials occupy their cells and pause, not disappear at first frost. Soil-supported vegetation becomes litter; rock/bark/wood/litter/water stands disappear without replacing their support. Fungal fruit bodies **do not independently mint nutrients through litter**. Saprotrophs advance only an existing, natural, authorized litter/deadwood clock by at most 60 seconds per attempt. The substrate's eventual consumed decomposition pays once using the existing capped nutrient path.
|
||
|
||
All nitrogen-fixer tags use a persisted two-unit **soil support** budget (`succ_fixed_nitrogen`), shared across death/recolonization/restart; a bark-dwelling fixer without a soil support gets no fertilizer effect. Matching ectomycorrhizal fungi reduce seedling establishment age from 21,600 to 16,200 only with a positive existing soil resource budget; successful growth debits at most one from that same budget. Failed growth creates/debits nothing. `parasite` and `allelopathy` remain deliberately **disabled descriptors**; no tree infection or neighboring-plant damage. `engineer`/`resin` stay habitat/resource descriptors, not autonomous terraforming/item generation. Pollination is exposed for wildlife/harvest integration, **not a berry-yield multiplier**: the unchanged botany harvest policy still returns at most two berries per real-day cooldown. Succession preserves a valid `botany_ready_at`; other non-succession provenance metadata still opts plants out.
|
||
|
||
### Owned full-block tree lifecycle
|
||
|
||
`D.tree_plan(species, stage)` supplies fresh full-block plans for all ten species. Succession validates integer offsets within ±4 horizontally and 0–9 vertically, at most 810 entries, no duplicate/foreign names, and a root. **One global tree-plan reservation per wall-clock second**, including denied attempts, covers plan construction/preflight/application regardless of player count. Old/new plan union work is bounded (at most two plans); there are no dynamic per-frame world tree registries.
|
||
|
||
Every destination and its immediate support must be loaded, unclaimed, externally unprotected and unmarked. New components require air; growth never silently replaces even natural groundcover. Existing components must match the root-key `succ_component`, species/stage expected node (season-canonicalized) and rotation. `succ_tree_stage` and `succ_species` persist on the root; components carry its plain coordinate key. Components never independently age or pay nutrients. A tree's own owned leaves do not shade its own root into automatic starvation; neighbouring crowns still shade recruits. Pine **and spruce** retain winter shade.
|
||
|
||
Senescence atomically clears only the root's matching components and leaves **one ground-supported full-cube fallen log**, `param2=12`; existing content supplies the axe requirement and one-log drop. No floating standing log pile, sculpted tree, or chain of extra resource logs is created. Any protected/marked/foreign component blocks collapse rather than being destroyed. Natural epiphytes losing their owned substrate are removed in the same bounded preflight, while foreign attachments block it.
|
||
|
||
`S.tree_removed(pos, oldnode) -> boolean` is the optional after-dig handoff. A standard `register_on_dignode` hook calls it when available. It validates an empty loaded root, old stage identity and every remaining matching owned component; it cannot clear unrelated mapgen/player wood. External removals that do not invoke this hook must explicitly call the handoff; a denied/unloaded cleanup is not queued/replayed. Unmanaged pre-existing mapgen tree canopies are not retroactively adopted as managed trees. The independently registered generic deadwood adoption remains intact.
|
||
|
||
## Focused validation and integration boundary
|
||
|
||
[tests/botany_spec.lua](../tests/botany_spec.lua) is a standalone Lua 5.1+ mocked-API suite. It checks all 80 positive niche vectors and each light/moisture/pH/substrate rejection; 8×10 disjoint counts; unique scientific names; exact legacy mappings; seasonal/host/water distinctions; texture and registration ownership; bounded interactions; toxic/food exclusion; denied/full/partial/reentrant harvest; reload/cooldown and invalid clocks/metadata.
|
||
|
||
Standalone focused suites: [tests/botany_spec.lua](../tests/botany_spec.lua), [tests/succession_spec.lua](../tests/succession_spec.lua) and [tests/seasons_spec.lua](../tests/seasons_spec.lua). They include all 80 real selector outputs and recruitment/spread/lifecycle pathways with supplied species niche vectors, separately tested **actual** water/wood/pH habitat sampling, water preservation/authority, atomic real tree plans, owned cleanup/restart, malformed plans, concurrency bounds, finite fungal/nitrogen effects and cube-safe seasonal registration. Verified markers: `BOTANY_SPEC_PASS 32`, `LAST_HEARTH_SUCCESSION_PASS 63`, `LAST_HEARTH_SEASONS_PASS 12` (107 named cases; the all-80 case additionally loops every taxon). Controlled niche-vector coverage is not evidence that all species spawn in one real-world patch. Require each final marker, all named passes and **empty stderr**; Fengari exit zero alone can hide assertion failures. On the network workspace, module source was read from disk once and executed in-memory to avoid repeated SMB reads timing out; tests/production modules were not rewritten for staging. No full engine or persistent world is run for this integration. Visual correctness, real-engine harvest/dig callback wiring and empirical gameplay/performance acceptance remain to be verified by main.
|
||
|
||
Security review followed CodeGuard: centralized runtime authorization, deny-by-default missing policy, finite validated positions/time, persistent bounded harvest, inventory preflight and reentrancy lock. No credentials, certificates, cryptography, network operations or dynamic executable inputs are introduced.
|