PURPOSE

Builds and tears down the permanent rectangular sealed arena used by mini-boss and boss levels. Unlike the legacy circular BossRoom that spawns large and shrinks over 30s, the sealed arena materializes at its final size already in 'locked' state and never moves. Three confinement layers — per-frame ship clamp in boss-room.ts, four Rapier static cuboid colliders, and visible thick walls — keep the player and entities inside. Wraps existing BossRoom infrastructure so spawnBoss, createBossArena, affixes, and abilities continue to function.

OWNS

  • spawnSealedArena() — strips biome terrain inside the footprint, constructs a locked rect BossRoom, registers four Rapier wall colliders, and stamps game._sealedArenaActive = true. Returns the BossRoom for the caller to assign to game.bossRoom.
  • teardownSealedArena() — clears the arena wall colliders and resets game._sealedArenaActive to false. Safe to call when no arena is active.
  • sealedArenaPlayerSpawn() — returns the player spawn position at arena center { x: 0, y: 0 }. Dynamic in case a future caller spawns from a non-origin anchor, but normally bridge.ts resets the ship to origin on level advance.
  • Internal destroyTerrainInsideArena(cx, cy, half) — drops terrain and floaters whose centroid sits inside an oversized cull box (half * 1.1) so chunks straddling the wall don’t leave half-pieces clipping inside. Rebuilds terrain chunks when anything is removed, teleports the Rapier ship body to arena center, and clears stray enemy bodies.

READS FROM

  • ship.x, ship.y — used as the arena center (cx, cy) so the room materializes around the player’s current position.
  • game.time — written into room.spawnTime.
  • world.terrain, world.floaters — iterated to find pieces inside the footprint.
  • SEALED_ARENA_HALF_SIZE, SEALED_ARENA_WALL_THICKNESS from ../../data/level-progression — geometric constants for arena half-extent and wall thickness.
  • RapierWorld.isReady() — gate for all physics work; the function still produces a valid BossRoom when physics isn’t initialized.

PUSHES TO

  • game._sealedArenaActive — boolean flag toggled to true on spawn, false on teardown so other systems can detect a sealed-arena level.
  • world.terrain — splices removed pieces out in-place.
  • world.floaters — splices removed floaters out in-place.
  • RapierTerrain.addArenaWall(...) — four calls (top/bottom/left/right) registering static cuboid colliders, each SEALED_ARENA_WALL_THICKNESS thick. Corners overlap by the wall thickness so diagonal knockback can’t squeeze through a gap.
  • RapierTerrain.removePiece(t) / RapierTerrain.removeFloater(f) — per-entity physics cleanup before the array splice.
  • RapierTerrain.clearArenaWalls() — called from teardownSealedArena.
  • RapierShip.teleportKeepVelocity(cx, cy) — snaps the Rapier ship body to arena center so visual position and physics body agree after terrain is stripped.
  • RapierEnemies.clear() — defensive sweep so any stray boss/enemy bodies from a prior frame are dropped before the new arena starts.
  • buildTerrainChunks(world) — rebuilt only when at least one terrain piece was removed.

DOES NOT

  • Does not spawn the boss itself — caller invokes spawnBoss after assigning the returned room to game.bossRoom.
  • Does not clear game.bossRoom or game.bossArena — that is the caller’s / onBossEncounterEnd’s responsibility.
  • Does not shrink, move, or animate the arena — the room is created already at its final size in 'locked' state.
  • Does not enforce the ship-inside-arena clamp itself — that is updateBossRoom in boss-room.ts.
  • Does not handle the circular BossRoom shrink path — sealed arenas are always shape: 'rect'.
  • Does not run any logic if RapierWorld.isReady() is false beyond skipping physics calls; the returned BossRoom is still valid.

Signals

  • game._sealedArenaActive is the cross-system signal that the current level is a sealed-arena level.
  • The BossRoom is returned in state: 'locked' with shape: 'rect', currentW === targetW === SEALED_ARENA_HALF_SIZE * 2, and currentR === targetR === SEALED_ARENA_HALF_SIZE so helpers that fall back to min(W, H) / 2 for circle-style queries get a coherent radius.

Entry points

  • spawnSealedArena() — called by the level-advance flow when starting a mini-boss or boss level. The returned BossRoom is assigned to game.bossRoom, then spawnBoss wraps it in a BossArena.
  • teardownSealedArena() — called by the level-advance flow after a boss is killed.
  • sealedArenaPlayerSpawn() — called by the level-advance flow to position the ship for the new level.

Pattern notes

  • Wall geometry uses sideHalfLen = half + t so each side extends past the corners by the wall thickness. Combined with the four perpendicular walls, this guarantees no diagonal gap at the corners.
  • Wall placement: top and bottom walls sit at cy ± (half + halfThick) with half-extents (sideHalfLen, halfThick); left and right walls sit at cx ± (half + halfThick) with half-extents (halfThick, sideHalfLen).
  • Cull radius for terrain removal is half * 1.1 — intentionally oversized so chunks whose centroid is just outside the arena but whose geometry crosses the wall are still removed.
  • targetR and currentR are set to half even though the shape is rect; this keeps circle-style helpers consistent without a separate branch.
  • After stripping terrain the ship’s Rapier body is teleported (velocity preserved) to the arena center because the visual ship.{x,y} and the physics body must agree once the biome around them disappears.
  • RapierEnemies.clear() is defensive — any leftover enemy bodies from the previous level are dropped before the new arena begins.
  • buildTerrainChunks is only rebuilt when terrain was actually removed, avoiding redundant work on empty footprints.