Shooting Star System

Shooting stars are rare collectible entities that spawn in the world during the second half of each level. Touching one freezes the game and queues a special meta-pick reward screen — a 2-choice offer drawn from a category pool that is disjoint from regular level-up rewards. The pool emphasizes utility (extra reroll, banish, or refuel charges) alongside broad-stroke power spikes (level up every weapon, ascend every artifact, full Star Power buff). Shooting-star picks always render at legendary rarity and are placed on a separate spawn schedule from regular reward picks.

Source: engine/world/shooting-star.ts (spawn + entity), engine/world/leveling.ts (generateShootingStarChoices, applyChoice case 'shooting_star'), engine/rendering/draw-shooting-star.ts (visuals).

Spawn schedule

Shooting stars run on their own log-normal spawn timer, fully independent of XP-driven level-ups.

  • Window: only active during the second half of each level (missionElapsed >= missionTimerMax / 2). Outside that window the spawner is dormant.
  • Cadence: ~1 star per current tier across the 120s window. Median interval is 120 / tierLevel seconds (tier 1 → 120s median, tier 5 → 24s median), drawn from a log-normal distribution with sigma 0.5 and clamped to [5s, 120s].
  • First star: on entering the window, the initial timer is rolled at half the normal interval so the first appearance comes sooner.
  • Spawn position: 600 px from the player at a random angle, with base velocity ~154 px/s aimed back through the player.

In-world entity

The star itself is a moving pickup, not a passive marker.

  • Visual signature: a 5-pointed white-hot star with an orange/gold gradient core, wrapped in a golden spinning ring of radius 100 px with 8 tick marks, and trailing a rainbow tail (red → orange → yellow → green → blue → indigo → violet) capped with a white-hot core line.
  • Collection radius: the outer golden ring at 100 px (world space) is the hitbox, not the 8 px star body. Touching anywhere inside the ring counts.
  • Player-reactive speed: as the player approaches, the star decelerates over 4.5s down to 0.5× speed, then accelerates back to 1.0× to “exit” — a chase that gives the player a real shot but isn’t free.
  • Trail buff: the rainbow tail (last ~3.6s of path, 120 sampled points) is also an active zone — the player gets +30% speed while inside TRAIL_BUFF_RADIUS = 60 px of any trail point. Threading the trail is part of the catch loop.
  • Despawn: stars only despawn when more than 3000 px from the player (roughly 3 screens away). Lifetime-based despawn is intentionally removed — a star must never vanish in front of the player.
  • Pickup bumps: nearby gems and weapon boxes are knocked by the star as it passes, with golden spark particles.

Collection sequence

On ring touch:

  1. Multi-phase golden particle burst centered on the player (ring shatter, gold sparks, white starburst, gold confetti, upward geyser, white-hot core flash).
  2. Expanding ring-pop VFX at the collection point.
  3. Game enters weapon-chest-style freeze (game._weaponChestFreeze = true, timeDilation = 0).
  4. Star flies from its world position to screen center over 0.6s, growing 1.0× → 1.5× scale and spinning faster.
  5. Reward screen appears with the shooting-star choices.

Reward categories

generateShootingStarChoices builds a pool of up to 8 categories, shuffles, and presents 2. All cards render as rarity: 'legendary'. Categories split into three families.

Power-spike rewards

Eligibility-gated. Only appear if the player has something for them to act on.

  • ALL WEAPONS (weapons) — +1 level on every owned weapon, capped at MAX_WEAPON_LEVEL = 20. Eligible if ship has any weapons.
  • ALL SHIP MODS (ship_upgrades) — bump every owned modifier by 1 level, capped at each mod’s maxLevel. Each rank is applied through _applyModifierPick so the mod system receives a normal registration. Eligible if any mod is owned.
  • ALL ARTIFACTS (artifacts) — ascend every owned artifact by 1 tier via grantArtifact. Eligible if any artifact is owned.
  • FORGE STRIKE (lowest_weapon) — add 3 levels to the lowest-level weapon (ties resolved by first-in-array). Eligible if any weapon exists below MAX_WEAPON_LEVEL.

Buff reward

  • STAR POWER (player_buff) — 60 seconds of the exclusive starpower state via applyExclusiveState(ship, 'starpower', 60). While active: invulnerable, 2× damage, +20% speed, no heat. Always eligible.

Utility / charge-grant rewards

Always eligible. These exist to refill the run’s meta resources. Card metadata is fixed strings, not stat-scaled.

  • +1 REROLL (grant_reroll) — game.rerolls += 1. Adds a reroll charge for future selection screens.
  • +1 BANISH (grant_banish) — game.banishes += 1. Adds a banish charge.
  • +1 REFUEL (grant_refuel) — game.refuels += 1. Adds a mid-run refuel charge that restores fuel.

Non-banishable cards

The three charge-grant categories (grant_reroll, grant_banish, grant_refuel) return null from banishKeyForChoice, which means the banish button is suppressed on those cards. The reasoning is encoded directly in the source: banishing a +1 REROLL card would block the only path to refilling rerolls for the rest of the run — same policy logic that applies to the other charge-grant cards.

Every other shooting-star category banishes under the key shooting_star|<category>. Banished categories are filtered out of the eligible pool by generateShootingStarChoices, so once you banish ALL WEAPONS it won’t reappear for the rest of the run.

The shooting-star pool is also independent of the regular reward pools (weapon, weapon_upgrade, artifact, modifier, etc.) — banishing inside the shooting-star pool does not affect what shows up at normal level-ups.

Tracking

Each shooting-star pick appends shooting_star_<category> to game.tracking.upgradesChosen, so post-run tracking can distinguish shooting-star picks from regular level-up choices.

Key constants

ConstantValuePurpose
STAR_RING_COLLISION_RADIUS100 pxCollision = the visible golden ring, not the star body.
STAR_DESPAWN_DISTANCE3000 pxStars only cull when ~3 screens away from the player.
STAR_FLY_DURATION0.6 sCollected-star fly-to-center animation length.
TRAIL_SAMPLE_INTERVAL0.03 sTrail records a point every 30 ms.
TRAIL_MAX_LENGTH120 points≈3.6 s of trail history = the +30% speed buff zone.
TRAIL_BUFF_RADIUS60 pxDistance from a trail point to count as “in the trail”.
TRAIL_BUFF_REFRESH_SECONDS0.12 sBoost timer refresh per frame in-trail (>1 sim step to avoid flicker).
MAX_WEAPON_LEVEL20Cap for ALL WEAPONS and FORGE STRIKE.
spawn distance600 pxDistance from player at spawn.
spawn base speed~154 px/s ± 20Initial velocity aimed at player.

Files

  • starship-survivors/src/starship-survivors/engine/world/shooting-star.ts — entity, spawner, update loop, collection, trail buff, ring-pop VFX.
  • starship-survivors/src/starship-survivors/engine/world/leveling.tsgenerateShootingStarChoices, applyChoice case 'shooting_star', banishKeyForChoice, _SHOOTING_STAR_CARDS metadata.
  • starship-survivors/src/starship-survivors/engine/rendering/draw-shooting-star.ts — rainbow trail, golden ring, 5-pointed star, fly-to-center animation.