level-progression.ts
Run-level sequence definitions, level-kind resolver, sealed-arena geometry constants, and boss kind-multipliers. Source of truth for “what kind of level is this?” and the physical size of sealed-arena fights.
Acheron (purpose)
Drives world-gen, AI director pinning, and end-conditions per level. Two sequences: normal mode (5 levels, one biome) and Challenge Mode (10 levels, same per-level curve, double payouts applied at run-assembly, not here). Also exports the sealed-arena half-extent / wall thickness and the boss “kind” HP/damage multipliers that scale mini-boss and boss tiers above their base enemy stats.
Lethe (key exports)
Types
LevelKind = 'normal' | 'mini_boss' | 'hard' | 'boss'
Sequences
RUN_LEVEL_SEQUENCE: readonly LevelKind[]— fixed normal-mode arc, index =level - 1:- 1:
normal(4-min biome level, normal ramp) - 2:
normal(same biome/palette, normal ramp) - 3:
mini_boss(sealed square arena, mini-boss + roster, ends on boss death) - 4:
hard(same biome/palette, ramp pinned at 1.0, basics-only window skipped) - 5:
boss(sealed square arena, boss with 2× HP/damage at the tier level, ends on boss death → results screen)
- 1:
CHALLENGE_LEVEL_SEQUENCE: readonly LevelKind[]— 10 levels for Challenge Mode (unlocked per-planet after clearing normal-mode final boss):- 1–2:
normal,normal - 3:
mini_boss - 4–5:
normal,normal - 6:
mini_boss - 7–8:
hard,hard - 9:
mini_boss - 10:
boss
- 1–2:
Resolvers
resolveLevelKind(currentLevel: number, isChallenge?: boolean): LevelKind— 1-indexed lookup.currentLevel < 1returnsseq[0]. Levels past the end clamp to the finalbosskind (dev-mode safety — the engine never crashes; production runs terminate at the final level → results screen).isFinalLevel(currentLevel: number, isChallenge?: boolean): boolean—currentLevel >= seq.length. Final level triggers post-clear → results screen._seqFor(isChallenge?: boolean)— internal switch betweenCHALLENGE_LEVEL_SEQUENCEandRUN_LEVEL_SEQUENCE.
Sealed-arena geometry
SEALED_ARENA_HALF_SIZE = 1400— half-extent in world units. Arena footprint is a2 * SEALED_ARENA_HALF_SIZEsquare (2800 units per side). Walls outside this footprint are unbreakable, enforced by per-frame ship clamp + Rapier static cuboid colliders + wall thickness rendered ≥SEALED_ARENA_WALL_THICKNESS.SEALED_ARENA_WALL_THICKNESS = 2000— wall thickness for sealed arenas. Combined with the per-frame clamp this guarantees no entity (player, projectile, knockback push) escapes.
Boss kind multipliers
v5.162.2 across-the-board 4× buff so bosses feel like real walls. Boss tier doubles mini’s mult so the relative scale (boss = 2× mini) is preserved. Depth scaling on top is applied in encounter.ts (currentLevel / final level → ×1.0–×1.5 ramp).
BOSS_KIND_HP_MULT_MINI = 4.0BOSS_KIND_HP_MULT_BOSS = 8.0BOSS_KIND_DAMAGE_MULT_MINI = 4.0BOSS_KIND_DAMAGE_MULT_BOSS = 8.0
Cocytus (consumers)
- World-gen / encounter director consumes
resolveLevelKindto decide spawn profile, arena vs open biome, ramp pin, and basics-only window behavior. - Sealed arena builders read
SEALED_ARENA_HALF_SIZE/SEALED_ARENA_WALL_THICKNESSto place walls, set Rapier static colliders, and clamp the player each frame. - Boss/mini-boss stat code multiplies base enemy HP and damage by the
BOSS_KIND_*constants; depth ramp (encounter.ts) multiplies on top. - Challenge Mode unlock pathway reads
CHALLENGE_LEVEL_SEQUENCEafter a planet’s normal final boss is cleared; payout doubling happens at run-assembly, not in this file.
Phlegethon (invariants)
- Index is 1-based.
currentLevel = 1is the first level.resolveLevelKind(0)(or any value < 1) returnsseq[0]. - Sequences are
readonly— never mutate at runtime. Add new kinds by extendingLevelKindand authoring a new sequence. - Out-of-bounds
currentLevelclamps to finalbossrather than throwing. This is a dev-mode escape hatch; production must callisFinalLevelbefore advancing. - Sealed-arena walls are unbreakable by contract. Anything that bypasses the clamp + colliders + wall geometry is a bug.
- Boss-tier multiplier ratio is locked:
BOSS = 2 × MINIfor both HP and damage. Changing this breaks the “bosses feel 2× mini” tuning intent.
EXTRACT-CANDIDATE
- Boss kind multipliers (
BOSS_KIND_HP_MULT_MINI/BOSS,BOSS_KIND_DAMAGE_MULT_MINI/BOSS) could move to a dedicated boss-tuning module alongside theencounter.tsdepth-ramp constants — they’re tuning knobs, not progression structure. Keeping them here couples sequence definition with combat-stat tuning. - Sealed-arena geometry constants (
SEALED_ARENA_HALF_SIZE,SEALED_ARENA_WALL_THICKNESS) could move to asealed-arena.tsdata file alongside other arena-only constants (wall material, render thickness floor). Currently they live here because mini-boss / boss levels are the only consumers, but the binding is conceptual not structural.