Burst Fire Rate Semantics — Bursts/sec vs Shots/sec
For burst-pattern weapons, the displayed fireRate is bursts-per-second, not shots-per-second. Each burst (cast / cycle / trigger) contains N sub-shots fired in quick succession at an intra-burst interval, so the actual rate at which damage rolls hit enemies is far higher than the stat sheet number suggests.
This distinction is invisible to most weapons (semi-auto kinetics fire one shot per trigger, so bursts/sec ≡ shots/sec) but matters whenever a weapon uses the burst_fire behavior or has a projectileCount greater than one per cast.
The two fire-rate dialects
Every weapon in WeaponCoreSpec exposes fireRate: { base, scaling }. That number is always casts-per-second — how often the trigger pulls. What changes between weapon families is how many damage events come out of one trigger pull:
- Semi-auto / single-projectile weapons (e.g. coilgun, cannon): one trigger pull → one shot. Stat sheet
fireRate= shots-per-second directly. - Multi-shot-per-cast weapons (e.g. Burst — the uncommon hitscan,
data/weapons/burst.ts): one trigger pull →projectileCountsimultaneous hitscan beams targeted at the nearest enemies. Stat sheetfireRate= casts-per-second; true shots/sec =fireRate × projectileCount. All sub-shots resolve on the same frame. - Burst-pattern weapons (e.g. Revolver — the epic,
data/weapons/revolver.ts): one trigger pull → coordinator bullet that spawnsburstCountsub-shots spacedintraBurstDelayapart over time. Stat sheetfireRate= bursts-per-second; true shots/sec =fireRate × burstCount, but spread across(burstCount − 1) × intraBurstDelayseconds rather than instant.
True shot DPS (burst-pattern)
For a burst_fire weapon:
True shots/sec = fireRate (bursts/sec) × burstCount (shots/burst)
True shot DPS = fireRate × burstCount × damage-per-shot
The Revolver illustrates this clearly. At L1 (data/weapons/revolver.ts):
fireRate.base = 0.768bursts/secburstPattern[L1] = 6shots/burstdamage.base = 90per shot- True shots/sec ≈ 4.6
- True shot DPS ≈ 414
The cast-DPS reading (fireRate × damage ≈ 69) underestimates the weapon by 6× because damage is per sub-shot, not per burst.
By L20 the gap widens further: burstPattern[L20] = 24 shots/burst, so a single burst now drops 24 separately-resolved damage events into the world, each able to crit, splash, trigger on-hit effects, and stack any per-shot procs.
How a burst plays out frame-by-frame
WeaponManager.fireBurst (engine/weapons/weapons.ts:1205) is the entry point for any weapon with behavior: 'burst_fire'. It does not fire the sub-shots directly — instead it spawns an invisible coordinator bullet anchored to the ship, then the burst_fire bullet behavior (engine/weapons/bullets.ts:691) advances a per-shot timer and emits one hitscan beam-trace sub-shot every intraBurstDelay:
- Trigger pulls (gated by
fireRate).fireBurstreadsburstCountfrom the steppedburstPatterntable andintraBurstDelayfromcadenceStepSec(falling back to0.045 s). It spawns a coordinator bullet with_burstShotsLeft = burstCount,_burstTimer = 0(first shot fires immediately), and alifetimeofburstCount × intraBurstDelay + 0.12 s. - Coordinator update (every frame, while alive). The
burst_firebehavior follows the ship, decrements_burstTimer, and when it hits zero:- Re-acquires the closest enemy in
acquireRange(only on the first sub-shot — subsequent sub-shots keep the locked target unless it dies) - Spawns one beam-trace bullet aimed at the locked target with ±1.5° jitter, dealing
_burstDmgdamage per shot - Resets
_burstTimer = _burstIntraSec, decrements_burstShotsLeft
- Re-acquires the closest enemy in
- Coordinator expires once all sub-shots have fired (lifetime ≈
burstCount × intraBurstDelay + 0.12 s).
The next burst doesn’t start until 1 / fireRate seconds after the previous trigger pull (not after the previous burst finishes). On slow-firing high-burst-count weapons the bursts can overlap if burstCount × intraBurstDelay > 1 / fireRate.
Why this matters for design and balance
- Damage-per-shot scaling is multiplicative with burst count. A +10% damage upgrade on a 24-shot burst is worth 24× more raw damage per cast than the same upgrade on a 1-shot semi-auto. Burst weapons concentrate scaling.
- On-hit procs roll per sub-shot, not per cast. Crit, lifesteal, bullet-tag blood splatters, splash AoE — each sub-shot is its own resolution. Burst weapons are disproportionately strong with proc-based modifiers.
- Stat sheets undersell burst DPS. Any tooltip showing
fireRate × damageis reading cast-DPS, not shot-DPS. Burst weapons appear weaker than they are on paper unless the display layer multiplies byburstCount(the Playground WeaponsTab shows the raw cadence pattern so the math is visible). intraBurstDelayis a feel knob, not a balance knob. Total damage out of a burst isburstCount × damageregardless of intra-burst spacing;cadenceStepSeconly controls how stretched the burst feels (0.06 sfor the Revolver reads as a fast trigger pull,0.30 swould read as a controlled tap-tap-tap).- Burst can miss mid-sequence. If the locked target dies before all sub-shots fire, the coordinator re-acquires. If no targets remain in
acquireRange, the remaining sub-shots still spawn but waste against empty space — burst weapons over-commit to dead targets in low-density situations.
Distinguishing the two “Burst” things
The codebase has two unrelated weapons with “burst” in the name and it’s easy to confuse them:
Burst(data/weapons/burst.ts, uncommon, familysniper) — does not useburst_firebehavior. It’s an instant hitscan that firesprojectileCountshots simultaneously at the nearest enemies (3 at L1). All sub-shots resolve on the same frame;cadenceStepSecis0.0. This is a multi-shot-per-cast weapon, not a burst-pattern weapon.Revolver(data/weapons/revolver.ts, epic,behavior: 'burst_fire') — the canonical burst-pattern weapon. 6–24 sub-shots per burst depending on level, spaced0.06 sapart, each homing toward the locked target.
Only the second uses the bursts/sec vs shots/sec semantics described here. The first is closer to a shotgun: the displayed fireRate already counts all of projectileCount worth of shots as one trigger pull, but they all land at the same instant rather than over time.
Source references
engine/weapons/weapons.ts:1205—WeaponManager.fireBurst, the cast-time spawner forburst_fireweaponsengine/weapons/bullets.ts:691—burst_firebullet behavior, the per-frame sub-shot emitterdata/weapons/revolver.ts— canonicalburst_fireweapon withburstPatternstepped scalingdata/weapons/burst.ts— multi-shot-per-cast hitscan (NOTburst_fire); kept here as a name-collision warningdata/weapons/_types.ts:292—cadenceStepSecfield definition