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.yis the midpoint of the line; it travels forward at_lineSpeedalong_lineAngle. _currentSpreadgrows from 0 toward_maxSpreadat_spreadSpeedpx/s, pushing the two balls outward along the perpendicular to forward travel.- Each frame,
bullets.tsrecomputes absolute endpoints:_ball1X/Y = midpoint + perp * spread_ball2X/Y = midpoint - perp * spread
- Bullet collision radius
b.radis widened tomax(_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:
- Build the segment direction
(ndx, ndy)and lengthlineLenfrom the two endpoints. - Query the enemy spatial grid centered on the midpoint with radius
lineLen / 2 + thickness + 30. - 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.
- Project enemy center onto the segment:
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
talong the segment. - On hit: 3-particle burst at the nearest point on the line, tinted by the bullet’s
c1color. - 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
_currentSpreadgrows — 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.ts—tesla_linebehavior (~line 1403). Owns endpoint computation, spread growth, cooldown ticking, and VFX.src/starship-survivors/engine/combat/collision-resolver.ts—resolveLineCollision(~line 245). Owns the point-to-segment distance test and damage application. Dispatched from the bullet loop when_ball1Xand_ball2Xare present (~line 101).