First-run onboarding: contextual coach-marks + How-to-Play card + dev replay toggle
Teaches the deep, interlocking loop — especially the inverted win condition (you win by CLEARING EXPEDITIONS, not by surviving base sieges; DR-042/DR-043). - OnboardingSystem: client-only observe-only PresentationSystemGroup overlay (own UIDocument @ sortingOrder 60), soft-gated 10-beat coach-mark sequence with a world-space ▶ pointer; never mutates sim / never destroys a ghost. - OnboardingStepMath: pure, unit-tested step machine (snapshot + IsSatisfied + scheme-aware prompts + pointer kinds + persisted-mask helpers). - HowToPlayPanel: tabbed reference card (Controls / The Loop / Build / Threats / Win-Lose), reachable from the main menu and the pause overlay. - Per-client client-local state in GameSettings (TutorialHints + OnboardingMask bitmask, additive) — a Join client keeps its own; a host save-wipe never re-teaches. Settings toggle + menu "Replay Tutorial". - Dev "Force Each Launch" toggle (GameSettings.ForceOnboardingEachLaunch): SettingsService.Boot wipes the mask + forces hints on in-memory every launch so the tutorial always replays fresh. - HudSystem suppresses its own location hint while onboarding is active (single prompt voice), via OnboardingState + [UpdateAfter(OnboardingSystem)]. Validated green: 20/20 EditMode; Play smoke confirmed overlay render, clean U+25B6 pointer glyph, no system sort-cycle, and the force-wipe end-to-end. Docs: DR-043 + session log; reusable lesson archived in the build-gotchas note. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -384,3 +384,14 @@ Added the **EB-2 felt spend ★** bullet ([[DR-033_EB2_Felt_Spend_Charge_Economy
|
||||
- **Resource-gated ability tiers / buffs ([[DR-026_Inventory_Equipment_Progression_Foundation]]):** dropped the "(replace/clear-by-SourceId → bounded buffer; folds into … both worlds)" mechanism phrasing → kept "reuse `StatModifier` … `StatRecomputeSystem`→`EffectiveAbilityStats`". (Note: this bullet's `GoalProgress.Charge` is the **goal-meter** charge, unrelated to EB-2's `ResourceId.Charge` turret ammo.)
|
||||
- **PlacedStructure ([[DR-014_M6_Build_Structures_Automation_Foundation]]):** dropped the "(turret reuses `NextTick` as fire cooldown; they're the offline-catch-up linchpin)" + "Data-driven `StructureCatalog` buffer" asides → kept the field layout + the bake-the-tick-fields rule + the DERIVED-occupancy rule.
|
||||
- **M7 Automation ([[DR-020_M7_Automation_Production_Chains]]):** dropped the "server-only `MachineInput`/`MachineOutput`" restatement and noted `Fabricator` is now LIVE on the palette via EB-2 while `Harvester`/`Conveyor` stay trimmed (code intact).
|
||||
|
||||
## 2026-06-29 — First-run onboarding validated (DR-043); CLAUDE.md line kept archive-only (file at cap)
|
||||
|
||||
DR-043's first-run onboarding shipped + Play-validated green (20/20 EditMode; live Play: a "veteran" full mask wiped by the dev toggle → tutorial replayed from Welcome, the `▶` pointer = a clean U+25B6 triangle (not tofu), no system sort-cycle from `HudSystem`'s new `[UpdateAfter(OnboardingSystem)]`). The deferred CLAUDE.md gotcha is parked HERE, not inline — the file sits at 40884/40960 B (76 B headroom) and the lesson is a one-time client-overlay pattern, not a high-recurrence hazard, so by CLAUDE.md's own "hottest rules only" rule it doesn't earn inline space. The lesson:
|
||||
|
||||
- **First-run / "has-played" / per-player UI flags belong in client-local `GameSettings` (settings.json), NEVER the host-only `SaveData`** — in co-op a Join client never sees the host save, and a host save-wipe must not re-teach. Progress = a completed-step **bitmask** in settings. Additive field, **0-default → no version bump** (a missing field deserializes to 0 = the safe off/fresh default; bumping `CurrentVersion` instead re-activates the migration path for every existing file — see the 2026-06-28 migration regression below/in DR-043).
|
||||
- **Onboarding overlay = a client-only observe-only `PresentationSystemGroup` `SystemBase`** (same constraints as `HudSystem`: never mutates sim, never destroys a ghost, reads replicated state once/frame) owning its own runtime `UIDocument` (sortingOrder 60 — above HUD 50, below pause 100, root `pickingMode=Ignore`). A static `OnboardingState.Active` (reset on `SubsystemRegistration`) lets `HudSystem` (`[UpdateAfter(OnboardingSystem)]`) blank its own location hint → a single prompt voice. Auto-suppress for veterans/co-op falls out of **ABSOLUTE count checks** (turret/fabricator count ≥1 satisfies on entry at an already-built base).
|
||||
- **Dev "Force Each Launch" toggle:** `GameSettings.ForceOnboardingEachLaunch` → `SettingsService.Boot` (a `RuntimeInitializeOnLoadMethod` that runs each editor Play-enter / built-player launch) wipes the mask + forces hints on **IN-MEMORY** (NOT written back; the system re-persists progress as the player advances, and the next launch wipes again). Validated by seeding `OnboardingMask=int.MaxValue` (veteran) + `force=1` then Play → mask observed back at 0/fresh.
|
||||
- **Forcing a specific step in a live Play smoke without input:** the step machine's `_step`/`_mask`/`_stepInit` are private — set them via reflection on `world.GetExistingSystemManaged(typeof(OnboardingSystem))`; remember an MCP `screenshot` can leave the editor **paused** (`EditorApplication.isPaused`), so the field write won't surface until you unpause. Pause + reflection-restyle the `_pointer` Label is also how to get a clean glyph capture (OnUpdate would otherwise overwrite the style next frame).
|
||||
|
||||
Net-zero: archive-only add (no CLAUDE.md bytes changed), so no inline condensation needed.
|
||||
|
||||
Reference in New Issue
Block a user