From d6d75b4706a778cde600deea19ce42228ba3c6b4 Mon Sep 17 00:00:00 2001 From: Luis Gonzalez Date: Thu, 18 Jun 2026 00:41:25 -0700 Subject: [PATCH] Docs: DR-039 Slice 2 (two classes) record + roadmap update Slice 2 complete: Warrior/Ranger, DRG-asymmetric, aim-directed cone, menu picker, class carrier via GoInGameRequest. Two VFX-polish items deferred (review-sanctioned). Co-Authored-By: Claude Opus 4.8 (1M context) --- Docs/Vault/06_Roadmap/Backlog.md | 2 +- ...26-06-17_Design_Redirect_Coop_Roguelite.md | 8 +++- ...R-039_Slice2_Two_Classes_Warrior_Ranger.md | 47 +++++++++++++++++++ 3 files changed, 54 insertions(+), 3 deletions(-) create mode 100644 Docs/Vault/07_Sessions/_Decisions/DR-039_Slice2_Two_Classes_Warrior_Ranger.md diff --git a/Docs/Vault/06_Roadmap/Backlog.md b/Docs/Vault/06_Roadmap/Backlog.md index 41f128544..f3f2e50bb 100644 --- a/Docs/Vault/06_Roadmap/Backlog.md +++ b/Docs/Vault/06_Roadmap/Backlog.md @@ -20,7 +20,7 @@ Last decluttered 2026-06-08 (removed all shipped `[x]` items; their context is p **Committed slices (sequence; ~70 % of the chassis already exists — reuse, don't rebuild):** - **Slice 1 — Combat Readability & HUD Declutter** ✅ **BUILT + engineering-validated 2026-06-17 ([[DR-038_Slice1_Combat_Readability_HUD_Declutter]])** — on-damage-sticky **enemy health bars** (+fade, <25 %HP always-on) in `CombatFeedbackSystem` (`Health.Current` already replicated); **telegraph fix** (per-enemy baked `EnemyTelegraph` windup ramp replacing the hard-coded `22f` + windup 18→22 + a scale-pulse); **build-mode toggle** (client `BuildPaletteState.PaletteOpen` gates the palette + a "Tab/Y — BUILD" chip); **Charger committed-lunge tell** (`[GhostEnabledBit] IsLunging` derived once/tick — one Charger re-bake). 345/345 EditMode; Play-validated (handshake intact, bake correct, no runtime errors). **Open: operator visual fun-gate.** -- **Slice 2 — Two Classes (Warrior / Ranger)** *(review-gated)*: route `MeleeComboSystem` damage through `EffectiveAbilityStats` (the highest-impact edit; `StatTarget.MeleeDamage` byte); class anchor via `AbilityRef.Id` + `CharacterId`; wire the **Cone** archetype (Warrior secondary), keep the Ranger projectile; second ability cooldown; class-select UX; class-conditioned starting `StatModifier`. Absorbs Path B's MC-6 intent (lighter). +- **Slice 2 — Two Classes (Warrior / Ranger)** ✅ **BUILT + validated 2026-06-18 ([[DR-039_Slice2_Two_Classes_Warrior_Ranger]])** — DRG-asymmetric (Warrior melee bruiser tankier/slower; Ranger ranged faster/squishier + co-op auto-assist hook); aim-directed Warrior cone (Cone archetype inline in `AbilityFireSystem`, server-only damage, same-tick); class carried on `GoInGameRequest.ClassId` (the per-world-`ConnectionConfig` blocker fixed) → `ClassTraits` seeds at spawn; character deltas via replicated `StatModifier` seeds on Default (no per-class blob); melee asymmetry folds in `MeleeComboSystem` (`StatTarget.MeleeDamage`/`MeleeRange`); menu class picker. 348/348 EditMode; Warrior Play-validated server==client (re-bake handshake intact). **Deferred polish:** cone client VFX + slash-arc reach. **Open: fun-gate (operator).** - **Slice 3 — Expedition Combat Spine** *(review-gated)*: reactivate `ExpeditionFieldSystem` → **authored-arena pool** sampled by `ExpeditionEpoch` (pool-sampling, NOT WFC/terrain-gen); `ZoneEnemySpawnSystem` (`RegionTag{Expedition}`); the **`EnemyAISystem` region-filter fix**; the **`ThreatDirector` mid-siege-return gate**; a **zone-clear reward** on `ExpeditionGateSystem`; a **replicated zone-theme byte** on the global ghost; the required sortie→clear→return→escalated-siege loop. Manage `RegionRelevancySystem` per-tick cost (cap ghosts; cosmetic props non-ghost). Absorbs Path B MC-2 (enemy mix) intent. - **Slice 4 — Persistent Meta-Progression** *(review-gated)*: **SaveData v6** (meta-currency, unlocked classes, persistent epoch); a between-runs growth surface at the hub; persist-the-seed-regenerate-the-layout (pure-function generator). diff --git a/Docs/Vault/07_Sessions/2026/2026-06-17_Design_Redirect_Coop_Roguelite.md b/Docs/Vault/07_Sessions/2026/2026-06-17_Design_Redirect_Coop_Roguelite.md index 1c9662867..12b2bd565 100644 --- a/Docs/Vault/07_Sessions/2026/2026-06-17_Design_Redirect_Coop_Roguelite.md +++ b/Docs/Vault/07_Sessions/2026/2026-06-17_Design_Redirect_Coop_Roguelite.md @@ -43,6 +43,10 @@ Ran the adversarial pre-code review (1 ground + 3 lenses: netcode/determinism · **Phase 1 BUILT + green (this session):** `StatIds` (CharacterId.Warrior=2/Ranger=3, AbilityId.WarriorCone=4, StatTarget.MeleeDamage=9/MeleeRange=10); `CharacterStatsRef.Id` → `[GhostField]` (the one re-bake); `MeleeComboSystem` folds the per-player melee mods (defensive `HasBuffer` guard → identity without seeds, so behavior-preserving). Compiles clean; **345/345 EditMode**. UNCOMMITTED (partial slice). -## Next (Slice 2 continuation) +## Slice 2 — COMPLETE + validated (2026-06-18, [[DR-039_Slice2_Two_Classes_Warrior_Ranger]]) -Build the rest of Slice 2: (1) `Tuning.ClassSourceId`; (2) the Warrior cone — `PendingConeFire` + `AbilityFireSystem` Cone-archetype branch (aim-directed, cooldown both worlds, signal server-only) + `ConeFireDamageSystem`; (3) the class carrier — `GoInGameRequest.ClassId` + `ClassSelection` singleton + `GoInGameClientSystem` send + `GoInGameServerSystem` class writes; (4) AbilityDatabase authoring rows (Warrior/Ranger `CharacterStatsBlob`; WarriorCone `AbilityDefBlob`, archetype Cone, wide angle in `AutoTargetConeRadians`); (5) the menu picker UI (`MainMenuController` → `WorldLauncher` → `ClassSelection`); (6) a client cone VFX + the slash-arc reading the folded melee range. Then re-bake + Play-validate (server==client, the class asymmetry, the cone, clean handshake) + class-fold/cone EditMode tests, then commit Slice 2 as one unit (DR-039). Slice 1's operator visual fun-gate also still open. +Built the whole slice across 4 committed chunks (`d9d67c4e7` → `431a7e2ed`): the data layer + melee-augment routing; the class carrier (`GoInGameRequest.ClassId` + `ClassSelection` + `ClassTraits`, the review's fix for the per-world-`ConnectionConfig` blocker); the aim-directed Warrior cone (inline in `AbilityFireSystem`'s server branch, same-tick); the WarriorCone ability authored + subscene re-baked; the menu class picker. Character deltas ride replicated `StatModifier` seeds on the Default character (prediction-correct, no per-class blob churn); melee asymmetry folds in `MeleeComboSystem`. **348/348 EditMode** (+3 `ClassTraits` tests); **Warrior Play-validated end-to-end, server==client** (`AbilityRef=WarriorCone`, 4 seeds, eff MaxHP 130 / MoveSpeed 5.1 / cone dmg 22; `conns=1` — re-bake handshake intact; zero runtime errors). **Deferred (review-sanctioned polish):** the WarriorCone client VFX + the slash-arc reading the folded melee reach. **Open: the Slice 2 fun-gate** (do the classes feel distinct — operator's eyes). + +## Next + +Continue the roadmap: **Slice 3 — Expedition Combat Spine** (the procedural run venue). Open it with its adversarial netcode/determinism design review FIRST ([[validate-netcode-design-before-coding]]) — it's the heaviest netcode slice (reactivate `ExpeditionFieldSystem` → authored-arena pool + `ZoneEnemySpawnSystem(RegionTag{Expedition})` + the `EnemyAISystem` region-filter fix + the `ThreatDirector` mid-siege-return gate + a zone-clear reward + a replicated zone-theme byte + the required sortie→clear→return→escalated-siege loop; manage `RegionRelevancySystem` per-tick cost). Then Slice 4 (persistent meta). Operator fun-gates for Slices 1 + 2 remain open (their eyes). diff --git a/Docs/Vault/07_Sessions/_Decisions/DR-039_Slice2_Two_Classes_Warrior_Ranger.md b/Docs/Vault/07_Sessions/_Decisions/DR-039_Slice2_Two_Classes_Warrior_Ranger.md new file mode 100644 index 000000000..f30112b27 --- /dev/null +++ b/Docs/Vault/07_Sessions/_Decisions/DR-039_Slice2_Two_Classes_Warrior_Ranger.md @@ -0,0 +1,47 @@ +--- +id: DR-039 +title: Slice 2 — Two Classes (Warrior melee / Ranger ranged) +status: accepted +date: 2026-06-18 +tags: +- decision +- design +- combat +- classes +- netcode +- slice +permalink: gamevault/07-sessions/decisions/dr-039-slice2-two-classes-warrior-ranger +--- + +# DR-039 — Slice 2: Two Classes (Warrior / Ranger) + +> The second build slice of the co-op-roguelite redirect ([[DR-037_Procedural_Expedition_Spine_Two_Classes_Persistent_Meta]]). Two playable classes whose identity = the primary-attack archetype + asymmetric stats, with run-acquired meta-buffs riding the existing `StatModifier` pipeline. Preceded by the mandatory adversarial design review ([[validate-netcode-design-before-coding]]); forks operator-locked. Built + validated; the WarriorCone client VFX is the one deferred polish item. + +## Context + +[[DR-037_Procedural_Expedition_Spine_Two_Classes_Persistent_Meta]] committed "two classes (Warrior melee / Ranger ranged)". The pre-code review (1 ground + 3 lenses — netcode/determinism · class-feel · reuse/scope, all **GO-WITH-CHANGES**) caught two strawman errors and the palette-swap risk; the operator locked all four recommended forks. + +## Operator forks (locked 2026-06-18) + +- **Asymmetric melee — DRG model:** Warrior = longer-reach + harder-hitting melee, slower + tankier; Ranger = shorter/weaker melee, faster + longer range. (Rejected: symmetric melee → reads as a skin swap.) +- **Aim-directed Warrior cone:** the Fire-slot cone is pointed by aim (a deliberate crowd-control burst), short range, wide arc, short cooldown. +- **Menu picker:** per-player class chosen in the menu, carried on the spawn RPC. +- **Two seeds + co-op synergy:** each class seeds 2 combat stats; the Ranger gets a wider auto-target assist so the Warrior's knockback feeds it. + +## Decision (with the review's fixes folded in) + +**1. Class carrier = `GoInGameRequest.ClassId` (NOT `ConnectionConfig`).** The review's BLOCKER: `ConnectionConfig` is a per-WORLD singleton cleared pre-spawn — it cannot hold N players' classes in co-op. Added a `byte ClassId` to the existing `GoInGameRequest` RPC. The client reads a client-world **`ClassSelection`** singleton (seeded by `WorldLauncher` from the menu's `SelectedClass`) into the RPC; **`GoInGameServerSystem`** (the system that already spawns the player + stamps GhostOwner/RegionTag) writes the class at spawn via **`ClassTraits`**: `ecb.SetComponent` the `AbilityRef.Id` (Warrior = WarriorCone / Ranger = Primary) + `ecb.AppendToBuffer` the trait seeds. 0/unknown → Warrior. + +**2. Character deltas ride replicated StatModifier SEEDS on the Default character (no per-class blob row).** `CharacterStatsRef.Id` stays `Default(1)`; the DRG-asymmetry (MoveSpeed/MaxHealth) + the kit (Range/AutoTargetRange/MeleeDamage/MeleeRange) are 4 `StatModifier` seeds per class on the reserved `Tuning.ClassSourceId` (never stripped). Because `StatModifier` is `[GhostField] OwnerSendType.All`, the owning client folds the correct `EffectiveCharacterStats`/melee — so the class is correct under prediction **without** new character blob rows. *(`CharacterStatsRef.Id` was still promoted to `[GhostField]` — committed in Phase 1 — and is currently unused-but-harmless; it future-proofs a mid-run class swap / blob-based char stats.)* + +**3. Melee asymmetry folds in `MeleeComboSystem`, not `EffectiveAbilityStats`.** New `StatTarget.MeleeDamage`/`MeleeRange` (bytes 9/10) are folded onto the live-tunable `TuningConfig.MeleeDamage`/`MeleeRange` base **inside `MeleeComboSystem`** (server-side cleave, deterministic-safe) off the player's replicated mods buffer (via a `BufferLookup` + `.WithEntityAccess()` — the query was already at the 7-arg `SystemAPI.Query` max; `HasBuffer`-guarded → identity without seeds). Chosen over routing through `EffectiveAbilityStats.Damage` to avoid conflating melee with the Fire ability's Damage axis — melee gets its own augment axis (the review's "single highest-impact edit"). + +**4. The Warrior cone = the Cone archetype, resolved INLINE in `AbilityFireSystem`'s server branch** (same-tick, mirroring the `MeleeComboSystem` cleave — it runs before `HealthApplyDamageSystem` in the predicted group), NOT a separate `ConeFireDamageSystem`. Cooldown advances on **both** worlds (the client predicts the gate); the cone **damage is server-only** (`if (isServer)`, `SourceTick`-stamped, `MeleeConeMath.InCone` collect-all over living enemies gathered once at the top). No projectile ghost, no `PendingConeFire` component. `WarriorCone` ability authored (archetype Cone, dmg 22, range 2.2, ~130° arc via `AutoTargetConeDegrees=65`, cd 22) and added to the gameplay subscene's AbilityDatabase (re-baked). + +## Consequences + +- **Validation (green):** clean compile; **348/348 EditMode** (incl. 3 new `ClassTraits` tests: ability mapping + the asymmetric seed folds). **Play-validated the Warrior end-to-end, server==client:** `AbilityRef=WarriorCone`, 4 seeds, `eff MaxHP 130 / MoveSpeed 5.1 (base 6×0.85) / cone dmg 22 / coneRad 1.13` identical on both worlds; **`conns=1` — the `CharacterStatsRef` re-bake did NOT break the handshake**; zero runtime errors. The Ranger path is the same carrier code with `classId=3` (covered by the EditMode seed test + the symmetric write); editor-default boot is always Warrior (the menu drives the choice). +- **Reusable patterns:** per-class deltas via replicated `StatModifier` seeds on a shared character (no per-class blob churn, prediction-correct via `OwnerSendType.All`); an archetype handled inline in the predicted fire system with a server-only damage branch (the cleave idiom) when no ghost is spawned. +- **Deferred polish (review-sanctioned):** the WarriorCone has **no client-side fire VFX** (server-only damage; the player sees damage numbers + enemy reactions but no cone effect on their own fire) — schedule a `CombatFeedbackSystem` cone-flash; and the melee **slash-arc visual still reads the base `TuningConfig.MeleeRange`**, so the Warrior's longer reach isn't shown in the arc (fold the local player's MeleeRange seed there). Neither is a functional gap. +- **Open (operator):** the Slice 2 fun-gate (do the two classes feel distinct in a real fight; does the cone read as a panic burst) + Slice 1's still-open visual fun-gate. +- **Files:** `StatIds`/`Tuning`/`CharacterStatsRef`/`MeleeComboSystem` (Phase 1, `d9d67c4e7`); `ClassSelection`/`ClassTraits`/`GoInGameRequest`/`GoInGameClientSystem`/`GoInGameServerSystem`/`AbilityFireSystem` (`a7fdd6f71`); `Ability_WarriorCone.asset` + subscene + `ClassTraitsTests` (`0a3a39e3d`); `WorldLauncher`/`MainMenuController` (`431a7e2ed`).