--- 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]]