Collection Screen (Ships Gallery)

The Ships screen is the player’s hull collection UI. It is the SHIPS sub-tab of ShipsScreen (screens/ShipsScreen.tsx); the ARTIFACTS sub-tab is a sibling surface, not part of this page.

Route: /games/starship-survivors/ships?tab=select (default tab). Deep-linked; URL stays in sync with the active sub-tab.

Layout

Two stacked regions inside screens/ships/SelectTab.tsx:

  • Top — hero card (340px tall) showing the currently selectedShipId. Live ship preview as full-bleed background, name, star bar, big-number stats (HP / SHIELD / SPEED), specialty slot, and weapon-slot circles glued to the bottom. (i) button in the top-right opens ShipInfoPopover with the full stat sheet.
  • Bottom — inventory grid. INVENTORY header strip with a FILTER toggle, followed by a scrollable grid of CollectibleCard tiles, one per owned hull. Grid uses repeat(auto-fill, minmax(108px, 1fr)).

Bottom-bar OverlayBottomBar carries BACK + SHIPS/ARTIFACTS sub-tabs.

Data source

The grid renders the player’s owned hulls only — no locked silhouettes are shown.

  • useInventoryStore (stores/inventoryStore.ts) holds ships: Record<string, ShipInstance>, keyed by hull_class. Each instance has { shipId, xp }. Starter hulls (Industria_Towncar, Junkrats_Tank, Solaris_Cargo) are granted on fresh accounts via resetToStarter.
  • HULL_CLASSES.filter(h => !!ships[h]) produces the visible list. Locked hulls do not appear; the inventory grid is owned-only.
  • Each card always passes locked={false}. (The CollectibleCard component supports a locked silhouette state, but SelectTab never invokes it — locked-state rendering lives in other surfaces such as the gacha-reveal flow.)

Grouping and sort

Hulls are NOT grouped by faction. The actual sort is a three-key descending order applied to all owned hulls:

  1. Rarity descending — legendary → epic → rare → uncommon → common (rank map in SelectTab.tsx).
  2. Star tier descending.
  3. XP descending (tiebreak within same rarity + star).

Effect: highest-rarity, highest-star hulls bubble to the top of the grid.

Star tier (1-5)

Per-hull star is derived from cumulative XP via starFromXp (data/ship-progression.ts). XP increments by 1 each duplicate pull through grantShipPull. First pull unlocks the hull at XP 0 (star 1). xpToNextStar drives the XP-bar fill on both the hero card and each CollectibleCard. Max star is 5; at 5 the bar pegs at 100%.

HullStarBar (components/HullStarBar.tsx) draws the gold star + slanted XP bar shared by the hero card and the inventory tiles.

Rarity badge

Each CollectibleCard shows a RarityBadge letter (legendary / epic / rare / uncommon / common) plus a colored frame. Rarity is the ship’s static def.rarity from getShipDef(hull, 1) — it does not change with star tier. The same rarity color drives the hero-card frame and glow, and is preserved per the ADR 20260513-001 exception inside the otherwise medical-token UI.

Filter

Press FILTER to open a panel above the grid with two filter rows:

  • BY RARITY — multi-select pills for LEGENDARY / EPIC / RARE / UNCOMMON / COMMON.
  • BY STAR — multi-select pills for 1★ through 5★.

Filter state lives in local React state (rarityFilter: Set<ShipRarity>, starFilter: Set<number>) inside SelectTab. Empty sets = no filter applied. A green count badge on the FILTER button shows the total number of active pills.

Empty result renders No ships match the current filter.

Selection — writes to sessionStore

Tapping a card calls setShip(hull) on useSessionStore (stores/sessionStore.ts):

  • Writes selectedShipId in the session store.
  • Fires update_player_save RPC via persistSelection to cloud-save the choice (fire-and-forget; errors logged, not surfaced).

selectedShipId is consumed by:

  • The hero card on the same screen (re-renders with new hull’s stats / preview / weapon slots).
  • LevelSelect and run-assembly screens, which read selectedShipId when building the RunDefinition.
  • The in-run game loop via the run bridge, which uses the session’s ship choice as the player hull.
  • The Hub at session reset.

Relation to the gacha screen

The gacha screen (chest opens, single/10-pulls) is the producer side of the same inventory store. Its grantShipPull(hull_class) call either:

  • Unlocks a new hull (unlocked: true, xpGained: 0) — first appearance in the Ships gallery.
  • Or grants 1 XP toward the existing hull (xpGained: 1) — bumps the star bar when viewed on this screen.

The Ships gallery is the readback: the player opens it after a chest pull to confirm the unlock or star-up.

Files

  • src/starship-survivors/screens/ShipsScreen.tsx — sub-tab shell + currency strip.
  • src/starship-survivors/screens/ships/SelectTab.tsx — hero card + filter panel + inventory grid.
  • src/starship-survivors/screens/ships/ShipInfoPopover.tsx — stat-sheet popover from (i) button.
  • src/starship-survivors/screens/ships/ShipLivePreview.tsx — full-bleed live ship preview on hero card.
  • src/metagame/components/CollectibleCard.tsx — per-hull grid tile.
  • src/starship-survivors/components/HullStarBar.tsx — gold-star + XP-bar unit.
  • src/starship-survivors/stores/inventoryStore.ts — owned hulls + XP + star derivation.
  • src/starship-survivors/stores/sessionStore.tsselectedShipId + cloud persist.
  • src/starship-survivors/data/ship-progression.tsstarFromXp, xpToNextStar.