--- title: Combat Attack & Ability Feel Guidelines (research-based) date: 2026-07-20 tags: [game-design, combat, feel, clarity, animation, vfx, guidelines, lantern] status: DRAFT — pending operator review permalink: gamevault/02-game-design/combat-attack-feel-guidelines --- # Combat Attack & Ability Feel Guidelines > **Why this doc:** the 07-19 melee weight pass surfaced a systemic problem — the cleave arc telegraphs 2.6 m > (3.9 m finisher) while the axe + arm visually covers ~1.7–1.8 m; at our camera (Distance 13, FOV 55°, ~13.5 m > visible plane) the finisher arc reads at **>2× the weapon's visual reach**. Rather than patch one number, this > is the **research-based grammar for every attack and ability** in LANTERN — player melee, the 4-socket kit > (projectile / cone / zone / movement), and enemy attacks. Deep-research run `wf_1e5183c1-13c`: 21 sources, > ~95 extracted claims, 3-vote adversarial verification on the top 25 — **12 confirmed (3-0/2-0), 0 refuted > anywhere, 13 unverified only because verifier agents died on session quota (each still carries a verbatim > source quote from the fetch pass)**. The confirmed set covers the load-bearing rules: the hit-stop canon (R5), > the D4 readability hierarchy (R6), the cancel/buffer taxonomy (R7), and the SoD skillshot framing (R9). > Sources cited inline. Scope + reach-philosophy + reference set approved by operator 07-19 (full grammar · > research-derived reach proposal · +Souls/MH +D4/PoE2 schools). **The one-sentence thesis:** *feel* is made by how an attack spends its time (anticipation → contact → recovery), *clarity* is made by drawing exactly what the simulation does — when it does it — inside a strict loudness hierarchy; every rule below is one of those two statements specialized. --- ## Part 1 — The research base (what the five schools agree on) ### R1. Every attack is three phases, and the contact phase is nearly instant Decompose every attack into **Anticipation → Contact → Recovery** ([gdkeys — Anatomy of an Attack](https://gdkeys.com/keys-to-combat-design-1-anatomy-of-an-attack/)). The contact (active) portion is a *few frames*, constant speed; weight and readability are bought in the other two phases. Published example frame data (Hollow Knight's Hornet @30fps): Dash = 15f anticipation / 10f attack / 10f recovery. ### R2. Player vs enemy anticipation INVERTS For the **player**, anticipation frames trade weight against responsiveness — too few reads weightless, too many reads as input lag ([gameanim — 12 Principles in Games](https://www.gameanim.com/2019/05/15/the-12-principles-of-animation-in-video-games/), [slynyrd — Melee Attacks](https://www.slynyrd.com/blog/2018/9/8/pixelblog-9-melee-attacks)). For **enemies**, anticipation IS the telegraph and should be deliberately *longer* — its function is giving the player time to react. Telegraph lead-time formula ([gdkeys](https://gdkeys.com/keys-to-combat-design-1-anatomy-of-an-attack/)): **lead = human reaction (0.25 s) + the player's escape-move trigger time + difficulty buffer.** ### R3. Weight = slower anticipation + longer recovery — never a slowed contact Perceived weight is inversely tied to speed, but the fix is *where the time goes*: heavy weapons get slower wind-ups and added hold frames while **smear/contact frames stay few and fast** ([gameanim](https://www.gameanim.com/2019/05/15/the-12-principles-of-animation-in-video-games/), [slynyrd](https://www.slynyrd.com/blog/2018/9/8/pixelblog-9-melee-attacks)). Recovery is the *safer* place to spend weight-frames than anticipation (the action already resolved). This is the Souls/MH commitment school: weight lives in commitment, not in slow-motion swings. ### R4. Honesty: the damage volume must match what is drawn — when it is drawn - **Diablo IV's "payload" rule**: damage areas animate over frames so the hit region *tracks the VFX in time* — AoEs expand outward with the visual, melee damage areas follow the weapon's motion ([D4 Quarterly Dec 2021](https://news.blizzard.com/en-us/diablo4/23746639/diablo-iv-quarterly-update-december-2021)). - **Smash's sword-lines rule**: when a hitbox is disjointed from the body, make the *visual* honest — add trails / enlarge the weapon at full extension — rather than shrinking the hitbox ([TVTropes — Hitbox Dissonance](https://tvtropes.org/pmwiki/pmwiki.php/Main/HitBoxDissonance)). - **Monster Hunter's lesson**: hitboxes grossly beyond the visual (Plesioth hipcheck) are infamous; MH:World moved to animation-driven honesty where only the attacking appendage damages. - **Generosity has a direction**: mismatches are tuned *in the player's favor only* — small player hurtboxes, Disc Room's ~50 ms lethal-contact grace, Overwatch's inflated friendly-projectile hitboxes ([Game-feel survey, arXiv 2011.09201](https://arxiv.org/pdf/2011.09201), [TVTropes](https://tvtropes.org/pmwiki/pmwiki.php/Main/HitBoxDissonance)). - **Reach tricks are legitimate**: animators deliberately elongate the weapon/arms at max swing extent and mark the dangerous part of a swing with a trail ([slynyrd](https://www.slynyrd.com/blog/2018/9/8/pixelblog-9-melee-attacks), [gdkeys](https://gdkeys.com/keys-to-combat-design-1-anatomy-of-an-attack/)). ### R5. Hit-stop is the cheapest weight in games — scale it, cap it, spend it beyond hits Sakurai: hit-stop (a brief pause at the moment of connection) is *critical* — "the same game plays completely differently with versus without it" ([Sakurai on Creating Games — Hit Stop](https://www.youtube.com/watch?v=OdVkEOzdCPw), [Source Gaming translation of Famitsu vol. 490](https://sourcegaming.info/2015/11/11/thoughts-on-hitstop-sakurais-famitsu-column-vol-490-1/)). The canon: - **Scales with damage** (Smash: ~8f for a mid hit in Melee up to 15f in Ultimate; hard caps 20–30f ≈ 333–500 ms). - **Both parties freeze together**, same duration; the victim *vibrates* during the freeze (visual only). - **Hand-tuned per attack**, not purely formula: sword tips ×1.25, electric ×1.5, projectiles get *less*. - Old-school flat values work too: Final Fight = a universal 6f; Capcom pauses only attacker+victim, the rest of the world keeps simulating ([shane-sicienski hitstop analysis](https://shane-sicienski.com/blog/blog-post-title-one-55pmn), [SSBWiki — Hitlag](https://www.ssbwiki.com/Hitlag)). - **Spend the technique beyond hits**: "boss stop" on kill-confirms and any big moment; brief slow-mo that snaps back. ### R6. Readability = a strict loudness hierarchy + ownership colors + fill-up timing - **D4's intensity hierarchy**: visual loudness is *keyed to ability power* — spammable skills deliberately quiet, ultimates loud; a skill's VFX intensity grows as its power grows ([D4 Quarterly Dec 2021](https://news.blizzard.com/en-us/diablo4/23746639/diablo-iv-quarterly-update-december-2021)). - **D4's dark-scene rule** (LANTERN-critical): pure PBR lighting muddies gameplay readability in the dark, so combat VFX deliberately break physics — they are *emissive and cast their own light*. - **Directional, body-part hit feedback**: impacts flow with the attack's direction and emit from the struck spot, not a generic centered flash. - **WildStar's telegraph canon** ([wiki](https://wildstar.fandom.com/wiki/Telegraph)): fixed ownership colors (blue = yours, red = enemy, green = ally); a **fill line whose arrival at the border = the impact moment**; the fill *direction* encodes one-shot vs persistent effects. Documented failure mode: red telegraphs on red floors. - **SoD's cautionary tale**: even a praised skillshot game saturates into unreadability in 4-player co-op when every ability is loud ([GameCritics review](https://gamecritics.com/gc-staff/shape-of-dreams-review/)) — the hierarchy must be enforced *per on-screen actor budget*, not per ability in isolation. - **SoD's accessibility rule**: every enemy attack has a *visual* telegraph; audio is reinforcement, never the sole channel. ### R7. Responsiveness = buffers + cancel windows anchored to contact frames God of War's combat designer ([Eric Williams — Combat Canceled](https://www.gamedeveloper.com/pc/combat-canceled-i-god-of-war-i-action-game-design)): cancel-window design is a *primary determinant* of whether combat reads crisp or sluggish (Kratos: ~4,000 authored cancel branches in GoW2). The taxonomy: **partial cancels** open in windows anchored to the hit frame (pre-hit / post-hit); Ninja Gaiden allows *zero* pre-hit cancels (full commitment school); inputs arrive via a **buffer** (stored, fires when the window opens — the forgiving default) or **instant** (frame-exact). Buffer norms: a few frames up to ~0.23 s ([arXiv survey](https://arxiv.org/pdf/2011.09201)); too-long buffers execute stale intent ([yosispring — Input Buffering](https://medium.com/@yosispring/input-buffering-action-canceling-and-also-forbidden-knowledge-47a3f8a95151)). ### R8. Juice has an optimum — medium-high, not maximum Empirical result (Kao study, via [arXiv survey](https://arxiv.org/pdf/2011.09201)): medium and high juiciness outperform both extremes. The canonical kit (screen shake, recoil, color flash, particles — *Juice it or lose it*, Nijman's *Art of Screenshake*, Swink's *Game Feel*) is applied **proportionally to significance**, and the loudest levels are *reserved* so they stay meaningful. ### R9. The SoD kit grammar (our closest cousin, by design lineage) Shape of Dreams = MOBA QWER grammar (HotS + RoR2 lineage, per the [director interview](https://screenrant.com/shape-of-dreams-eunseop-shim-interview/), [Loot Level Chill interview](https://lootlevelchill.com/features/shape-of-dreams-interview-we-are-standing-on-the-shoulders-of-giants/)): manual-aim skillshots, 4 slotted "memories" + essence modifiers, **cooldown-gated (no resource)**, positioning as the core skill, short compact fights, and difficulty redesigned away from real-time pressure *so players can read their abilities*. Its praised feel comes from the MOBA cast grammar; its documented failure is VFX saturation in co-op. We inherit the first and must budget against the second. > **Research gaps (flagged honestly):** Hades primary sources (Noclip documentary, Supergiant GDC talks) and > PoE2 / Last Epoch / V Rising specifics resisted extraction (video-heavy sources) — the Hades school here rides > on the general R1–R5 principles it exemplifies rather than on quoted primary material. If we want its exact > numbers (e.g., dash-cancel windows), that's a follow-up frame-count session, not a blocker for these rules. --- ## Part 2 — The LANTERN rulebook (the grammar applied to this game) Ground truth these rules bind to: 60 Hz server-auth prediction; swing/cast anim window = `CastFacingTicks` (26t ≈ 0.43 s) < `MeleeRecoverTicks` (30t ≈ 0.50 s); cleave = instant server cone at `SwingStartTick`; camera Distance 13 / FOV 55° (≈13.5 m visible plane); Rukhanka client-derived anim; procedural FX; the murk (Exp² teal fog, emissive reads win — R6 *is* our art direction's physics). ### G1 — Time signature: every attack declares its three phases in ticks Author every player attack/ability as **(anticipation, contact, recovery)** ticks against the R1/R3 template: - **Player light verbs** (Swing1/2): anticipation ≤ 10t (~0.17 s), contact 2–4t, recovery carries the weight. - **Player heavy verbs** (finisher, slam): anticipation 15–22t, *held* top-of-swing frames allowed, contact still 2–4t. - **Enemy attacks**: anticipation ≥ **reaction + escape + buffer** = 15t (0.25 s) + dash-trigger (~6t) + 9–15t difficulty buffer → **≥ 30t (0.5 s) floor for basics, 45–60t for heavies** (Grunt/Charger windups already comply). - Contact is *never* slowed to add weight (R3). Weight lives in anticipation holds + recovery + impact (G5). ### G2 — Reach honesty: the damage cone, the weapon, and the arc must be one object **The fix for the driving complaint**, per R4 (recommended package, operator-approvable line by line): 1. **Damage resolves at the contact tick, not the swing-start tick.** Today the cleave lands at `SwingStartTick` while the animation's blade contact is ~16t later — damage precedes the visible hit. Move the server cone resolve to `SwingStartTick + ContactTick` (per-step, from the clip's WindUp take length). This is D4's payload rule (hit region tracks the visual *in time*) and makes the arc a *true* record of the blade. *(Netcode note: a scheduled-tick pattern identical to enemy `AttackWindup` — well-trodden, but it is a gameplay change: light melee gains a ~0.2 s windup. The heavy fantasy wants exactly this; flag for feel-test.)* 2. **Shrink the drawn arc to the real cone and the real cone to the believable weapon**: `MeleeRange` 2.6 → **2.2 m** (axe tip ~1.8 m + a 0.4 m player-favoring margin per R4-generosity; still comfortably beats enemy melee range), finisher ×1.5 → **×1.25 range** (keep ×1.5 on damage/recover — reach was the offender, not power). 3. **Sell the remaining reach visually** (R4 reach-tricks): scale the axe ×1.25 (Synty proportions absorb it); anchor a short **weapon-tip trail** (the smear) that traces the actual swept edge from the blade, not a body-centered ring; keep the ground arc as the *hit-region* record but draw its outer rim AT 2.2 m so rim = truth. 4. **The arc sweep must finish WITH the contact** (R6 fill-timing): sweep-reveal completes at the contact tick, then only fades — never still-painting reach after the blade has passed. ### G3 — Ownership palette (the WildStar canon, LANTERN-toned) Locked vocabulary across every system (already ~true; now it's law): - **Enemy threat = red family** (per-kind hue variants stay: amber/crimson/toxic/yellow) — ground decals only for *incoming* danger, fill-to-border = impact moment (already our `EnemyDangerTelegraphSystem` contract). - **Local player = warm-white/teal weapon-anchored FX**; the finisher's lamp-amber is *the* warm accent (light is territory — warm = ours). - **Ally = dimmer cool-blue of the same shapes** (RemoteSlashColor already complies). - **Never red for player/ally anything; never warm-amber for enemies.** Murk-red-on-red check: the seabed is teal — we're structurally safe from WildStar's red-floor failure, keep it that way (no red biomes without re-audit). ### G4 — Loudness hierarchy + the co-op budget (SoD's failure is our ceiling) Priority when pixels compete: **enemy telegraphs > local player contact/impact > ally attacks > ambient**. - Per-ability loudness is keyed to *cost/cooldown/power* (R6-D4): the melee chain and spammable sockets stay quiet (current toned arc = correct); big-cooldown sockets get the loud pass; "ultimate-grade" reserved for rare moments. - **Budget rule:** at 4 players + a wave on screen, ally FX auto-degrade first (fewer particles, shorter lives — a `FeelConfig` ally-fx scalar), enemy telegraphs *never* degrade. Add a dev toggle that spawns the worst case (4 casters + 12 enemies) as the standing saturation test. - **All combat VFX are emissive** (R6 dark-scene rule) — never rely on scene light to read an attack in the murk. Audio reinforces but never solely carries a threat (SoD accessibility rule; our per-kind windup voices are the reinforcement layer, the decal is the message). > ✅ **SHIPPED 07-21 (H)**: the budget rule — `SaturationMath.AllyScale` off the live enemy count degrades > ally-attributed FX only (remote arc brightness + remote shakes; FeelConfig `AllyFxDegradeStart/Full/Floor` > 8/16/0.35); enemy telegraphs structurally never degrade; the standing saturation test = DebugOverlay > `- Saturation (G4) -` (Spawn 12 Drowners + the 3-fake-caster stress toggle). [[Socket_Honesty_Saturation_Build_Spec]] ### G5 — Impact ladder (hit-stop canon mapped to our camera-only stack) `Time.timeScale` stays banned (netcode); our hit-stop = camera `Hold` + FOV kick + flash — same ladder, per R5: - **Scale with significance:** chip hit = flash only; solid melee hit = 2-frame hold (current) + directional hit-flash; **finisher/slam connect = 4–5 frame hold + the victim-vibrate trick** (offset the enemy's *render* child ±3 cm for the hold frames — visual only, zero sim); kill = the loudest (existing kill-pop) — a "boss-stop" grade hold (~8 frames + flash) reserved for elite/boss kills. - **Per-attack hand-tuning is expected** (R5): expose the hold frames per verb in `FeelConfig`, don't derive. - **Cap it:** ≤ 8 frames (~130 ms) — beyond Smash's feel-cap territory scaled to our pace. - Directional feedback (R6): hit-flash bursts flow *along the swing direction* from the struck body part (`EmitColored` already takes a position — use the contact point, not the enemy center). ### G6 — Cast grammar for the 4-socket kit (the SoD inheritance, made explicit) - **Move-cast is the default** (SoD/MOBA school): sockets never self-root; the cast window turns the body (`CastFacingTicks`) while legs keep the move vector (already our model — now it's the rule for every new Spark). - **Cooldown-gated, no resource** (R9) — matches the socket model; any future resource is a per-Spark exception. - **Every socket declares its G1 time signature + a G3-compliant visual**: projectile = muzzle + tracer (its trail *is* its honesty); cone = the G2 arc rules; zone = a WildStar-grade fill decal (fill completes at damage start, one-shot vs persistent encoded by fill direction per R6); movement = trail + i-frame shimmer (no telegraph — it's an escape verb). - **Skillshots stay honest**: no homing-by-default; assist only where a Spark explicitly buys it (the existing `AutoTargetRange = 0` default per DR-050). > ✅ **SHIPPED 07-21 (H)** — the cone + zone halves: the Cone socket (SpecialSlam) damages at its CONTACT tick > (`ConeContactPending`, knob 32, 0 = legacy; cue latched to contact, arc reveal ends at contact) and zones draw > the WildStar-grade fill decal (ZoneEffect GhostFields Caster/Radius/NextTick; `ZoneTelegraphSystem` — rim = true > radius, fill arrival = the damage moment, on the INTERPOLATED timeline; mine warm/teal vs ally dim-blue). > NOTE: no current frame loadout sockets a Cone spark — the archetype is honest-but-dormant until one does. > Projectile tracer + movement trail were already true; per-Spark G1 time signatures remain a designer pass. > [[Socket_Honesty_Saturation_Build_Spec]] ### G7 — Responsiveness contract (buffers + cancels, netcode-shaped) - **Input buffer ≈ 6–10 ticks (0.1–0.17 s)** for melee-chain and socket presses: a press during the last ~10t of a lock fires on the first legal tick (R7 buffer method; our `InputEvent`s already latch per tick — the buffer is a server-side "held intent" check against `LockUntilTick`, deterministic and rollback-safe). Never longer — stale intent (R7's SSBU warning). - **Cancel taxonomy per class fantasy** (R7): the **Bathynaut commits** — no pre-contact cancel on swings (Ninja Gaiden school; the dash already refuses to interrupt a swing — keep); **post-contact, dash-cancel opens** (spend recovery on weight but hand back agency for the escape verb — the Souls-roll compromise). The **Harpooner** is the crisp school: pre-contact cancel on line-casts at the cost of the cooldown tick. - Movement-commit (`MeleeSwingMoveScale` 0.35) is the melee's "self-root lite" — keep; it is the commitment read. ### G8 — Enemy telegraph compliance (mostly ratifying what exists) The existing system already matches canon (per-kind color + shape + voice, fill-ends-at-impact, strike beep at 8t). Additions to make it law: every NEW enemy attack ships **decal shape = true damage volume** (G2 honesty applies to enemies doubly — their generosity direction is *toward the player*: decal may be ≥ the true volume, never smaller); anticipation floors per G1; one **signature silhouette pose** per kind's windup (readable without the decal, R2). --- ## Part 3 — Review items (the forks in this doc) | # | Fork | Recommendation | Status | |---|---|---|---| | 1 | **Damage at contact tick** (G2.1) — light melee gains ~0.2 s real windup | DO — it's the honest heavy-weapon model; feel-test behind a knob | ✅ SHIPPED 07-20 (`MeleeCleavePending` schedule-and-consume; knob `MeleeContactTicks`, 0 = legacy) | | 2 | **Reach retune** (G2.2) — `MeleeRange` 2.6→2.2, finisher reach ×1.5→×1.25 | DO with #1 as one package | ✅ SHIPPED 07-20 (`MeleeFinisherRangeMult` knob 30) | | 3 | **Weapon scale ×1.25 + tip trail** (G2.3) | DO — cheapest honesty win | ✅ SHIPPED 07-20 (axe ×1.25; blade-smear ribbon; arc reveal ends AT contact) | | 4 | Victim-vibrate + finisher hold ladder (G5) | DO — pure presentation | ✅ hold ladder SHIPPED (`FinisherHoldFrames`); vibrate CUT (Rukhanka inverse-fold no-op, review C8) → **superseded 07-21 by the enemy HIT-REACT pass**: React/Stagger anim tiers off Health-drop edges, windup-honesty gated ([[2026-07-21_Hit_React_Pass_G]]) | | 5 | Input buffer 6–10t (G7) — a small predicted-sim change | DO after #1 lands (same system) | ✅ SHIPPED 07-20 (`BufferedAttackTick` GhostField; unlock-edge validity, batch-slack 4) | | 6 | Class cancel policies (G7) — Bathynaut commit / Harpooner crisp | Ratify as design law | ✅ LAW + Bathynaut side SHIPPED (dash refused pre-contact); Harpooner side lands with the Harpooner | | 7 | Hades frame-count follow-up session (fill the research gap) | Optional, low priority | ☐ operator | > Implementation record: [[2026-07-20_Melee_Feel_Forks_B]] (design review `wf_000bc247`: 19 confirmed findings folded in, 5 refuted). *Checklist for any new attack/ability (the doc in one box):* declare G1 ticks → G2 volume==visual==timing → G3 color → G4 loudness tier → G5 impact tier → G6 cast grammar → G7 buffer/cancel policy → (enemy) G8 decal+pose.