Telegraphed Attack Pattern

What this is

A four-phase attack state machine shared by melee-lunge enemies. The enemy approaches normally, freezes and broadcasts a visible commit (windup), executes a locked dash on the windup-time direction (lunge), sits inert and vulnerable (recovery), then waits a cooldown before being able to commit again. Lunge direction is locked at the moment windup ends — the enemy does not re-aim during windup or lunge, which is the player’s dodge window.

The canonical implementation lives in the charger behavior. Other enemies that route through this same state machine: Brute (slow, tanky melee — same behavior, different stats).

The same windup → commit → recovery → cooldown shape recurs across other archetypes (orb dash-sploot, sniper aim-fire-cooldown), but those use their own phase names and timings; this page covers only the shared melee-lunge variant.

The four phases

PhaseState idMovementContact damageVisual / forecastExit condition
Approach_chargerPhase = 0Normal seek toward player along straight lineOff (generic contact damage skipped for charger archetype)NoneDistance to player < windup range AND cooldown elapsed
Windup_chargerPhase = 1Frozen (velocity zeroed on entry)Offcharger_bar loading forecast spawned at enemy position, oriented along locked aim angle, duration = full windupWindup timer reaches windup duration
Lunge_chargerPhase = 2Committed dash along locked aim direction at lunge-speed multiplier of base speedOn — single hit per lunge (_lungeHit flag), body overlap triggers itDamage flash held at maximumTravelled distance reaches bar length
Recovery_chargerPhase = 3Frozen, inertOff (explicitly cleared so player can punish freely)Damage flash clearedRecovery timer reaches recovery duration

On recovery exit, phase resets to 0 (approach), and the per-enemy cooldown timer is set; the enemy cannot enter windup again until cooldown ticks to zero, even if the player is in range.

Hit resolution during lunge:

  • Damage = lunge-damage constant × worldKnobs.rarityScale.
  • Knockback velocity applied to ship in the enemy’s exact movement direction.
  • Hit-freeze pause applied via _hitFreezeTimer for impact feel.
  • _lungeHit flag set on first connect, preventing repeated damage during the same lunge.

Per-enemy timings

All numerics below are the shared charger-behavior constants from behaviors.ts. Brute uses identical timings because it routes through the same charger behavior; only the base stats from its archetype data differ.

ConstantValueUnit
Windup trigger range250px
Windup duration3.2s
Lunge speed multiplier6.3× base speed
Lunge / bar length300px
Recovery duration1.5s
Cooldown between commits10.0s
Knockback velocity on hit220px/s
Lunge damage (pre-rarity scale)50flat
Hit-freeze on impact0.12s wall-time

Per-enemy base stats that feed into the shared timings:

EnemyBehaviorBase speedLunge speed (× 6.3)HPRadiusMeleeXP
Chargercharger1106934410yes12
Brutecharger6037810014yes25

Visual / audio telegraphs

CueWhenSource
charger_bar forecastSpawned at windup entry, fills along the locked aim direction for the full windup durationForecast object pushed in combat() on windup entry
Locked facingEnemy angle snaps to the aim angle on windup entry and stays locked through windup, lunge, and recovery — does not re-aim mid-attackPhase-based angle assignment
Damage flashHeld at maximum (_dmgFlash = 1, _dmgActive = true) during the lunge phase onlyLunge-phase block in behavior tick
Recovery inertnessDamage flash explicitly cleared on recovery entry to signal the punish windowRecovery-phase block in behavior tick
Forecast cleanup on deathAll charger_bar forecasts owned by this enemy removed on death so the targeting bar does not linger after a mid-windup or mid-lunge killonDeath handler
Kill SFXenemy_kill_charger plays a thud + crackle layered impact on deathmicro-sfx.ts enemy_kill_charger sample