Edge-Arrow HUD (Off-Screen Indicator)

When a high-value world entity is off-screen, the HUD draws an amber chevron at the screen edge pointing toward it. Iconic Survivor.io / Vampire-Survivors-style loot-pointer telegraph — reserved for rare, kite-worthy props so the cue never spams the field.

Trigger

A prop participates in the edge-arrow system if and only if its PropTypeDef sets edgeArrow: true (src/starship-survivors/data/props.ts). Today exactly two prop tiers opt in:

PropRim colorWhy arrowed
Supply Pod (supply_pod)#ffaa44 amber80 HP, 12 orbs × 0.035 XP frac (~42% of level bar). The big-bundle reward; worth the kite.
Magnetar Pulse (magnetar_pulse)#a050ff violet35 HP, break triggers a 1s gravity field. Findable like Supply Pods so the field-control beat doesn’t go missed.

Common-tier props (Scrap Pile, Drone Wreck, Comet Fragment, Volatile Crystal, Mineral Vein) default to edgeArrow: undefined/false and stay quiet — adding the flag to a new tier opts it in with zero engine changes.

Iteration path

The prop pool exposes forEachEdgeArrowProp(cb) (engine/world/props.ts) which walks active slots and invokes the callback only for slots whose type def has edgeArrow: true. The HUD layer (engine/rendering/hud.ts, _drawSupplyPodArrows, despite the legacy name, handles all edge-arrow props) calls this each frame.

Per-frame cost is O(active edge-arrow props) — capped at ~1–2 active by densityMult weights (Supply Pod 0.15, Magnetar Pulse 0.20) against the shared MAX_DENSITY = 10. Well inside the HUD <2ms budget; ~6 ctx ops per draw.

Two states: off-screen vs on-screen

The same draw function handles both visibility states by checking whether the prop’s world coord projects inside the viewport (sx > 0 && sx < W && sy > 0 && sy < H).

Off-screen — edge chevron

Drawn on a circle of radius Math.min(W, H) * 0.42 around screen center, rotated to face the prop’s world angle from the ship. Amber palette: outer stroke #ffcc66 (3.2px), inner “hot core” filament #fff2c2 (1.6px), #ffaa44 shadow blur. Base pulse is a 4 Hz sine on alpha (same rhythm as the portal indicator).

On-screen — bobbing marker

Once the prop enters the viewport, the indicator switches to a downward chevron floating 70px above the prop, bobbing ±6px on a 3 Hz sine. Same color palette, slightly tighter footprint, no rotation.

Distance modulation — the “drama” ramp

Distance from ship to prop drives a drama scalar that intensifies the arrow when the prop is far away and calms it as the player closes in. The threshold is DRAMA_DIST_PX = 900 world units (about a screen width at default zoom).

drama = clamp((dist - 900) / 900, 0, 1)
  • dist ≤ 900px: drama = 0. The arrow rides the steady 4 Hz pulse so it doesn’t compete with the on-target glow the prop itself emits.
  • dist between 900 and 1800px: drama ramps 0 → 1.
  • dist ≥ 1800px: drama = 1. Maximum dramatic-telegraph mode.

Drama modulates three values:

PropertyEffect
dramaThrobAdds a slower 2 Hz overlay on the pulse — bigger “breathing” rhythm for distant pods.
sizeMult = 1 + drama * 0.5Chevron grows up to 50% larger when far.
shadowBlurIncreases from 10px to 18px (10 + 8 * drama).

The combined pulse is capped at 1.2 so the alpha doesn’t oversaturate. Players see a fresh spawn pop hard from across the map, then settle into a calmer chevron as they kite toward it — the dramatic open, then the quiet closer.

No numeric distance label

There is no literal distance readout (no “150m” pip). The cue is purely intensity-driven: bigger + brighter = farther. This keeps the HUD readable on phone and reinforces the field-feel-first priority — the player just rotates toward the brightest amber arrow.

Relationship to other edge indicators

The edge-arrow HUD shares the chevron idiom with two sibling cues, all drawn in hud.ts:

SystemFunctionColorTrigger
Directional hit indicators_drawHitIndicatorsredRecent damage from off-screen attacker
Portal navigation indicator_drawPortalIndicatorpurplePersistent — extraction portal
Edge-arrow loot pointer (this page)_drawSupplyPodArrowsamber / violetActive prop with edgeArrow: true

The shared chevron geometry keeps the HUD legible — every edge cue means “look this way” and the color says why.

Extending

Opt a new prop tier into the system by adding edgeArrow: true to its PropTypeDef entry in data/props.ts. No engine, pool, or HUD changes required — the iterator picks it up automatically and draws the same amber chevron. (A future enhancement could read per-prop arrow color from the rimColor field; today the chevron palette is hard-coded amber in hud.ts.)