Skip to content

Dimension Choice Popup — Design (replaces the portal system)

Dimension Choice Popup — Design (replaces the portal system)

Section titled “Dimension Choice Popup — Design (replaces the portal system)”

Status: open questions resolved (2026-07-10, Toby — draft answers, not set in stone). Not yet planned into executable tasks, not implemented. Batch D of the 2026-07-09 playtest feedback list.

The current portal system spawns one physical wormhole per Dimension (Sim._spawn_area_wormhole, sim.wormholes) in a ring around the boss’s death point, which the player must physically fly into. Toby has hit “many bugs” with portal placement (ring math near arena walls, spacing, which portal ends up reachable) and wants the mechanic replaced outright: a UI popup after the boss fight (and after any post-boss upgrade cards are resolved) offering 3 randomly-chosen Dimensions to warp to next, excluding the one just completed. Removing the physical entities removes the whole bug class at the root, rather than patching placement math again.

Current architecture (what’s being replaced)

Section titled “Current architecture (what’s being replaced)”
  • Sim.wormholes: Array — up to AreaDefs.DIMENSION_IDS.size() (currently 7) portal dicts {pos, dest, element, name}, spawned by _spawn_area_wormhole(pos) in a ring around the boss’s death spot (PORTAL_SPREAD_RADIUS, arena-edge clamping via PORTAL_ARENA_MARGIN).
  • Sim.portals_open: bool + Sim._portal_timer — while true, several other systems gate on it: spawn-suppression (_spawn_wave early-return), _update_dimension_hazard, dimension_hazards.update (both already changed in Batch A to stop pausing on boss-alive, but still pause on portals_open), and _check_pilot_hit’s “not portals_open” gate on _move_enemies-adjacent updates.
  • Sim._update_wormholes() — per-tick: check player overlap with any portal (→ emit a "warp" area_event + clear), else count down _portal_timer and auto-close if ignored.
  • render/wormhole_renderer.gd (WormholeRenderer) — draws the physical portal ring.
  • main.gd’s _begin_warp(dest) / _finish_warp() — the actual area-transition (freeze + fade + _apply_area_change) is already fully decoupled from wormholes — it just takes a dest id. This is the one piece we keep and reuse as-is.
  • A separate, independent “teaser wormhole” system (_spawn_teaser_wormhole, teaser_wormhole, TEASER_*) exists purely as a v0.1 “coming soon” hint on the first FunZo kill — unrelated to real navigation, out of scope, do not touch.

Replace the physical-wormhole flow with a pause-and-choose popup, matching the existing level-up panel’s own pattern (_paused_for_levelup / LevelUpPanel / CrystalsLevelUpPanel) rather than inventing a new one:

  • Sim side: a new pending_dimension_choice: Array[String] (empty = none pending), populated once boss death handling AND every resulting pending_levelups have been fully drained (see sequencing below). Drawn via upgrade_rng, not rng — this is a player-facing choice offer, not a spawn decision, and must not desync the spawn stream (same rule the crystal-grant rolls already follow). Pick logic (per Q1 answer below): track visited_dimensions: Array[String] for the run (append current_area on every warp); candidate pool is AreaDefs.DIMENSION_IDS minus visited_dimensions. Failsafe: if the candidate pool has fewer than 3 entries, pop the earliest entry off visited_dimensions (oldest-visited, not most-recent) and add it back into the pool — repeat until ≥3 candidates, then shuffle and take the first 3. HOME is never added to visited_dimensions/never a candidate (Q3 — Home is one-way, never re-offered).
  • main.gd side: a new _paused_for_dimension_choice: bool (added to every existing freeze condition alongside _paused_for_levelup) and a new UI panel (e.g. ui/dimension_choice_panel.gd) that shows the 3 offered Dimension names in a plain vertical list inside a bordered box, centered on screen (Q2 — deliberately minimal for this draft: reuse LevelUpPanel’s existing card-list/box visuals as-is, no bespoke “star-map” treatment). On pick: hide the panel, unpause, call the existing _begin_warp(dest) — no changes needed to the warp-transition/fade code at all.
  • Deleted: Sim.wormholes, portals_open, _portal_timer, _spawn_area_wormhole, _update_wormholes, PORTAL_SPREAD_RADIUS/PORTAL_ARENA_MARGIN, render/wormhole_renderer.gd and its main.gd wiring, and every not portals_open gate this removes the need for (since a hard pause — like the level-up screen already does — makes those gates redundant: nothing ticks during the popup, so there’s nothing to suppress).

Sequencing: boss death → upgrades → dimension choice

Section titled “Sequencing: boss death → upgrades → dimension choice”

A boss kill already can produce pending_levelups (XP crosses a threshold same tick), and the existing level-up flow already handles the “possibly more than one queued” case (_on_upgrade_chosen re-opens _open_levelup() if pending_levelups > 0 after a pick). The dimension-choice popup must only open after that whole chain is empty — i.e. hook its open condition where _on_upgrade_chosen’s “no more pending levelups” branch already resumes normal play, checking sim.pending_dimension_choice there instead of (or in addition to) resuming. If the boss death produced zero level-ups, the popup should still open on the same tick/frame the boss death is detected, same as _open_levelup()’s own pending_levelups > 0 check today.

Boss deaths only happen well past BOSS_FIRST_TIME (210s) — nowhere near the pinned 600-tick/10s baseline window — so this is expected to need no re-pin, same reasoning as every other boss/late-game feature. Confirm, don’t assume (per the project’s own standing rule) once built.

Draft answers, not set in stone — revisit at plan time if something doesn’t fit once built.

  1. Exclusion scope: exclude all already-visited Dimensions this run (Toby changed his mind from the original brief, which only excluded the immediately-previous one). Failsafe: if fewer than 3 unvisited Dimensions remain, drop the earliest entry off the visited list and re-include it as an offerable option — just enough to keep 3-of-N satisfiable when the run has cycled through most/all Dimensions, not a real gameplay mechanic.
  2. Panel visuals: just the 3 Dimension options in a plain list (like the upgrade cards) inside a box, centered on screen. Reuse existing visuals — no bespoke treatment needed, this is a draft.
  3. Home/Generic: confirmed — no going back to Home. It’s a one-time tutorial/warm-up Dimension, never re-offered once left.
  4. First-boss-at-Home: yes, Warden’s death at HOME triggers the popup too (otherwise there’d be no way to leave Home) — unchanged from the current wormhole system. Mid-dimension bosses (i.e. bosses that aren’t the last thing standing) do not trigger it — the player only changes Dimension once every wave and boss in the current one is cleared, so “boss death” here specifically means “the Dimension is now fully cleared,” not any individual boss kill.

Once the open questions above are answered, turn this into an executable task-by-task plan (same format as docs/superpowers/plans/2026-07-06-centurion-enemy.md) via superpowers:writing-plans / subagent-driven-development, sized similarly (content/UI/sim wiring + removal tasks, each with its own failing-test-first step).