Docs: DR-030 MC-4 combo melee (primary verb) + session log; Path_to_Fun MC-1 passed / MC-4

MC-1 fun-gate PASSED (kill-switch cleared); MC-4 built as the combo-chain melee primary-verb variant, both adversarial reviews folded in. Roadmap MC-1/MC-4 status updated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-10 17:23:10 -07:00
parent d77649fe16
commit 5b0af63a3b
3 changed files with 93 additions and 0 deletions
+4
View File
@@ -90,6 +90,8 @@ Depth = a **dialogue**. Enemies ask distinct, readable questions (a committed lu
- **Dependencies:** none. **Kill-risk:** none — but skipping it makes every downstream gate a debate instead of a glance.
### MC-1 — Fight in a Box: the dash + the question it answers `~2.53.5 wk` · risk MEDIUMHIGH · **review-gated**
> **Status (2026-06-10):** ✅ **FUN-GATE PASSED** — the dash duel is fun (operator: *"it's fun, the dash feels fine"*); the project kill-switch is CLEARED, the combat thesis holds. Code [[2026-06-09_MC1_Implementation]]; gate passed [[2026-06-10_MC4_Combo_Melee]].
**Goal:** turn stand-and-click into a bait-and-punish **duel** — a snappy i-frame dash answering ONE **Charger**'s readable, committed, whiff-punishable lunge. This is the genuinely-smallest fun slice. *(The Swarmer moves to MC-2; it answers a different question — see [Boundary judgment](#boundary-judgment-re-cut-mc-1-default-order-mc-4-early).)*
> **This is 34 distinct risky slices, not one** ([secretly-multi](#secretly-multi-milestones-why-the-estimates-widened)): a new predicted `DashSystem` + replication; a Burst-affecting `CharacterProcessor` edit with its own restart/validate cycle; the `DamageEvent.SourceTick` refactor across THREE stamp sites + the negation branch; and a new Charger brain (lunge/stagger/whiff-detection) + telegraph tuning + dash juice. Each needs its own focused-editor Play-validation — hence the widened estimate.
@@ -121,6 +123,8 @@ Depth = a **dialogue**. Enemies ask distinct, readable questions (a committed lu
**Dependencies:** MC-0 (so the bench is measurable). **Kill-risk:** the dash doesn't FEEL like a blink (the sharpness override is make-or-break) OR the telegraph is unreadable under latency OR the i-frame negation mis-aligns across the group boundary and reads as "flaky/cheap" — any one collapses the duel into spam/RNG and nothing downstream matters. **MC-1 is the kill-switch for the whole project**: if its gate fails after a real tuning pass, STOP and re-cut combat — do not build on an unfun core.
### MC-4 — Offense gets a verb: ability ARCHETYPE byte + melee cone `~0.751 wk` · risk LOW
> **Status (2026-06-10):** 🔨 **CODE-COMPLETE + reviewed** — built as the **combo-chain** variant with melee as the **PRIMARY** verb (left-click/pad-West; ranged demoted to right-click/pad-LT), per the operator's forks. Predicted-replicated combo `Step`, server-only cleave, 9 live `TuningConfig` knobs. 294/294 EditMode green, Play-validated (no cycle, re-bake server==client). The **archetype byte is DEFERRED to MC-6** (the melee is its own verb, not an ability dispatch). The MC-4 **fun-gate** is the open operator item. See [[2026-06-10_MC4_Combo_Melee]] · [[DR-030_MC4_Combo_Melee_Primary_Verb]].
**Goal:** stop offense being "auto-aim and hold." A byte-dispatched ability archetype with an instant short-range **melee cleave** that makes attacking a *positioning* decision (dash-in → cleave → dash-out). **Runs second, right after MC-1** — verified-low-risk and kills the second-most-felt hollowness; see [Boundary judgment](#boundary-judgment-re-cut-mc-1-default-order-mc-4-early). *(Also the de-risking spike for MC-6's archetype dispatch.)*
- **Archetype byte** on `AbilityDefBlob`/`EffectiveAbilityStats` (Projectile=0 keeps today's path) + a `switch` in `AbilityFireSystem` (stored as **byte** per the Burst cross-assembly enum rule; baked, no replication).
- **MeleeCone / cleave (byte=2):** server-side select all living enemies in a cone around `PlayerFacing.Direction` (**reuse `AutoTarget.Resolve`'s `dot` vs `cos(halfAngle)` cone math as a collect-all selector**), append `DamageEvent` (`SourceTick`-stamped) + `KnockbackState`. Instant, short-range, higher per-hit. No new ghost, pure server damage. `PlayerFacing.Direction` is already a replicated `[GhostField] float2`.
@@ -0,0 +1,50 @@
---
date: 2026-06-10
type: session
tags:
- session
- combat
- mc-4
- melee
- netcode
- prediction
permalink: gamevault/07-sessions/2026/2026-06-10-mc4-combo-melee
---
# MC-4 — Combo-chain melee as the PRIMARY verb (code-complete; fun-gate pending)
> Implements [[DR-030_MC4_Combo_Melee_Primary_Verb]]. Direction [[Path_to_Fun]] (MC-4) · [[DR-028_Combat_Primary_Verb_Depth_First]] · [[combat-first-depth-before-breadth]]. **MC-1 PASSED its fun-gate this session** (operator: *"It's fun — the dash feels fine"*) → the project kill-switch is cleared, the combat thesis holds, depth continues. **Status: all code + 294/294 EditMode + Play-validation done; the MC-4 FUN-GATE (feel pass) is the open operator item.**
## MC-1 fun-gate: PASSED
The first action was the MC-1 feel gate (the kill-switch). Operator confirmed the dash feels right and the duel is fun — DR-028's literal *"spacing/timing matters and we didn't want to stop"* sign-off. Recorded here as the milestone event; MC-1 is now genuinely done (was code-complete-but-ungated per [[2026-06-09_MC1_Implementation]] / [[2026-06-10_MC0_TuningConfig_LiveTuning]]).
## What was built (the full uncommitted slice)
The operator chose, from a 4-option ladder, the **combo-chain Attack** shape and **melee = PRIMARY verb** (ranged demoted). Hades-like. NEW: `MeleeConeMath` (pure collect-all cone predicate — AutoTarget's exact range+bearing test, lifted as a bool because `AutoTarget.Resolve` is a single-winner reducer), `MeleeCombo` (predicted owner-replicated `[GhostField]` Step/SwingStartTick/LockUntilTick), `MeleeComboSystem` (the system). MODIFIED: `PlayerInput` (+`Attack` InputEvent), `TuningConfig` (+9 melee knobs across all 6 surfaces + the wire report), `PlayerAuthoring` (bake `MeleeCombo`), `PlayerDeathStateSystem` (clear on death), `PlayerInputGatherSystem` (the rebind), `DebugOverlay` (melee tuning rows), `CombatFeedbackSystem` (swing juice).
- **Input rebind (direct device reads, no `.inputactions` regen — the Dash precedent):** melee = left-click / pad-West (PRIMARY); ranged `Fire` = right-click / pad-left-trigger; both `&& !BuildPaletteState.Active`. Right-click stays build-cancel (modal — the guard separates the uses).
- **Combo state machine (predicted, idempotent ABSOLUTE writes):** fresh `Attack` press (not locked, not mid-dash, dash wins same-tick ties via `!Dash.IsSet`) → chain if re-pressed in `[LockUntilTick, LockUntilTick+grace)` else reset to 1; finisher (Step==ComboLength) widens range + scales dmg/knockback/recovery by `MeleeFinisherMult`. Movement-commit scales `MoveVelocity` while in the swing window (lower-bounded on `SwingStartTick` — the DashSystem rollback fix), and a dash overrides it (`[UpdateBefore(DashSystem)]`).
- **Server-only cleave** (mirrors `ProjectileDamageSystem`): collect living enemies (EnemyTag/TrainingDummyTag, Health>0) in the per-step cone, append `SourceTick`-stamped `DamageEvent` + stamp `KnockbackState`. Drained same tick by `HealthApplyDamageSystem`. So a cleave on a staggered Charger scores the whiff-punish for free.
- **Live-tunable feel:** 9 `TuningConfig` knobs in `DEV ▲` → "Melee …" rows. No per-step blob; the **MC-6 archetype byte was deferred** (the melee is its own verb — building dispatch infra now would be breadth-before-need; flagged to the operator, not silently dropped).
- **Juice:** `CombatFeedbackSystem` swing whoosh/arc-burst/camera-nudge edge-detected off `MeleeCombo.SwingStartTick`, burst scales with the step, finisher FOV-pop keyed off the live `ComboLength`.
## The two mandatory adversarial reviews (operator's standing rule)
- **Pre-code (3 lenses → 21 findings → 8 confirmed):** PRED-1 (the combo Step is path-dependent → REPLICATE it + absolute-write; the draft's `prev+1` would diverge under rollback) · GRP-1 (my "append in the predicted group to keep the whiff-punish" rationale was FALSE — the stagger is a 36-tick window scored once regardless of group; the placement is still right but for the `ProjectileDamageSystem`-precedent reason) · BURST-1/F5-1/SCOPE-1 (archetype byte → `AbilityDefBlob` only / defer to MC-6; no `MeleeStepBlob`; melee feel all-live) · REUSE-1 (extract a `MeleeConeMath` predicate, don't call `Resolve`) · INPUT-1 (NOT right-mouse — it's build-cancel + the kbm-scheme sentinel). All folded in before coding.
- **Post-build (3 lenses → 18 findings → 4 confirmed, ALL FIXED):** (1) server gathered every enemy every tick even with no swing + (4) client allocated lists it never filled → **restructured** to queue swings in the player loop and gather enemies server-only ONCE, only when a swing fired; (2) the finisher juice-pop hard-coded `step>=3` → now keys off the live `ComboLength` (so tuning ComboLength=2 still pops); (3) test-coverage gaps → **+4 tests** (comboLen=2 finisher, same-tick Attack+Dash tie, Attack-during-dash-recovery, two-player co-op cleave). The 14 refuted were correctly dismissed (e.g. `AppendToBuffer` is safe — enemies always bake a `DamageEvent` buffer; `NonZero` deadline can't wrap; comboLen=1 heavy-only is an intended floor).
## Validation (all gates)
- **Compile:** clean — 0 errors / 0 warnings (Bursted `MeleeComboSystem` incl. the post-fix restructure; no enum-on-Burst, byte combo-length).
- **EditMode: 294/294 green** (was 290 pre-fix; +23 over the 267 baseline: 8 `MeleeConeMath`, 15 `MeleeCombo` incl. the +4 hardening tests). The generic `TuningConfigTests` (iterating `< Count`) auto-cover the 9 new knobs and guard that all surfaces are wired.
- **Play (real netcode session, server+client), twice (pre- and post-restructure):** no `ComponentSystemSorter` cycle (chain acyclic), player ghost re-baked with `MeleeCombo` in BOTH worlds (idle 0/0/0 — server==client; the new `[GhostField]`s didn't break the handshake), `MeleeComboSystem` registered + running both worlds, **zero console errors/exceptions** (only the benign Server-Tick-Batching warnings). The refactored OnUpdate is a valid Burst entry point (no stale-binary / "not a known entry point").
## Notes / deviations (deliberate)
- **Archetype byte deferred to MC-6** (stated to the operator up front, not silently). The melee is a standalone verb; MC-6 owns ability dispatch.
- Live damage E2E (a cleave landing on a Husk during a siege) is the operator's feel gate; the synthetic E2E is covered deterministically by the 19 `MeleeCombo` EditMode tests (real system, GameServer world). No mid-Play input injection (no click sim).
- `Docs/Vault/_scratch/MC4_ComboAttack_DesignDraft.md` was the pre-code review artifact; superseded by [[DR-030_MC4_Combo_Melee_Primary_Verb]] and removed.
## Open items (operator)
- **The MC-4 fun-gate** (gate 3): focused-editor feel pass — dash-in → light·light·finisher → dash-out reads as one verb; `cleaveTargetsPerSwing > 1.5` in a swarm; cleave chosen over the projectile when surrounded; finisher reads as the big hit. Tune live via `DEV ▲` → "Melee …" rows (dmg/range/cone/recover/chain/move-x/knock/finish-x/combo-len). MC-4 is NOT done until it passes.
- After MC-4 passes → **EB-1 (machines can die)** is the next committed milestone ([[Path_to_Fun]]).
- The slice is uncommitted (12 files: 5 new + 7 modified). Offered for commit.
## Links
[[DR-030_MC4_Combo_Melee_Primary_Verb]] · [[Path_to_Fun]] · [[DR-028_Combat_Primary_Verb_Depth_First]] · [[DR-029_Path_A_Fork_Locks]] · [[2026-06-09_MC1_Implementation]] · [[2026-06-10_MC0_TuningConfig_LiveTuning]] · [[validate-netcode-design-before-coding]]
@@ -0,0 +1,39 @@
---
id: DR-030
title: MC-4 = a combo-chain melee as the PRIMARY verb (ranged demoted); predicted-replicated combo state
status: accepted
date: 2026-06-10
tags:
- decision
- design
- combat
- netcode
- mc-4
permalink: gamevault/07-sessions/decisions/dr-030-mc4-combo-melee-primary-verb
---
# DR-030 — MC-4: Combo-Chain Melee as the Primary Verb
## Context
[[2026-06-10_MC4_Combo_Melee]] · direction [[Path_to_Fun]] (MC-4) · forks ritual [[DR-029_Path_A_Fork_Locks]] · combat thesis [[DR-028_Combat_Primary_Verb_Depth_First]] / [[combat-first-depth-before-breadth]].
**MC-1 PASSED its fun-gate** (operator, 2026-06-10: *"It's fun — the dash feels fine"*). The project kill-switch is cleared and the combat thesis is validated — so depth continues. The operator then chose to address the flattest-feeling thing: the default offense (`AbilityFireSystem`'s auto-aim hold-to-fire projectile — the *only* offense verb). Of the four MC-4 shapes presented (single cleave / Attack+Special pair / combo-chain / full weapon-kit), the operator picked **combo-chain Attack**, and on the role/binding fork picked **melee as the PRIMARY verb** (ranged demoted to a secondary poke). Hades-like.
The slice is netcode-heavy (predicted combo state under rollback), so per the standing rule it went through the **mandatory pre-code adversarial review** (3 lenses → 21 findings → 8 confirmed) before any code, and a **post-build review** (3 lenses → 18 findings → 4 confirmed, all fixed).
## Decision
**1. Melee combo is the player's PRIMARY verb.** Left-click / gamepad-West = the 23-hit combo; the ranged projectile (`Fire`) is demoted to right-click / gamepad-left-trigger. Both are direct device reads in `PlayerInputGatherSystem` (no `.inputactions` regen — the MC-1 Dash precedent), both suppressed while the build palette is open. Right-click stays the build-cancel (modal; the `!BuildPaletteState.Active` guard separates the two uses). Dash unchanged.
**2. The combo `Step` is path-dependent → it is REPLICATED, not derived.** Unlike the dash (a stateless-per-tick decision whose `DashState` is non-replicated and re-derived each tick), which chain-link you are on depends on the *sequence + timing* of prior presses, and the bounded input command buffer cannot reconstruct it across a reconnect / long rollback. So `MeleeCombo{ [GhostField] byte Step; [GhostField] uint SwingStartTick; [GhostField] uint LockUntilTick }` is **owner-predicted replicated state** (one player-ghost re-bake; in-family with `DashCooldown`/`AbilityCooldown`). The ADVANCE writes are **ABSOLUTE functions of (restored Step, tick)** — never an in-place `prev+1` of a non-restored field — so a rollback restores the authoritative anchor and the predicted re-sim converges (the `DashSystem` idempotency idiom). *(Pre-code review PRED-1/ROLLBACK-1 — this overrode the reuse-lens scope objection: correctness over saving a re-bake.)*
**3. Damage is SERVER-ONLY in the predicted group, mirroring `ProjectileDamageSystem`** (not for a whiff-punish reason — that premise was false; the Charger stagger is a multi-tick window scored once regardless of group). The combo STATE advance + movement-commit run on BOTH worlds (predicted, idempotent, instant local feel); the cleave (collect-all enemies in the per-step cone via the new `MeleeConeMath` predicate, append `SourceTick`-stamped `DamageEvent` + stamp `KnockbackState`) runs only `if (isServer)`. `MeleeComboSystem` sorts `[UpdateAfter(PlayerControlSystem)] [UpdateBefore(DashSystem)]` so a dash overrides the swing's movement (dash-cancel) and `HealthApplyDamageSystem` ([UpdateAfter(DashSystem)]) drains the cleave the same tick. Chain `PlayerControl < MeleeCombo < Dash < Health` is acyclic (Play-validated).
**4. The combo is fully LIVE-tunable — no per-step authored blob, and the MC-6 archetype byte is DEFERRED.** The whole per-step shape derives from ~9 live `TuningConfig` knobs (damage / range / cone-half-angle / recover / chain-grace / move-scale / knockback-speed / finisher-mult / combo-length) + one finisher multiplier — so the operator tunes feel mid-Play with no recompile/re-bake. Because the melee is its own verb (own input → own system, not an ability dispatch), the `Archetype`-byte-on-`AbilityDefBlob` dispatch infrastructure is **deferred to MC-6** where it is actually needed (avoids building breadth before it earns its place; pre-code review BURST-1/F5-1/SCOPE-1).
## Consequences
- The combo grammar is **dash-in → light → light → finisher → dash-out**, with the projectile as a ranged poke. The finisher (Step == ComboLength) widens range + scales damage/knockback/recovery by `MeleeFinisherMult`; `ComboLength` is a live knob (1 = heavy-only floor, 2 = light+finisher, 3 = light·light·finisher default).
- **Status: code-complete + reviewed; the MC-4 FUN-GATE is the open operator item** — `cleaveTargetsPerSwing > 1.5` in a swarm, dash-in/cleave/dash-out chosen over the projectile when surrounded, a blind-test watcher can tell cleave from projectile by feel. Tunable live via `DEV ▲` → the new "Melee …" rows. MC-4 cannot "pass" until MC-1's feel is held (it just was). After MC-4 passes → **EB-1** (machines can die) is the next committed milestone.
- Reversible via the same fork ritual; the live-singleton picks flip at playtest. The structural shapes (Step replicated, melee-primary binding, server-only cleave) are committed.