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>
17 KiB
tags, updated, permalink
| tags | updated | permalink | ||
|---|---|---|---|---|
|
2026-06-08 | 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 —float2Move/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.DeltaTimeonly);PlayerInputGatherSystem(client,GhostInputSystemGroup);GoInGameClientSystem(client) /GoInGameServerSystem(server — spawns the owner-predicted ghost, stampsGhostOwner,LinkedEntityGroupauto-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.PlayerInputgainsFire(InputEvent). - Systems:
AbilityFireSystem(predicted;IsFirstTimeFullyPredictingTick-gated predict-spawn; server branch appliesAutoTarget);ProjectileMoveSystem(predicted);ProjectileClassificationSystem(client; predicted-spawn match bySpawnId; non-Burst);ProjectileDamageSystem(server; swept segment-vs-sphere hit);HealthApplyDamageSystem(server; DamageEvent → Health, dummy death-despawn);TrainingDummySpawnSystem(server; one-shot). Input:PlayerInputGatherSystemrewritten as managedSystemBaseover the generatedProjectMInputaction-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.Directionreplicate. 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(singletonBlobAssetReference<AbilityDatabaseBlob>;AbilityDefBlob/CharacterStatsBlobkeyed byAbilityId/CharacterIdbyte) +AbilityPrefabElement(companion entity-ref buffer for projectile prefabs);AbilityRef([GhostField]id) /CharacterStatsRef;StatModifier(replicated[GhostField]buffer,OwnerSendType.All, raw-byteStatTarget/ModOp);EffectiveAbilityStats/EffectiveCharacterStats(derived, not replicated);UpgradePickup/UpgradePickupSpawner.StatMath(pure fold). Removed M2'sAbilityStats/PlayerMoveStats. - Systems:
StatRecomputeSystem(predicted,[UpdateBefore]Aim/Move; folds blob base + modifier buffer →Effective*every tick — rollback-correct);AbilityFireSystemrerouted (effective stats + prefab-by-id + snapshot-at-fire);PlayerMoveSystem→ effective move;UpgradePickupSpawnSystem/UpgradePickupSystem(server; overlap-grant viaAppendToBuffer);DebugModifierInjectionSystem(editor-only, server world);HealthApplyDamageSystemclamps to effective MaxHealth. Authoring:AbilityDefinition/CharacterStatsDefinitionSOs +AbilityDatabaseAuthoringblob 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) +StorageOpconsts;StorageMath(deposit-merge / withdraw-clamp-drop, unit-tested). - Systems:
SharedStorageSpawnSystem(server one-shot — instantiate the container ghost atCellToWorld(cell), destroy spawner);StorageOpReceiveSystem(serverSimulationSystemGroup, NOT predicted — apply the RPC to the singleton container's buffer viaStorageMath);StorageOpSendSystem(client managedSystemBase— E/Q keyboard + editor-onlyDeposit/Withdrawstatics →StorageOpRequestRPC).GoInGameServerSystemre-rooted ontoBaseGridMath.PlotCenter(BaseAnchor)(with aTryGetSingletonfallback). - Netcode shape: base config = baked, ghost-free, identical both worlds (not replicated). Storage container = ownerless interpolated server-spawned ghost; its
StorageEntrybuffer is a[GhostField](noOwnerSendType/GhostOwner) so server mutations replicate to all clients. Deposit/withdraw = server-authoritativeIRpcCommandresolved 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 onBaseGridMath+ 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+ pureEnemyAIMath;Dead(enableable, derived) +RespawnState+ pureRespawnMath;TickUtil.NonZero(the cooldown 0-sentinel guard). - Systems:
EnemyAISystem(server-only, plainSimulationSystemGroup,[UpdateAfter(PredictedSimulationSystemGroup)]— the interpolated Husk ghost seeks the nearest living player, deals contactDamageEvent) + theWaveSystemthreat director (escalating waves of Husk variants Grunt/Swarmer/Brute — replaced the flat sustain; see 2026-06-02_GameFeel_Deepening);HealthApplyDamageSystem+EnemyTagdeath;PlayerDeathStateSystem(both worlds, predicted — derivesDeadfromHealth<=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
LocalTransformreplication;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 ofResourceNode) +ClutterFieldSpawner(optional baked singleton);StructureType.Wall=5/Pylon=6(byte consts; 2–4 are now in use by the M7 production machines — Harvester/Conveyor/Fabricator). - Systems:
ResourceHarvestSystemunified 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);ExpeditionFieldSystemscatters/clears clutter beside the node field (distinct seed);BuildPlaceSystemunchanged (already type-generic) — new genericStructureAuthoring{byte Kind}+ two additiveStructureCatalogrows +BuildSendSystemV/N keys. Client:WorldFeedbackSystem(observe-onlyPresentationSystemGroup— chip/shatter juice, proximity-gated so region-transit stays silent) + live-tunableWorldFeelConfig. - Netcode/world shape: clutter = ownerless interpolated, region-scoped via GhostRelevancy (like nodes); Wall/Pylon = ownerless interpolated structure ghosts (
PlacedStructure.Typebyte-additive → no re-bake); Wall carries aPhysicsCollider(CC-blocking), Pylon cosmetic. Visual:M_Aether_Wild/M_Aether_Orderedpalette materials + a classic-URP Blightfield rock basin inGame.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 buffersMachineInput/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 ofStorageMath). - Systems (
ProjectM.Server/Automation, server-only, plainSimulationSystemGroupordered chain[UpdateAfter(PredictedSimulationSystemGroup)]→ Harvester → Conveyor → Fabricator):HarvesterProductionSystem(→ ownMachineOutput),ConveyorTransportSystem(pull adjacent upstream output →ResolveMoves→ settleConveyorItem/deposit sinkMachineInput),FabricatorProductionSystem(input-limited recipe → global ledger). Persistence:SaveStructureScan(shared autosave/quit scan ofRuntimePlacedTagstructures), one-shotBaseRestoreSystem(charge-free replay),SaveDatav2 (StructureSave[]+ flatStructureIoRow[], cooldown as epoch-independent REMAINING ticks). Build:BuildPlaceSystemstamps conveyorDirection+RuntimePlacedTag;BuildSendSystemH/F/C keys +[/]rotate;BuildPlaceRequest+byte Direction. Authoring: 3 MonoBehaviours + bakers;Harvester/Fabricator/Conveyor.prefab(dupTurret.prefab) in the catalog. - Netcode shape: production is SERVER-ONLY (no prediction/rollback); results replicate ONLY via the global
StorageEntryledger ([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):
HudSystemrewritten on UI Toolkit (clientPresentationSystemGroup, observe-only) — a runtime UIDocument (sharedRuntimePanelSettings, sortingOrder 50 behind the pause overlay; rootpickingMode=Ignore) in the Aether-cyanMenuUi/HudUipalette: health+cooldown bars, HUSKS, phase/cycle/countdown + resources + location + goal, DOWNED overlay, and a build-palette bar lazy-built from the clientStructureCatalog(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).CombatFeedbackSystemdamage 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
BuildPlaceRequestRPC (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(singletonBlobAssetReference<ItemDatabaseBlob>;ItemDefBlob{ushort ItemId; byte Category; byte Tier; int StackMax; FixedString64Bytes Name; byte EquipSlot; byte GrantedAbilityId; 4× INLINE ItemModSpec}— ID-keyedTryGetItem; inline mods, NOT a nested BlobArray sinceTryGetItemreturns by value; zero-code content adds);InventorySlot([GhostField]buffer,OwnerSendType.All— per-player bag) + pureInventoryMath(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(IRpcCommands). Equip stat-mods taggedTuning.EquipSourceIdBase+slot. - Systems: server (
ProjectM.Server/Economy, plainSimulationSystemGroup, NOT predicted) —ResourceHarvestSystemrerouted to deposit harvest into the owner's bag (owner via optionalComponentLookup<GhostOwner>; remainder/un-owned → global ledger);InventoryDepositSystem(bag → ledger via theGRPC);EquipSystem(event-driven equip/unequip: move item bag↔slot ATOMICALLY — no item loss; weapon→AbilityRef.Id, gear→StatModifiers stripped target-agnostically viaRemoveBySourceId;DefaultAbilityon weapon-unequip). Client:InventoryDepositSendSystem(G) /EquipSendSystem(keys 1-9 / U + build-safe static hooks);HudSysteminventory + equipment panels (observe-only, click-to-equip). Authoring:ItemDefinitionSO +ItemDatabaseAuthoringbaker. - Netcode shape:
InventorySlot/EquipmentSlot/StatModifier=[GhostField]OwnerSendType.Allbuffers 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 predictedStatRecomputeSystem+ the already-replicatedAbilityRef, 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.