canRetarget
What this is
canRetarget is a boolean weapon-definition field on WeaponDef (src/starship-survivors/data/weapons/_types.ts) that controls whether a homing projectile is allowed to acquire a new target after its locked target dies, freezes, or enters the dying state. It is read at fire time, stored per-bullet as _canRetarget, and consulted by the homing update loop in bullets.ts. The flag is independent of homing strength — a projectile can have very strong homing (homingStrength: 999) and still be set to canRetarget: false, in which case it tracks its initial target and fizzles when that target is gone.
The field defaults to true. The default is applied at the barrel-build step (canRetarget: def.canRetarget !== false) and again when the barrel is converted to a bullet (_canRetarget: barrel.canRetarget !== false), so any weapon that omits the field gets retargeting enabled.
| Field | Type | Default | Stored on bullet as |
|---|---|---|---|
canRetarget | boolean | undefined | true | _canRetarget |
Which weapons set it
Every weapon that explicitly sets canRetarget: false is listed below. No weapon in the codebase sets it to true explicitly — true is always the default.
| Weapon | File | Homing strength (base) | Notes |
|---|---|---|---|
| Rifle | src/starship-survivors/data/weapons/rifle.ts | 999 | Strong lock-on, one target per round; round fizzles if target dies mid-flight |
| Coilgun | src/starship-survivors/data/weapons/coilgun.ts | 999 | Same single-target rail-slug pattern as Rifle |
| Mega Bullet (legendary) | src/starship-survivors/data/weapons/legendaries.ts | 0 | Non-homing legendary; flag set so the projectile cannot grab a new target if its launch target dies |
| Trailblazer (legendary) | src/starship-survivors/data/weapons/legendaries.ts | 0 | Non-homing legendary; same rationale as Mega Bullet |
All other weapons (missile, etc.) inherit the default true.
Runtime behavior
The flag is consulted inside the homing update branch in bullets.ts only when the current locked target is no longer a valid chase target.
A target is considered “lost” if any of the following are true:
Condition on _homingTarget | Meaning |
|---|---|
!alive | Enemy was killed |
_frozenForLag | Enemy was frozen due to lag-handling |
_dying | Enemy is in its death animation |
When a target is lost:
_canRetarget value | Outcome |
|---|---|
false | b.l = 0 — bullet lifetime is set to zero and the projectile fizzles on the same frame (the homing update returns immediately) |
true (default) | _homingTarget is cleared to null; the same frame’s seeking pass finds a new alive enemy via the spatial grid (radius 500) and locks onto it per targetMode |
There is no separate retarget cooldown or cadence. Retargeting is purely event-driven by target death — it can happen at most once per frame, only when the current target becomes invalid. While the locked target remains alive, the projectile keeps chasing it regardless of whether closer or lower-HP enemies appear; canRetarget has no effect during normal pursuit.
The flag is also gated by the homing delay phase. During the initial delay (_homingAge < _homingDelaySec) the projectile is accelerating from launch speed toward cruise speed and the seeking branch is not yet running, so a target death in that window is not checked. Seeking — and therefore retargeting — only begins once the delay has elapsed.
| Phase | _canRetarget checked? |
|---|---|
Delay phase (age < delay) | No — seeking branch not reached |
| Seeking phase, target alive | No — flag is only read when target is lost |
| Seeking phase, target lost | Yes — determines fizzle vs. re-lock |
Interaction with target modes
canRetarget and targetMode are independent fields that combine at runtime.
| Field | Controls |
|---|---|
targetMode | How a target is picked when a re-lock is needed (e.g. low_hp picks lowest-HP enemy, default picks nearest) |
canRetarget | Whether a re-lock is allowed at all when the current target is lost |
If canRetarget is false, targetMode is effectively only consulted once — at launch, for the initial lock — because no second lock will ever occur. Subsequent target deaths fizzle the projectile.
If canRetarget is true (the default), every re-lock pass re-applies the targetMode logic. A weapon with targetMode: 'low_hp' and canRetarget: true will, on each target death, scan the spatial grid (radius 500), filter to alive non-frozen non-dying enemies, and pick the one with the lowest HP — ties broken by distance. A weapon with default targetMode does the same scan but picks by squared distance only.
The combination matters most for high-homingStrength weapons like Rifle and Coilgun: setting canRetarget: false is what stops two rifle rounds from collapsing onto the same surviving enemy when their original targets die — instead, each round dies with its target, preserving the spread of damage across the field that the initial multi-lock established.