Files
kronic a7405c3f38 Docs: reconcile vault roadmap + indexes to current state
Bring the living docs up to 2026-06-08 reality (Milestones/Backlog/Home/Systems_Index were stalled at 2026-06-06): add the 6 missing Milestones rows (animation, enemy animation, HUD Synty skin, world redo, world collision, Inventory+Equipment Phase 0/1); declutter Backlog to open-work-only (remove shipped [x] items whose context lives in their DRs/session logs, resolve items completed by later slices, surface Inventory/Equipment Phase 2-4 as next); refresh Home links (DR range -> DR-027, latest sessions); add a Systems_Index Items/Inventory/Equipment section + fix the stale 'StructureType 2-4 reserved for M7' note + a pointer to systems documented only in their DRs.

Reconcile DR-026's roadmap (mark Phase 0/1 done; the Phase-1 line now carries the DR-027 event-driven supersession note). Trim Data_Driven_Abilities resolved open-questions + fix cross-links. DRs + session logs (the historical record) untouched apart from that one DR-026 note.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 11:33:46 -07:00

72 lines
17 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.
---
tags:
- design
- index
updated: 2026-06-08
permalink: gamevault/02-game-design/systems-index
---
# Systems Design — Index
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.