Crystals full-screen level-up panel
Crystals full-screen level-up panel
Section titled “Crystals full-screen level-up panel”Status: DESIGN APPROVED 2026-06-26 — not yet implemented. Crystals-mode only. Author: Opus.
Replace the compact level-up card row, IN CRYSTALS MODE ONLY, with a full-screen decision screen: upgrade choices on the left, the player’s full build/inventory on the right, a complete change-preview for the focused choice, and a best→worst ranking — so the player makes an informed pick fast and Chris can see at a glance whether the crystals build system is working. After a pick, a 0.5s “get ready” freeze (with brief i-frames) lets the player re-orient before live play resumes.
- Crystals ruleset only (
sim.ruleset == RULESET_CRYSTALS). Reactions/story/survival keep the existingLevelUpPanelunchanged. - Out (YAGNI): no change to WHICH upgrades are offered (still the Task-4 roll); no mouse — “hover” is controller/remote FOCUS; no animation polish beyond the freeze; no persistence.
Components
Section titled “Components”Sim.upgrade_effects(id: String) -> Dictionary (NEW, pure /sim, read-only)
Section titled “Sim.upgrade_effects(id: String) -> Dictionary (NEW, pure /sim, read-only)”The testable core. Given an offered upgrade id, returns its full change-set WITHOUT mutating any state:
{ "kind": "crystal" | "weapon" | "stat" | "mod", "headline": String, # e.g. "+4 Fire, +2 Light" "changes": [ {"target": String, "detail": String, "evolve": bool}, ... ], "dead": bool, # true if it does nothing useful in crystals (e.g. a reaction-buff mod) "score": float, # impact score for ranking}Per kind:
- crystal grant (
crystals:fire=4,light=2): simulatewallet' = wallet + bundle; for every OWNED weapon, diff “threshold rules met under wallet’ vs wallet” → each NEWLY-crossed rule is a change (target= weapon name,detail= the mod kind or “EVOLVE”,evolve= rule is an evolve). Also a “progress” change per affected element (“Fire 7→11, next: Tesla evolve @18”). Pure: builds a temp count map, never touchesSim.crystalsor the weapons. - weapon grant (
weapon:X): change list = “Add Weapon X” + the thresholds X would IMMEDIATELY inherit from the current wallet (grant_weapon back-applies them — Task 5). - stat mod / transformative mod: reuse the existing
upgrade_preview(id)now→after; for a transformative mod whose effect is inert in crystals mode (reaction buffs — Catalyst/Lingering), setdead = trueand say so.
Helper: a pure WeaponThresholds/Sim function rules_met(weapon_id, count_map) -> Array[int]
(indices of rules satisfied by a given count map) so both the live _eval_thresholds and the preview
diff share one definition (DRY).
Scoring / ranking (diagnostic — “for now”)
Section titled “Scoring / ranking (diagnostic — “for now”)”score in upgrade_effects: evolutions weighted highest (e.g. +100 each), then +20 per newly-crossed
non-evolve threshold, weapon grant = +15 (free slot) + inherited-threshold value, stat mod = its
magnitude × a per-stat weight, dead picks = 0. The panel sorts choices by score and labels them
BEST → GOOD → OK → WEAK (or 1..N). Transparent (the change list justifies the rank) so the system’s
reasoning is auditable. This is a temporary evaluation aid, easily removed later.
ui/crystals_levelup_panel.gd (NEW, render/UI CanvasLayer)
Section titled “ui/crystals_levelup_panel.gd (NEW, render/UI CanvasLayer)”- Full-screen, dims the game behind. Two columns:
- Left: the N offered choices as a vertical, focus-navigable card list; each card shows name + one-line headline + a rank badge. The focused card is the “hovered” one.
- Right: the live build readout — each owned weapon with level (count of
wm:-equivalent threshold upgrades applied) + evolved flag, the full player stat block, the crystal wallet (per-element counts), and the list of upgrades taken this run.
- On focus change → render the focused choice’s
changes(the full preview) in a detail area. signal chosen(id). Controller nav mirrorsLevelUpPanel(stick/d-pad + ui_accept/JOY_BUTTON_A, debounced); the Apple TV Siri Remote works as a joypad.
main.gd wiring
Section titled “main.gd wiring”_open_levelup(): if crystals mode, build choices as{id, effects = sim.upgrade_effects(id)}and show the crystals panel; else the existing panel. Keep_paused_for_levelupsemantics._on_upgrade_chosen(id):sim.apply_upgrade(id); hide panel; if more levels pending, re-open; else start the resume grace:_resume_grace = 0.5, show a “READY…” beat, and grant the player a short invuln window (via the existing i-frame mechanism) so re-orientation is safe._process: while_resume_grace > 0, decrement by real delta and DO NOT tick the sim (frozen beat); when it hits 0, resume normal ticking with liveinput_router.poll(). (No input is replayed — the player re-grips during the freeze, per the chosen resume model.)
Determinism
Section titled “Determinism”upgrade_effects is pure + read-only → no state change. The panel is render/UI. The resume grace +
the crystals i-frame window are set only by main in crystals mode, which the determinism test never
enters → reactions baseline byte-identical; crystals determinism trace unaffected.
Testing
Section titled “Testing”upgrade_effects(pure) is unit-tested: crystal-grant cascade lists the correct newly-crossed thresholds (incl. an evolve at a high count); weapon-grant lists inherited thresholds; a reaction-buff mod is flaggeddead; ranking order is correct for a constructed wallet (best = the evolve-crossing crystal, worst = the dead mod).rules_metpure helper unit-tested (met/unmet boundaries).- The panel + freeze/i-frame resume are render/UI → boot-check + playtest (no GUT seam).
Open / tuning
Section titled “Open / tuning”- Exact score weights (above are starting values — tune so the BEST label matches intuition).
- Invuln window length (start 0.5s, matching the freeze).
- Right-column density on the tvOS safe-area (overscan) — keep within the safe rect.