Tesla Line-Segment Hitbox

The lightning weapon’s tesla_line behavior fires a single master bullet that tracks two energy ball endpoints spreading apart perpendicular to the fire direction. The damage hitbox is not the balls themselves but the line segment between them — modeled as a capsule via a point-to-segment distance test against every nearby enemy.

Geometry

  • Master bullet’s b.x / b.y is the midpoint of the line; it travels forward at _lineSpeed along _lineAngle.
  • _currentSpread grows from 0 toward _maxSpread at _spreadSpeed px/s, pushing the two balls outward along the perpendicular to forward travel.
  • Each frame, bullets.ts recomputes absolute endpoints:
    • _ball1X/Y = midpoint + perp * spread
    • _ball2X/Y = midpoint - perp * spread
  • Bullet collision radius b.rad is widened to max(_ballRadius, spread + 10) so the spatial grid returns candidates that touch any part of the segment.

Collision math

Handled by CollisionResolver.resolveLineCollision (in combat/collision-resolver.ts), gated by the presence of _ball1X / _ball2X on the bullet:

  1. Build the segment direction (ndx, ndy) and length lineLen from the two endpoints.
  2. Query the enemy spatial grid centered on the midpoint with radius lineLen / 2 + thickness + 30.
  3. For each candidate enemy:
    • Project enemy center onto the segment: proj = (e - p1) · n, clamped to [0, lineLen].
    • Compute nearest point on the segment.
    • Distance dist = hypot(e - nearest).
    • Hit if dist < _lineThickness + enemyCollisionRadius.

Because clampedProj is bounded to the segment, the distance test naturally covers both endpoints and every point in between — touching either ball or any point along the line is a hit. The capsule is _lineThickness wide (default 6 px) plus the enemy’s own radius.

Cooldowns

A single sustained line would melt anything it touches frame-by-frame, so each enemy gets a per-bullet cooldown:

  • _contactCooldowns: Map<enemy, timer> stored on the bullet.
  • On hit, the enemy is inserted with timer _contactCooldownSec (default 0.3 s).
  • The behavior ticks every entry down each frame and deletes expired ones.
  • Cooldowned enemies are skipped before the distance test even runs.

This matches the sweep/orbit cooldown pattern — enemies inside the segment take damage at roughly 1 / _contactCooldownSec ticks per second.

VFX

  • Per-frame: ~70% chance to spawn a cyan spark particle at a random t along the segment.
  • On hit: 3-particle burst at the nearest point on the line, tinted by the bullet’s c1 color.
  • On bullet death: 6-particle bursts at both ball positions.

Why the line, not the balls

The visual is two energy balls flying apart with electricity arcing between them. Modeling the hitbox as the line instead of the two ball circles means:

  • Enemies caught crossing the path between the balls take damage (the intuitive read).
  • The effective hit area grows automatically as _currentSpread grows — no per-ball collision check, no missed gap in the middle.
  • One distance test per enemy covers both endpoint contact and mid-line contact, since the segment-projection clamp degenerates to endpoint distance when the enemy is past either end.

Source

  • src/starship-survivors/engine/weapons/bullets.tstesla_line behavior (~line 1403). Owns endpoint computation, spread growth, cooldown ticking, and VFX.
  • src/starship-survivors/engine/combat/collision-resolver.tsresolveLineCollision (~line 245). Owns the point-to-segment distance test and damage application. Dispatched from the bullet loop when _ball1X and _ball2X are present (~line 101).