feat: comprehensive balance pass (#239)
Security Scan and Upload / Security & DefectDojo Upload (push) Successful in 1m9s
CI / Lint, Build & Test (push) Successful in 1m12s

## 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>
This commit was merged in pull request #239.
This commit is contained in:
2026-04-06 18:58:04 -07:00
committed by Naomi Carrigan
parent 1195b657a0
commit e7164257c5
13 changed files with 160 additions and 41 deletions
+20 -20
View File
@@ -95,29 +95,29 @@ export const PRESTIGE_COMBAT_BASE = 4;
export const RESOURCE_CAP = 1e300;
/**
* Probability of quest failure per zone — scales from 10% (early game) to 40% (end game).
* Probability of quest failure per zone — scales from 4% (early game) to 15% (end game).
* On failure the quest resets to "available" with no rewards; the player must wait the
* full duration again on their next attempt.
*/
export const zoneFailureChance: Record<string, number> = {
abyssal_trench: 0.24,
astral_void: 0.2,
celestial_reaches: 0.22,
cosmic_maelstrom: 0.4,
crystalline_spire: 0.28,
eternal_throne: 0.32,
frozen_peaks: 0.14,
infernal_court: 0.26,
infinite_expanse: 0.36,
primeval_sanctum: 0.4,
primordial_chaos: 0.34,
reality_forge: 0.38,
shadow_marshes: 0.16,
shattered_ruins: 0.12,
the_absolute: 0.4,
verdant_vale: 0.1,
void_sanctum: 0.3,
volcanic_depths: 0.18,
abyssal_trench: 0.09,
astral_void: 0.08,
celestial_reaches: 0.08,
cosmic_maelstrom: 0.15,
crystalline_spire: 0.11,
eternal_throne: 0.12,
frozen_peaks: 0.05,
infernal_court: 0.1,
infinite_expanse: 0.14,
primeval_sanctum: 0.15,
primordial_chaos: 0.13,
reality_forge: 0.14,
shadow_marshes: 0.06,
shattered_ruins: 0.05,
the_absolute: 0.15,
verdant_vale: 0.04,
void_sanctum: 0.11,
volcanic_depths: 0.07,
};
/**
@@ -451,7 +451,7 @@ export const computePartyCombatPower = (state: GameState): number => {
};
const basePrestigeThreshold = 1_000_000;
const runestonesPerPrestigeLevelClient = 15;
const runestonesPerPrestigeLevelClient = 20;
const maxBaseRunestones = 200;
/**