Files
Project-M/Docs/Vault/02_Game_Design/Systems_Index.md
T
2026-06-04 21:49:03 -07:00

10 KiB
Raw Blame History

tags, updated, permalink
tags updated permalink
design
index
2026-06-04 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 stay reserved for M7).
  • 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).

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.