What this is

burst_fire is a bullet behavior (registered in engine/weapons/bullets.ts) that turns one trigger pull into a timed sequence of hitscan sub-shots. The cast spawns a single invisible coordinator projectile; that coordinator stays glued to the ship and emits N short-lived beam-trace bullets at fixed intervals until its budget is spent.

It is selected by a weapon declaring behavior: 'burst_fire' in its WeaponCoreSpec, which routes its cast through WeaponManager.fireBurst in engine/weapons/weapons.ts.

FieldValue
Behavior idburst_fire
Bullet behavior priority5
Coordinator archetypeburst_coord
Coordinator collision modebeam_trace
Coordinator visibleNo
Sub-shot collision modebeam_trace
Sub-shot archetypesniper
Sub-shot lifetime0.06 s
Sub-shot pierce999
Player-bullet cap (guard)100

Weapons that declare behavior: 'burst_fire':

WeaponFile
Revolverdata/weapons/revolver.ts

The burst weapon (data/weapons/burst.ts) is named similarly but does not use this behavior — it is a single-cast multi-target hitscan that fires its shots all on the same frame via fireBeamTrace, not via the burst_fire coordinator.

How the coordinator works

fireBurst reads stats off the weapon spec and spawns one coordinator bullet with the burst budget packed into private fields. On every engine tick, the burst_fire update runs:

StepAction
1Snap coordinator position to ship.x, ship.y
2Decrement _burstTimer by dt
3If _burstTimer <= 0 and _burstShotsLeft > 0: fire one sub-shot, decrement _burstShotsLeft, reset _burstTimer to _burstIntraSec (default 0.045 s if missing)
4While _burstShotsLeft > 0: clamp b.l (lifetime) to a minimum of 0.5 s so the coordinator survives
5When _burstShotsLeft <= 0: clamp b.l to a maximum of 0.08 s so the coordinator despawns shortly after the final sub-shot
Coordinator fieldSourcePurpose
_burstShotsLeftburstPattern stepped-stat at rawLvl, plus getExtraProjectiles from more_projectiles upgradesSub-shot budget
_burstTimerSpawned at 0Triggers first shot on the very next tick
_burstIntraSecdef.cadenceStepSec (or 0.045 s fallback)Time between sub-shots
_burstDmgdamage passed into fireBurstDamage per sub-shot
_burstAcquireRangedef.acquireRange at level, scaled by horizontal range upgradesTarget search radius
_burstBeamRangeacquireRange * beamLengthMultHitscan trace length fallback
_burstBeamWidthdef.beamWidth at level, fallback 2.2Sub-shot beam width
_burstAimAngleaimAngle from the firing pipelineInitial aim, used when no auto-target
lifetimeburstCount * intraSec + 0.12Coordinator hard expiry

The coordinator carries behaviors: ['burst_fire'] and collisionMode: 'beam_trace' but it is invisible to the player. It does not deal damage on its own — only its emitted sub-shots do.

The locked-target rule

Auto-fire bursts use a single locked target for the entire sequence. The lock is established lazily on each sub-shot’s tick and held until that enemy dies or is frozen.

ConditionBehavior
_manualFire is setSkip target acquisition; use the original _burstAimAngle for every sub-shot
_burstTarget is null, dead, _frozenForLag, or _dyingRe-acquire: scan world.enemies, pick the closest alive non-spawning enemy within acquireRange
Enemy still spawning (_spawnT > 0.15)Excluded from target search
_burstTarget is aliveReuse it — do not re-scan

When a locked target is available, the sub-shot aims at it directly. Aim is recomputed each sub-shot, so a moving target stays in the cone for the full sequence.

Per sub-shot aim adjustmentValue
Aim angleatan2(target.y - ship.y, target.x - ship.x)
Random jitter applied to aim(rand - 0.5) * 0.052 rad (about ±1.5°)
Beam range useddist_to_target + target.radius + 30

The +30 unit overshoot guarantees the beam trace passes through the enemy’s body even at the edge of the lock. If acquisition fails (no enemy in range), the coordinator skips firing this tick — the timer still resets, so the next opportunity comes one _burstIntraSec later.

Hitscan sub-shots

Each sub-shot is a short-lived beam_trace bullet, not a travelling projectile. It is pushed into world.playerBullets (subject to the 100-bullet cap) and resolves its damage via the engine’s beam-trace collision the same frame it spawns.

Sub-shot fieldValue
x, yship.x, ship.y
vx, vy0, 0 (no travel)
dmg_burstDmg (per-shot damage, not split)
pierceCount999
rad_burstBeamWidth (fallback 2.2)
l / maxLifetime0.06 s
homingStrength0
blastRadius0
archsniper
_collisionModebeam_trace
_beamRangeshotRange (target distance + radius + 30, or _burstBeamRange if no target)
_beamWidth_burstBeamWidth
_beamAngleRecomputed per sub-shot from the locked target
_behaviors[] (sub-shots carry no behavior; coordinator owns the sequence)

Cadence is governed by two weapon stats:

StatField on WeaponCoreSpecRole
Sub-shot count per castburstPattern (stepped stat by level)Plus getExtraProjectiles(weaponId, more_projectiles_count)
Sub-shot intervalcadenceStepSecFalls back to 0.045 s if the weapon omits it

Revolver’s tuning (from data/weapons/revolver.ts):

StatValue
burstPattern (sub-shots)6 at L1, 8 at L5, 12 at L10, 18 at L15, 24 at L20
cadenceStepSec0.06 s
warmupSec0.25 s

Because the sub-shots are hitscan and vx, vy are zero, projectile speed and homing strength on the weapon spec do not affect the burst itself — they affect the seeker projectiles spawned by other revolver mechanics, not the coordinator’s emitted beam traces.