Extraction Timer
Every mission carries a per-difficulty countdown — “SURVIVE M:SS until extraction”. The player must stay alive until the timer hits zero to clear the level; the timer can be skipped by spending gems. Letting the player’s hull die before extraction (or any other loss condition) fails the mission.
Per-difficulty values
The full table lives in EXTRACTION_TIMER_BY_DIFFICULTY (src/starship-survivors/data/mission-postings.ts). The spec default is 4 minutes at Standard; tiers fan out by ±60 seconds per step, monotonic with difficulty:
| Difficulty | Seconds | Minutes | Note |
|---|---|---|---|
| Routine | 180 | 3:00 | Onboarding tier — kept snappy |
| Standard | 240 | 4:00 | Spec default |
| Hazardous | 300 | 5:00 | |
| Critical | 360 | 6:00 | |
| Black Flag | 420 | 7:00 | Endurance tier |
The map is exhaustive over MissionDifficulty. TypeScript fails the build if a new difficulty is added without an entry. Each MissionPosting plumbs its tier’s seconds through extractionTimerSeconds, which becomes RunDefinition.node.timerSeconds at mission deploy.
How the timer is wired
At run start engine/bridge.ts writes the resolved seconds into two fields on the global game state:
game.missionTimer— current remaining seconds, drained every sim tick.game.missionTimerMax— the starting value, used by the HUD for the “untimed mission” gate (missionTimerMax <= 0hides the banner entirely, e.g. boss arenas).
Elapsed-time tracking lives on game.missionElapsed, which counts up from zero on the same tick the timer counts down. missionElapsed is what drives director ramps, spawner pressure, and the level-end mission-result payload (timerRemainingSeconds = max(0, timerSeconds - missionElapsed)).
When the timer drains
The drain rule lives in engine/core/loop.ts. The timer decrements only when all four conditions hold:
game.phase === 'playing'(not menu, not levelup, not cinematic, not pause).!game.overtime— the post-zero overtime path freezes the timer at zero.!game.runDef?.sandbox— sandbox runs (playground, dev workbench) never tick down.!game.bossRoom— sealed-arena boss rooms freeze the timer for the duration of the encounter.
missionElapsed follows the same gate against runDef.sandbox so director ramps don’t progress in sandbox either. Pause freezes both via game.time not advancing.
Skip with gems
The player can spend gems to skip the remaining time and end the level immediately. The skip is paid from the gem balance — see gameplay/concepts/gem-economy.md for the cost curve.
Failure condition
Timer reaching 0 does not by itself end the run — it flips game.overtime, which signals the level-end branch in the loop. The actual fail condition is death before extraction: if ship.alive goes false during a timed mission, the run ends with a fail outcome. If the player survives to missionTimer === 0, the loop advances to the next level (normal/hard kinds) — boss/mini-boss kinds never reach this branch because the boss-clear flag ends the level first.
HUD banner
The “SURVIVE M:SS until extraction” banner (engine/rendering/mission-timer-hud.ts) renders above the bottom weapon slots whenever the timer is active. The number turns urgent (medical statusBad red stripe, statusWarn amber number) under 30 seconds remaining, pulsing in sync with the top-right HUD timer. The banner hides on phase levelup / cinematic, when ship.alive is false, when missionTimerMax <= 0 (untimed missions like boss arenas and sandbox), and when the timer has already expired (overtime path owns its own messaging).
Related
gameplay/missions/mission-postings.md— per-posting metadata and the planet-to-tier mapping.gameplay/concepts/gem-economy.md— skip cost.engine/rendering/mission-timer-hud.md— banner geometry and pulse tuning.