DRIFTLANDS co-op survival on drifting tides — studio progress

Combat System

Status: Designed — independent review applied
Independent /design-review: NEEDS REVISION 2026-08-26 — all findings applied same day (C1 F4 example corrected; C2 telegraph hard floor 350→400 = Rule 11 contract; C3 stale tide cross-ref reconciled; H1 pairwise swing-guard ranges; H2 dodge-economy worst case quantified; H3 F5 tiebreak + AC-17b; H4 party-size scaling contract; H5 gear-shareability Pillar 5 constraint; H6 jitter AC-24b; M1–M7; L-pass. Full report: design/reviews/combat-system-review-2026-08-26.md)
Author: Jeroen + agent panel
Last Updated: 2026-08-26
Creative Director Review (CD-GDD-ALIGN): APPROVED 2026-08-26 (3 advisory notes: Edge Case 16 = playtest watch item — fix via dash tuning, never lag compensation; helpless wait owned by Open Question 5; Enemy AI quick-spec inherits a telegraph-density constraint)
Implements Pillar: Pillar 4 — Scrappy Hands, Serious World (primary); serves Challenge aesthetic (#4) and Pillar 5's new-friend floor
Prototype Reference: prototypes/combat-netcode-feel-concept/REPORT.md (hypothesis CONFIRMED 2026-08-25)

Overview

Combat is Driftlands' moment-to-moment action layer: fast, top-down dash-and-strike fighting where you close on an enemy, read its telegraphed attack, dodge through or around it, and answer with a melee swing — positioning and timing beat gear-checks, and friction lives in the world, never in your hands (Pillar 4). Every fight exists in service of the loop: enemies stand between you and the resources on a docked land, and deeper inland means nastier inhabitants — difficulty is spatial, chosen by how far you push, never statistical scaling. Character power is flat by design; the only combat progression is the crafted gear you bring (Pillar 3/5 — a brand-new friend fights with the same verbs as a veteran). Beneath the action sits a prototype-confirmed network model that shapes the design itself: each player's movement and dash are instant because they own them; all damage is decided by the host; and strikes are built with deliberate windup-and-swing duration so network delay disappears into the fiction of a sword taking time to swing. MVP ships one weapon type plus dash against 2–3 telegraphed enemy types; the system must stay readable at a glance under the Tideline silhouette rule.

Player Fantasy

Combat's fantasy is the fight you win with your feet — earned reflexes, not earned power. You feel graceful before you feel strong: the strike is deliberate (windup, swing, recovery — a sword takes time), so every hit you land is one you placed, and every hit you take is one you misread. The anchor moment: deep in the drifting marsh, twenty minutes before it departs, three raiders close in. You don't out-damage them — you read the third one's windup, step through the gap between two swings, and put your strike exactly where the opening was. No health bar dipped below half. The arc of a good fight is focus → flow → clean, almost silent triumph — the reward is the movement working, not a fanfare. This is Competence made visible (concept: "combat is a visible skill you improve at") at its honest MDA rank: Challenge is aesthetic #4 — combat serves the loop, it doesn't headline it.

Its necessary shadow is the fight you didn't want: the bag three-quarters full of tin, violet climbing on the horizon, a boar-camp guarding the last vein. You take the hit rather than lose the run, because ten minutes costs more than ten HP. Combat's stakes are always opportunity cost under the tide's deadline — never loss spirals, never punitive framing. And at the session level, the individual fantasy compounds into a group one: a fight that took five deaths three weeks ago is clean and wordless tonight, and someone types "we're actually good at this now."

**Tone and design tests this fantasy imposes (binding on future combat features):**

Reference anchor: Hades — fast, readable, joyful top-down handling; enemies telegraph, you dodge, positioning beats gear-checks. Two deliberate divergences from the anchor, so Hades-calibrated players arrive braced: there is no attack-cancel out of Active/Recovery (commitment is the point, Rule 3), and hits are host-resolved with no rollback — a problem space Hades, being single-player, offers no precedent for (Edge Case 16).

Detailed Design

Core Rules

States and Transitions

Player combat actor (Hurt is a non-blocking overlay — knockback/flash atop the current state, never an input lockout; Pillar 4):

From Event To Notes
Idle/Movedash input (off cooldown)Dashingi-frames on; cooldown starts now
Idle/Movestrike inputStrike:Windup—
Dashing0.14 s elapsedIdle/Move—
Strike:Windupwindup elapsedStrike:Active—
Strike:Windupdash inputDashingthe bail-out; swing intent revoked
Strike:Activeactive elapsedStrike:Recoverycommitted — nothing cancels this
Strike:Recoveryrecovery elapsedIdle/Move—
any live statedamage takensame state + Hurt overlaynon-blocking flash + nudge
any live stateHP ≤ 0, ally alive on landDownedcrawl, no combat, bleed-out timer
any live stateHP ≤ 0, otherwiseDeadrespawn home, full HP, all items kept
Downedally revive channel doneIdle/Movepartial HP (knob)
Downedarrives home (tide deposit, Rule 13; rejoin after departure, EC2)Idle/Movestabilizes at reviveHPFraction, timer cancelled (Edge Case 8; ruled 2026-08-27)
Downedbleed-out expiresDead—

Enemy combat actor (interface): `Idle/Patrol (AI-owned) → Telegraph → AttackActive → Recovery → (Stagger from any strikeable state, per meter) → Death`. Telegraph is interruptible only where the enemy's flag allows; AttackActive is never interruptible; Recovery is the universal punish window.

Feedback timing (authority table):

Effect Timing
Windup/swing animation, whiff sound, blade trail, recovery lockoutInstant — client-predicted (outcome-independent)
Impact VFX, hitstop, damage number, victim stagger/knockback, deathHost-confirmed broadcast only

Prototype evidence: hit→stagger ~150 ms at 141 ms RTT read as "the swing taking time," not lag.

Same-tick simultaneity: two players striking one enemy → both hits resolve independently, both apply; **kill credit is deliberately not tracked** (no drops, no kill-based progression — stated to close off future scope creep). Enemy dies before a swing's resolution instant → honest whiff. Player and enemy killing blows on the same tick → both apply (enemy damage resolves first, then the player's; you can die and still fell the enemy — neither event cancels the other; locked 2026-08-26).

Interactions with Other Systems

System Combat consumes Combat provides Interface owner
Player Movement (quick-spec ✅ 2026-08-27)Owner-authoritative transform; move/dash executed on the movement railDash parameters, state-machine gates (e.g., Downed forces crawl speed)Movement owns locomotion; Combat owns when verbs are legal
Netcode Session (quick-spec ✅ 2026-08-27; ADR pending playtest)Host tick, RPC delivery, NetworkTransform interpolationSwing-intent message shape (→ ADR), feedback-authority tableCombat owns rules; ADR owns mechanisms
Crafting & Gear (GDD Reviewed 2026-08-26)DamageModifier, DamageReduction per equipped itemThe two hook signatures; flat-stat guarantee (gear never touches HP/speed/cooldowns)Combat owns hooks; Crafting owns values
Enemy AI (quick-spec ✅ 2026-08-27)Attack initiation calls (EnterTelegraph(attackId))The full enemy combat interface (Rule 11): telegraph contract, stagger/knockback, interrupt flag, archetype exemplarsCombat owns everything downstream of the attack decision
Tide Scheduler / Land Dock/UndockDocked boolean (re-sampled at resolution); soft-close eventThe soft-close combat rulings (Rule 13)Tide owns the clock; Combat owns combat-state consequences
UI/HUD (quick-spec ✅ 2026-08-27)—HP display data, Downed ally indicator (edge-of-screen, reuses art bible §7.2 hat-silhouette system)UI owns presentation

Not consumed: Harvesting (parallel peer — shares only the re-sample-at-resolution principle), Save & Persistence (combat state is never saved: HP/downed/aggro are session state; players load at full HP).

Formulas

All combat math is deterministic — no RNG anywhere in the damage path. Rationale: multiplayer consistency (host and clients must agree on displayed outcomes) and the Player Fantasy's ban on invisible stats and one-shot randomness. Every combat outcome is a pure function of positioning, timing, and gear tier. Zero damage variance is a rule, not a tuning default: a damage roll — even a small, UI-visible one — decouples "I placed that hit well" from "that hit did X," which is exactly the fantasy this system sells.

Rounding is pinned: round() throughout this section means round-half-away-from-zero (MidpointRounding.AwayFromZero in C#) — never the C# Math.Round banker's default. An unpinned mode would let designer hand-math and host results silently diverge at future tier values, violating AC-9's bit-identical promise.

Baseline constants (cloth tier, MVP)

Constant Value Rationale
Player max HP100Round number; mistake-cost math reads as fractions of it
Cloth weapon base damage28Derived to hit the 2–4-hit TTK band against the archetype HP bands below
Unarmed base damage14Fists exist (cross-GDD review D2, 2026-08-27): half the cloth sword, same arc/range/phase constants, staggerHitContribution 18. A fresh world's first fight — before any workbench or sword — is defined, never zero, never null. Deliberately outside the 2–4-hit band: unarmed is the pre-sword state and the first sword must read as a real upgrade
Rusher HP / damage45–65 HP / 10–16 dmg per hitInterface band for Enemy AI — fast, low-HP, punishes greed
Spacer HP / damage55–75 HP / 14–20 dmg per hitMid — ranged pressure, not melee-tanky
Bulwark HP / damage90–112 HP / 18–26 dmg per hitSlow, hits hardest; HP capped at 112 so 4 cloth hits (112 dmg) always kill — 120 would need a 5th hit and breach the TTK target

TTK check (cloth, zero variance): 28 dmg/hit → Rusher 2–3 hits, Spacer 2–3 hits, Bulwark 4 hits across its full 90–112 band (3 × 28 = 84 falls short of 90; 112 is the exact 4-hit boundary — why the cap sits there). All inside the 2–4 placed-hits target. TTD check (mistake budget): a "mistake" = one unmitigated hit. Against Bulwark's top damage (26): ⌈100/26⌉ = 4 → downed on the 4th mistake. Against Rusher (10–16): 7–10 mistakes. Worse enemies costing more mistakes is intended — difficulty is spatial, and Bulwarks live deeper inland. Unarmed TTK (14 dmg/hit, cross-GDD D2): Rusher 4–5 hits, Spacer 4–6, Bulwark 7–8 — fightable on day one, clearly worse than the sword. **Reinforced cloth garb TTD (crafting's per-item DamageReduction 0.10, cross-GDD D1): Bulwark 26 → round(26 × 0.9) = 23 → ⌈100/23⌉ = 5** mistakes (from 4); Spacer 20 → 18; Rusher 16 → 14 → 8 mistakes (from 7). The deep band buys one extra mistake against the enemy that guards it.

F1. damageDealt

damageDealt = round(weaponBaseDamage × damageModifier)

Variables:

Variable Symbol Type Range Description
Weapon base damageweaponBaseDamageint14 (unarmed — empty weapon slot) / 28 (cloth); higher tiers TBD by Crafting GDDFlat per-hit value from the equipped weapon, data-driven; an empty weapon slot reads the unarmedBaseDamage constant (D2)
Gear damage modifierdamageModifierfloat1.0 (cloth) – ~2.2 (iron, provisional)Multiplicative gear-tier scalar — the ONLY scaling input; no crit, no RNG, no character stat contributes
ResultdamageDealtint≥ 0Final damage value passed to F2

Output Range: bounded in practice by the tier ladder (cloth → iron); always deterministic. Higher-tier damageModifier values are **provisional until the Crafting GDD locks them** — Combat owns the hook, Crafting owns the values. Example: cloth weapon, cloth modifier: 28 × 1.0 = 28.

F2. damageTaken

damageTaken = max(0, round(incomingDamage × (1 − damageReduction)))

Variables:

Variable Symbol Type Range Description
Incoming damageincomingDamageintper-enemy band (10–26 at MVP)Raw hit value, pre-mitigation
Gear damage reductiondamageReductionfloat0.0 (cloth) – ~0.35 (iron, provisional)Percentage mitigation from equipped armor tier
ResultdamageTakenint[0, incomingDamage]Final HP deducted

Output Range: [0, incomingDamage], rounded, floored at 0. Percentage chosen over flat reduction (rule, with rationale): flat reduction makes weak enemies trend toward zero damage at high armor tiers — "whiff-harmless" trash collapses the spatial-difficulty premise. Percentage keeps every archetype's relative threat stable across gear tiers: a Bulwark is scarier than a Rusher at every tier; a veteran in iron still respects a Rusher swarm, they just survive it longer. Example: Bulwark hit for 22, cloth armor: round(22 × 1.0) = 22.

F3. hitArcTest

`hitConfirmed = (angleTo(target) ≤ baseArc/2 + arcTolerance) AND (distanceTo(target) ≤ baseRange + rangeTolerance) AND targetIsValid(target)`

Variables:

Variable Symbol Type Range Description
Angle to targetangleTo(target)float (deg)[0, 180]Between attacker's aim direction and vector to target, at the resolution instant
Weapon arcbaseArcfloat (deg)100 (cloth baseline)Native swing arc
Arc tolerancearcTolerancefloat (deg)+12 (locked, Rule 7)Fixed forgiveness — never scaled by RTT
Distance to targetdistanceTo(target)float (u)≥ 0At the resolution instant
Weapon rangebaseRangefloat (u)1.6 (cloth baseline)Native reach
Range tolerancerangeTolerancefloat (u)+0.2 (locked, Rule 7)Fixed forgiveness — never scaled by RTT
Target validitytargetIsValidbool—False for dead/despawned enemies and for allied players (friendly fire off — allies are transparent)
ResulthitConfirmedbool—Gate before F1/F2 apply

Output Range: boolean; evaluated by the host at the fixed resolution instant only — no position history (Rule 7). Example: enemy 46° off-center at 1.75 u: half-arc + tolerance = 50° + 12° = 62° ≥ 46° ✓; range + tolerance = 1.8 u ≥ 1.75 u ✓; alive ✓ → hitConfirmed = true.

F4. staggerMeter

staggerMeter_new = min(staggerThreshold, staggerMeter_old − decayRate·Δt + hitContribution') where hitContribution' = hitContribution × 0.5 if the previous hit on this enemy was < diminishWindow ago, else full value; meter floored at 0.

Variables:

Variable Symbol Type Range Description
Prior meterstaggerMeter_oldfloat[0, 100]Accumulated at last evaluation
Hit contributionhitContributionfloatper-weapon (cloth: 35; unarmed: 18)Halved inside the diminishing window — prevents stagger-lock from rapid multi-hits
Diminish windowdiminishWindowfloat (s)1.0 (knob)Window for the 50% reduction
Passive decaydecayRatefloat (/s)20 (knob)Applied while not being hit
ThresholdstaggerThresholdfloat100 (constant)Stagger fires at threshold; meter resets to 0
ImmunitypostStaggerImmunityfloat (s)1.5 (knob)Enemy cannot be re-staggered during this window

Output Range: [0, 100], clamped both ends. Example: Rusher at meter 40 hit inside the window (35 → 17.5): min(100, 40 + 17.5) = 57.5, no stagger. A full-value hit (+35) one second later, minus 1 s of decay in the gap (−20): `min(100, 57.5 − 20 + 35) = 72.5`. Another full-value hit one second after that (−20, +35): min(100, 72.5 − 20 + 35) = 87.5; the next hit — even halved (+17.5) — crosses the threshold (87.5 + 17.5 ≥ 100) and fires the stagger; then 1.5 s immunity.

F5. knockbackApplied

knockbackApplied = knockbackVector(argmax over same-tick contributions of |force|)

Variables:

Variable Symbol Type Range Description
Contributionscontributionslist (vector, magnitude)≥ 1All knockback-causing hits resolved the same host tick against one target
ResultknockbackAppliedvectorbounded by the contributing weapon's knockback capOnly the single largest contribution applies — never summed

Output Range: bounded by the largest contributor's own cap. Tie rule (determinism): on an exact magnitude tie, the contribution from the attacker with the lowest OwnerClientId applies — host-stable and deterministic; never summed, never order-of-arrival (zero-variance rule). Example: two players hit the same enemy the same tick, 4 u and 7 u in different directions → only the 7 u vector applies; the 4 u is discarded.

F6. Revive parameters — knobs, not formulas

Revive channel time, revive HP fraction, and bleed-out duration are direct designer levers with no derived math — they are defined in Tuning Knobs. Bleed-out expiry transitions Downed → Dead → home-island respawn per the state table. Ranges, defaults, and failure modes live in the Tuning Knobs table — F6 deliberately carries no variable-table/example block because there is no derived formula to document.

Edge Cases

Tide soft-close edge cases (in-flight swings, enemy attack cancellation, downed deposit, aggro discard, Docked re-sampling) are resolved in Core Rule 13; same-tick simultaneity in States and Transitions. The cases below cover the remaining rule space.

Disconnects

Downed cluster

Boundary values

Spatial

Timing

Interrupt flag

Dependencies

Upstream (Combat depends on):

System Hard/Soft Interface consumed Status
Player MovementHardOwner-authoritative transform rail; move/dash execution; boundary rules for dash/knockback vectors (Edge Case 13 — answered by movement Rule 4: collision blocks and never damages, water is a hard edge, so a dash/knockback vector simply stops at the boundary)Quick-spec ✅ 2026-08-27 — contract confirmed (status updated cross-GDD W6)
Netcode SessionHardHost tick, swing-intent RPC delivery, NetworkTransform interpolation, telegraph-phase replication (Edge Case 17)Quick-spec ✅ 2026-08-27; ADR gated on friend playtest
Tide Scheduler (via Land Dock/Undock)HardDocked boolean re-sampled at resolution; soft-close event ordering (Rule 13)GDD Reviewed; independent review APPROVED WITH CONDITIONS 2026-08-26
Crafting & GearSoftDamageModifier / DamageReduction values per item — combat runs correctly at cloth defaults (1.0 / 0.0) with no gear system presentGDD Reviewed 2026-08-26
Art bibleConstraint sourceSilhouette/telegraph/feedback rules (§1, §4.x, §5.x, §7.2, §8.A.4) — design-time, not runtimeApproved

Downstream (systems that depend on Combat):

Dependent Hard/Soft Interface provided
Enemy AI (quick-spec ✅ 2026-08-27 — the mandated density AC exists: its Rule 6 caps simultaneous uninterruptible telegraphs per target at 1, with a dedicated criterion)HardThe enemy combat interface (Rule 11): telegraph contract incl. 400 ms floor, stagger/knockback/interrupt flags, archetype bands (Formulas baseline table), EnterTelegraph(attackId) boundary. Inherited constraint (binding, CD advisory 2026-08-26): uninterruptible telegraphs must never combine with enemy density into a de facto player lockout — the CC ban is on the experience, not just the mechanism; the quick-spec must carry an acceptance criterion capping simultaneous uninterruptible telegraphs targeting one player at MVP density. Party-size scaling contract: F1/F2 apply per-hit with no party-size term — simultaneous strikes fully stack by design (zero-variance rule); if playtest shows 5-player mob-shredding, the lever is enemy HP bands / spawn density (Enemy AI + Land Dock/Undock), never per-hit damage reduction
Crafting & Gear (GDD)HardThe two hook signatures + flat-stat guarantee (gear never touches HP/speed/cooldowns). Durability events (added 2026-08-27, cross-GDD W2 — closes crafting OQ5's combat half): host-side SwingLanded(attacker, weaponInstance) per confirmed hit and DamageAbsorbed(victim, armorInstance) per confirmed damage-taken, consumed by crafting F3 — emitted only on host confirmation (feedback-authority table), never client-predicted; unarmed swings emit nothing. Binding Pillar 5 constraint: gear must be equippable by any party member regardless of who crafted it — a new friend handed the group's spare weapon fights at parity in their first session (no binding, no crafter locks)
UI/HUD (quick-spec)SoftHP display data, Downed edge-of-screen indicator
Base-Defense Events (Tier 1, first-to-slip)DeferredNone designed — deliberately, per phase brief

Bidirectional-consistency obligations:

Forward references owned elsewhere: swing-intent message shape, resolution-instant scheduling in NGO ticks, tick-order guarantee for soft-close-before-combat, telegraph-phase NetworkVariable, and tide Edge Case 8's position-snapshot interface → netcode ADR. Dash/knockback boundary behavior at shore/water edges → Player Movement quick-spec. Gear tier values → Crafting GDD.

Tuning Knobs

Not tuning knobs (rules, locked by this GDD): zero damage variance, friendly-fire off, keep-everything death, no field healing at MVP, host resolution with no position history, both-apply mutual kills, telegraph pose-change-first, i-frames' existence on dash. Changing any of these is a design change requiring GDD revision + CD review, not a config edit.

Knob Default Safe range Too low Too high Interacts with
moveSpeed6 u/sPlayer Movement's knob (safe 5–8) — cited, not owned (cross-GDD W4, 2026-08-27); combat's feel band is 5–7 within itSluggish, kiting diesEnemies can't pressuredash ratio; enemy speeds (AI spec); a retune re-runs a combat feel pass (movement Rule 7)
dashSpeed22 u/s18–26Dash feels like a walkTeleport-y, breaks readabilitydashDuration (distance = speed × duration)
dashDuration0.14 s0.10–0.18Blink; i-frame window uselessLong invuln window trivializes telegraphsi-frame window = full dash duration
dashCooldown0.45 s0.35–0.60Chain-dash spamDash stops being a reliable answer to telegraphstelegraphFloor — a dodge must be available for most telegraphs
swingWindup0.10 s0.08–0.15 (pairwise: 0.08 legal only if swingActive ≥ 0.10)Latency stops hiding in the swingStrikes feel mushyGuard: swingWindup + swingActive ≥ 0.18 s (≥ 140 ms RTT ceiling + margin — the resolution instant must sit ≥ 1 RTT after intent, or Rule 7's arc test loses its forgiveness-for-free property; the same guard also narrows the Edge Case 16 dash-state race — a dash message gets ≥ 0.18 s to reach the host before resolution)
swingActive0.10 s0.08–0.15 (pairwise: 0.08 legal only if swingWindup ≥ 0.10)same guardSweeps feel floatysame guard
swingRecovery0.05 s0.03–0.10No commitment costWhiffs overly punisheddash-cancel only exists in windup
baseArc100°80–120Precision-demanding, anti-new-friendAim stops matteringarcTolerance
baseRange1.6 u1.4–2.0Face-huggingSpear gamerangeTolerance
arcTolerance+12°+8–15"Game cheated" rejections returnSword feels dishonestly longre-validate feel at 140 ms RTT after any change
rangeTolerance+0.2 u+0.1–0.3samesamesame
playerMaxHP10080–150Mistake budget < 3 vs BulwarkDamage numbers lose meaningTTD check (Formulas)
weaponBaseDamage (cloth)2824–32Bulwark needs 5 hits (TTK breach)Rusher band compresses to a flat 2-hit kill (only the 65 HP top still takes 3) — the 2–3 variance diesre-run the TTK band check after any change; archetype HP bands
unarmedBaseDamage1410–18Fresh-world bootstrap fights become unwinnable (Bulwark > 10 hits)The first sword stops reading as an upgradeweaponBaseDamage (cloth) — keep unarmed ≤ 0.6× cloth; re-run the unarmed TTK note after any change (D2)
telegraphFloor400 ms400–600, hard floor 400 (= the Rule 11 contract)Remote clients lose the read (latency budget, Rule 11)Enemies feel sluggishdashCooldown, dashDuration
staggerHitContribution (cloth)3525–50Stagger never firesStagger-locks light enemiesstaggerThreshold 100 (constant), diminishWindow
diminishWindow1.0 s0.5–2.0Multi-player stun-lock returnsCo-op focus fire feels deaddecayRate
decayRate20/s10–40Meters effectively never resetStagger unreachable solo—
postStaggerImmunity1.5 s1.0–3.0Re-stagger chainsStagger feels pointless—
reviveChannelTime3.0 s2–5Revives free under fireRevives impossible mid-fightenemy pressure (AI spec)
reviveHPFraction40%25–50%Revived = instant re-downDeath loses its biteplayerMaxHP
maxTargets (proposed by ADR-0015, pending GDD assent)11–3Single-target only (the reading every AC and TTK check assumes)Cleave — re-run TTK bands and the "mob-shredding lever is density, never per-hit damage" position firstweaponBaseDamage, enemy density; F3 is a per-target predicate silent on cardinality
reviveReach (proposed by ADR-0015)1.5 u1.0–2.5Revives need face-huggingRevive from safetyreviveChannelTime; reviver leaving reach = a 4th channel-cancel cause beside EC5 (damage) and EC6 (dash), consistent with "anything other than standing-still-channeling cancels"
bleedOutTimer45 s30–60No time for a rescue runHelpless too long (see Edge Case 4)land size / travel times
hitstopDuration0.05 s0.03–0.08Hits feel weightlessCombat stutterslocal presentation only — never pauses host simulation or other players' views

Knob-interaction warnings: weaponBaseDamage, playerMaxHP, and the archetype HP/damage bands form one balance system — retune together and re-run the Formulas TTK/TTD checks. dashCooldown + dashDuration vs telegraphFloor define the dodge economy: a telegraph must usually be answerable by an off-cooldown dash — quantified: after a dash starts, the next dash returns at dashCooldown (0.45 s default, 0.60 s max), so a telegraph opening within the first dashCooldown − telegraphFloor of a dash (0.05 s at defaults, up to 0.20 s at knob extremes) goes active before the dash returns and cannot be answered by dashing. This is a stated design position, not a bug: dash is the premium dodge, not a guaranteed one — movement and spacing remain the fallback answer, and Open Question 8 probes whether that fallback is actually exercised. The swing-phase guard (≥ 0.18 s to resolution) is a netcode invariant wearing a feel-knob costume — violating it silently breaks Rule 7's no-history design; enforce it at config load. All values are data-driven (per coding standards), validated at load per Edge Cases 10–12; prototype baselines are starting values, not gospel — the friend-group playtest retunes them.

Visual/Audio Requirements

Combat's visual contract is owned by the art bible; this section states what combat imposes and cites rather than restates.

Telegraph language (binding, per Rule 11): the primary channel is a silhouette/pose change readable in solid black (bible §1, §3.1, §5.2) — if windup and idle silhouettes are near-identical, redesign the pose, never lean on color. Crimson rim-accent on the attacking limb is reinforcement only, never full-body (would collide with Stagger White's "impact" meaning); hard-edged flat ground decals (no gradients, §6.2) only for AOE/lunge attacks. **Combat sprites and telegraph VFX are never tide-tinted** — they sit outside §8.A.4's DriftLayer tinting scope (art-bible fix-pass item: state the combat-sprite exemption explicitly) — telegraphs must read identically under amber, neutral, and violet.

Hit feedback stack (ranked): 1) hitstop (local presentation only, capped per Tuning Knobs), 2) Stagger White one-frame silhouette flash (§4.1/§4.2 — luminance-based, survives tint and colorblindness), 3) knockback displacement, 4) small flat-colored particle burst — palette-locked, low count, Nuclear Throne restraint (§9), 5) sound landing the same frame as hitstop start. Rejected: screen shake beyond a small cap (breaks co-op silhouette tracking), any particle that obscures a silhouette edge, color-flicker VFX, gradient flashes.

Silhouette-first test for combat assets (pass/fail): at solid black, 64–96 px (§5.4): enemy identity and threat tier legible; windup pose distinct from idle; with 2–5 players and 2–3 enemies on screen, the telegraphing enemy is not lost among allies (enemy proportions never overlap the player's 1:2.5 ratio, §5.2); ground decals hard-edged; everything readable while the world moves (§1).

Player states: dash = 2-pose stretch-blur (§5.3), shape-only, no color change. Hurt = Stagger White flash on the player's own sprite — one shared impact language for dealing and receiving. **Downed = distinct low prone silhouette + edge-of-screen indicator reusing the §7.2 hat-silhouette ally-tracking system** — a downed friend must be findable at a glance (Fellowship depends on it); reuse, don't invent.

Audio (desirable, not MVP-blocking): swing/whiff sounds predicted client-side; impact/stagger sounds host-confirmed, same frame as hitstop; death/downed presentation tone-safe per the project tone rule (world facts, no punitive stingers).

📌 *Asset Spec note: after this GDD is approved, run /asset-spec system:combat-system to derive per-asset visual descriptions, dimensions, and generation prompts from this section.*

UI Requirements

Combat contributes three elements to the HUD (visual contract: art bible §7): the player HP display (own + party), the **Downed ally edge-of-screen indicator** (§7.2 hat-silhouette variant), and host-confirmed damage numbers (the only numeric combat feedback). Binding constraints on any UI consuming combat data: damage/death copy follows the project tone rule (world facts, never "YOU DIED" theatrics — a downed banner reads "[Name] is down", never a failure message); no UI element may reveal hidden combat state that gameplay doesn't (**no enemy HP bars at MVP** — stagger flash and death are the feedback; revisit at playtest, ruling 2026-08-26); dash cooldown feedback must be glanceable without reading numbers.

📌 UX Flag — Combat: this system has UI requirements. In Pre-Production, run /ux-design for the HUD before writing epics; combat stories referencing UI must cite design/ux/hud.md, not this GDD.

Acceptance Criteria

[UNIT] = EditMode NUnit over pure logic (damage math, arc-test geometry, stagger meter, state-machine transitions, config validation) — the hit pipeline (F1–F5) must be designed pure/DI-testable, no live NGO session required. [INTEGRATION] = NGO runtime, Multiplayer Play Mode + Network Simulator. [MANUAL] = human feel judgment, never automated (coding standards) — but scoped to a concrete, checkable claim. All criteria use locked baseline constants (cloth tier) unless the vector specifies otherwise.

Core Verbs & Commitment (Rules 1–4)

Flat Stats & Gear Hooks (Rule 5, F1, F2)

Hit Resolution (Rule 7, F3)

Stagger & Knockback (Rule 11, F4, F5)

Telegraph & Latency (Rules 2, 11)

Death, Downed, Friendly Fire (Rules 6, 9, 10)

Same-Tick & Mutual Kills

Tide Soft-Close Ordering (Rule 13)

Config Validation (load-time, fail loudly)

Disconnects & Home (Edge Cases 1–2, 8)

Minimum smoke set (run first)

Four are [UNIT]; AC-35 is [INTEGRATION] but runs headless in Multiplayer Play Mode — together they run in under a minute as a pre-QA-handoff gate.

Deliberately not criteria (and why)

Coverage note: Core Rules 1–13 and F1–F5 each have ≥1 criterion. F6 (revive knobs) has no dedicated AC — pure tuning values, exercised via AC-26/AC-35/AC-41. Edge Cases 13–14 (shore-seam geometry) are closed as geometry impossibilities and are not independently testable — flagged, not omitted.

Open Questions

# Question Owner Target
1Swing-intent RPC shape, resolution-instant scheduling in NGO ticks, soft-close-before-combat tick-order guarantee, telegraph phase as synced NetworkVariable (Edge Case 17), tide Edge Case 8 position snapshotNetcode ADRAfter friend-group playtest
2~~Dash/knockback vector behavior at shore/water boundaries (Edge Case 13)~~ RESOLVED 2026-08-27 — player-movement quick-spec Rule 4: collision blocks and never damages; water is a hard edge; the vector stops at the boundary (cross-GDD W6)~~Player Movement quick-spec~~ ResolvedDone
3Higher-tier damageModifier / damageReduction values (provisional ~2.2 / ~0.35 iron)Crafting GDDSystems Design phase
4Disconnect-to-heal accepted as non-concern — does real play ever distort around it?MVP friend-group playtestPost-MVP review
5Independent bleed-outs on group down (no mercy wipe) — does the helpless wait read badly?MVP friend-group playtestPost-MVP tuning
6No enemy HP bars at MVP — does stagger/death feedback suffice?MVP friend-group playtestPost-MVP tuning
7Do the TTK/TTD bands (2–4 hits, ~4-mistake floor) hold up in real co-op play?MVP friend-group playtestPost-MVP tuning
8Does dash-through dominate all single-enemy engagements — is movement-only dodging (spacing, kiting) a viable secondary skill expression, or is dash always the answer?MVP friend-group playtestPost-MVP tuning
10Home heal rate: Rule 6 says "restores to full while present" with no rate; ADR-0015 implements immediate on arrival and flags it — a homeHealRate ramp is a one-line change + a knob row if the playtest prefers it. Also: maxTargets and reviveReach above need this GDD's assent before the first combat storyCombat GDD (/quick-design)Before the first combat story
9reviveChannelTime / bleedOutTimer defaults are calibrated for 2-player MVP — do they hold for 5-player groups, where 4 potential rescuers trivialize the 45 s window?Tier 1 design passTier 1