Leaderboard System
Per-planet global rankings for the two designated leaderboard planets. Each leaderboard planet replaces the normal Challenge Mode / progress-bar UI with a tier-reward track and a top-50 leaderboard table.
Leaderboard planets
Defined in src/starship-survivors/data/planet-config.ts via the optional isLeaderboard?: boolean field on PlanetDef. Exactly two planets have it set to true:
| Planet ID | Name | Boss | Enemy set | Pressure |
|---|---|---|---|---|
| 3 | THE VOIDSTAR | cenotaph | bugs | enemyCountMult: 2.0, spawnGraceSeconds: 0 |
| 31 | SPEEDWAY | spire | bugs_shooter | enemyCountMult: 1.0, spawnGraceSeconds: 1 |
Voidstar is the high-pressure track (double enemy count, zero grace). Speedway is a shooter-gauntlet track at normal pressure.
Ranking metric
Players are ranked by highest tier reached on the planet. Tier is the 4-minute mission difficulty-escalation counter (game._currentLevel). It is tracked per planet — your Voidstar best and Speedway best are independent records.
Source-of-truth lives in Supabase. The hydrated runtime cache is useTierStore (src/starship-survivors/stores/tierStore.ts), populated from bootstrap_player on app open and updated from finalize_run responses.
Submission flow
When a run ends, finalizeRun(result, planetIndex) in src/starship-survivors/services/runProgressionService.ts posts the run to the finalize_run Supabase RPC. The payload includes:
planet_id— fromuseSessionStore.getState().runDef.context.planetIdtier_reached—result.progression.tierReachedweapons_at_end,kills,duration_s,result, and other run fields
The server atomically writes match_history, computes currency rewards, updates player_currencies, and (when the run is on a leaderboard planet) updates the player’s tier_record for that planet. The response includes tier_record: { planet_id, highest_tier } and the client mirrors it into useTierStore.updateFromRunResult(...).
There is no separate “leaderboard submit” call — every finalized run on a leaderboard planet is also a leaderboard submission, because the rank metric is the persisted tier record.
Fetching the leaderboard
LeaderboardWrapper (src/metagame/components/LeaderboardWrapper.tsx) fetches the top entries on mount via the get_tier_leaderboard RPC, keyed by p_planet_id. The header comment says “top 50 by highest tier.”
Each entry has:
player_id,display_name,rankhighest_tier— the displayed scorekills— lifetime kills shown next to the name with a 💀 suffixweapons_json: WeaponEntry[]— the weapon loadout at end-of-best-run, rendered as icons viagetWeaponIconPathwith an emoji fallback fromWEAPON_EMOJIS
The row for the local player gets the lb-row-self CSS modifier so the player can spot themselves in the list.
Hub UI behavior
HubScreen.tsx (src/metagame/screens/HubScreen.tsx) branches on PLANETS[activePlanetId]?.isLeaderboard:
- Normal planets render
PlanetProgressBar+ Challenge Mode controls in thehub-progress-challenge-row. - Leaderboard planets render the tier-reward track (claimable milestones every 5 tiers, granted by
claim_tier_milestoneRPC) and overlayLeaderboardWrapperinside thehub-building-overlapcontainer.
Challenge Mode exclusion
Leaderboard planets are explicitly carved out of Challenge Mode:
const canUseChallengeMode = challengeUnlocked && !_activePlanetIsLeaderboard;Inline comment at HubScreen.tsx:103-105:
Challenge Mode is unavailable on leaderboard planets (Voidstar, Speedway) per the design — those are their own ranked tracks.
The Challenge Mode toggle does not render on these planets even after the player clears the planet’s final boss. This keeps the ranked tracks on a single fair difficulty surface.
Milestone rewards on leaderboard planets
Even though leaderboard planets don’t run the normal progress-bar track, they still grant tier milestone gems: every 5 tiers reached unlocks a claim via claim_tier_milestone. useTierStore.canClaimMilestone(planetId) checks highest_tier >= nextMilestone && !alreadyClaimed. Reward formula: getTierMilestoneGems(milestone) = milestone * 2 gems.
Key files
src/starship-survivors/data/planet-config.ts—isLeaderboardfield on planets 3 and 31src/metagame/components/LeaderboardWrapper.tsx— top-50 table,get_tier_leaderboardRPCsrc/metagame/screens/HubScreen.tsx— leaderboard-planet UI branching, Challenge Mode exclusionsrc/starship-survivors/services/runProgressionService.ts—finalize_runsubmission withtier_reached+planet_idsrc/starship-survivors/stores/tierStore.ts— hydrated per-planet tier record cache + milestone trackingsrc/metagame/components/ChallengePopover.tsx—claim_tier_milestoneRPC call site