Files
Project-M/Docs/Vault/02_Game_Design/Systems_Index.md
T
kronic c87dd04e87 Docs: truth pass over CLAUDE.md, DRs and the MOCs (audit M13/M14/M15/M16/M19/L11/L12)
CLAUDE.md — it is loaded every session, so its errors cost the most:
- M13: it still said "No world code until the world-model spike passes
  its design review" while DR-049 has been `accepted` since 07-13 and the
  roadmap says PASSED. A hard contradiction between the two authority
  docs, with CLAUDE.md winning by being always-loaded — plausibly part of
  why Phase 2 never started. Now points at the build spec, warns it needs
  re-anchoring, and links the parked engine fork.
- Records the H2 hazard as a rule: a socket whose SparkId is missing from
  the baked AbilityDatabase silently reads Damage/Range/Cooldown 0.
- L12: the asmdef table was stale on all four rows; also notes the fifth
  compilation unit (Scripts/Editor has no asmdef) and that tests now
  reference Authoring.
- M19: per-machine setup — the machine-loss recovery checklist — was
  missing Blender + blendermcp (which the committed /art-dev skill
  hard-requires) and ctx7, and claimed a ${CLAUDE_PROJECT_DIR} in
  .mcp.json that isn't there.
- Structures / harvest / persistence / BefourStudios bullets rewritten:
  they described deleted subsystems.
- Net-zero rule honoured: the additions pushed it to 41,057 (over the
  40,960 hard limit), so two long art-pipeline bullets moved verbatim to
  the gotchas archive under a dated heading. 39,661 now — inside the
  file's own 39,936 soft ceiling.

- M14: DR-053 files the Bevy fork as PARKED. The guide was reachable from
  NOTHING — one file, zero inbound links, and its gating instruction
  lived only in machine-local memory, which CLAUDE.md explicitly forbids
  as a sole home. It now has a committed home and inbound links.
- DR-054 records this purge, including the three files that were in the
  delete set until their consumers were checked and turned out to be
  load-bearing.
- M15: 13 superseded DRs re-statused with `superseded_by`. Only DR-002
  carried the field before, though the project's own template prescribes
  it.
- M19: Home.md stated three wrong facts (latest DR, latest session, date)
  — refreshed. Pillars.md and Systems_Index.md now carry warning banners:
  both assert things the purges falsified, and Systems_Index still claims
  to be "the accurate map of what exists in code today".
- L11: the 4 genuinely-broken wikilinks fixed (typo'd DR filenames). Zero
  broken vault-internal links now.
- Session log written — the last three commits before today had none,
  breaking the protocol's own bookend rule.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 13:44:18 -07:00

84 lines
18 KiB
Markdown
Raw 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.
---
tags:
- design
- index
updated: 2026-06-08
permalink: gamevault/02-game-design/systems-index
---
> [!warning] **AUDIT NOTE 2026-08-07 — this index is NOT the accurate map of current code.**
> Last genuinely updated 2026-06-08. Since then DR-051 and [[DR-054_Audit_Purge_2026-08]] deleted the M7
> automation chain, the run/room lifecycle, meta shop, boons, build/structures, storage,
> inventory/equipment, the enemy variants and the boss; and the LANTERN socket/Spark/relevancy systems it
> never mentions are the live combat model. **Read the code, or [[DR-054_Audit_Purge_2026-08]], not this.**
# Systems Design — Index
> [!info] LANTERN pivot (2026-07-13, [[DR-048_Lantern_Adoption_Full_Pivot]]).
> The systems catalogued below are the **Awakening-Engine / Co-op-Hades-era spine**. They are **not deleted** —
> under LANTERN they are **quarry**: each has a keep / rework / mothball verdict in
> [[Lantern_Strip_Mothball_Inventory]]. This index stays the accurate map of *what exists in code today*; the
> inventory is the map of *what happens to it*. New LANTERN systems get sections here as they land.
One design doc per gameplay system, linked here. Each should state: purpose, components (`IComponentData`), systems (`ISystem`), netcode shape (ghost? predicted vs interpolated? inputs / RPCs), and open questions.
## Systems
### M1 — Player (twin-stick predicted movement) · [[2026-05-30_M1_Player_Slice]]
- **Components** (`ProjectM.Simulation`): `PlayerTag`; `PlayerInput` (IInputComponentData — `float2` Move/Aim, `[GhostField]`, flows via AutoCommandTarget); `PlayerMoveStats` (baked tunables); `PlayerFacing` (`[GhostField]` Direction); `PlayerSpawner` (baked prefab singleton); `GoInGameRequest` (IRpcCommand).
- **Systems:** `PlayerMoveSystem`, `PlayerAimSystem` (`PredictedSimulationSystemGroup`, `.WithAll<Simulate>()`, deterministic — `SystemAPI.Time.DeltaTime` only); `PlayerInputGatherSystem` (client, `GhostInputSystemGroup`); `GoInGameClientSystem` (client) / `GoInGameServerSystem` (server — spawns the owner-predicted ghost, stamps `GhostOwner`, `LinkedEntityGroup` auto-despawn).
- **Netcode shape:** player = **owner-predicted** ghost; client sends input only; server is authoritative. Status: **code-complete + EditMode-verified**; live runtime blocked by [[DR-002_Unity66_Alpha_Netcode_Transport]].
### M2 — Combat (predicted projectile, server damage) · [[2026-05-31_M2_Combat]]
- **Components** (`ProjectM.Simulation`): `Health` (`[GhostField]` Current; baked Max); `HitRadius`; `DamageEvent` (IBufferElementData); `AbilityStats` (auto-target range/cone, cooldown ticks — baked); `AbilityCooldown` (`[GhostField]` NextFireTick); `Projectile` (`[GhostField]` Direction + SpawnId; baked Speed/Damage/Range); `ProjectileSpawner` / `TrainingDummySpawner` (baked singletons); `TrainingDummyTag`. `PlayerInput` gains `Fire` (`InputEvent`).
- **Systems:** `AbilityFireSystem` (predicted; `IsFirstTimeFullyPredictingTick`-gated predict-spawn; server branch applies `AutoTarget`); `ProjectileMoveSystem` (predicted); `ProjectileClassificationSystem` (client; predicted-spawn match by `SpawnId`; **non-Burst**); `ProjectileDamageSystem` (server; **swept** segment-vs-sphere hit); `HealthApplyDamageSystem` (server; DamageEvent → Health, dummy death-despawn); `TrainingDummySpawnSystem` (server; one-shot). Input: `PlayerInputGatherSystem` rewritten as managed `SystemBase` over the generated `ProjectMInput` action-map wrapper.
- **Netcode shape:** projectile = **owner-predicted** ghost, client predict-spawns + classifies against server truth by `SpawnId=(ownerNetId<<16)|absoluteFireCount`; **auto-target & damage server-authoritative**; `Health.Current`/`Projectile.Direction` replicate. Status: **foundation built + runtime-validated** (server loop + replication); live keypress-fire pending an interactive test. Decisions: [[DR-003_M2_Combat_Netcode_Architecture]].
### M3 — Data-driven abilities & modifiers · [[2026-05-31_M3_Data_Driven_Abilities]]
- **Components** (`ProjectM.Simulation`): `AbilityDatabase` (singleton `BlobAssetReference<AbilityDatabaseBlob>`; `AbilityDefBlob`/`CharacterStatsBlob` keyed by `AbilityId`/`CharacterId` byte) + `AbilityPrefabElement` (companion entity-ref buffer for projectile prefabs); `AbilityRef` (`[GhostField]` id) / `CharacterStatsRef`; `StatModifier` (replicated `[GhostField]` buffer, `OwnerSendType.All`, raw-byte `StatTarget`/`ModOp`); `EffectiveAbilityStats` / `EffectiveCharacterStats` (derived, not replicated); `UpgradePickup` / `UpgradePickupSpawner`. `StatMath` (pure fold). **Removed** M2's `AbilityStats` / `PlayerMoveStats`.
- **Systems:** `StatRecomputeSystem` (predicted, `[UpdateBefore]` Aim/Move; folds blob base + modifier buffer → `Effective*` **every tick** — rollback-correct); `AbilityFireSystem` rerouted (effective stats + prefab-by-id + snapshot-at-fire); `PlayerMoveSystem` → effective move; `UpgradePickupSpawnSystem` / `UpgradePickupSystem` (server; overlap-grant via `AppendToBuffer`); `DebugModifierInjectionSystem` (editor-only, server world); `HealthApplyDamageSystem` clamps to effective MaxHealth. Authoring: `AbilityDefinition`/`CharacterStatsDefinition` SOs + `AbilityDatabaseAuthoring` blob baker.
- **Netcode shape:** definitions = baked config (not replicated, identical both worlds); modifiers = **replicated ghost buffer** on the player → both worlds recompute identical effective stats (prediction-correct, validated under tick-batching); pickup = **interpolated** server-authoritative ghost. Status: **built + runtime-validated** (EditMode 38/38). Decisions: [[DR-004_M3_DataDriven_Abilities_Modifiers]].
### M5 — Home base: base-layer + shared storage · [[2026-06-02_M5_HomeBase_BaseLayer]]
- **Components** (`ProjectM.Simulation/HomeBase`): `BaseAnchor` (baked singleton — `AnchorPos`, `GridOrigin`, `CellSize`, `int2 GridDims`; flat/blittable, no entity refs); `BaseGridMath` (pure static — WorldToCell/CellToWorld/IsCellInPlot/IsPointInPlot/ClampCell/PlotCenter; corner-origin, center-returning, half-open, floor); `StorageEntry` (`[GhostField]` buffer — `ushort ItemId`, `int Count`); `SharedStorageContainer` (tag); `StorageSpawner` (baked singleton — prefab + `int2 Cell`); `StorageOpRequest` (`IRpcCommand` — byte Op/ItemId/Count) + `StorageOp` consts; `StorageMath` (deposit-merge / withdraw-clamp-drop, unit-tested).
- **Systems:** `SharedStorageSpawnSystem` (server one-shot — instantiate the container ghost at `CellToWorld(cell)`, destroy spawner); `StorageOpReceiveSystem` (server `SimulationSystemGroup`, NOT predicted — apply the RPC to the singleton container's buffer via `StorageMath`); `StorageOpSendSystem` (client managed `SystemBase` — E/Q keyboard + editor-only `Deposit`/`Withdraw` statics → `StorageOpRequest` RPC). `GoInGameServerSystem` re-rooted onto `BaseGridMath.PlotCenter(BaseAnchor)` (with a `TryGetSingleton` fallback).
- **Netcode shape:** base config = **baked, ghost-free, identical both worlds** (not replicated). Storage container = **ownerless interpolated** server-spawned ghost; its `StorageEntry` buffer is a `[GhostField]` (no `OwnerSendType`/`GhostOwner`) so server mutations replicate to all clients. Deposit/withdraw = **server-authoritative `IRpcCommand`** resolved against the single container singleton, applied outside the predicted loop (no rollback double-apply). Status: **built + runtime-validated** (server == client buffer; EditMode 62/62). Decisions: [[DR-008_M5_HomeBase_BaseLayer_Storage]]. M6 (grid placement) + M7 (production) build on `BaseGridMath` + the runtime-ghost-into-cell spawn path.
### M5.5 — Game feel & identity ("First Blood") · [[2026-06-02_GameFeel_Identity]]
- **Components** (`ProjectM.Simulation`): `EnemyTag`/`EnemyStats`/`EnemyAttackCooldown`/`EnemySpawner` + pure `EnemyAIMath`; `Dead` (enableable, **derived**) + `RespawnState` + pure `RespawnMath`; `TickUtil.NonZero` (the cooldown 0-sentinel guard).
- **Systems:** `EnemyAISystem` (server-only, **plain `SimulationSystemGroup`**, `[UpdateAfter(PredictedSimulationSystemGroup)]` — the interpolated Husk ghost seeks the nearest living player, deals contact `DamageEvent`) + the `WaveSystem` threat director (escalating waves of Husk variants Grunt/Swarmer/Brute — replaced the flat sustain; see [[2026-06-02_GameFeel_Deepening]]); `HealthApplyDamageSystem` +`EnemyTag` death; `PlayerDeathStateSystem` (both worlds, predicted — derives `Dead` from `Health<=0`, gates movement/aim/fire via `.WithDisabled<Dead>()`); `PlayerRespawnSystem` (server-only — schedule + refill + reposition). Client presentation (managed, `PresentationSystemGroup`): `CombatFeedbackSystem` (damage numbers / VFX / procedural SFX / camera shake by edge-detecting replicated Health) + `HudSystem` (code-built uGUI health / cooldown / threat / DOWNED) + `PrototypeCameraRig.AddShake`.
- **Netcode shape:** Husk = **ownerless interpolated** server-driven ghost (stock `LocalTransform` replication; `Health` `[GhostField]`); `Dead` = **local derived** enableable (NOT replicated — pure function of replicated Health); juice/HUD **observe** replicated state only (client world, never the sim). Identity: [[Identity]] (sci-fi frontier colony). Status: **built + runtime-validated** (Husks spawn(6)/replicate/chase/strike; death→respawn loop; HUD; emissive dark-sci-fi look); EditMode **74/74**. Decisions: [[DR-009_GameFeel_Identity_FirstBlood]].
### Post-M8 — World-space cohesion pass (clearing + buildable Wall/Pylon + Blightfield dressing) · [[2026-06-04_World_Space_Cohesion_Pass]]
- **Components** (`ProjectM.Simulation`): `BlightClutter` (ownerless-interpolated ghost — `[GhostField]` Remaining+Variant, server-only ScrapResourceId/ScrapPerHit; RegionTag{Expedition} **sibling of `ResourceNode`**) + `ClutterFieldSpawner` (optional baked singleton); `StructureType.Wall=5`/`Pylon=6` (byte consts; 24 are now in use by the M7 production machines — Harvester/Conveyor/Fabricator).
- **Systems:** `ResourceHarvestSystem` **unified** to sweep nodes AND clutter in one best-target loop (required — two separate sweeps would double-destroy an overlapping projectile at ECB playback; `math.max(1,(int)yield)` guards the immortal-sink); `ExpeditionFieldSystem` scatters/clears clutter beside the node field (distinct seed); `BuildPlaceSystem` **unchanged** (already type-generic) — new generic `StructureAuthoring{byte Kind}` + two additive `StructureCatalog` rows + `BuildSendSystem` V/N keys. Client: `WorldFeedbackSystem` (observe-only `PresentationSystemGroup` — chip/shatter juice, proximity-gated so region-transit stays silent) + live-tunable `WorldFeelConfig`.
- **Netcode/world shape:** clutter = ownerless interpolated, region-scoped via GhostRelevancy (like nodes); Wall/Pylon = ownerless interpolated structure ghosts (`PlacedStructure.Type` byte-additive → **no re-bake**); Wall carries a `PhysicsCollider` (CC-blocking), Pylon cosmetic. Visual: `M_Aether_Wild`/`M_Aether_Ordered` palette materials + a classic-URP Blightfield rock basin in `Game.unity` (relief via props, **no terrain**). Status: **built + validated** (EditMode **142/142**; in-editor Play introspected via execute_code; 4-lens adversarial review → 4 findings fixed). Decisions: [[DR-018_World_Space_Cohesion_Pass]] (builds on the M6 region split / build pipeline + M8 persistent base — those systems are documented in their DRs).
### M7 — Automation: self-running production chains (Harvester → Conveyor → Fabricator) · [[2026-06-05_M7_Automation]]
- **Components** (`ProjectM.Simulation/Automation`): `Harvester{byte ResourceId;int Yield;int PeriodTicks}` (fixed-yield generator), `Fabricator{byte In/OutResourceId;int In/OutAmount;int PeriodTicks}` (recipe → global ledger), `Conveyor{byte Direction;int PeriodTicks}`; server-only buffers `MachineInput`/`MachineOutput` (DISTINCT types, NO `[GhostField]`); `ConveyorItem` (enableable, baked DISABLED); `RuntimePlacedTag` (player-built = persistable, single source of truth). Pure math: `ProductionMath` (NeedsInit/CyclesDue/RemainingTicks/RestoreNextTick), `ConveyorMath` (DirOffset/CellKey/ResolveMoves — deterministic), `MachineSlotMath` (byte-id deposit/withdraw, the non-replicated twin of `StorageMath`).
- **Systems** (`ProjectM.Server/Automation`, server-only, plain `SimulationSystemGroup` ordered chain `[UpdateAfter(PredictedSimulationSystemGroup)]` → Harvester → Conveyor → Fabricator): `HarvesterProductionSystem` (→ own `MachineOutput`), `ConveyorTransportSystem` (pull adjacent upstream output → `ResolveMoves` → settle `ConveyorItem`/deposit sink `MachineInput`), `FabricatorProductionSystem` (input-limited recipe → global ledger). Persistence: `SaveStructureScan` (shared autosave/quit scan of `RuntimePlacedTag` structures), one-shot `BaseRestoreSystem` (charge-free replay), `SaveData` v2 (`StructureSave[]` + flat `StructureIoRow[]`, cooldown as epoch-independent REMAINING ticks). Build: `BuildPlaceSystem` stamps conveyor `Direction`+`RuntimePlacedTag`; `BuildSendSystem` H/F/C keys + `[`/`]` rotate; `BuildPlaceRequest` +`byte Direction`. Authoring: 3 MonoBehaviours + bakers; `Harvester/Fabricator/Conveyor.prefab` (dup `Turret.prefab`) in the catalog.
- **Netcode shape:** production is **SERVER-ONLY** (no prediction/rollback); results replicate ONLY via the global `StorageEntry` ledger ([GhostField], untagged CycleDirector ghost) + `PlacedStructure.Type`; per-machine I/O buffers never hit the wire. "Offline catch-up" = within-session tick math + a preserved stockpile across quit (NO wall-clock minting). Status: **built + runtime-validated** (real netcode worlds, server==client; quit→Continue restore; EditMode **190/190**). Decisions: [[DR-020_M7_Automation_Production_Chains]] (builds on [[DR-014_M6_Build_Structures_Automation_Foundation]]).
### Presentation — UI Toolkit HUD + build-palette HUD · [[2026-06-05_HUD_Rework_UITK_BuildPalette]]
- **Client (presentation/UI):** `HudSystem` rewritten on **UI Toolkit** (client `PresentationSystemGroup`, observe-only) — a runtime UIDocument (shared `RuntimePanelSettings`, sortingOrder 50 behind the pause overlay; root `pickingMode=Ignore`) in the Aether-cyan `MenuUi`/`HudUi` palette: health+cooldown bars, HUSKS, phase/cycle/countdown + resources + location + goal, DOWNED overlay, and a **build-palette bar** lazy-built from the client `StructureCatalog` (cost + affordability + selection highlight). `BuildPaletteState` (client-local), `BuildPreviewMath` (pure validity, unit-tested), `BuildSendSystem` (cursor→cell ground **ghost** green/red, left-click place, right-click/Esc cancel, [/]/R rotate; self-contained guards), `PlayerInputGatherSystem` (fire suppressed in build mode), `PauseMenuController.Open` (pause guard). `CombatFeedbackSystem` damage numbers re-skinned to the Aether/Blight palette.
- **Netcode shape:** none new — UI observes replicated state (ledger/cycle/goal/player ghost) + the client-baked catalog; placement reuses the M7 `BuildPlaceRequest` RPC (server-authoritative). Status: **built + runtime-validated** (UITK render + palette + selection + ground ghost + placement; EditMode **194/194**). Decisions: [[DR-021_HUD_UITK_BuildPalette]] (builds on [[DR-019_Frontend_Menu_Settings_Saves_Build]] UITK frontend + [[DR-020_M7_Automation_Production_Chains]] structures).
### Items · Inventory · Equipment — Phase 0 (backbone) + Phase 1 (slots) · [[2026-06-08_Inventory_Equipment_Progression_Phase0]] · [[2026-06-08_Equipment_Slots_Phase1]]
- **Components** (`ProjectM.Simulation/Items`): `ItemDatabase` (singleton `BlobAssetReference<ItemDatabaseBlob>`; `ItemDefBlob{ushort ItemId; byte Category; byte Tier; int StackMax; FixedString64Bytes Name; byte EquipSlot; byte GrantedAbilityId; 4× INLINE ItemModSpec}` — ID-keyed `TryGetItem`; **inline mods, NOT a nested BlobArray** since `TryGetItem` returns by value; zero-code content adds); `InventorySlot` (`[GhostField]` buffer, `OwnerSendType.All` — per-player bag) + pure `InventoryMath` (deposit/withdraw/stack-cap/`CanDeposit`); `EquipmentSlot` (`[GhostField]` buffer, **index = slot** — Weapon=0/Armor=1/Trinket=2/Tool=3) + `EquipSlotId`; `DefaultAbility` (baked, non-replicated, unarmed fallback); `InventoryDepositRequest`/`EquipRequest`/`UnequipRequest` (`IRpcCommand`s). Equip stat-mods tagged `Tuning.EquipSourceIdBase+slot`.
- **Systems:** server (`ProjectM.Server/Economy`, plain `SimulationSystemGroup`, NOT predicted) — `ResourceHarvestSystem` rerouted to deposit harvest into the owner's bag (owner via optional `ComponentLookup<GhostOwner>`; remainder/un-owned → global ledger); `InventoryDepositSystem` (bag → ledger via the `G` RPC); `EquipSystem` (event-driven equip/unequip: move item bag↔slot ATOMICALLY — no item loss; weapon→`AbilityRef.Id`, gear→`StatModifier`s stripped target-agnostically via `RemoveBySourceId`; `DefaultAbility` on weapon-unequip). Client: `InventoryDepositSendSystem` (`G`) / `EquipSendSystem` (keys 1-9 / U + build-safe static hooks); `HudSystem` inventory + equipment panels (observe-only, click-to-equip). Authoring: `ItemDefinition` SO + `ItemDatabaseAuthoring` baker.
- **Netcode shape:** `InventorySlot`/`EquipmentSlot`/`StatModifier` = `[GhostField]` `OwnerSendType.All` buffers on the owner-predicted player → replicate to the owner; the **server is the sole writer** (event-driven, plain group → no rollback double-apply). Equip effects fold through the predicted `StatRecomputeSystem` + the already-replicated `AbilityRef`, so the swap is prediction-correct with no client equip-prediction. Catalog = baked config (identical both worlds, NOT replicated). Session-only (no save persistence yet — Phase 3). Status: **built + Play-validated** (236/236 EditMode). Decisions: [[DR-026_Inventory_Equipment_Progression_Foundation]] (Phase 0), [[DR-027_Equipment_Slots_Phase1]] (Phase 1). Forward roadmap (Phase 2 tool-gated harvesting → Phase 4) in [[Backlog]].
> **Systems documented in their DRs/session logs but not yet given a full section here:** aim controls ([[DR-012_Aim_Controls_Cursor_Gamepad]]), persistent base + player-driven pacing ([[DR-017_Persistent_Base_Player_Driven_Pacing]]), frontend menu/settings/saves ([[DR-019_Frontend_Menu_Settings_Saves_Build]]), animation pipeline ([[DR-022_Animation_Pipeline_Rukhanka_Synty]] / [[DR-023_Enemy_Animation_MonsterMash]]), HUD Synty skin ([[DR-024_HUD_Synty_Skin_Theme]]), world environment + collision ([[DR-025_World_Environment_Redo_Natural_Frontier]] / [[2026-06-08_World_Collision_HUD_Scaling]]).
## Conventions
DOTS/ECS conventions live in repo `CLAUDE.md` and the `dots-dev` skill's `dots-conventions.md`. Don't duplicate volatile API details here — link to context7-derived notes instead.