Target Lead Prediction

When a weapon spawns a projectile that has nonzero travel time, aiming directly at an enemy’s current position misses moving targets — by the time the round arrives, the enemy has moved. Target lead prediction estimates where the enemy will be when the projectile arrives and aims at that future point instead.

Starship Survivors uses two flavors of lead, both in src/starship-survivors/engine/weapons/weapons.ts:

  1. Linear lead — used by arc-mortar landing-point selection. Flight time is known up front (arcTime), so the predicted position is a single linear extrapolation.
  2. Quadratic intercept solve — used by direct-fire aimed projectiles. Flight time depends on how far away the target moves to, so the projectile’s travel time and the target’s travel time must be solved simultaneously.

Beam and chain weapons (no projectileSpeed) hit instantly and skip lead entirely — they always aim at the target’s current position.

Linear lead (arc mortar)

Arc-mortar shells fly along a parabolic arc for a fixed duration arcTime (typically 1.0 seconds, configured per weapon). The shell only damages on landing, so the predicted landing point uses:

landX = enemy.x + enemy.vx * arcTime
landY = enemy.y + enemy.vy * arcTime

This runs once per target candidate in fireArcMortar, scoring enemies by targetMode (closest, furthest for mortar backline targeting, flanking for cannon-style perpendicular shots). The chosen target’s predicted position becomes the centerX/centerY for shell scatter — random scatter shells land within scatterRadius of that point; perpendicular-layout shells fan out along the perpendicular to the aim direction.

Random scatter and perpendicular spacing are applied on top of the predicted center, so individual shells can land off the predicted point even when the target is stationary.

Quadratic intercept solve (aimed projectiles)

For projectiles with a fixed speed bulletSpeed, the time t until intercept satisfies:

|target.pos + target.vel * t - gun.pos| = bulletSpeed * t

Squaring both sides and grouping by t produces a quadratic a*t^2 + b*t + c = 0 where:

a = vx^2 + vy^2 - bulletSpeed^2
b = 2 * (dx*vx + dy*vy)
c = dx^2 + dy^2

Here dx = target.x - gun.x and dy = target.y - gun.y are the gun-to-target offset, and vx/vy are the target’s velocity.

calcLeadTime solves for the smaller positive root:

t = (-b - sqrt(b^2 - 4ac)) / (2a)

If the discriminant is negative the projectile cannot catch the target (target outrunning the bullet from this geometry) and calcLeadTime returns 0 (the caller then aims at the current position as a fallback).

The aim helper accepts the result only when 0 < leadT < 2 seconds — capping at 2s prevents wildly mispredicted shots when the target moves nearly as fast as the bullet. Inside the cap, the predicted aim point is:

predX = target.x + target.vx * leadT
predY = target.y + target.vy * leadT
aimAngle = atan2(predY - ship.y, predX - ship.x)

bulletSpeed is pulled from the requesting weapon’s projectileSpeed stat at its current level. Each weapon’s lead is computed against its own bullet speed, so slow heavy projectiles lead farther than fast laser-fast bolts.

Key facts

  • Source file: src/starship-survivors/engine/weapons/weapons.ts.
  • Functions: calcLeadTime (quadratic solve), inline linear lead in fireArcMortar (arc landing center), aim-prediction block above calcLeadTime (direct-fire aim).
  • Inputs: gun position, target position, target vx/vy, projectile speed (direct fire) or arcTime (mortar).
  • Output: predicted aim point or predicted landing point, in world coordinates.
  • Used by: arc-mortar / artillery weapons (landing center), every aimed direct-fire weapon that exposes a projectileSpeed stat (aim angle).
  • Skipped by: beam and chain weapons (no projectile speed → no travel time → no lead). Manual-fire mortar shots also skip enemy search and fire along aimAngle.
  • Solver: quadratic discriminant b^2 - 4ac. Negative discriminant returns 0 (no solution); smaller positive root chosen for earliest intercept. Cap leadT < 2s prevents extreme leads.
  • Coupling: scatter and perpendicular shell layout offsets are applied on top of the predicted center, so individual shells can still miss a maneuvering target even with correct lead.