Files
Project-M/Docs/Vault/07_Sessions/2026/2026-06-10_MC4_Combo_Melee.md
T
kronic 5b0af63a3b 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>
2026-06-10 17:23:10 -07:00

7.4 KiB

date, type, tags, permalink
date type tags permalink
2026-06-10 session
session
combat
mc-4
melee
netcode
prediction
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.

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