feat(retention): next-goal on the run-end screen, and a codex that pa… - #26
Merged
Conversation
…ys out Two retention features that both attack the same problem — the game recorded progress but never gave the player something to aim at. ## Next goal (src/views/nextGoal.ts) A run ending is the moment a player decides whether to hit Restart, and the screen was a dead end: stats, then nothing. It now names the single nearest unfinished thing: "One floor short of your deepest (9). One." "2 codex entries from complete — 21/23 found." "You found 2 of 3 smiths — the Spear of Lugh needs 1 more." Ordered by how *close* a goal is, not how big — a record two floors away pulls harder than "collect everything". Pure and host-free: the caller supplies the cross-run records, which live outside the simulation. Victory reframes around the codex and the heat ladder instead. The records have to be read BEFORE recordRunEnd, which folds this run into them — otherwise the line would compare the run against itself. Added StorageService.getDeepestFloor for that. ## Codex completion + unlocks (src/codex.ts) The codex was a read-only trophy case. It now shows a completion bar (`9 / 23 discovered`) and an unlock ladder: 25/50/75/100% each grant a small permanent perk at every future run start — 50 starting gold, a tier-I Geis, a Deathward charge, a tier-II Geis. Configured in balance.json's `codex.unlocks` (+ schema), applied via Game.applyCodexUnlocks(pct). The host computes the percentage from storage and passes it in, so the simulation still touches no persistence. Boon rolls go through the run's rng, so a seeded run's unlocks are reproducible too. Discovery counting ignores ids that no longer match live content — after the Tuatha Dé rename, a returning player's saved 'scathach'/'fedelm'/'bricriu' entries would otherwise have inflated their total. - balance.json + schema + balance.ts: codex.unlocks ladder - runSetup.ts: applyCodexUnlocks; game.ts delegator; main.ts wiring - codex-modal + style.css: progress bar and ladder (earned vs locked) - game-over-modal/ui.ts/main.ts: the next-goal line - 27 new tests (13 goal-selection, 14 codex/unlock), both new modules added to the coverage include list - README: two Core-systems rows, file-tree entries Verified live: the ladder renders at a seeded 39% with the first rung earned and the rest locked, no page errors. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V6kP5vUAcNbxLK5EnPKAhv
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
…ys out
Two retention features that both attack the same problem — the game recorded progress but never gave the player something to aim at.
Next goal (src/views/nextGoal.ts)
A run ending is the moment a player decides whether to hit Restart, and the screen was a dead end: stats, then nothing. It now names the single nearest unfinished thing:
"One floor short of your deepest (9). One."
"2 codex entries from complete — 21/23 found."
"You found 2 of 3 smiths — the Spear of Lugh needs 1 more."
Ordered by how close a goal is, not how big — a record two floors away pulls harder than "collect everything". Pure and host-free: the caller supplies the cross-run records, which live outside the simulation. Victory reframes around the codex and the heat ladder instead.
The records have to be read BEFORE recordRunEnd, which folds this run into them — otherwise the line would compare the run against itself. Added StorageService.getDeepestFloor for that.
Codex completion + unlocks (src/codex.ts)
The codex was a read-only trophy case. It now shows a completion bar (
9 / 23 discovered) and an unlock ladder: 25/50/75/100% each grant a small permanent perk at every future run start — 50 starting gold, a tier-I Geis, a Deathward charge, a tier-II Geis. Configured in balance.json'scodex.unlocks(+ schema), applied via Game.applyCodexUnlocks(pct).The host computes the percentage from storage and passes it in, so the simulation still touches no persistence. Boon rolls go through the run's rng, so a seeded run's unlocks are reproducible too.
Discovery counting ignores ids that no longer match live content — after the Tuatha Dé rename, a returning player's saved 'scathach'/'fedelm'/'bricriu' entries would otherwise have inflated their total.
Verified live: the ladder renders at a seeded 39% with the first rung earned and the rest locked, no page errors.
Claude-Session: https://claude.ai/code/session_01V6kP5vUAcNbxLK5EnPKAhv