data/run-config.ts — RunDefinition schema
The single typed contract the engine consumes to boot a run. The metagame assembles a RunDefinition from player loadout, mission selection, ship choice, facility bonuses, codex bonuses, and world knobs; the engine reads it without knowing where any value came from. No field is optional (except the few explicitly marked ?). The engine throws on missing or invalid fields via validateRunDef.
Schema version is 2. Header comment notes this matches archive DEFAULT_RUN_DEF v4 structure with three adaptations: heat-system fields kept for data completeness but not consumed by engine; durability removed; audio removed.
Per-run kill threshold
EVENT_KILL_THRESHOLD = 25
Kill count required before sub-events (charge, etc.) start spawning during a run. Not a progression unlock — resets every run.
Top-level shape
RunDefinition is a flat object with seven top-level keys (plus version and the optional sandbox).
| Key | Purpose |
|---|---|
version | Schema version. Current = 2. Validator throws on mismatch. |
node | Mission identity + world configuration (WHAT and WHERE). |
ship | Vessel the player is flying — combat stats, slot counts, starting weapons. |
context | Metagame state read by engine but never written (facilities, codex, world knobs, bonuses). |
spawn | Spawn timing, quantity curve, quality curve. |
player | Player input tuning. |
sandbox? | Optional. Disables timer, director, leveling. Used by /sandbox playground. |
timing | Death timer, hit-flash decays. |
Sub-interfaces
MissionObjective
| Field | Type | Notes |
|---|---|---|
type | string | `‘explore' |
count | number | Beacons/items/kills required. 0 = N/A for survive_timer. |
BossConfig
| Field | Type | Notes |
|---|---|---|
id | string | Boss enemy type ID. Empty string = no boss. |
baseMult | number | Base stat multiplier applied to boss template. |
penaltyPerMiss | number | Score penalty per missed objective (0–1). |
VisionConfig
| Field | Type | Notes |
|---|---|---|
baseRadius | number | Lit radius around ship (world units). Default 1020. |
dimWidth | number | Width of dim-to-dark gradient ring. |
darkOpacity | number | Dark-zone opacity (0 = transparent, 1 = pitch black). |
ambientLight | number | Ambient floor for dark zone (0–1). |
EventPoolConfig
Single field pool: string[] — event type IDs that can spawn during this mission. Empty pool triggers bridge.ts buildDefaultEventPool() (default mix 45 / 26.7 / 16.7 / 6.7 / 5).
ShipCombatStats
Everything the engine uses for ship physics and damage. Grouped into five sub-blocks.
Core combat
| Field | Notes |
|---|---|
hpMax, hpRegen | Hull HP cap + per-second regen. |
shieldMax, shieldRegenRate, shieldRegenDelay, shieldRegenFillTime | Shield cap, regen rate, delay before regen starts, fill time. |
damageReduction | Flat DR. |
thrust, maxSpeed, drag | Locomotion. |
weaponDamagePct, fireRatePct | Weapon bonuses (percent, 0 = no bonus). |
luck, magnetRange, currencyBonus | Drop quality, pickup range, currency bonus. |
turnSpeed | Radians per frame at 60fps. |
Heat / overheat
| Field | Notes |
|---|---|
overheatBurn | Damage per second when overheated. |
overheatCool | Cooling rate (units per second). |
heatSpeedTarget | Speed at which heat stops accumulating. |
heatBoostMult | Speed multiplier when boosting. |
heatCurve | `‘linear' |
burnoutSeverity | Burnout damage scale. 0.35 = mild, 1.0 = standard, 2.0 = devastating. |
Physics / collision (per-ship tunable)
| Field | Notes |
|---|---|
shipScale | Visual + collision scale multiplier. |
ramSpeedBleed | Speed fraction retained after high-speed ram. |
contactSpeedBleed | Speed fraction retained on low-speed enemy contact. |
terrainRestitution | Bounce coefficient off terrain (1.09 = 9% reflect). |
terrainFriction | Tangential friction on terrain bounce (0 = ice, 1 = velcro). |
ramThreshold | Minimum speed (u/s) to deal ram damage. |
ramDamageLo, ramDamageHi | Ram damage at threshold and at max ram speed. |
pushRatio | Ship’s share of push-apart on overlap. |
enemySolidity | How much overlap push goes to ship vs enemy. |
contactDecel | Speed multiplier on low-speed enemy contact. |
contactCooldown | Seconds of invuln between body contacts. |
meleeMult? | Ramming damage multiplier (0.1×–3.0×, default 1.0). |
shipClass? | `‘heavy' |
Visual / sprite
| Field | Notes |
|---|---|
spriteHue | Hue rotation (−180 to 180). |
spriteSaturation, spriteContrast, spriteBrightness | Standard image filters. |
Vehicle feel
| Field | Notes |
|---|---|
rotates | true = ship faces input direction. false = hovercraft (fixed angle, omnidirectional thrust). |
fixedAngleDeg | Fixed facing angle when rotates === false (0=up, 90=right). Ignored when rotates === true. |
accelCurve | `‘linear' |
dragCurve | `‘linear' |
stopShakeIntensity | Camera shake on hard stop. |
heatShakeIntensity | Continuous camera shake while heat above threshold. |
heatShakeThreshold | Heat fraction (0–1) above which heat-shake kicks in. |
stopSpringAmt | Spring-back amount when stopping. |
ShipMetaStats
Non-combat modifiers that stack with combat-side equivalents.
| Field | Notes |
|---|---|
luck | Stacks with combatStats.luck. |
currencyBonus | Stacks with combatStats.currencyBonus. |
objectiveSpeed | Objective completion speed bonus (percent). |
StartingWeapon
{ id: string; level: number }. Ship must have at least one (validator throws on empty array).
FacilityBonuses
Additive percentages from metagame buildings unless noted. Zero = no bonus. Negative values are valid for time reductions.
Legacy (kept for compat): weaponDamagePct, upgradeChoicesBonus, dropRarityBonus, xpBonusPct, objectiveBonusPct.
Current building bonuses:
| Field | Source building | Effect |
|---|---|---|
arcadeCreditsPct | — | +% credits from arcade runs |
missionCreditsPct | Yard | +% credits from missions |
buildTimePct, moveTimePct, upgradeTimePct, gemSkipCostPct | Refinery | −% construction / move / upgrade time, −% gem skip cost |
worldEventSpawnPct | Array / Foundry / Beacon | +% world-event spawn rate |
chestRewardPct, challengeXpPct | Archive | +% chapter chest rewards, +% challenge XP |
missionSuccessChancePct, missionBoardSlots, missionShipSlots, missionRewardPct | Relay | Mission board mechanics |
missionDurationPct | Port | −% mission duration |
shipDropChancePct | Yard / Port | +% ship drop chance |
eventRewardQualityPct | Beacon | +% event reward quality |
hullRolePackagePct, hullMissionContributionPct | Hangar | Per-hull package/role bonuses |
weaponDamageBonusPct | Lab | +% damage for assigned weapon |
enemyCountPct | Barracks | +% enemy count & density |
eliteRarityCap | Barracks | `‘rare' |
CodexBonuses
| Field | Type | Notes |
|---|---|---|
damageVs | Record<string, number> | Damage multipliers vs enemy types, e.g. { 'scout': 0.1, 'juggernaut': 0.15 }. |
weaponBonuses | Record<string, number> | Per-weapon bonuses, e.g. { 'rifle': 0.05 }. |
WorldKnobs
Multipliers on enemy stats and rewards.
| Field | Notes |
|---|---|
enemyHpMult, enemyDamageMult, enemySpeedMult, enemyCountMult | Direct multipliers. |
rewardMult | Rewards multiplier. |
rarityScale | Hidden ship-rarity difficulty scale. 1.0 = legendary baseline, lower = easier run. Set at run-assembly from player’s ship rarity. Already baked into enemyCountMult / HpMult / DamageMult above; also applied separately at hardcoded melee/charger damage call sites (which bypass enemy.damageMult). |
SessionBonuses
| Field | Notes |
|---|---|
dailyBonusActive | Daily login bonus flag. |
rareSignalRewardMult | Reward multiplier for rare-signal runs. |
deathDefianceTokens | Token count. |
Top-level fields
node
| Field | Notes |
|---|---|
id | Unique node/mission ID for tracking. |
seed | World-gen seed. 0 = auto-generate. |
heat | Difficulty heat level (1–10). Validator enforces range. |
missionType | `‘explore' |
missionId | Specific mission definition ID (scripted missions). |
biome | Biome identifier for world generation. |
timerSeconds | Mission timer (0 = untimed). |
objective | MissionObjective. |
boss | BossConfig. |
vision | VisionConfig. |
events | EventPoolConfig. |
isRareSignal | Bonus-rewards flag. |
levelConfig? | LevelConfig from ./level-config. If omitted, engine uses DEFAULT_LEVEL_CONFIG. |
weaponBoxCount | Weapon boxes spawned in the world. |
artifactBoxCount | Artifact boxes spawned per level. |
bossDefId? | Override boss. When set, this BossDef.id spawns at end-of-level instead of the level-based default. Used by playground / dev scenarios. Empty/undefined → engine resolves from _currentLevel via LEVEL_BOSS_ROTATION in engine/bridge.ts. |
ship
| Field | Notes |
|---|---|
id | Ship type ID, e.g. 'green_dart', 'blue_bigrig'. |
color, accent | Hex color strings. |
combatStats | Full ShipCombatStats — applied to ShipState at mission start. |
metaStats | ShipMetaStats. |
weaponSlotCount | Validator requires ≥1. |
nonWeaponSlotCount | Module/artifact slot count. |
startingWeapons | Non-empty StartingWeapon[]. |
context
Read-only from the engine’s perspective. Mostly bonus pools and unlock state.
| Field | Notes |
|---|---|
accountLevel | Validator requires ≥1. |
weaponPool, upgradePool, artifactPool | `string[] |
startingArtifactId | Player-chosen starting artifact. Granted at run start at tier 0 (uncommon). Null = no starting artifact. |
levelupRarityCap, weaponCacheRarityCap | Max rarity tier for level-up and weapon cache pools. |
eventsUnlocked, starsUnlocked | Both gate on lifetime kills ≥ 25. |
eventTier | 0=locked, 1=basic, 2=mid, 3=high. |
planetId | Planet ID (matches display name, e.g. 3, 12, 21). Used for nebula archetype selection. 0 = random biome archetype. |
isChallenge? | Challenge Mode — 10-level run, same per-level difficulty curve as normal mode. Unlocked per planet after clearing normal-mode final boss. Doubled rewardMult is applied at run-assembly time. |
supplyLevel | Supply building level — controls XP crate count at run start (0 = none). |
facilities | FacilityBonuses. |
codex | CodexBonuses. |
worldKnobs | WorldKnobs. |
bonuses | SessionBonuses. |
spawn
Quantity controls how many enemies are on screen. Quality controls what enemies spawn (tier selection). Both use tent-pole keyframes { time: seconds, value: 0–1 }; engine lerps between keyframes.
| Field | Notes |
|---|---|
initialDelay | Seconds before first enemy. |
spawnInterval | Base seconds between waves. |
spawnIntervalVariance | Random variance added to spawnInterval (0 = fixed). |
batchSize | Max enemies per wave. |
quantityCurve? | Tent-poles. Default ramp: 0→0 at 0s, 0→0.3 at 30s, 0.3→1.0 at 120s, flat 1.0 after. |
qualityCurve? | Tent-poles. Default: continuous linear ramp 0→1 over full run duration. |
curveRandomization? | ±randomization range within each tent-pole (0–1). 0 = deterministic, 0.2 = ±20%. Default 0.15. |
player
| Field | Notes |
|---|---|
invulnDuration | Spawn invuln seconds. |
autoAimRange | Auto-aim detection radius (world units). |
deadzone | Joystick deadzone (0–1 normalized magnitude). |
sandbox?
Optional boolean. Disables timer, director, and leveling. Used by /sandbox playground. Defaults to false.
timing
| Field | Notes |
|---|---|
deathTimer | Seconds on death screen before results. |
enemyFlashDecay | Hit-flash decay rate (per second, multiplied by dt). |
damageFlashDecay | Screen damage-flash decay rate. |
validateRunDef(def)
Throws on any missing or invalid field. Called once before passing to createMission().
Hard checks (all throw):
deftruthy;version === 2.node.idnon-empty string;seedis number;heat∈ [1,10];missionTypeandbiomenon-empty strings;timerSeconds >= 0;objective.typeandboss.idare strings;vision.baseRadius > 0.ship.idnon-empty;startingWeaponsnon-empty array.- Combat stats:
hpMax > 0,shieldMax >= 0,thrust > 0,maxSpeed > 0,drag > 0,magnetRange >= 0,weaponSlotCount >= 1. context.accountLevel >= 1;worldKnobs.enemyHpMult > 0,enemyCountMult > 0,rewardMult > 0.spawn.initialDelay >= 0,spawnInterval > 0,batchSize >= 1.player.invulnDuration >= 0,autoAimRange > 0,deadzone∈ [0,1].timing.deathTimer > 0,enemyFlashDecay > 0,damageFlashDecay > 0.
DEFAULT_RUN
Reference default. Used for quick-play and as the assembly template for the metagame. Values match archive DEFAULT_RUN_DEF v4 where applicable.
| Block | Notable defaults |
|---|---|
node | id='quickplay', seed=0, heat=5, missionType='explore', biome='landing_site', timerSeconds=240 (4 min/level), objective survive_timer/0, boss { id:'', baseMult:1.0, penaltyPerMiss:0.3 }, vision { 1020 / 150 / 0.99 / 0.03 }, empty event pool (bridge fills default), weaponBoxCount=0, artifactBoxCount=1 (bridge applies 5% spawn chance — 20× rarer than always). |
ship | id='Backwater_Lizard', color #ffffff / accent #333333. Combat stats: hp 80, shield 10, thrust 50, maxSpeed 100, drag 0.3, turnSpeed 0.32, magnetRange 128, weaponDamagePct 12, fireRatePct 6, luck 12, currencyBonus 8. Heat: linear curve, burnoutSeverity 1.0, heatBoostMult=2.5. Physics: shipScale 1.0, ramSpeedBleed 0.80, terrainRestitution 1.09, ramThreshold 150, ramDamageLo/Hi 40/1000. Vehicle: rotates=true, accelCurve='linear', dragCurve='exponential'. 3 weapon + 3 non-weapon slots. Starting weapon rifle@1. |
context | accountLevel 1, all pools null (use-all), eventsUnlocked=false, starsUnlocked=false, eventTier=0, planetId=0, supplyLevel=0. All facilities zeroed; eliteRarityCap=null. Codex empty. WorldKnobs { hp 0.5, dmg 0.75, speed 1, count 1.0, reward 1, rarityScale 1 }. Bonuses { daily=false, rareSignalMult=1, deathDefianceTokens=1 }. |
spawn | initialDelay=1.0, spawnInterval=0.8, spawnIntervalVariance=0.5, batchSize=5. Quantity curve [0→0, 30→0.3, 120→1.0, 9999→1.0]. Quality curve [0→0, 30→0.1, 120→0.5, 240→1.0, 9999→1.0]. curveRandomization=0.15. |
player | invuln 2.0s, autoAimRange 400, deadzone 0.08. |
timing | deathTimer 3.0s, enemyFlashDecay 6, damageFlashDecay 3. |
Cross-references
./level-config—LevelConfigreferenced bynode.levelConfig?.engine/bridge.ts—buildDefaultEventPool()(called whennode.events.poolis empty),LEVEL_BOSS_ROTATION(used whennode.bossDefIdis unset).createMission()— caller; receives the validatedRunDefinition.
EXTRACT-CANDIDATE
- Heat-system fields in
ShipCombatStatsare kept in the schema for data completeness but not consumed by the engine per header comment (lines 12–13). Candidates for extraction to a separateHeatConfiginterface (or removal) once the heat system’s status is decided:overheatBurn,overheatCool,heatSpeedTarget,heatBoostMult,heatCurve,burnoutSeverity,heatShakeIntensity,heatShakeThreshold.EVENT_KILL_THRESHOLD(line 22) also references the events unlock at lifetime-kills ≥ 25 that appears incontext.eventsUnlocked/starsUnlocked— single source of truth candidate. - Legacy fields in
FacilityBonuses(lines 190–199):weaponDamagePct,upgradeChoicesBonus,dropRarityBonus,xpBonusPct,objectiveBonusPctare explicitly marked legacy in comments and overlap with newer fields (weaponDamageBonusPct,objectiveBonusPctvsmetaStats.objectiveSpeed, etc.). Candidate for deletion sweep once the metagame stops reading them. durabilityandaudioremoved per header comment — confirm no stale references elsewhere in the codebase.isChallenge?is optional butrewardMultdoubling is described as happening “at run-assembly time” — verify the assembly site applies it consistently and consider promoting to required field with defaultfalse.bossDefId?empty-string vs undefined — header says “Empty/undefined” both fall back to default; consider tightening to a single sentinel (drop the?and require empty string for “no override”) for fewer branches in consumers.weaponPool/upgradePool/artifactPoolusenullto mean “use all”, butlevelupRarityCap/weaponCacheRarityCapalso usenullfor “no cap” — convention is consistent but worth documenting in one place rather than re-explaining at each field.