worldbox-observer-mod/docs/event-prose-audit.md

132 lines
7.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Event prose audit (vs game call sites)
Authored IdleSpectator sentences must match what the game actually does when an id fires.
Source of truth: `Assembly-CSharp` apply sites (`changeHappiness`, relationship patches, status receive), not English guesses from the id string.
Audit date: 2026-07-16.
Scope: Happiness (all authored), Relationship (all), Status/WorldLog/Plot/etc. sampled.
## Fixed this pass
| Id | Was | Actual | Now |
|----|-----|--------|-----|
| `just_born` | "is born into the world" / "first breath" | `Actor.newCreature` spawn; may precede egg form | "appears in the world" / "is brought into existence"; strength 48; no hatch strength floor |
| `just_kissed` | "shares a kiss" | Mating afterglow (`BehCheckForBabiesFromSexualReproduction`) | "mates with …" / "shares intimacy …" |
| `just_killed` | "takes a life" | Only with trait `bloodlust` | Explicit bloodlust wording |
| `add_child` / `EventReason.NewChild` | "{a} and {b} have a new child" | `{b}` is the child | "{a} gains a child, {b}" |
| `baby_created` / `EventReason.BabyBorn` | "{a} welcomes a baby" | `{a}` is the baby | "{a} is created as a baby" |
| `just_had_child` | Named newborn `{related}` | Related newborn not wired | "has a child" / parenthood only |
| `just_started_war` | Anyone cheers war | Culture trait `happiness_from_war` | "is glad war has started" / culture-loves-war |
| `just_talked` | Always warm | Talk can be good or bad | Neutral "finishes talking" |
| `just_inspired` | Onset of inspiration | Status `inspired` **end** | "comes down from inspiration" |
| `slept_outside` | Rough night over | Sleep start, no house | "has to sleep without a house" |
| `just_rebelled` | Joins / rises | Whole city units swept | "is swept up as the city rebels" |
| `just_forced_power` | Vague forced power | God force shove | "is flung by a god power" |
| `got_caught` | Shady catch Signal | No game caller found | Ambient, weak strength |
| `lost_fight` | Decisive loss | Yield / submit mid-fight | "is forced to yield" |
| `just_talked_gossip` | Generic juicy gossip | `gossip_lovers` culture | Lover-gossip wording |
| `become_king` | "crowned king" | Any sex ruler | "takes the crown" / "becomes ruler" |
| `become_alpha` | Fight for alpha | Oldest living family unit | "becomes the family alpha" |
| Dreams | "wakes from …" | Dream status on receive | "has a … dream" |
## Policy notes
- Hatch camera floor (`Max(strength, 88)`) applies only to `just_got_out_of_egg` / `just_hatched`, not `just_born`.
- True hatch prose (`just_got_out_of_egg` → "hatches from an egg") matches `Actor.eventHatchFromEgg`.
- Live parent beat is `just_had_child`, not `just_born`.
## Still clean (sample)
Grief deaths, `fallen_in_love`, gifts, house find/lose, `just_got_out_of_egg`, `just_became_adult`, WorldLog `king_new` template roles, Status `egg` / `drowning` prose.
## A/B camera policy (2026-07-16)
Interesting = interest candidate. Director ranks by strength.
Happiness Ambient only for true FX: poke, caught stub, tax, ate, pooped.
Status CreatesInterest false only for brief FX/cooldowns + egg gain.
Gate helper: `EventCatalog.IsCameraWorthy`. Drops: `InterestDropLog`.
## Hatch camera path (fixed)
Hatch was Activity-only for many units:
- Status `egg` has `CreatesInterest=false` (becoming an egg is not a camera beat).
- `OnStatusChange` previously ignored **losses**, so "Hatches from an egg" never became interest.
- Happiness `just_got_out_of_egg` is often suppressed (`!hasEmotions()`).
- Even when a hatch Label existed, dossier identity filter dropped verbs without ` is ` (e.g. "Name hatches …").
Fix:
- Egg status **loss**`InterestFeeds.EmitHatch` + `EventReason.Hatch`, key `hatch:{id}` shared with happiness.
- Keep `Name verb …` EventReason sentences in `UnitDossier.OwnedEventReason`.
## Reasons
EventLed camera candidates always get a `Label` (orange reason) when ownership + sentence filters pass.
Sources: `EventReason.*`, happiness catalog `PlainClause`, or discrete catalog templates.
Character fill / ambient may show empty reason.
Not every Activity line is a camera event (Ambient happiness, status gains with `CreatesInterest=false`, status losses except egg).
## Happiness trigger pass (2026-07-16)
Full 67-id call-site audit vs decompile + BehaviourTask / StatusLibrary / City civic force.
| Id | Finding | Keep-on fix |
|----|---------|-------------|
| `just_laughed` / `just_swore` | Fire at **end** of emotion tasks | Prose → finishes laughing / swearing |
| `just_ate` | Fires on `consumeFoodResource` | Prose → eats / enjoys a meal |
| `just_felt_the_divine` | Divine light clears bad traits | Prose → cleansed by divine light |
| `just_played` | Child play task end | Prose → finishes playing around |
| `got_caught` | Library asset, **no caller** | Stay Ambient; honest prose |
| `got_robbed` / gifts / kiss / talk / kill | Related optional but bag missing | Context bags on steal/gift/kiss/talk/kill |
| `fallen_in_love` | Status `fell_in_love` receive + bridge | Keep bridge for no-emotions |
| Civic aggregates / BehAddHappiness | Live via forceUnits / Beh* | Marked accurate |
Worksheet: `docs/event-trigger-audit.tsv` (happiness domain done).
## Status trigger pass (2026-07-16)
Full 58-id audit vs `StatusLibrary` + apply sites. Universal Activity hook:
`ActorStatusPatches``addNewStatusEffect` / `Status.finish` (egg loss also `InterestFeeds.EmitHatch`).
| Id | Finding | Keep-on fix |
|----|---------|-------------|
| `angry` | `addAggro`, not the `rage` combat buff | Prose → flies into a fury |
| `possessed_follower` | Possession talk converts **target** | Prose → converted into possessed follower |
| `pregnant` / `pregnant_parthenogenesis` | Finish → `BabyMaker` birth | Loss prose → gives birth (/ alone) |
| `taking_roots` | Finish → vegetative offspring | Loss prose → sprouts offspring |
| `budding` | Finish → bud offspring | Loss prose → splits off a bud offspring |
| `uprooting` | Plant babies emerging | Prose → emerges as a young plant |
| Dreams / `inspired` / combat FX | Sleep pot, finish→happiness, CombatActionLibrary | Already accurate |
Worksheet: `docs/event-trigger-audit.tsv` (status domain done).
## WorldLog / plot / trait pass (2026-07-16)
Phases 47 worksheet complete (all audit rows `done`).
| Area | Finding | Keep-on fix |
|------|---------|-------------|
| WorldLog `{a}`/`{b}` | `special1`/`special2` match game replacers (kingdom/king, war kingdoms) | Accurate |
| Disasters | Policy rows; camera via paired `disaster_*` WorldLog | Accurate (no DisasterManager patch) |
| Wars / eras | `WarManager` + `WarInterestFeed`; `WorldAgeManager.startNextAge` | Accurate |
| Plots | Same label for new/complete/cancel | `EventReason.Plot` phase rewrite |
| Traits | JSON said `Trait: {id} ({a})`; live was `gains`/`loses` | Catalog labels → `{a} gains {id}` |
| Boat/meta harness | Crumb strings ≠ `EventReason` | Harness uses `EventReason.Boat` / `MetaNew` |
| Building / combat | Already `EventReason.BuildingEat` / `Fight` | Accurate |
Worksheet: `docs/event-trigger-audit.tsv` (all domains done).
## Family pack join false positive (2026-07-16)
`BehFamilyGroupJoin` returns `Continue` even when `getNearbyFamily` is null (no `setFamily`).
IdleSpectator used to Emit on any Continue → orange “joins a family pack” with no Family meta.
Gate: Emit only when `hasFamily()` after execute.
## Observe → confirm → emit (2026-07-16)
Shared pipeline: `EventOutcome` predicates + `EventObservation.Confirm*` before feeds.
Relationship Beh hooks (find_lover, family group new/join/leave) migrated first.
Rule: never treat `BehResult.Continue` as semantic success.
Docs: `docs/event-observation.md`.