Bunkers that are actually different from each other (§7) - #211
Merged
Conversation
WHAT WAS ACTUALLY THERE. Not "the scaling half-exists already", which is what §7 claimed. Measured with the new `npm run measure:acts`, which mounts the real engine at every act: bunker 5 vs bunker 1: floor area 1.00x rooms 0.95x enemies 0.93x The last bunker was very slightly EMPTIER than the first. Floor size came from `depth` alone, which runs 1-5 inside every bunker, so the act never entered the arithmetic at all. Worse, actHardMode had ONE entry, for act 4 — so bunkers 1 through 4 were mechanically identical, since enemy stats also scale on depth. The only thing that changed across those four was which archetypes could roll: variety, not difficulty. "Which bunker should I raid?" had no mechanical answer to give. THE TWO AXES, NOW REAL RISK — actHardMode covers all five acts: 1.00/1.20/1.45/1.70/2.00 HP. Gentle at the start so a new player's first raid is not harder than the campaign taught them, steep at the end so the last bunker earns its T3 shards. Act 4 is UNCHANGED at 2.0/1.3 — those numbers were tuned against the boss one-shotting players and nothing here reopens that. TIME — ACT_SIZE in dungeon.js adds width, height and one room per act. Rooms are what carry enemies and containers, so effort and reward scale together without a second knob. Measured after: 1.65x the area of bunker 1 and 1.55x the enemies. Deliberately modest — a MiniPay session is 3-10 minutes on a mid-range phone, so bunker 1 stays the SHORT option. That is the point of an axis rather than a global difficulty bump. AND THE PLAYER CAN SEE IT A difference nobody is shown is not a choice. The map's bottom bar now carries risk pips, shard tier and length under the bunker's name — on the way to the ENTER button. Locked bunkers show it too: knowing Bunker 5 is the only T3 source is a reason to keep going. lib/constants/bunkers.ts holds what the map renders, and it is the same shape as the `bunkers` block that made game-config.ts a liability. So it is guarded differently: test:bunkers mounts the engine, reads its own actHardMode and shard-tier rule, MEASURES floor size per act, and fails if any number disagrees. The engine stays the source of truth. A BUG THIS SURFACED, in WorldMapHub apply() runs twice — once from the instant localStorage draft, again when the Firestore copy lands, typically a network round trip later, which is exactly while the player is looking at the map. Its clamp (prev > highest ? highest : prev) ran both times, so tapping a locked bunker to read what it holds snapped the selection back to your own bunker a beat later, with no explanation. The clamp now applies only until the player taps something. Found by the UI test failing on an assertion I had assumed was about my own markup. Length is shown as "+65% LONGER" rather than "1.6x": at one decimal place bunker 4 (1.55) and bunker 5 (1.65) both rendered "~1.6x", so two rungs of the ladder looked identical on the one line meant to tell them apart. npm run measure:acts the before/after table above npm run test:bunkers 34 assertions, constants vs the live engine npm run test:bunkers-ui 15 assertions in a real browser Re-measured after: fragment thresholds unchanged (act 0 is untouched), traversability still 0 failures in 100 floors, seeded floors still 15/15. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017A764RdnwpyWnG7uCNhMiQ
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
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.
§7's last
[TARGET]flips to[TODAY]. No smart contract, ABI, or contract address is touched.What was actually there before
Not "the scaling half-exists already", which is what §7 claimed. Measured with the new
npm run measure:acts, which mounts the real engine at every act:The last bunker was very slightly emptier than the first.
Floor size came from
depthalone, which runs 1–5 inside every bunker, so the act never entered the arithmetic. Worse:actHardModehad one entry, for act 4 — so bunkers 1 through 4 were mechanically identical, since enemy stats also scale on depth. The only thing that changed across those four was which archetypes could roll: variety, not difficulty."Which bunker should I raid?" had no mechanical answer to give.
The two axes, now real
Risk —
actHardModecovers all five acts. Gentle at the start so a new player's first raid is not harder than the campaign taught them; steep at the end so the last bunker earns its T3 shards. Act 4 is unchanged at 2.0/1.3 — those numbers were tuned against the boss one-shotting players, and nothing here reopens that.Time —
ACT_SIZEindungeon.jsadds width, height and one room per act. Rooms are what carry enemies and containers, so effort and reward scale together without a second knob.Measured after: 1.65× the area, 1.55× the enemies. Deliberately modest — a MiniPay session is 3–10 minutes on a mid-range phone, so bunker 1 stays the short option. That is the point of an axis rather than a global difficulty bump.
And the player can see it
A difference nobody is shown is not a choice. The map's bottom bar now carries risk pips, shard tier and length under the bunker's name — on the way to the ENTER button, not somewhere to go looking for. Locked bunkers show it too: knowing Bunker 5 is the only T3 source is a reason to keep going.
Keeping this file honest
lib/constants/bunkers.tsis the same shape as thebunkersblock that madegame-config.tsa liability — a table describing bunkers, sitting next to the engine that really decides. So it is guarded differently:npm run test:bunkersmounts the engine, reads its ownactHardModeand shard-tier rule, measures floor size per act, and fails if any number in the constants disagrees. The engine stays the source of truth; the constants are a view of it the React shell can import (the engine is a static<script>— rule 1).A bug this surfaced
apply()inWorldMapHubruns twice — once from the instant localStorage draft, again when the Firestore copy lands, typically a network round trip later, which is exactly while the player is looking at the map. Its clamp (prev > highest ? highest : prev) ran both times.So tapping a locked bunker to read what it holds snapped the selection back to your own bunker a beat later, with no explanation. The clamp now applies only until the player taps something.
Found by the UI test failing on an assertion I had assumed was about my own markup — I spent three rounds treating it as a test race before dumping the panel HTML and seeing the selection had genuinely reverted.
One presentation fix
Length reads "+65% LONGER" rather than "1.6×": at one decimal place bunker 4 (1.55) and bunker 5 (1.65) both rendered
~1.6×, so two rungs of the ladder looked identical on the one line meant to tell them apart. A test now asserts every note is distinct.Verification
npm run measure:acts(new)npm run test:bunkers(new)npm run test:bunkers-ui(new)npm run test:traversablenpm run test:seedednpm run measure:bunkernpm run test:raid·test:contracts·test:floors·test:inventory·test:streak·test:streak-uitsc --noEmitlintaudit·check:copy·check:market·check:cssvars·check:attributionbuildDocs:
GAME-DESIGN.md§7 rewritten with the measured before/after, §2 marker and the build order updated, andgame-mechanics.md's bunker table now carries the real numbers instead of "Beginner / Easy / Medium / Hard".Still open
How long is one bunker in real play? The sandbox can measure rooms and enemies, not a person's minutes. If a full 5-floor clear of Bunker 5 runs past ~10 minutes, a "raid one floor and extract" option is the next thing to add — that is the shape a MiniPay session actually has. Left as a stated open question in §7 rather than guessed at.
Generated by Claude Code