Files
Project-M/Docs/Vault/02_Game_Design/Systems_Index.md
T
kronic 89dd155f85 Docs: LANTERN Phase 0 — full pivot adoption + all-doc sweep
Adopt LANTERN as the operative direction (co-op action-RPG, light-as-territory),
superseding the Awakening Engine fiction + the Co-op Hades iteration. Existing
code becomes salvage quarry; the mechanical pillars hold.

Phase 0 deliverables (doc portions):
- DR-048 adoption decision record (0.1)
- Lantern_Strip_Mothball_Inventory — system-by-system keep/rework/mothball (0.2)
- Lantern_World_Model_Spike — pocket-graph + seed-pinning BRIEF, review-first (0.3)
- Art_Direction_Lantern — style bible + material decision + A0 gate (0.4)

Docs/skills sweep: Pillars pointers + LANTERN locked decisions; superseded
banners on Identity, Iteration_CoopHades, Path_to_Fun, Milestones, Backlog,
End_Of_Month (all redirect to the LANTERN roadmap); Home MoC + current-direction
callout (and stale serena line fixed); Systems_Index era note; CLAUDE.md
core-loop line -> LANTERN direction pointer (verbose co-op-Hades invariants
archived to the gotchas archive, net-neutral under the 40 KB budget); dots-dev
validation-harness expedition-run recipe flagged era-specific.

Two Phase-0 gate items remain OPEN (not doc work): the 0.3 world-model design
review and the 0.4 in-engine style-proof scene.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-13 23:13:13 -07:00

78 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
---
# 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.