## Summary
- **Server**: `authMiddleware` no longer calls `logger.error` for expired tokens β expiry is expected behaviour, not an error. Only tampered signatures and malformed tokens (genuinely suspicious) still log.
- **Client**: `fetchJson` now handles 401 responses by clearing `elysium_token` and `elysium_save_signature` from localStorage and redirecting to `/`. Players whose 30-day token has expired will see the login page instead of a stuck "Invalid or expired token" error screen with no recovery path.
Closes#241β¨ This PR was created with help from Hikari~ πΈ
Reviewed-on: #241
Co-authored-by: Hikari <hikari@nhcarrigan.com>
Co-committed-by: Hikari <hikari@nhcarrigan.com>
## Summary
- **fix(#148)**: Boss fights now return a fresh HMAC signature in the response; both the manual and auto-boss paths update `signatureReference` from it, ending the signature-mismatch loop that stopped auto-boss after the first fight
- **fix(#145)**: Militia `baseCost` lowered from 100g β 65g, smoothing the peasantβmilitia jump from 10Γ to ~6.5Γ
- **fix(#144)**: `crystal_shard` buffed from `1.65Γ/1.2Γ` β `1.9Γ/1.3Γ` β now competitive as an epic trinket
- **fix(#142)**: Click-power recipe progression smoothed across zones 13β18 and ceiling raised: z13 1.20β1.22, z15 1.22β1.25, z17 1.25β1.28, z18 1.28β1.30
- **close(#143)**: `elder_bark_shield` (1.2Γ), `void_fragment_amulet` (1.15Γ), and `soul_bound_catalyst` (1.2Γ) are all already at or above their target values from a prior pass
Closes#148Closes#145Closes#144Closes#142
Reviewed-on: #240
Co-authored-by: Hikari <hikari@nhcarrigan.com>
Co-committed-by: Hikari <hikari@nhcarrigan.com>
## Summary
Working through all 15 open balance tickets in a coordinated multi-pass approach.
### Pass 1 β Quest failure rates (closes#172)
- Capped all zone quest failure chances at 15% (down from up to 40%)
- Proportional scaling preserved (harder zones still fail more than easier ones)
### Pass 2 β Crystal economy (closes#165, #173, #215)
- Added `crystal_pulse` (3,000 crystals), `crystal_surge` (20,000), `crystal_tempest` (150,000) upgrades to fill the dead zone between 600 and 2M crystal sinks
- Bumped `click_deity`, `prestige_master`, and `prestige_legend` achievement crystal rewards (5Kβ15K, 5Kβ15K, 25Kβ75K)
- Added crystal rewards to `first_steps` (+5) and `goblin_camp` (+10) early quests
### Pass 3 β Runestone/prestige loop (closes#166, #170)
- Bumped `runestonesPerPrestigeLevel` from 15 β 20 (~33% yield increase for mid-game runs)
- Reduced `income_10` cost from 22,500 β 15,000 and `income_11` from 60,000 β 35,000
- Kept client/server parity: `runestonesPerPrestigeLevelClient` in tick.ts updated to match
### Pass 4 β Quest content (#175, #178)
- Both already resolved in commit 666a5b2: quests now reach 5e141 CP across reality_forge, cosmic_maelstrom, primeval_sanctum, and the_absolute β fully covering P60βP212
### Pass 5 β Daily challenges (closes#167)
- Added `crafting` as a new `DailyChallengeType`
- Added 3 crafting challenge templates (craft 1/2/3 recipes)
- Changed generation to guarantee: 1 clicks + 1 crafting + 1 from progression pool
- Added crafting challenge tracking in `craft.ts` (awards crystals on recipe craft)
- Stuck players now have 2/3 daily challenges always completable
### Pass 6 β Transcendence costs (#179)
- Already resolved in commit 666a5b2: echo meta costs are 15/45/100 (was 25/75/200)
### Also closed as stale
- #171 (milestone bonus already quadratic)
- #174 (production multiplier already 1.3^n)
- #176 (expanse_sovereign HP already at 3e39)
- #177 (recipe costs already in expected range)
- #178 (post-absolute quests already present)
- #179 (echo meta costs already reduced)
β¨ This PR was created with help from Hikari~ πΈ
Reviewed-on: #239
Co-authored-by: Hikari <hikari@nhcarrigan.com>
Co-committed-by: Hikari <hikari@nhcarrigan.com>
## Summary
- Bumped minor/patch versions for `@hono/node-server`, `@types/node`, `@types/react`, `@types/react-dom`, `@vitejs/plugin-react`, `hono`, `jsdom`, `react`, `react-dom`, `tsx`, and `vite`
- Full pipeline (lint β build β test) confirmed passing
## Held back
- **TypeScript** β held at 5.8.2; 6.x is incompatible with the current `@nhcarrigan/eslint-config`
- **Vitest** β held at 3.x; 4.x changes v8 coverage pragma handling, needs investigation
- **Prisma** β held at 6.x; 7.x requires a `prisma.config.ts` migration
β¨ This PR was created with help from Hikari~ πΈ
Reviewed-on: #237
Co-authored-by: Hikari <hikari@nhcarrigan.com>
Co-committed-by: Hikari <hikari@nhcarrigan.com>
## What changed and why
### Runestone formula (`prestige.ts`)
- Swapped `sqrt` for `cbrt` β much stronger diminishing returns for large gold values
- Added base cap of **200** (β ~1,125 max with all upgrades at 5.625Γ multiplier)
- Prevents extended AFK sessions from producing runestone windfalls that allow immediate upgrade purchasing and rapid prestige chaining
### Prestige threshold formula (`prestige.ts`)
- Old: `1,000,000 Γ 5^n` β exponential, grows impossibly fast, prestige 10+ takes years
- New: `1,000,000 Γ (n+1)Β²` β polynomial, peaks at ~1 day/run around P8β10, then gets *easier* as the production multiplier overtakes it
- Removed `thresholdScaleFactor` constant (no longer needed)
### Production multiplier (`prestige.ts`)
- Old: `1.15^n`
- New: `1.25^n` β compounds faster, ensures the polynomial threshold eventually gets easy in the late game
### Boss prestige requirements (`bosses.ts`)
- Rescaled proportionally from 0β88 range to 0β20 range
- The Absolute One now requires prestige **20** (was 88), making transcendence reachable in a few weeks of idle play
### Echo formula (`transcendence.ts`)
- Constant changed from 853 β **224**
- At the target prestige of 20: `floor(224 / sqrt(20)) = 50 echoes` per transcendence (no meta upgrades)
- With all echo_meta upgrades (3.75Γ total): up to **187 echoes** per transcendence
### Transcendence upgrade costs (`transcendenceUpgrades.ts`)
- Old total: **866 echoes** β New total: **400 echoes** (roughly halved across all categories)
- Apotheosis still requires **all 15 upgrades** purchased
### Balance fixes (closes#141, #142, #143, #144, #145)
- Equipment: `philosophers_stone` click multiplier 2.25β2.5, `crystal_shard` 1.55β1.65 (#144)
- Recipes: added `primal_omega_lens` cross-zone click_power recipe at 1.38Γ (#142)
- Adventurers: `celestial_guard` base cost adjusted to smooth tier 14β15β16 cost curve (#145)
### Quest reward rebalancing (closes#136, #137)
- Shadow Marshes: buffed `shadow_mere`, `witch_coven`, `plague_ruins` rewards to match combat requirements (#136)
- Astral Void: added gold to `void_rift`, increased rewards across all Astral Void quests (#137)
### Boss reward additions (closes#138, #139, #140)
- Assigned 9 unassigned adventurer-specific upgrades to Crystalline Spire through Eternal Throne bosses that had empty `upgradeRewards` arrays (#140)
### Combat power documentation (closes#153)
- Expanded JSDoc on `computePartyCombatPower` to clarify companion `bossDamage` multiplier behaviour
### Effective adventurer stats (closes#154)
- Added `computeEffectiveAdventurerStats` to `tick.ts` and updated `AdventurerCard` to display effective post-multiplier stats
### Adventurer upgrade timing (closes#158)
- Audited every adventurer-specific upgrade reward β upgrades now land within the same progression window where that adventurer tier is still a meaningful contributor
### Sync and save fixes (closes#147, #148, #151)
- Fixed sync new content count to report only genuinely changed items (#147)
- Fixed signature mismatch after first auto-boss completion (#148)
- Added auto-buy cap (100) on non-max-tier adventurers (#151)
### Auto-adventurer persistence (closes#156)
- Auto-buy preference now preserved across prestige resets
### Broken CDN image (closes#159)
- Uploaded missing `auto_adventurer.jpg` to CDN
### Codex unlock hints (closes#146)
- Locked codex entries now display a hint generated from `sourceType` and `sourceId`
### Exploration bug fixes (closes#160, #161)
- Fixed auto-save race condition discarding exploration materials collected mid-tick (#160)
- Fixed exploration areas failing to unlock when zone was unlocked via boss kill or quest completion (#161)
### Concurrent prestige fix (closes#162)
- Added optimistic locking via `updatedAt` β concurrent prestige requests return 409
### Prestige UX (closes#163)
- Added `reloadSilent` to game context β no loading screen flash after prestige
### Balance adjustments (closes#164, #165, #166, #167)
- Reduced `shadow_mere` CP requirement 5,000,000 β 2,000,000 (#164)
- Buffed crystal drops from Shadow Marshes bosses and quests (#165)
- Increased runestone yield from 10 β 15 per prestige level (#166)
- Daily challenge set always includes a clicks challenge (#167)
### Progression QoL (closes#168, #169)
- Added `computeProjectedRunestones()` and persistent `+N On Prestige` resource bar row (#168)
- Added `enablePrestigeAnnouncements` setting per player (#169)
---
## Comprehensive balance audit (closes#187, #191, #192, #193, #194, #195, #196, #197, #198)
### Crystal economy fixes
- Zeroed crystal rewards for all Zone 7+ boss drops (Celestial Reaches onwards) β crystals are an early/mid-game currency and should not flow freely into the endgame (#187)
- Zeroed crystal rewards for all Zone 9+ quest rewards (Infernal Court onwards) β same rationale (#191)
### Achievement additions and fixes
- Added quest milestone achievements at 75 quests (10,000 crystals) and 100 quests (15,000 crystals)
- Added boss milestone achievement at 50 bosses (15,000 crystals)
- Added prestige milestone achievements at P50, P100, P150, P200 β rewarding **runestones** rather than crystals to match the late-game economy
- Added gold milestone achievements through 1e90 gold earned
- Fixed `quest_eternal` condition from 122 β **112** (actual quest count) β was permanently impossible (#197)
- Fixed `fully_equipped` condition from 65 β **78** (actual equipment count after new items) (#197)
- Fixed `devourer_slayer` description to remove incorrect zone reference
### Upgrade balance
- Fixed Essence Guild multiplier 1.5Γ β **2Γ** β was identical to the cheaper Merchant Alliance for 5Γ the cost (#194)
- Raised Void Ascendancy crystal cost 10M β **50M** β was trivially cheap compared to the parallel Celestial Mandate upgrade (100B essence + 50T gold) (#195)
- Fixed Sunken Temple quest rewards (gold 2M β 60M, essence 1,500 β 25,000, crystals 75 β 400) β was rewarding less than its easier prerequisite Witch Coven (#193)
### Equipment balance
- Buffed Eternal Prism stats to click 5Γ, combat **3Γ**, gold **2.5Γ** β was only marginally better than the free Eternity Stone boss drop for 100M crystals (#196)
### Missing content
- Created **13 missing equipment items** for Zones 15β18 (primordial_chaos through the_absolute) that were referenced by late-game boss `equipmentRewards` arrays but never existed in `equipment.ts` (#198):
- `chaos_mantle`, `titan_core` (Primordial Chaos)
- `expanse_blade`, `void_armour_mk2` (Infinite Expanse)
- `cosmos_blade`, `reality_plate` (Reality Forge)
- `maelstrom_edge`, `cosmic_plate` (Cosmic Maelstrom)
- `primeval_blade`, `ancient_aegis` (Primeval Sanctum)
- `absolute_blade`, `eternity_plate`, `omniversal_core` (The Absolute)
- Stats scale from combat 14Γ / gold 9Γ (Zone 15) up to combat 28Γ / gold 20Γ for the final boss drops
### Type system
- Extended `AchievementReward` type to support `runestones` field
- Updated tick engine achievement processing to award both crystals and runestones
---
## Target progression timeline (optimal play, ~16h/day idle)
- First cycle to P20: ~375h (~3.3 weeks)
- Each subsequent cycle gets faster as echo upgrades boost income/combat/threshold
- Expected **~5 transcendences** before apotheosis at 50β187 echoes/transcendence
- **~6 months** to apotheosis for a dedicated player
## Test plan
- [ ] Lint, build, and test pipeline passes (100% coverage maintained)
- [ ] Prestige threshold at P0 is still 1,000,000 gold
- [ ] Prestige runs feel ~1 day long around P8β10 and get easier after
- [ ] The Absolute One is locked until prestige 20
- [ ] Transcendence at P20 awards 50 echoes (no meta upgrades)
- [ ] All 15 transcendence upgrades cost 400 echoes total
- [ ] Bosses in Zones 7+ drop 0 crystals; Zones 1β6 retain crystal drops
- [ ] Quests in Zones 9+ reward 0 crystals; Zones 1β8 retain crystal rewards
- [ ] Sunken Temple rewards more gold/essence/crystals than Witch Coven
- [ ] Essence Guild gives 2Γ income (stronger than Merchant Alliance 1.5Γ)
- [ ] Void Ascendancy costs 50M crystals
- [ ] Eternal Prism stats are click 5Γ, combat 3Γ, gold 2.5Γ
- [ ] Late-game bosses (primordial_titan through the_absolute_one) drop equipment on kill
- [ ] `quest_eternal` achievement requires 112 quests
- [ ] `fully_equipped` achievement requires 78 equipment pieces
- [ ] P50/P100/P150/P200 prestige achievements reward runestones
- [ ] Adventurer cards show effective post-multiplier stats
- [ ] Exploration areas unlock correctly when their zone is unlocked
- [ ] Concurrent prestige requests return 409
- [ ] No loading screen flash after prestige
- [ ] Daily challenge set always includes a clicks challenge
- [ ] Resource bar shows `+N On Prestige` runestone preview
β¨ This PR was crafted with help from Hikari~ πΈ
Reviewed-on: #135
Co-authored-by: Hikari <hikari@nhcarrigan.com>
Co-committed-by: Hikari <hikari@nhcarrigan.com>
## Summary
- Grants the Elysian Discord role to players on login/registration and persists an `inGuild` flag on the Player record
- Connects to the Discord Gateway via WebSocket to keep `inGuild` in sync as players join or leave the server
- Shows a dismissible "Join our community" modal to players who are not yet in the guild
- Hardens `inGuild` exposure through the load endpoint and game context
- Moves all non-secret Discord IDs (guild, role, client, redirect URI) out of env vars and into hardcoded constants; removes them from `prod.env`
## Test plan
- [ ] Lint, build, and test pipeline passes (100% coverage maintained)
- [ ] New player auth grants Elysian role and sets `inGuild: true`
- [ ] Existing player auth re-attempts role grant and updates `inGuild`
- [ ] Join community modal appears for players not in the guild
- [ ] Modal does not reappear within the same browser session after dismissal
- [ ] Gateway correctly sets `inGuild: true/false` on member add/remove events
β¨ This issue was created with help from Hikari~ πΈ
Reviewed-on: #134
Co-authored-by: Hikari <hikari@nhcarrigan.com>
Co-committed-by: Hikari <hikari@nhcarrigan.com>
## Summary
- Sync New Content now **injects** missing entries AND **patches canonical fields** on all existing entries to match current defaults
- Adventurers: stats (baseCost, combatPower, goldPerSecond, essencePerSecond, name, class, level)
- Quests: duration, prerequisites, combat requirement, rewards
- Bosses: HP, damage, rewards, prestige requirement, upgrade rewards
- Zones: unlock conditions (boss/quest required)
- Upgrades: multiplier, costs
- Equipment: bonus, cost, set membership
- Achievements: condition, reward
- Crafting: multipliers recomputed from `craftedRecipeIds` so recipe balance changes apply retroactively
Closes#126
## Test plan
- [ ] On an existing save, click Sync New Content and verify the notification reports patched counts for all content types
- [ ] Verify that rebalanced adventurer/boss/upgrade stats are reflected in the UI after syncing
- [ ] Verify that player-owned state (counts, unlock status, boss HP, quest status) is preserved after syncing
- [ ] Verify crafting multipliers are correct after syncing if any recipes were previously crafted
β¨ This issue was created with help from Hikari~ πΈ
Reviewed-on: #130
Co-authored-by: Hikari <hikari@nhcarrigan.com>
Co-committed-by: Hikari <hikari@nhcarrigan.com>
## Summary
- Fix `NaN` displayed in Sync New Content / Force Unlock notifications by guarding against undefined counts
- Poll server for exploration claimability before showing Collect button to prevent client/server desync
- Return authoritative materials list from craft API to prevent client desync causing false affordability
- Add test coverage for `sync-new-content` and `explore/claimable` endpoints
Closes#125Closes#127Closes#128
## Test plan
- [ ] Trigger a sync with new content and verify the notification shows a real count instead of `NaN`
- [ ] Start an exploration, wait for it to complete, and verify the Collect button only appears after the server confirms claimable
- [ ] Attempt to craft a recipe and verify the material counts in the UI update to match the server's authoritative values
β¨ This issue was created with help from Hikari~ πΈ
Reviewed-on: #129
Co-authored-by: Hikari <hikari@nhcarrigan.com>
Co-committed-by: Hikari <hikari@nhcarrigan.com>
Adds a new debug panel button that injects any adventurers, quests,
bosses, equipment, upgrades, achievements, zones, and exploration areas
that exist in the current game data but are missing from an existing
player save (e.g. content added after the save was first created).
## Summary
Comprehensive balance pass addressing 20 tickets (#103β#122) plus one audit-discovered fix (#123), ensuring no player soft-locks and aligning all content counts with achievements and progression milestones.
### Changes
- **Equipment** (#103β#111): Differentiated all stat pairs so every piece has a unique bonus combination; added missing stats to `eternal_flame` and increased `eternal_prism` multiplier to justify cost tier
- **Recipes** (#112β#115): Added 4 cross-zone crafting recipes requiring materials from multiple zones to incentivise exploration breadth
- **Achievements** (#116β#118): Aligned `fully_equipped` (40β65), `quest_eternal` (72β95), and `boss_eternal` (60β72) thresholds with actual content counts; updated `devourer_slayer` description
- **Quest CP scaling** (#120β#122): Verified and corrected combat power requirements across all zones to follow consistent 4Γ/4Γ progression pattern
- **Zone file ordering** (#123): Swapped Frozen Peaks and Shadow Marshes quest sections so file order matches the actual unlock chain (no gameplay change)
### Tickets Closed
Closes#103Closes#104Closes#105Closes#106Closes#107Closes#108Closes#109Closes#110Closes#111Closes#112Closes#113Closes#114Closes#115Closes#116Closes#117Closes#118Closes#120Closes#121Closes#122Closes#123β¨ This PR was created with help from Hikari~ πΈ
Reviewed-on: #124
Co-authored-by: Hikari <hikari@nhcarrigan.com>
Co-committed-by: Hikari <hikari@nhcarrigan.com>
## Summary
Addresses all community feedback tickets from the last deploy, plus several UI improvements made during the same session.
### Bug fixes & balance
- **#97** β Fix auto-adventurer tier priority: sort by combat power instead of current cost so the highest-tier affordable unit is always purchased
- **#98** β Add Dark Templar adventurer (80k CP) to bridge the Volcanic Depths progression wall; rewire upgrade and quest rewards accordingly
- **#99** β Reorder and buff Shadow Assassin (55k CP, level 12) so Witch Coven feels rewarding rather than a regression
- **#100** β Display effective Gold/s (all multipliers applied) in the resource bar
- **#101** β Add Peasant tier 2 (10x, essence) and tier 3 (50x, crystals) upgrades for meaningful late-game scaling
### Other fixes
- Sync game state to server before auto-boss challenges (matching manual challenge behaviour)
- Refresh Discord avatar hash on every game load via bot token so stale CDN URLs are corrected automatically
### UI improvements
- Replace Donate / Discord / Support / View Profile / Edit Profile buttons with a single avatar dropdown menu
- Collapse all resources except Gold into a click-to-toggle dropdown; orange alert dot appears when a hidden resource is capped
## Closes
Closes#97Closes#98Closes#99Closes#100Closes#101
Reviewed-on: #102
Co-authored-by: Hikari <hikari@nhcarrigan.com>
Co-committed-by: Hikari <hikari@nhcarrigan.com>
Closes#95
## Summary
`void_walker` adventurers (130K combat power each) were locked behind `fire_temple`, which requires 4.8B combat power. The best adventurer available before completing that quest was `arcane_scholar` at 45K CP each β meaning players needed ~107K arcane scholars to break through, versus ~37K if they had void walkers. Classic chicken-and-egg wall.
## Changes
- Moved `void_walker` adventurer reward from `fire_temple` to `lava_flows` (the entry quest to Volcanic Depths, no CP requirement)
- Added 40M gold reward to `fire_temple` to replace the removed adventurer unlock
Players now unlock `void_walker` as soon as they enter the zone, giving them the combat power boost before they need to grind toward the temple.
β¨ This issue was created with help from Hikari~ πΈ
Reviewed-on: #96
Co-authored-by: Hikari <hikari@nhcarrigan.com>
Co-committed-by: Hikari <hikari@nhcarrigan.com>
The auto-adventurer toggle is now surfaced directly in the adventurer shop panel header, mirroring the auto-boss button. It only renders when the `auto_adventurer` prestige upgrade has been purchased, so players who have not reached prestige see no change.
Closes#89β¨ This PR was created with help from Hikari~ πΈ
Reviewed-on: #94
Co-authored-by: Hikari <hikari@nhcarrigan.com>
Co-committed-by: Hikari <hikari@nhcarrigan.com>
The force-unlock debug route now scans completed quests for adventurer rewards and ensures those tiers are marked as unlocked in game state.
The UI and API response type both surface the new `adventurersUnlocked` count alongside the existing zone/quest/boss/exploration counts.
Closes#88β¨ This PR was created with help from Hikari~ πΈ
Reviewed-on: #93
Co-authored-by: Hikari <hikari@nhcarrigan.com>
Co-committed-by: Hikari <hikari@nhcarrigan.com>
## Summary
Fixes#87. When a quest failed, the tick loop detected the failure and turned auto-quest off so the "player could reassess". This meant every quest failure required the player to manually re-enable the toggle.
## Root Cause
The tick applies quest failure by resetting the quest to `status: "available"` with `lastFailedAt` set. Auto-quest picks up `available` quests automatically β so turning off auto-quest on failure was entirely unnecessary, it just broke the loop.
## Fix
Remove the auto-quest-off-on-failure block entirely. The quest returns to `available` immediately after failure, so auto-quest naturally retries on the next tick. Players can still disable it manually if they want to stop.
β¨ This PR was created with help from Hikari~ πΈ
Reviewed-on: #92
Co-authored-by: Hikari <hikari@nhcarrigan.com>
Co-committed-by: Hikari <hikari@nhcarrigan.com>
## Summary
Fixes#86. When the client state is ahead of the server save, the auto-boss tick would receive a "Boss is not currently available" error from the API. This error was already acknowledged as an expected race condition and suppressed from telemetry β but it was still setting the error state and turning auto-boss off.
## Root Cause
The `catch` handler treated all errors identically: set `autoBossError`, turn off `autoBoss`. The race-condition case should instead silently skip so the next tick can retry naturally.
## Fix
When the error is `"Boss is not currently available"`, return early from the `catch` handler. The `finally` block still runs, resetting `isAutoBossingReference.current = false`, so the next tick retries cleanly.
β¨ This PR was created with help from Hikari~ πΈ
Reviewed-on: #91
Co-authored-by: Hikari <hikari@nhcarrigan.com>
Co-committed-by: Hikari <hikari@nhcarrigan.com>
## Summary
Fixes#85. Quest combat power requirements for Shadow Marshes, Volcanic Depths, and Astral Void were all drastically too low, breaking the zone progression curve.
### Root Cause
All three zones appear to have had their `combatPowerRequired` values entered at the wrong magnitude. Shadow Marshes was using K values where M was intended; Volcanic Depths and Astral Void were similarly off, resulting in later zones being trivially easier than earlier ones.
### Changes
| Zone | Before | After |
|---|---|---|
| Shadow Marshes | 5K / 20K / 80K / 300K | 5M / 20M / 80M / 300M |
| Volcanic Depths | 2M / 8M / 30M / 120M | 1.2B / 4.8B / 18B / 72B |
| Astral Void | 50M / 200M / 800M / 3B | 300B / 1.2T / 4.8T / 18T |
### Progression
All values now maintain a consistent ~Γ4 multiplier within each zone and ~Γ4 jump between zones, matching the established pattern from Verdant Vale through Frozen Peaks.
β¨ This PR was created with help from Hikari~ πΈ
Reviewed-on: #90
Co-authored-by: Hikari <hikari@nhcarrigan.com>
Co-committed-by: Hikari <hikari@nhcarrigan.com>
## Summary
- Move quest failure explanation to a static note above the quest list (cards now show failure % only)
- Show zone unlock requirements (boss + quest) on the Boss and Quest panels, matching the existing Exploration panel behaviour
- Display combat power per adventurer on adventurer cards, alongside gold/s and essence/s
β¨ This issue was created with help from Hikari~ πΈ
Reviewed-on: #84
Co-authored-by: Hikari <hikari@nhcarrigan.com>
Co-committed-by: Hikari <hikari@nhcarrigan.com>
## Summary
Resolves player confusion about equipment combat bonuses not affecting quest combat power. The behaviour is by design β combat multipliers only apply to boss DPS β but this was never communicated anywhere.
- **Equipment bonus labels** now read `+X% Boss Combat` instead of `+X% Combat` (both individual items and set bonuses)
- **About panel** β both equipment entries updated to explicitly state that combat bonuses only affect boss fights, and that quest combat power is determined solely by adventurers
No game logic changed.
Closes#81β¨ This PR was created with help from Hikari~ πΈ
Reviewed-on: #83
Co-authored-by: Hikari <hikari@nhcarrigan.com>
Co-committed-by: Hikari <hikari@nhcarrigan.com>
## Summary
Addresses recurring community confusion about quests failing β multiple players asked whether it was a bug or intended behaviour with no in-game explanation.
- **Exports `zoneFailureChance`** from `tick.ts` so the quest panel can read it
- **Quest cards** now show a `π² X% failure chance` note on all available quests, with a brief explanation that a failure resets the quest with no rewards
- **"Last attempt failed" hint** now reads `"β οΈ Last attempt failed β no rewards were granted."` so players understand the consequence immediately
- **About panel** updated to document the failure mechanic, including the 10%β40% range across zones
Closes#80β¨ This PR was created with help from Hikari~ πΈ
Reviewed-on: #82
Co-authored-by: Hikari <hikari@nhcarrigan.com>
Co-committed-by: Hikari <hikari@nhcarrigan.com>
## Summary
Audits all `logError` call sites in `gameContext.tsx` and suppresses telemetry for expected business logic rejections, eliminating alert fatigue without hiding real errors.
### Changes per call site
| Context | Before | After |
|---|---|---|
| `auto_save` | Logged all non-signature errors | Network failures silently swallowed β next tick retries |
| `auto_prestige` | Logged eligibility failures | Silently ignored β eligibility re-checked every tick |
| `auto_boss` | Logged all errors | Filters out `"Boss is not currently available"` (race condition); other errors still logged |
| `challenge_boss` | Logged all errors | Filters out `"Boss is not currently available"` (race condition); other errors still logged |
| `start_exploration` | Logged then rethrew | Removed useless try/catch β error propagates to UI naturally |
| `collect_exploration` | Logged then rethrew | Removed useless try/catch β error propagates to UI naturally |
Genuine errors (`buy_prestige_upgrade`, `transcend`, `apotheosis`, `buy_echo_upgrade`, `craft_recipe`) are unchanged β they still fire telemetry.
Closes#73β¨ This PR was created with help from Hikari~ πΈ
Reviewed-on: #79
Co-authored-by: Hikari <hikari@nhcarrigan.com>
Co-committed-by: Hikari <hikari@nhcarrigan.com>
## Summary
Adds a note below the leaderboard subtitle informing players that rankings update when they prestige. This addresses a recurring community question from `tau.deusmortis` and `minjo70`.
Closes#63β¨ This PR was created with help from Hikari~ πΈ
Reviewed-on: #78
Co-authored-by: Hikari <hikari@nhcarrigan.com>
Co-committed-by: Hikari <hikari@nhcarrigan.com>
## Summary
Closes#62
Adds five **Essence Infusion** upgrades (IβV) to give essence an ongoing meaningful use deep into a prestige run, when gold upgrades are all purchased and essence reserves are in the trillions with nowhere to go:
| Upgrade | Cost | Multiplier |
|---|---|---|
| Essence Infusion I | 1T essence | Γ2 global |
| Essence Infusion II | 5T essence | Γ2 global |
| Essence Infusion III | 25T essence | Γ2 global |
| Essence Infusion IV | 100T essence | Γ3 global |
| Essence Infusion V | 500T essence | Γ5 global |
All five start `unlocked: true` (no prerequisite boss or quest required) and cost zero gold and zero crystals β they are purely essence sinks. Combined, they provide a Γ120 global income multiplier for players willing to pour their essence reserves into the guild. The About panel's Upgrades section is also updated to inform players these exist.
CDN art assets will need to be generated for IDs `essence_sink_1` through `essence_sink_5` in the `upgrades` folder.
β¨ This issue was created with help from Hikari~ πΈ
Reviewed-on: #77
Co-authored-by: Hikari <hikari@nhcarrigan.com>
Co-committed-by: Hikari <hikari@nhcarrigan.com>
## Summary
Closes#61
- Adds the **Autonomous Recruitment** prestige upgrade (50 runestones) to both the API and web data files
- Adds `autoAdventurer?: boolean` to the `GameState` type for backwards-compatible saves
- Adds tick-loop logic in GameContext that automatically purchases the highest-tier unlocked adventurer the player can afford each frame when the toggle is enabled
- Adds `toggleAutoAdventurer` callback and exposes it through the context
- Adds toggle UI in the Prestige Shop (mirrors the existing Auto-Prestige toggle pattern)
- Updates the How to Play guide in the About panel to document the new automation feature
β¨ This issue was created with help from Hikari~ πΈ
Reviewed-on: #76
Co-authored-by: Hikari <hikari@nhcarrigan.com>
Co-committed-by: Hikari <hikari@nhcarrigan.com>
## Summary
- Adds a `π‘` stacking note directly in the upgrade panel below the progress counter so players see it without visiting the About page
- Updates the About panel's Upgrades how-to-play entry to replace the vague "compound with each other" with explicit multiplicative stacking language, including an example (two Γ2 upgrades = Γ4) and a note that global upgrades multiply on top of adventurer-specific ones
## Test plan
- [ ] Verify the stacking note appears in the upgrade panel below the progress counter
- [ ] Verify the About panel Upgrades entry reflects the updated wording
- [ ] Confirm lint, build, and tests all pass
Closes#60
Reviewed-on: #75
Co-authored-by: Hikari <hikari@nhcarrigan.com>
Co-committed-by: Hikari <hikari@nhcarrigan.com>
## Summary
- Locked exploration zones now show a `π This zone is locked. Unlock exploration by:` hint above the area list, with the specific `βοΈ Defeat: {boss}` and `π Complete: {quest}` required
- Updated the About panel's Exploration how-to-play entry to document the zone unlock rule explicitly
- No new data required β unlock conditions are read directly from `zone.unlockBossId` and `zone.unlockQuestId` already in state
## Test plan
- [ ] Verify locked exploration zones display the correct boss and quest unlock hints
- [ ] Verify already-unlocked zones show no hint
- [ ] Verify starter zone (no unlock conditions) shows no hint
- [ ] Verify the About panel Exploration entry reflects the updated description
- [ ] Confirm lint, build, and tests all pass
Closes#59
Reviewed-on: #74
Co-authored-by: Hikari <hikari@nhcarrigan.com>
Co-committed-by: Hikari <hikari@nhcarrigan.com>
## Summary
- Adds a `βοΈ Combat Power` entry to the always-visible resource bar
- Value is computed client-side as the sum of each adventurer's `combatPower Γ count`
- No new props required β computed directly from `state` via the existing `useGame()` hook
- Players can now see their combat strength at a glance before attempting boss fights or quests
## Test plan
- [ ] Verify the Combat Power stat appears in the resource bar
- [ ] Verify the value increases as more adventurers are recruited
- [ ] Verify the value displays correctly with `formatNumber` for large numbers
- [ ] Confirm lint, build, and tests all pass
Closes#58
Reviewed-on: #72
Co-authored-by: Hikari <hikari@nhcarrigan.com>
Co-committed-by: Hikari <hikari@nhcarrigan.com>
## Summary
- Adds a `getCurrentProgress` helper that mirrors the tick engine's achievement-checking logic to compute the player's current progress for each condition type
- Locked achievement cards now display a `<progress>` bar and a numeric `{current} / {target}` label so players can see exactly how close they are to each achievement
- Unlocked achievements are unaffected β no progress bar shown once earned
## Test plan
- [ ] Verify locked achievement cards display a progress bar and numeric label
- [ ] Verify the progress values match what the tick engine uses for unlock checking
- [ ] Verify unlocked achievement cards show no progress bar
- [ ] Confirm lint, build, and tests all pass
Closes#57
Reviewed-on: #71
Co-authored-by: Hikari <hikari@nhcarrigan.com>
Co-committed-by: Hikari <hikari@nhcarrigan.com>
## Summary
- Adds a `π‘οΈ Affects: {Name}` label to upgrade cards that target a specific adventurer
- Resolves player confusion caused by class-based language (e.g. "doubles cleric output") without specifying which adventurer tiers count as that class
- Label appears in all three card states: available, purchased, and locked
## Test plan
- [ ] Verify adventurer-targeted upgrade cards display the correct adventurer name
- [ ] Verify global, click, boss, and prestige upgrade cards show no affects label
- [ ] Confirm lint, build, and tests all pass
Closes#56
Reviewed-on: #70
Co-authored-by: Hikari <hikari@nhcarrigan.com>
Co-committed-by: Hikari <hikari@nhcarrigan.com>
## Summary
Two improvements to the equipment system in one PR:
### Balance fixes (closes#54)
Full equipment audit revealed 9 items with duplicated stats, regressions, or purchasable items weaker than free boss drops:
| Item | Change | Reason |
|---|---|---|
| Void Conduit | 4x β 7x combat | 100M essence sink was equal to a zone-6 boss drop |
| Void Edge | 2.75x β 3.25x combat | Purchasable was weaker than free Celestial Blade (3x) |
| Astral Robe | 2.25x β 2.75x gold | Boss drop was weaker than purchasable Titan's Aegis (2.5x) |
| Philosopher's Stone | 2x β 2.25x click | Duplicated Frost Crystal's click multiplier |
| Eternal Flame | 1.15x β 1.25x gold | Gold regressed vs Philosopher's Stone (1.25x) |
| Celestial Focus | 2.5x β 3x click | 20M essence sink was weaker than free Angel's Halo (2.75x click + 1.3x gold) |
| Abyssal Tome | 3x β 3.75x gold | 50M essence sink was equal to free Heaven's Mantle (3x) |
| Crystal Matrix | 4x β 4.75x gold | 20M crystal sink was equal to free Sinslayer Aegis (4x) |
| Infernal Gem | 3.5x β 4x click | 5M crystal sink was identical to free Prism Eye |
### Equipment sorting (closes#55)
Equipment cards within each slot now render in ascending order of combined bonus power β the sum of all multiplier bonuses β so stronger items always appear further down the list. Hybrid items such as Volcanic Plate sort correctly without needing a per-slot primary stat.
## Test plan
- [ ] All purchasable weapons/armour/trinkets now exceed the stats of the highest free boss drop at their tier
- [ ] No duplicate stat values between adjacent items in the same progression track
- [ ] Equipment cards within each slot render weakest β strongest
- [ ] Hybrid multi-stat items sort sensibly alongside single-stat items
- [ ] Full pipeline green (lint + build + tests at 100% coverage)
β¨ This PR was created with help from Hikari~ πΈ
Reviewed-on: #69
Co-authored-by: Hikari <hikari@nhcarrigan.com>
Co-committed-by: Hikari <hikari@nhcarrigan.com>
## Summary
- Exploration timers showed more time than the area's stated duration when the server clock was ahead of the client's
- The timer was derived from `startedAt = endsAt - durationMs`, then computed as `durationSeconds - (clientNow - startedAt) / 1000` β any server/client clock skew directly inflated the result
- Now stores `endsAt` (the server-computed completion timestamp) directly in `ExplorationAreaState` and computes the timer as `(endsAt - Date.now()) / 1000`, which is immune to clock drift
- Old saves without `endsAt` fall back gracefully to the previous `startedAt`-based calculation
## Test plan
- [ ] Start a new exploration β timer should show exactly the area's stated duration (no more "1h area shows 1h15m")
- [ ] Refresh the page mid-exploration β timer should resume from the correct remaining time (using server-anchored `endsAt`)
- [ ] Old saves with `startedAt` but no `endsAt` should still display a timer via the fallback path
Closes#53β¨ This PR was created with help from Hikari~ πΈ
Reviewed-on: #68
Co-authored-by: Hikari <hikari@nhcarrigan.com>
Co-committed-by: Hikari <hikari@nhcarrigan.com>
## Summary
- Bosses defeated before `bountyRunestonesClaimed` was introduced had `status: "defeated"` but the field `undefined`
- After prestige, the preservation check (`=== true`) missed these bosses, so the first-kill bounty was re-awarded on the next run
- Now also treats `status === "defeated"` as proof the bounty was already earned, covering the migration case
## Test plan
- [ ] Existing test: `preserves bountyRunestonesClaimed flag on bosses across prestige` β still passes
- [ ] New test: `sets bountyRunestonesClaimed on bosses defeated before the flag was introduced` β covers the legacy save migration path
- [ ] Full coverage maintained at 100%
Closes#52β¨ This PR was created with help from Hikari~ πΈ
Reviewed-on: #67
Co-authored-by: Hikari <hikari@nhcarrigan.com>
Co-committed-by: Hikari <hikari@nhcarrigan.com>
## Summary
- `buildPostPrestigeState` was constructing the post-prestige `GameState` from `initialGameState`, which hard-codes `autoQuest` and `autoBoss` to `false`
- Neither flag was being carried forward, so both automation settings silently reset after every prestige
- Now both values are explicitly preserved from `currentState` (with `?? false` fallback for safety)
Closes#51β¨ This issue was created with help from Hikari~ πΈ
Reviewed-on: #66
Co-authored-by: Hikari <hikari@nhcarrigan.com>
Co-committed-by: Hikari <hikari@nhcarrigan.com>
## Summary
- Adds a new **Debug** tab to the game UI with two self-service tools for players with broken save state
- **Force Unlocks**: scans the player's save and grants any zones, quests, bosses, and exploration areas they've earned but that are still locked β shows a breakdown of what was unlocked (or reports nothing needed fixing)
- **Hard Reset**: wipes progress back to a fresh save (preserving lifetime stats), guarded behind a confirmation modal to prevent accidental clicks
## Files added
- `apps/api/src/routes/debug.ts` β two POST endpoints (`/force-unlocks`, `/hard-reset`)
- `apps/web/src/components/game/debugPanel.tsx` β the Debug tab UI
- `apps/web/src/components/ui/confirmationModal.tsx` β reusable confirmation modal
## Files modified
- `apps/api/src/index.ts` β registers the debug router
- `packages/types/src/interfaces/api.ts` β adds `ForceUnlocksResponse` type
- `packages/types/src/index.ts` β exports the new type
- `apps/web/src/api/client.ts` β adds `forceUnlocks()` and `debugHardReset()` API calls
- `apps/web/src/context/gameContext.tsx` β wires both functions into game context
- `apps/web/src/components/game/gameLayout.tsx` β adds the Debug tab
- `apps/web/src/styles.css` β styles for action buttons, cards, result messages, and confirmation modal
β¨ This PR was created with help from Hikari~ πΈ
Reviewed-on: #65
Co-authored-by: Hikari <hikari@nhcarrigan.com>
Co-committed-by: Hikari <hikari@nhcarrigan.com>
Closes#50
## Summary
- Calls `forceSync()` before every boss challenge so the server always fights against the player's live state (equipped items, upgrades, etc.) rather than a potentially stale snapshot
- Adds a `bossError` state that captures and displays error messages from failed manual boss challenges in the boss panel, matching the existing `autoBossError` display pattern
β¨ This PR was created with help from Hikari~ πΈ
Reviewed-on: #64
Co-authored-by: Hikari <hikari@nhcarrigan.com>
Co-committed-by: Hikari <hikari@nhcarrigan.com>
## Summary
- Applies the same sticky-zone pattern from #48 to the crafting panel (`elysium_craft_zone` key in sessionStorage)
- Introduces a `handleZoneSelect` wrapper so sessionStorage is updated alongside React state on every zone change
- Gracefully falls back to `verdant_vale` if no stored value exists
## Test plan
- [x] Lint β zero errors, zero warnings
- [x] Build β all packages build cleanly
- [ ] Manual: select a non-default zone in the crafting panel, navigate away and back β zone should still be selected
- [ ] Manual: log out and back in β zone should reset to Verdant Vale
β¨ This PR was created with help from Hikari~ πΈ
Reviewed-on: #49
Co-authored-by: Hikari <hikari@nhcarrigan.com>
Co-committed-by: Hikari <hikari@nhcarrigan.com>