Cooldown commit

What this is

Cooldown commit is the moment in the fire pipeline when a weapon’s cooldownMax is computed from its final fire rate and fireTimer is reset to that value. It happens once per shot, inside the actual-fire branch of WeaponManager.fire, after warmup completes and immediately before projectiles spawn. The committed cooldown is what the per-frame updateCooldowns tick decrements toward zero to gate the next shot.

The commit reads three inputs to produce the final fire rate:

InputSource
Base fire rategetWeaponStatAtLevel(def.fireRate, curvedLevel)
Horizontal modifier1 + def.ratePerH * horizontalDamageUpgrades
Early-level nerfgetPlayerFireRateNerf(rawLevel)

Final fire rate = base × horizontal × early-level nerf. The cooldown is then 1 / finalFireRate, with optional per-shot jitter applied multiplicatively.

FieldValue at commit
weapon.cooldownMax1 / finalFireRate (or FALLBACK_COOLDOWN = 0.5 if rate ≤ 0)
weapon.fireTimerSet equal to weapon.cooldownMax
Jitter rangecooldownMax * (1 ± def.fireRateJitter)

When cooldown is set

The cooldown is set on the fire frame — the frame the projectile actually spawns — not on the frame warmup started, not on the frame the weapon last came off cooldown. The ordered sequence inside fire():

StepAction
1Tick active warmup if _warmingUp is set
2If warmup not complete this frame, return without touching cooldown
3On warmup-complete frame (or no-warmup ready frame), enter actual-fire branch
4Compute curved level, raw level, early nerfs, horizontal counts
5Compute finalDamage from base damage × multipliers
6Compute finalFireRate from base rate × horizontal × early nerf
7Assign weapon.cooldownMax = 1 / finalFireRate
8Apply jitter to cooldownMax if def.fireRateJitter > 0
9Assign weapon.fireTimer = weapon.cooldownMax
10Crash if cooldownMax is non-finite
11Dispatch to family-specific projectile spawn

A non-finite cooldownMax throws — invalid fire-rate data is treated as a broken weapon spec, not silently fallen back.

Per-frame, WeaponManager.updateCooldowns(ship, dt) runs once per ship before per-weapon fire calls and decrements fireTimer by dt, clamped at zero. A weapon is fire-eligible only when fireTimer <= 0 and _warmingUp is false.

Interaction with warmup

Cooldown is committed after warmup, not before. The fire-eligibility check (fireTimer <= 0 + valid target) is what triggers warmup-start; warmup itself runs while fireTimer is already at zero. During warmup the cooldown is not touched — only _warmupTimer accumulates. When warmup completes the actual-fire branch executes and commits the new cooldown.

PhasefireTimer state_warmingUp
Ready, no target0false
Ready, target acquired, warmup begins0 (held at zero)true
Mid-warmup0true
Warmup completes, actual fire commitsReset to cooldownMaxfalse
Cooling downDecremented each frame toward 0false

The full inter-shot interval for a warmup weapon is therefore cooldown + warmup, because warmup runs after cooldown reaches zero rather than overlapping it. Horizontal fire-rate modifiers shorten cooldown only; they do not shorten warmup.

Practical effect

Cooldown committing on the fire frame rather than the warmup-start frame has three consequences:

EffectDetail
Warmup is additive to inter-shot intervalA weapon with 0.5s cooldown and 0.25s warmup fires every 0.75s, not every 0.5s
Fire-rate horizontals never accelerate warmupThe ratePerH modifier feeds into finalFireRate, which only affects the 1/x cooldown computation — warmup duration is a fixed spec value
Mid-warmup spec edits would not apply until next shotCooldown reflects the fire-frame’s level, horizontal count, and nerf state; any change between warmup-start and fire is captured at commit time, not at warmup-start time

For warmup weapons specifically, this means high-warmup weapons (Railgun at 0.55s, Lightning at 0.35s, Hellrain beam at 0.30s — see gameplay/concepts/warmup) have a wider effective DPS gap from their nominal fire rate than low-warmup weapons. Pure fire-rate stacking reduces the cooldown half of the interval to a floor near the per-shot jitter range, but the warmup half is untouched and continues to dominate the cycle.