Continued

This commit is contained in:
2026-06-04 00:06:18 -07:00
parent 8e9b4412ce
commit 5c11ff4fad
42 changed files with 1287 additions and 29 deletions
@@ -76,6 +76,33 @@ the return gate comes back to base AND starts Defend early; HUD reads phase/coun
(both gates baked with authoring defaults — the BaseGate worked only by coincidence); fixed with
`manage_components set_property` + verified via the `mcpforunity://scene/gameobject/{id}/component/...` resource.
**Stage 3 + 4 — build/structures (automation-ready) + turret + ability tiers + goal (DONE + validated).**
Adversarially design-reviewed first (3 critics incl. a dedicated M7-automation-readiness lens; caught the co-op
double-spend, the BaseAnchor-occupancy-cache contradiction, the persistence/offline-catch-up entanglement).
Built a GENERIC structure foundation per the operator's automation-forward directive: `PlacedStructure`
([GhostField] Type byte + server-only Cell/NextTick/LastProcessedTick — the tick fields are the M7 offline-
catch-up linchpin, baked now), a data-driven `StructureCatalog` buffer, occupancy DERIVED from live ghosts,
`BuildPlaceRequest` RPC with in-place ledger+occupancy commit (co-op-atomic), a hitscan `Turret` (reversed
`EnemyAISystem` → direct `DamageEvent`), ability tiers via a growing `StatModifier` (replace-by-SourceId), a
`GoalProgress` meter on the CycleDirector ghost, and HUD goal bar. **Validated headless + screenshots:** turret
placed at cell (10,10) (Ore 50→40, replicated); two same-tick requests for one cell → one structure + one
withdraw; built turrets auto-killed all Husks; ability damage 20→25→30 with the buffer bounded to one row;
goal increments per cycle (HUD "GOAL n/10"). **Persistence disk-writer DEFERRED to post-M7** (schema frozen +
tick fields baked so it's additive). Full architecture + the M7 contract: [[DR-014_M6_Build_Structures_Automation_Foundation]].
**Fiction reconciliation — "The Awakening Engine" (DONE).** A judge-panel workflow (3 framings: Aether-as-Blight /
Reawakened-Operator / unreliable-Voice → synthesis) reconciled the magic-tech/amnesia/voice/THEM premise with the
locked frontier-colony identity into ONE coherent, skin-only world (no mechanical rework). Core: **Aether is the
single substance** (cyan = claimed/ordered, orange-red = wild **Blight**, white-hot = conversion) powering every
machine/ability/structure and, uncontained, animating the colony's dead into **Husks** — it maps 1:1 onto the
built economy + loop. The base = the **Awakening Engine** (recharge pod restoring memory+power from deposited
Aether); the voice = **the Echo** (your future-self imprint); the goal = **the Wellspring** ("ENGINE CHARGE →
THEM"); phases re-named **Sortie/Siege/Forge**; field = **the Blightfield**; gates = **Wake-/Recall-Gate**.
Operator locked four forks: Echo **ambiguous**, THEM **kept open** (3 readings), corruption **atmospheric-only**,
**per-Sleeper** Echo. Rewrote [[Identity]], recorded [[DR-015_The_Awakening_Engine_Fiction_Adoption]], updated
[[Pillars]] — resolves the DR-013 fiction-reconciliation open item. Narrative payoff (memory beats + Echo VO,
subtitle-first) is net-new content for a later milestone, not a systems change.
See [[DR-013_M6_Aether_Cycle_Region_Split]] for the full architecture + validated evidence.
## Decisions
@@ -0,0 +1,69 @@
---
id: DR-014
title: M6 Stage 3/4 — generic build/structure foundation (automation-ready) + turret + ability tiers + goal; persistence deferred
status: accepted
date: 2026-06-03
tags:
- decision
- netcode
- building
- automation
- structures
- m6
- m7
permalink: gamevault/07-sessions/decisions/dr-014-m6-build-structures-automation-foundation
---
# DR-014 — M6 Stage 3/4: build/structure foundation (automation-ready) + turret + ability tiers + goal
## Context
M6 Stages 02 + the visibility pass delivered the playable core loop (region split, cycle phases, resources +
harvest, walk-in gates, HUD). Stage 3 is the **"spend" half** — the original M6 "build placement" — plus a
buildable defense (turret) and a resource sink for ability power; Stage 4 is the **goal meter** + (originally)
disk persistence. The operator directive: *"design these next systems with the scope that eventually we are
going to have some basic automation aspects in this"* — so the STRUCTURE model is the foundation the locked
**automation pillar** ([[Pillars]], M7 production chains) builds on, and must extend to
harvesters/fabricators/conveyors/recipes/tick-production/offline-catch-up **without a rework**. The operator
will make game-design decisions *after* the technical foundation is solid.
A 3-critic + synthesis design-review workflow (netcode / determinism / **M7-automation-readiness**) pressure-
tested the design pre-code and caught real issues (the co-op placement double-spend, the BaseAnchor-occupancy-
cache contradiction, the ability-modifier path, the persistence/offline-catch-up entanglement). See
[[2026-06-03_M6_Aether_Cycle_CoreLoop]].
## Decision
1. **Generic structure model (the automation foundation).** `PlacedStructure { [GhostField] byte Type; int2 Cell; uint NextTick; uint LastProcessedTick }` on an ownerless interpolated ghost (`RegionTag{Base}`, world-owned, runtime-spawned). Only **Type** replicates (a cheap byte for client visual branching); **Cell** is server-only (clients derive it from the replicated `LocalTransform` via `BaseGridMath.WorldToCell`). **`NextTick`/`LastProcessedTick`** are server-only raw `NetworkTick` values (`TickUtil.NonZero`-guarded) — the turret reuses `NextTick` as its fire cooldown NOW, and they are the **M7 linchpin**: the next-production-tick + the deterministic offline-catch-up baseline (`produced = floor((now LastProcessedTick)/period)`). These two tick fields are baked now because they are the timing identity that **cannot be reconstructed retroactively**. `StructureType` byte consts `{None=0, Turret=1}` with **Harvester=2/Fabricator=3/Conveyor=4 reserved** (free).
2. **Data-driven catalog.** A baked `StructureCatalog` singleton carrying a `StructureCatalogEntry { byte Type; Entity Prefab; byte CostResourceId; int CostAmount }` buffer (modeled on `AbilityPrefabElement` — prefab via `GetEntity`, never a blob). M7 adds a recipe column to the element additively (baked, not replicated → no format break). Authoring is flat fields for the single turret entry now (the MCP `component_properties` enum/array set is unreliable — see gotcha); the runtime buffer is already the extensible shape.
3. **Occupancy DERIVED, not cached.** `BuildPlaceSystem` (server-only, runs on a placement RPC) scans live `PlacedStructure` ghosts into a Temp `NativeHashSet<int2>` — structures are the source of truth (restart-/replay-order-safe). NOT a mutable buffer on the immutable baked `BaseAnchor`. Pure `BuildPlacementMath.CanPlace(anchor, occupied, cell)` (= `IsCellInPlot` && !occupied), unit-test-ready.
4. **Placement = `BuildPlaceRequest` RPC `{ byte StructureType; int CellX; int CellZ }`** (scalar cells, NOT `int2`, per the scalar-only RPC precedent). `BuildPlaceSystem` validates catalog→legality→occupancy→cost and **commits IN-PLACE** (`StorageMath.Withdraw` on the global ledger + reserve the cell in the set) so two same-tick requests for one cell can't both pass — the `StorageOpReceiveSystem` in-place idiom; only the `Instantiate` goes through the ECB. Plain server `SimulationSystemGroup` (not predicted).
5. **Turret = hitscan (reversed `EnemyAISystem`).** `Turret { float Range; int CooldownTicks; float Damage }`; `TurretFireSystem` snapshots living Husks, picks the nearest in its region within Range, and on a `NextTick` cooldown appends a direct `DamageEvent{Damage, SourceNetworkId=-1}` — reuses `HealthApplyDamageSystem` (already despawns at HP≤0), **no projectile/tunnelling/friendly-fire**. Self-gates (Husks exist only in Defend).
6. **Ability tiers via `StatModifier` (no new replicated component).** `AbilityUpgradeRequest` RPC → `AbilityUpgradeSystem` spends fixed Aether and grows a single `StatModifier{Target=Damage, Op=PercentAdd, SourceId=<sentinel>}` on the player (**replace-by-SourceId** so the `[InternalBufferCapacity(8)]` buffer stays bounded). `StatRecomputeSystem` folds it into `EffectiveAbilityStats.Damage` on both worlds — same path as `UpgradePickup`.
7. **Goal meter shipped; persistence WRITER deferred.** `GoalProgress { [GhostField] int Charge; int Target }` on the global CycleDirector ghost, **single-writer** in `CyclePhaseSystem` (increment per completed cycle); HUD shows a bar. The disk-persistence writer is **deferred to a post-M7 slice** (deepest M7 entanglement; in-session-only state, consistent with DR-008's deferral) — but the M7-additive surface is locked NOW: the tick fields (§1), the data-driven catalog (§2), occupancy-derive (§3), and a **frozen save schema** `{ cycleNumber, savedServerTick, ledger[] absolute, structures[]{type,cell,nextTick,lastProcessedTick, RESERVED io-rows slot}, abilityModifiers[] }` so M7 needs no format version bump.
## Consequences
- **Validated at runtime on 6.4.7 (single in-editor client), headless via `execute_code` + screenshots:**
- **Build:** turret placed at cell (10,10) → world (5.5,1,5.5) (correct snap, baked Scale preserved), Ore 50→40, replicated to the client.
- **Co-op atomicity:** two same-tick `BuildPlaceRequest` for one cell → exactly **one** structure + **one** withdraw (Ore 40→30) — the named double-spend blocker is fixed.
- **Turret defense:** built turrets auto-acquired + **killed all spawned Husks** (hitscan `DamageEvent` → death → despawn); both turrets' `NextTick` cooldowns advanced independently.
- **Ability tiers:** damage 20→25→**30** across two upgrades; the `StatModifier` buffer stayed at **one** row (replace-by-SourceId grew 0.25→0.5); Aether 50→30→10.
- **Goal:** `GoalProgress.Charge` increments per cycle; HUD reads **"GOAL n / 10"** from the replicated value.
- All systems visible in the HUD (phase/countdown/cycle/resources/location/gate-hint/goal/husks/health). Console clean of code/Burst/RPC errors.
- **No new asmdefs.** New code under `…/Building/` (Simulation/Server/Client) + `…/World/GoalProgress.cs`; reuses `EnemyAISystem`/`EnemyAIMath` (turret), `StorageMath`+global ledger (cost), `BaseGridMath` (grid), `StatModifier`/`StatRecomputeSystem` (tiers), the runtime-ghost + duplicate-`UpgradePickup` prefab recipe, the byte-RPC pattern.
- **This is the M7 contract.** Production chains add: a recipe column on `StructureCatalogEntry`; per-structure `StorageEntry` I/O buffers on harvester/fabricator prefabs only; a `StructureProductionSystem` ticking recipes off `NextTick` + computing offline catch-up off `LastProcessedTick`; conveyor adjacency/topology — all **additive** (new `StructureType` codes + components), no migration of the turret ghost or the frozen schema.
## Open / deferred (mostly M7)
- **Disk-persistence writer + `BaseRestoreSystem`** — post-M7 slice. Restore replays structures through a charge-free placement path (extract from `BuildPlaceSystem` then), restores the ledger as an ABSOLUTE set (no re-`Withdraw`), and **rebases ticks** (saved ticks are relative to the old `ServerTick` origin — store as deltas-from-`savedServerTick`).
- **Per-structure I/O buffers, recipe field, `StructureProductionSystem`, conveyor/harvester/fabricator + adjacency graph** — M7 (type-codes reserved; tick fields baked).
- **Ghost-relevancy ceiling** — the unspawned/transiting "sees-everything" fallback in `RegionRelevancySystem` + its O(ghosts×connections)/tick scan become load-bearing at M7 structure counts; redesign deferred (document the N×M ceiling).
- **Client build UX** — currently `B` builds a turret at the local player's cell + an editor static; a mouse-cell-preview build mode (reusing `AimMath`/`AimReticleSystem`) is a polish pass.
- **Visuals** — turret reuses the pickup glow material; per-type structure + per-type resource-node colors are a polish pass (operator's game-design decisions come after the foundation).
## Build gotcha recorded this session
- **`manage_gameobject create` `component_properties` (and `manage_prefabs modify_contents` `component_properties`) SILENTLY DROP enum + Vector3 + nested-array fields** (object-refs + simple scalars apply; baked authoring enums/arrays stay at C# defaults). Set those via a follow-up `manage_components set_property` (with a `properties` dict for scalars / `value` for one) and VERIFY via the `mcpforunity://scene/gameobject/{id}/component/{Type}` resource. For authoring with enum/array config, prefer **flat scalar fields + byte consts in the baker** over inspector enums/arrays. (First hit on the M6 gates; re-confirmed on the structure catalog.)
Builds on [[DR-013_M6_Aether_Cycle_Region_Split]] (the cycle/ledger/CycleDirector + region split this extends), reuses the patterns from [[DR-008_M5_HomeBase_BaseLayer_Storage]] (grid, RPC, runtime ghost) / [[DR-009_GameFeel_Identity_FirstBlood]] (enemy AI, DamageEvent) / [[DR-004_M3_DataDriven_Abilities_Modifiers]] (StatModifier). Serves the automation pillar in [[Pillars]].
@@ -0,0 +1,58 @@
---
id: DR-015
title: The Awakening Engine — fiction reconciliation (Aether unifier + Sleeper/Echo/THEM spine), skin-only
status: accepted
date: 2026-06-03
tags:
- decision
- identity
- narrative
- fiction
- vision
- m6
permalink: gamevault/07-sessions/decisions/dr-015-the-awakening-engine-fiction-adoption
---
# DR-015 — The Awakening Engine: fiction reconciliation (skin-only)
## Context
The M6 design pass introduced a new narrative direction (amnesiac protagonist; a voice in your ear; reach
"THEM by any means"; a magic + sci-fi world of "magic-powered machines"; harvest magical energy to build/charge;
procedural expeditions; periodic base defense; the "Awakening Engine" lean) that sat alongside — and partly in
tension with — the **locked frontier-colony identity** ([[Identity]] / [[DR-009_GameFeel_Identity_FirstBlood]]:
industrial sci-fi outpost, the Blight, Husks, cyan/orange palette, automation). [[DR-013_M6_Aether_Cycle_Region_Split]]
explicitly deferred the **fiction reconciliation** pending operator sign-off. By the time of this decision the
codebase had real fiction-bearing systems built + validated (Aether/Ore/Biomass resources, the Aether Cycle
loop, the base↔expedition region split, walk-in gates, Husk waves, buildable turrets, ability tiers, a goal
meter toward THEM) — so the fiction had to reconcile into **one coherent world that reads onto everything built**
without contradicting the locked **mechanical** pillars ([[Pillars]]).
A judge-panel design workflow generated three reconciliation framings (Aether-as-Blight / Reawakened-Operator /
unreliable-Voice) and synthesised the strongest, honouring the operator's chosen "Awakening Engine" lean. The
operator then locked four narrative forks. See [[2026-06-03_M6_Aether_Cycle_CoreLoop]].
## Decision
**Adopt "The Awakening Engine" as the game's fiction — a pure SKIN-ONLY reconciliation (no mechanical rework; DR-009/013/014 code untouched).** The full identity is in [[Identity]]. The reconciliation rests on five moves:
1. **Aether is the single unifying substance — the only fantastical thing.** Raw magical energy the colony's mundane sci-fi machines were built to burn (abilities, turrets, gates, fabricators, the pod all run on it like electricity). It has **two states the existing palette already encodes**: cyan = Aether claimed/ordered (you, owned structures, the Engine); orange-red = Aether gone wild (the **Blight**, the **Husks**, corrupted nodes); white-hot = the conversion. This maps 1:1 onto the built economy: **Aether** (id 1) charges/tiers abilities + structures, **Ore** (id 2) builds, **Biomass** (id 3) is the automation feedstock. The palette is **re-labelled, not repainted** — no art change to adopt.
2. **The Blight is uncontained Aether overcharge** animating the colony's own dead drones/machinery/fauna into Husks — tying the threat to the resource economy instead of leaving it unexplained.
3. **The base = the Awakening Engine** (a hibernation/restoration pod): deposited Aether restores the Sleeper's memory **and** power together (why you start feeble and the loop makes you whole — capability and comprehension on one curve), powers the colony, and banks goal charge. The single shared ledger is save-state + XP + fuel at once. (`BaseAnchor` ⇒ "the Anchor".)
4. **The spine = a Sleeper, the Echo, and THEM.** You wake amnesiac (Aether-scrambled); the **Echo** is an imprint of your own future self in the Aether, guiding you toward THEM. The Aether Cycle reads as the Engine's recharge rhythm (phases **Sortie / Siege / Forge**); the incursion timer = harvesting is "loud" (a beacon — *local concentration drops, global attention spikes*, so intuition never fights the systems); the regions = the Engine's Aether-bubble vs **the Blightfield**; gates = **Wake-Gate / Recall-Gate**; the goal meter = **"ENGINE CHARGE → THEM"** → the **Wellspring**.
5. **Four narrative forks locked by the operator:** (a) **the Echo is AMBIGUOUS** (never fully trusted; needs a payoff beat at the goal); (b) **THEM stays OPEN** — three live readings (Wellspring / lost crew / the Echo), arbitrated by a later milestone; (c) **Aether-as-corruption is ATMOSPHERIC ONLY** — no stat penalty for upgrading (honours the skill-expression pillar); a literal corruption meter is possible future net-new design, not part of this; (d) **PER-SLEEPER Echo** in co-op (each player's own future-self).
## Consequences
- **Resolves the [[DR-013_M6_Aether_Cycle_Region_Split]] "fiction reconciliation — operator sign-off" open item.** [[Identity]] is rewritten as "The Awakening Engine (Aether-colony sci-fi)"; the frontier colony is recontextualised, not replaced. The lexicon (Sleeper / the Echo / the Wellspring / the Awakening Engine / the Blightfield / Aether Gates / the Aether Cycle's Sortie-Siege-Forge) is the canonical naming, each anchored to a code component.
- **Reads onto every built system with NO code change** (verified in the synthesis coherence pass): resources ⇄ ability-tier StatModifier path + structure costs + reserved M7 automation codes; cycle ⇄ `CyclePhaseSystem`/`CycleState`; regions ⇄ the GhostRelevancy split; gates ⇄ `ExpeditionGate`; goal ⇄ `GoalProgress`; turret ⇄ the hitscan defense; Husks ⇄ `WaveSystem`; base ⇄ `BaseAnchor`/grid/ledger; palette ⇄ the DR-009 emissive look.
- **Violates no locked pillar.** The voice / memory beats / THEM reveal are **client presentation + content**, never the predicted sim (observe-only, like the existing HUD/VFX). The recharging pod stays a **persistent anchor** (death = respawn), not a roguelike reset.
- **What this adoption is NOT:** any *mechanic* the fiction implies — a literal corruption/risk meter, memory-gated abilities, an authored ending payoff — is **net-new design/content for a later milestone**. Adopting the identity is copy + light-asset only. The goal meter currently fills (`Target=10`) with no authored payoff; **staged memory beats + the Echo's voice lines (playable as subtitles first) are the net-new narrative content** tracked for a future milestone.
## Open / deferred
- **Narrative payoff + the Echo's voice/memory-beat content** — a later narrative milestone (writing + light/optional VO; subtitle-first). The three THEM readings are arbitrated then.
- **Art additive (none required now):** the Awakening Engine hero set-piece + charge readout, the cyan/orange Aether-veining motif, gate portal shimmer, node glow, memory-fragment motes, the Echo voice + Aether hum.
- **Pillars.md** [[Identity]] note updated to point at the Awakening-Engine framing.
Evolves [[DR-009_GameFeel_Identity_FirstBlood]] (the frontier-colony identity); skins the locked [[Pillars]]; reads onto [[DR-013_M6_Aether_Cycle_Region_Split]] + [[DR-014_M6_Build_Structures_Automation_Foundation]]. Skin-only — no system in M1M6 changes.