Mission Board (UI)

What it is

The mission board is the metagame screen where the player picks the contract for the next deploy. It is a full-screen white-panel layout titled “JOB BOARD // CYGNUS NET v3.2” that lists exactly two posting cards drawn from the static mission-postings table. Each card describes one in-universe contract (planet, hiring faction, difficulty tier, objective vibe, payout, author tag) and exposes a single primary action that assembles the run definition and launches the run. The board has no filter controls, no detail page, and no per-card refresh. The screen lives at the metagame route /games/starship-survivors/board.

Stats tables

Layout regions

RegionPositionContents
Header barTop of viewportBack-home link, screen title, UTC timestamp
PreambleBelow headerSingle-line caption (“INCOMING POSTINGS — 2 OF MANY. SELECT ASSIGNMENT.“)
Cards areaCenter, takes remaining vertical spaceTwo posting cards laid out horizontally, wrapping on narrow viewports
FooterBottom of viewportIn-universe legal-banner line

Posting slot count

StatValue
Cards rendered per visit2
Total postings in pool11 (one per planet in planet order)
Same posting on both cardsNever (selector enforces distinct indices)
Empty / placeholder slotsNone

Per-card information shown

FieldSourceDisplay style
Factionposting.factionUppercase secondary-color label, top row
Difficulty pillposting.difficultyOutlined pill, color from difficulty token
Location labelresolved planet display name via PLANETS[posting.planetId]“LOCATION:” prefix, value in main text color
Objective headlineposting.objectiveLabelLarge uppercase cyan-accent line
Objective blurbposting.objectiveBlurbOne-line italic-feel quote with a left accent stripe
Payout lineposting.payoutLineAmber, bold
Posted-by tagposting.postedBySmall muted text, ”— Posted by {name}“
Accept actiononAccept handlerFull-width primary button labeled “ACCEPT”

Difficulty pill colors

DifficultyColor token
Routinestatus-good (green)
Standardaccent (cyan)
Hazardousamber
Criticalstatus-warn (orange)
Black Flagstatus-bad (red)

Any unrecognized difficulty value falls back to the muted-text color.

Action buttons

ControlLocationBehavior
HOME (back link)Header leftNavigates to the metagame root route
ACCEPTFooter of each cardAssembles a run definition from the selected ship and posting planet, writes it to the session store, navigates to the play route

There is no “decline,” “reroll,” “details,” “favorite,” or “compare” control. Each card has exactly one outgoing action.

Posting fields stored per record

FieldTypePurpose
idstringStable unique key
planetIdnumeric planet idDrives biome, level preset, enemy multipliers at assembly
factionone of ten in-universe factionsHeader label on the card
objectiveLabelone of Explore / Find / Protect / BattleHeadline copy + engine objective tuning
objectiveBlurbstringOne-line positive in-universe description
difficultyone of Routine / Standard / Hazardous / Critical / Black FlagPill color and extraction timer
payoutLinestringReward copy on the card
postedBystringIn-universe author tag
extractionTimerSecondsnumberSurvive-time-until-extraction the run uses

Refresh and reroll rules

TriggerEffect on the rendered pair
Initial mount of the screenPair is selected once from pickTwoPostings() using the current Date.now() as the seed
In-screen state change (re-render)No effect — the pair is held in component state initialized once via the lazy initializer
Soft navigation back to the screen (new mount)New pair drawn using the new Date.now() seed
Page reloadNew pair drawn using the new Date.now() seed
Manual reroll buttonNot present
Refresh timerNot present

The header UTC timestamp is captured once on mount via useMemo and does not tick.

Selector behavior

StatValue
Selector function usedpickTwoPostings(seed)
Seed defaultDate.now()
DeterminismSame seed always produces the same pair
Index A formulahashed seed modulo pool size
Index B formulasecond hash modulo (pool size − 1), shifted to skip index A
Planet-biased variant availableYes (pickTwoPostingsForPlanet) but not consumed by this screen
Fallback on zero matches (biased variant only)Unbiased two-card draw

How it works

  • Route mount instantiates the screen component; the component reads selectedShipId and setRunDef from the session store.
  • The two-card pair is picked once via pickTwoPostings(); the result is stored in component state with a lazy initializer so re-renders do not redraw.
  • The header timestamp is built once via useMemo and shown in YYYY-MM-DD HH:MM:SS UTC format.
  • Each card is rendered through a card panel using the medical-UI card variant. Layout flows top-to-bottom: faction + difficulty pill, location, objective headline, blurb, payout, posted-by tag, accept button.
  • The location label resolves the posting’s planet id to its display name via the planets map and falls back to the raw id if the entry is missing.
  • The accept button calls a handler that calls the run-assembly service with the current selected ship id and the posting’s planet id, writes the resulting run definition into the session store, then navigates to the play route. The objective tuning and extraction timer travel with the posting via the underlying assembly path; this screen only forwards the planet id and ship id.
  • The back-home link is a router link that returns to the metagame root route.
  • The screen has no internal state besides the held pair and the memoized timestamp.

Interactions

  • Mission postings data: the screen consumes the static posting table and the pickTwoPostings selector. The pool count, per-record fields, difficulty tiers, and objective vibes all live in that data module.
  • Planets: each card resolves its planet display name from the planets map keyed by the posting’s planet id. The accept handler forwards the planet id to the run assembler as the planet index.
  • Run assembly: the accept handler invokes the run-assembly service with the current ship id and the chosen planet id. The service uses the planet id to set biome, level preset, planet enemy multiplier, and starting context, and returns the run definition the play route consumes.
  • Session store: the screen reads selectedShipId and calls setRunDef to publish the assembled run definition before navigating away. The session store carries the run definition into the play route.
  • Routing: the back-home link uses the metagame root route; the accept action navigates to the play route.
  • Medical-UI panel: each card is rendered through the shared card-variant panel component, which provides the white-surface aesthetic, hairline border, shadow, and shared spacing tokens.
  • Difficulty extraction timer: each posting carries an explicit extraction-timer-seconds field (3, 4, 5, 6, or 7 minutes per tier), which is consumed downstream by the run engine to drive the in-run “SURVIVE M:SS until extraction” banner. The card itself does not display the numeric timer.

What it does NOT do

  • Does not show more than two cards at a time.
  • Does not let the player reroll, refresh, swap, or shuffle the pair without leaving and remounting the screen.
  • Does not surface loss conditions, fail states, or warning copy on the card body.
  • Does not display the extraction-timer numeric value on the card.
  • Does not display the per-objective engine tuning (enemy-count multiplier, weapon-box count) on the card.
  • Does not let the player pick the planet from this screen — the planet is bound to the posting record.
  • Does not let the player pick the ship from this screen — the ship is whatever the session has selected when the screen mounts.
  • Does not bias the pair toward a specific planet when reached via this route (the planet-biased selector exists in the data layer but is not invoked here).
  • Does not allow both cards to resolve to the same posting; the selector guarantees distinct indices.
  • Does not animate the cards in or out beyond the panel’s default mount behavior.
  • Does not tick the header timestamp; the timestamp is captured once on mount.
  • Does not consume the planet id passed from the hub at the React-Router level; the screen always draws from the unbiased pool.
  • Does not gate cards by player progression, account age, owned ships, owned artifacts, or any unlock state; all eleven postings are eligible on every mount.
  • Does not store the chosen posting on the run definition or on the session store as a posting record — only the planet id is forwarded into the assembler.
  • Does not surface the per-planet challenges list, tier KPI, or claim flow; those live in the challenges popover reached from the hub.
  • Does not persist the rendered pair across mounts or reloads; the seed defaults to wall-clock at mount time, so navigating away and back picks a fresh pair.
  • Does not block the accept action while a run is being assembled; assembly is synchronous.
  • Does not validate that the selected ship still exists or is owned; ship validity is handled upstream by the session store hydrator.