generated from nhcarrigan/template
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
d84725921a
|
|||
|
e4808680ed
|
|||
|
f001acc382
|
|||
|
8a38d02e69
|
|||
|
eed61db410
|
|||
|
0ae6aa12b2
|
|||
|
0d6d05e50b
|
|||
|
74dd3bf463
|
|||
|
959b86fa8b
|
@@ -1,135 +0,0 @@
|
|||||||
# Vampire Expansion — Implementation TODO
|
|
||||||
|
|
||||||
Branch: `feat/expansions`
|
|
||||||
|
|
||||||
Thematic currency names:
|
|
||||||
- Gold → **Blood**
|
|
||||||
- Essence → **Ichor**
|
|
||||||
- Crystals → **Soul Shards**
|
|
||||||
- Runestones → **Bloodstones**
|
|
||||||
- Echoes → **Whispers**
|
|
||||||
- Click action → **Hunt**
|
|
||||||
- Adventurers → **Thralls**
|
|
||||||
- Prestige → **Siring** (working name)
|
|
||||||
- Transcendence → **The Awakening** (working name)
|
|
||||||
- Apotheosis → **Eternal Sovereignty** (role ID: 1486144657023959180)
|
|
||||||
|
|
||||||
CDN prefix for all vampire art: `https://cdn.nhcarrigan.com/elysium/vampire/<folder>/<id>.jpg`
|
|
||||||
Local scratch dir (delete before committing): `img/vampire/`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Phase 1 — Types
|
|
||||||
|
|
||||||
- [ ] Add `VampireExpansionState` interface to `packages/types/src/interfaces/` mirroring full `GameState` structure (zones, bosses, quests, adventurers, upgrades, equipment, achievements, prestige, transcendence, apotheosis, exploration, resources, baseClickPower, lastTickAt, dailyChallenges, codex, autoQuest, autoBoss, autoAdventurer, companions, story)
|
|
||||||
- [ ] Add `ExpansionsState` interface: `{ vampire?: VampireExpansionState }`
|
|
||||||
- [ ] Add `expansions?: ExpansionsState` field to `GameState`
|
|
||||||
- [ ] Export new types from `packages/types/src/index.ts`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Phase 2 — Data files (vampire content)
|
|
||||||
|
|
||||||
All data files go in `apps/api/src/data/vampire/`.
|
|
||||||
Same content scale as base game; use vampire theming throughout.
|
|
||||||
|
|
||||||
- [ ] `zones.ts` — 18 vampire-themed zones (crypts, blood forests, cursed castles, etc.)
|
|
||||||
- [ ] `bosses.ts` — 72 vampire-themed bosses (4 per zone)
|
|
||||||
- [ ] `quests.ts` — match base game quest count (~95); vampire-themed names/descriptions
|
|
||||||
- [ ] `adventurers.ts` — 32 thrall tiers with progressive stats
|
|
||||||
- [ ] `upgrades.ts` — match base game upgrade count (~57); vampire-themed
|
|
||||||
- [ ] `equipment.ts` — match base game equipment count (~53); vampire-themed sets
|
|
||||||
- [ ] `equipmentSets.ts` — vampire equipment sets
|
|
||||||
- [ ] `achievements.ts` — match base game count (~40); vampire-themed conditions
|
|
||||||
- [ ] `explorations.ts` — 72 areas across 18 vampire lore zones
|
|
||||||
- [ ] `materials.ts` — match base game material count (~54); vampire-themed
|
|
||||||
- [ ] `recipes.ts` — match base game recipe count (~36); vampire-themed
|
|
||||||
- [ ] `prestigeUpgrades.ts` — 25 "Siring" upgrades
|
|
||||||
- [ ] `transcendenceUpgrades.ts` — 15 "Awakening" upgrades
|
|
||||||
- [ ] `dailyChallenges.ts` — 10 vampire daily challenges
|
|
||||||
- [ ] `initialState.ts` — `initialVampireState()` function mirroring `initialGameState` structure
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Phase 3 — Art generation & CDN upload
|
|
||||||
|
|
||||||
For each category below, generate images via Gemini API (`gemini-3-pro-image-preview`),
|
|
||||||
save locally to `img/vampire/<folder>/`, upload to R2, then delete local files.
|
|
||||||
|
|
||||||
Use soft-shaded anime style; vampire/gothic aesthetic; crimson/black/dark purple palette.
|
|
||||||
|
|
||||||
- [ ] Zone banners (18) → `img/vampire/zones/` → CDN `vampire/zones/`
|
|
||||||
- [ ] Boss portraits (72) → `img/vampire/bosses/` → CDN `vampire/bosses/`
|
|
||||||
- [ ] Quest banners (match count) → `img/vampire/quests/` → CDN `vampire/quests/`
|
|
||||||
- [ ] Adventurer/thrall portraits (32) → `img/vampire/adventurers/` → CDN `vampire/adventurers/`
|
|
||||||
- [ ] Equipment icons (match count) → `img/vampire/equipment/` → CDN `vampire/equipment/`
|
|
||||||
- [ ] Achievement icons (match count) → `img/vampire/achievements/` → CDN `vampire/achievements/`
|
|
||||||
- [ ] Exploration area art (72) → `img/vampire/explorations/` → CDN `vampire/explorations/`
|
|
||||||
- [ ] Material icons (match count) → `img/vampire/materials/` → CDN `vampire/materials/`
|
|
||||||
- [ ] Story chapter banners (match count) → `img/vampire/story-chapters/` → CDN `vampire/story-chapters/`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Phase 4 — API changes
|
|
||||||
|
|
||||||
- [ ] Add `inGuild` to Prisma `Player` model → update `initialGameState` if needed (already done in #134 — verify migration)
|
|
||||||
- [ ] Update Prisma schema: no DB changes needed (expansion state is inside the `GameState` JSON blob)
|
|
||||||
- [ ] Update `initialState.ts` to include `expansions: {}` in `initialGameState`
|
|
||||||
- [ ] Update `sync-new-content` debug route to inject/patch vampire expansion content when expansion is unlocked
|
|
||||||
- [ ] Add vampire-specific unlock trigger: when base-game apotheosis count ≥ 1, set `expansions.vampire` to `initialVampireState()` and `unlocked: true`
|
|
||||||
- [ ] Update the load endpoint to pass expansion state through to the client
|
|
||||||
- [ ] Ensure prestige/transcendence/apotheosis routes only reset state for their own expansion (base game routes must NOT touch `expansions.*`)
|
|
||||||
- [ ] Add vampire prestige, transcendence, and apotheosis routes (mirrors of base game routes, scoped to `expansions.vampire`)
|
|
||||||
- [ ] Grant `Eternal Sovereignty` role (ID: `1486144657023959180`) on vampire apotheosis
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Phase 5 — Frontend changes
|
|
||||||
|
|
||||||
### Expansion switcher
|
|
||||||
- [ ] Add expansion toggle buttons below the Early Access warning in the sidebar
|
|
||||||
- [ ] Always render all expansion buttons; disable any where `unlocked !== true`
|
|
||||||
- [ ] Active expansion stored in React state (not game state); defaults to `"base"`
|
|
||||||
- [ ] Switching expansion updates which data the UI panels display
|
|
||||||
|
|
||||||
### Resource bar
|
|
||||||
- [ ] Show ALL currencies from ALL expansions as separate labelled lines
|
|
||||||
- [ ] Vampire currencies use distinct icons/colours (crimson tint for blood, etc.)
|
|
||||||
- [ ] The "expand" button label shows the gold-equivalent currency of the active expansion
|
|
||||||
|
|
||||||
### Thematic UI
|
|
||||||
- [ ] When vampire expansion is active, swap labels: gold → Blood, essence → Ichor, etc.
|
|
||||||
- [ ] Apply `.vampire-mode` CSS class to game container when vampire is active
|
|
||||||
- [ ] Vampire colour palette: deep crimsons (`#5C0A1A`), rich crimson (`#C41E3A`), blacks, desaturated purples
|
|
||||||
|
|
||||||
### Tick engine
|
|
||||||
- [ ] Update `apps/web/src/engine/tick.ts` to compute passive income for all unlocked expansions every tick (not just base game)
|
|
||||||
- [ ] Offline income calculation must also cover all expansions
|
|
||||||
|
|
||||||
### Profile
|
|
||||||
- [ ] Profile panel: tab stats by expansion (base game tab + one tab per unlocked expansion)
|
|
||||||
- [ ] Show correct thematic prestige/transcendence/apotheosis badge names per expansion
|
|
||||||
- [ ] Lifetime stats (gold earned, clicks, etc.) tracked separately per expansion
|
|
||||||
|
|
||||||
### About / How to Play
|
|
||||||
- [ ] Update `aboutPanel.tsx` `HOW_TO_PLAY` array to document the expansion system
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Phase 6 — Tests & CI
|
|
||||||
|
|
||||||
- [ ] Unit tests for all new data files (at minimum, validate structure/required fields)
|
|
||||||
- [ ] Unit tests for `initialVampireState()`
|
|
||||||
- [ ] Tests for vampire unlock trigger route
|
|
||||||
- [ ] Tests for vampire prestige/transcendence/apotheosis routes
|
|
||||||
- [ ] Tests for updated tick engine (expansion income)
|
|
||||||
- [ ] Maintain 100% coverage on `apps/api` and `packages/types`
|
|
||||||
- [ ] Full pipeline: lint → build → test passing before PR
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Phase 7 — Final
|
|
||||||
|
|
||||||
- [ ] Delete `img/vampire/` directory before committing
|
|
||||||
- [ ] Update `MEMORY.md` with new content counts
|
|
||||||
- [ ] Open PR → request review
|
|
||||||
+58
-58
@@ -226,7 +226,7 @@ export const defaultBosses: Array<Boss> = [
|
|||||||
name: "The Void Titan",
|
name: "The Void Titan",
|
||||||
prestigeRequirement: 0,
|
prestigeRequirement: 0,
|
||||||
status: "locked",
|
status: "locked",
|
||||||
upgradeRewards: [],
|
upgradeRewards: [ "dark_templar_1" ],
|
||||||
zoneId: "frozen_peaks",
|
zoneId: "frozen_peaks",
|
||||||
},
|
},
|
||||||
// ── Volcanic Depths ───────────────────────────────────────────────────────
|
// ── Volcanic Depths ───────────────────────────────────────────────────────
|
||||||
@@ -353,7 +353,7 @@ export const defaultBosses: Array<Boss> = [
|
|||||||
id: "seraph_guardian",
|
id: "seraph_guardian",
|
||||||
maxHp: 500_000_000,
|
maxHp: 500_000_000,
|
||||||
name: "The Seraph Guardian",
|
name: "The Seraph Guardian",
|
||||||
prestigeRequirement: 6,
|
prestigeRequirement: 1,
|
||||||
status: "locked",
|
status: "locked",
|
||||||
upgradeRewards: [ "click_4" ],
|
upgradeRewards: [ "click_4" ],
|
||||||
zoneId: "celestial_reaches",
|
zoneId: "celestial_reaches",
|
||||||
@@ -371,7 +371,7 @@ export const defaultBosses: Array<Boss> = [
|
|||||||
id: "fallen_archangel",
|
id: "fallen_archangel",
|
||||||
maxHp: 2_000_000_000,
|
maxHp: 2_000_000_000,
|
||||||
name: "The Fallen Archangel",
|
name: "The Fallen Archangel",
|
||||||
prestigeRequirement: 7,
|
prestigeRequirement: 2,
|
||||||
status: "locked",
|
status: "locked",
|
||||||
upgradeRewards: [],
|
upgradeRewards: [],
|
||||||
zoneId: "celestial_reaches",
|
zoneId: "celestial_reaches",
|
||||||
@@ -389,7 +389,7 @@ export const defaultBosses: Array<Boss> = [
|
|||||||
id: "divine_judge",
|
id: "divine_judge",
|
||||||
maxHp: 8_000_000_000,
|
maxHp: 8_000_000_000,
|
||||||
name: "The Divine Judge",
|
name: "The Divine Judge",
|
||||||
prestigeRequirement: 8,
|
prestigeRequirement: 2,
|
||||||
status: "locked",
|
status: "locked",
|
||||||
upgradeRewards: [ "divine_covenant" ],
|
upgradeRewards: [ "divine_covenant" ],
|
||||||
zoneId: "celestial_reaches",
|
zoneId: "celestial_reaches",
|
||||||
@@ -407,7 +407,7 @@ export const defaultBosses: Array<Boss> = [
|
|||||||
id: "celestial_titan",
|
id: "celestial_titan",
|
||||||
maxHp: 30_000_000_000,
|
maxHp: 30_000_000_000,
|
||||||
name: "The Celestial Titan",
|
name: "The Celestial Titan",
|
||||||
prestigeRequirement: 9,
|
prestigeRequirement: 2,
|
||||||
status: "locked",
|
status: "locked",
|
||||||
upgradeRewards: [],
|
upgradeRewards: [],
|
||||||
zoneId: "celestial_reaches",
|
zoneId: "celestial_reaches",
|
||||||
@@ -425,7 +425,7 @@ export const defaultBosses: Array<Boss> = [
|
|||||||
id: "the_first_light",
|
id: "the_first_light",
|
||||||
maxHp: 100_000_000_000,
|
maxHp: 100_000_000_000,
|
||||||
name: "The First Light",
|
name: "The First Light",
|
||||||
prestigeRequirement: 10,
|
prestigeRequirement: 2,
|
||||||
status: "locked",
|
status: "locked",
|
||||||
upgradeRewards: [],
|
upgradeRewards: [],
|
||||||
zoneId: "celestial_reaches",
|
zoneId: "celestial_reaches",
|
||||||
@@ -444,7 +444,7 @@ export const defaultBosses: Array<Boss> = [
|
|||||||
id: "depth_leviathan",
|
id: "depth_leviathan",
|
||||||
maxHp: 250_000_000_000,
|
maxHp: 250_000_000_000,
|
||||||
name: "The Depth Leviathan",
|
name: "The Depth Leviathan",
|
||||||
prestigeRequirement: 9,
|
prestigeRequirement: 2,
|
||||||
status: "locked",
|
status: "locked",
|
||||||
upgradeRewards: [],
|
upgradeRewards: [],
|
||||||
zoneId: "abyssal_trench",
|
zoneId: "abyssal_trench",
|
||||||
@@ -462,7 +462,7 @@ export const defaultBosses: Array<Boss> = [
|
|||||||
id: "kraken_elder",
|
id: "kraken_elder",
|
||||||
maxHp: 1_000_000_000_000,
|
maxHp: 1_000_000_000_000,
|
||||||
name: "The Elder Kraken",
|
name: "The Elder Kraken",
|
||||||
prestigeRequirement: 10,
|
prestigeRequirement: 2,
|
||||||
status: "locked",
|
status: "locked",
|
||||||
upgradeRewards: [ "abyssal_pact" ],
|
upgradeRewards: [ "abyssal_pact" ],
|
||||||
zoneId: "abyssal_trench",
|
zoneId: "abyssal_trench",
|
||||||
@@ -480,7 +480,7 @@ export const defaultBosses: Array<Boss> = [
|
|||||||
id: "abyssal_colossus",
|
id: "abyssal_colossus",
|
||||||
maxHp: 4_000_000_000_000,
|
maxHp: 4_000_000_000_000,
|
||||||
name: "The Abyssal Colossus",
|
name: "The Abyssal Colossus",
|
||||||
prestigeRequirement: 11,
|
prestigeRequirement: 2,
|
||||||
status: "locked",
|
status: "locked",
|
||||||
upgradeRewards: [],
|
upgradeRewards: [],
|
||||||
zoneId: "abyssal_trench",
|
zoneId: "abyssal_trench",
|
||||||
@@ -498,7 +498,7 @@ export const defaultBosses: Array<Boss> = [
|
|||||||
id: "the_deep_one",
|
id: "the_deep_one",
|
||||||
maxHp: 15_000_000_000_000,
|
maxHp: 15_000_000_000_000,
|
||||||
name: "The Deep One",
|
name: "The Deep One",
|
||||||
prestigeRequirement: 12,
|
prestigeRequirement: 3,
|
||||||
status: "locked",
|
status: "locked",
|
||||||
upgradeRewards: [ "global_4" ],
|
upgradeRewards: [ "global_4" ],
|
||||||
zoneId: "abyssal_trench",
|
zoneId: "abyssal_trench",
|
||||||
@@ -516,7 +516,7 @@ export const defaultBosses: Array<Boss> = [
|
|||||||
id: "elder_abomination",
|
id: "elder_abomination",
|
||||||
maxHp: 50_000_000_000_000,
|
maxHp: 50_000_000_000_000,
|
||||||
name: "The Elder Abomination",
|
name: "The Elder Abomination",
|
||||||
prestigeRequirement: 13,
|
prestigeRequirement: 3,
|
||||||
status: "locked",
|
status: "locked",
|
||||||
upgradeRewards: [],
|
upgradeRewards: [],
|
||||||
zoneId: "abyssal_trench",
|
zoneId: "abyssal_trench",
|
||||||
@@ -535,7 +535,7 @@ export const defaultBosses: Array<Boss> = [
|
|||||||
id: "demon_prince",
|
id: "demon_prince",
|
||||||
maxHp: 120_000_000_000_000,
|
maxHp: 120_000_000_000_000,
|
||||||
name: "The Demon Prince",
|
name: "The Demon Prince",
|
||||||
prestigeRequirement: 12,
|
prestigeRequirement: 3,
|
||||||
status: "locked",
|
status: "locked",
|
||||||
upgradeRewards: [],
|
upgradeRewards: [],
|
||||||
zoneId: "infernal_court",
|
zoneId: "infernal_court",
|
||||||
@@ -553,7 +553,7 @@ export const defaultBosses: Array<Boss> = [
|
|||||||
id: "hellfire_titan",
|
id: "hellfire_titan",
|
||||||
maxHp: 500_000_000_000_000,
|
maxHp: 500_000_000_000_000,
|
||||||
name: "The Hellfire Titan",
|
name: "The Hellfire Titan",
|
||||||
prestigeRequirement: 13,
|
prestigeRequirement: 3,
|
||||||
status: "locked",
|
status: "locked",
|
||||||
upgradeRewards: [ "celestial_mandate" ],
|
upgradeRewards: [ "celestial_mandate" ],
|
||||||
zoneId: "infernal_court",
|
zoneId: "infernal_court",
|
||||||
@@ -571,7 +571,7 @@ export const defaultBosses: Array<Boss> = [
|
|||||||
id: "lord_of_sin",
|
id: "lord_of_sin",
|
||||||
maxHp: 2_000_000_000_000_000,
|
maxHp: 2_000_000_000_000_000,
|
||||||
name: "The Lord of Sin",
|
name: "The Lord of Sin",
|
||||||
prestigeRequirement: 14,
|
prestigeRequirement: 3,
|
||||||
status: "locked",
|
status: "locked",
|
||||||
upgradeRewards: [],
|
upgradeRewards: [],
|
||||||
zoneId: "infernal_court",
|
zoneId: "infernal_court",
|
||||||
@@ -589,7 +589,7 @@ export const defaultBosses: Array<Boss> = [
|
|||||||
id: "infernal_sovereign",
|
id: "infernal_sovereign",
|
||||||
maxHp: 6_000_000_000_000_000,
|
maxHp: 6_000_000_000_000_000,
|
||||||
name: "The Infernal Sovereign",
|
name: "The Infernal Sovereign",
|
||||||
prestigeRequirement: 15,
|
prestigeRequirement: 3,
|
||||||
status: "locked",
|
status: "locked",
|
||||||
upgradeRewards: [ "click_5" ],
|
upgradeRewards: [ "click_5" ],
|
||||||
zoneId: "infernal_court",
|
zoneId: "infernal_court",
|
||||||
@@ -607,7 +607,7 @@ export const defaultBosses: Array<Boss> = [
|
|||||||
id: "the_fallen",
|
id: "the_fallen",
|
||||||
maxHp: 8_000_000_000_000_000,
|
maxHp: 8_000_000_000_000_000,
|
||||||
name: "The Fallen",
|
name: "The Fallen",
|
||||||
prestigeRequirement: 16,
|
prestigeRequirement: 4,
|
||||||
status: "locked",
|
status: "locked",
|
||||||
upgradeRewards: [],
|
upgradeRewards: [],
|
||||||
zoneId: "infernal_court",
|
zoneId: "infernal_court",
|
||||||
@@ -626,7 +626,7 @@ export const defaultBosses: Array<Boss> = [
|
|||||||
id: "prism_golem",
|
id: "prism_golem",
|
||||||
maxHp: 2e16,
|
maxHp: 2e16,
|
||||||
name: "The Prism Golem",
|
name: "The Prism Golem",
|
||||||
prestigeRequirement: 15,
|
prestigeRequirement: 3,
|
||||||
status: "locked",
|
status: "locked",
|
||||||
upgradeRewards: [],
|
upgradeRewards: [],
|
||||||
zoneId: "crystalline_spire",
|
zoneId: "crystalline_spire",
|
||||||
@@ -644,7 +644,7 @@ export const defaultBosses: Array<Boss> = [
|
|||||||
id: "crystal_drake",
|
id: "crystal_drake",
|
||||||
maxHp: 8e16,
|
maxHp: 8e16,
|
||||||
name: "The Crystal Drake",
|
name: "The Crystal Drake",
|
||||||
prestigeRequirement: 16,
|
prestigeRequirement: 4,
|
||||||
status: "locked",
|
status: "locked",
|
||||||
upgradeRewards: [ "void_ascendancy" ],
|
upgradeRewards: [ "void_ascendancy" ],
|
||||||
zoneId: "crystalline_spire",
|
zoneId: "crystalline_spire",
|
||||||
@@ -662,7 +662,7 @@ export const defaultBosses: Array<Boss> = [
|
|||||||
id: "the_faceted",
|
id: "the_faceted",
|
||||||
maxHp: 3e17,
|
maxHp: 3e17,
|
||||||
name: "The Faceted",
|
name: "The Faceted",
|
||||||
prestigeRequirement: 17,
|
prestigeRequirement: 4,
|
||||||
status: "locked",
|
status: "locked",
|
||||||
upgradeRewards: [],
|
upgradeRewards: [],
|
||||||
zoneId: "crystalline_spire",
|
zoneId: "crystalline_spire",
|
||||||
@@ -680,7 +680,7 @@ export const defaultBosses: Array<Boss> = [
|
|||||||
id: "diamond_colossus",
|
id: "diamond_colossus",
|
||||||
maxHp: 1e18,
|
maxHp: 1e18,
|
||||||
name: "The Diamond Colossus",
|
name: "The Diamond Colossus",
|
||||||
prestigeRequirement: 18,
|
prestigeRequirement: 4,
|
||||||
status: "locked",
|
status: "locked",
|
||||||
upgradeRewards: [],
|
upgradeRewards: [],
|
||||||
zoneId: "crystalline_spire",
|
zoneId: "crystalline_spire",
|
||||||
@@ -698,7 +698,7 @@ export const defaultBosses: Array<Boss> = [
|
|||||||
id: "crystal_sovereign",
|
id: "crystal_sovereign",
|
||||||
maxHp: 4e18,
|
maxHp: 4e18,
|
||||||
name: "The Crystal Sovereign",
|
name: "The Crystal Sovereign",
|
||||||
prestigeRequirement: 19,
|
prestigeRequirement: 4,
|
||||||
status: "locked",
|
status: "locked",
|
||||||
upgradeRewards: [],
|
upgradeRewards: [],
|
||||||
zoneId: "crystalline_spire",
|
zoneId: "crystalline_spire",
|
||||||
@@ -717,7 +717,7 @@ export const defaultBosses: Array<Boss> = [
|
|||||||
id: "void_herald",
|
id: "void_herald",
|
||||||
maxHp: 1e19,
|
maxHp: 1e19,
|
||||||
name: "The Void Herald",
|
name: "The Void Herald",
|
||||||
prestigeRequirement: 18,
|
prestigeRequirement: 4,
|
||||||
status: "locked",
|
status: "locked",
|
||||||
upgradeRewards: [],
|
upgradeRewards: [],
|
||||||
zoneId: "void_sanctum",
|
zoneId: "void_sanctum",
|
||||||
@@ -735,7 +735,7 @@ export const defaultBosses: Array<Boss> = [
|
|||||||
id: "eternal_shade",
|
id: "eternal_shade",
|
||||||
maxHp: 5e19,
|
maxHp: 5e19,
|
||||||
name: "The Eternal Shade",
|
name: "The Eternal Shade",
|
||||||
prestigeRequirement: 19,
|
prestigeRequirement: 4,
|
||||||
status: "locked",
|
status: "locked",
|
||||||
upgradeRewards: [ "divine_harmony" ],
|
upgradeRewards: [ "divine_harmony" ],
|
||||||
zoneId: "void_sanctum",
|
zoneId: "void_sanctum",
|
||||||
@@ -753,7 +753,7 @@ export const defaultBosses: Array<Boss> = [
|
|||||||
id: "the_unmaker",
|
id: "the_unmaker",
|
||||||
maxHp: 2e20,
|
maxHp: 2e20,
|
||||||
name: "The Unmaker",
|
name: "The Unmaker",
|
||||||
prestigeRequirement: 20,
|
prestigeRequirement: 5,
|
||||||
status: "locked",
|
status: "locked",
|
||||||
upgradeRewards: [],
|
upgradeRewards: [],
|
||||||
zoneId: "void_sanctum",
|
zoneId: "void_sanctum",
|
||||||
@@ -771,7 +771,7 @@ export const defaultBosses: Array<Boss> = [
|
|||||||
id: "void_progenitor",
|
id: "void_progenitor",
|
||||||
maxHp: 8e20,
|
maxHp: 8e20,
|
||||||
name: "The Void Progenitor",
|
name: "The Void Progenitor",
|
||||||
prestigeRequirement: 21,
|
prestigeRequirement: 5,
|
||||||
status: "locked",
|
status: "locked",
|
||||||
upgradeRewards: [],
|
upgradeRewards: [],
|
||||||
zoneId: "void_sanctum",
|
zoneId: "void_sanctum",
|
||||||
@@ -789,7 +789,7 @@ export const defaultBosses: Array<Boss> = [
|
|||||||
id: "void_emperor",
|
id: "void_emperor",
|
||||||
maxHp: 3e21,
|
maxHp: 3e21,
|
||||||
name: "The Void Emperor",
|
name: "The Void Emperor",
|
||||||
prestigeRequirement: 22,
|
prestigeRequirement: 5,
|
||||||
status: "locked",
|
status: "locked",
|
||||||
upgradeRewards: [],
|
upgradeRewards: [],
|
||||||
zoneId: "void_sanctum",
|
zoneId: "void_sanctum",
|
||||||
@@ -808,7 +808,7 @@ export const defaultBosses: Array<Boss> = [
|
|||||||
id: "throne_warden",
|
id: "throne_warden",
|
||||||
maxHp: 1e22,
|
maxHp: 1e22,
|
||||||
name: "The Throne Warden",
|
name: "The Throne Warden",
|
||||||
prestigeRequirement: 21,
|
prestigeRequirement: 5,
|
||||||
status: "locked",
|
status: "locked",
|
||||||
upgradeRewards: [],
|
upgradeRewards: [],
|
||||||
zoneId: "eternal_throne",
|
zoneId: "eternal_throne",
|
||||||
@@ -826,7 +826,7 @@ export const defaultBosses: Array<Boss> = [
|
|||||||
id: "eternal_knight",
|
id: "eternal_knight",
|
||||||
maxHp: 5e22,
|
maxHp: 5e22,
|
||||||
name: "The Eternal Knight",
|
name: "The Eternal Knight",
|
||||||
prestigeRequirement: 22,
|
prestigeRequirement: 5,
|
||||||
status: "locked",
|
status: "locked",
|
||||||
upgradeRewards: [ "infernal_fury" ],
|
upgradeRewards: [ "infernal_fury" ],
|
||||||
zoneId: "eternal_throne",
|
zoneId: "eternal_throne",
|
||||||
@@ -844,7 +844,7 @@ export const defaultBosses: Array<Boss> = [
|
|||||||
id: "the_undying",
|
id: "the_undying",
|
||||||
maxHp: 2e23,
|
maxHp: 2e23,
|
||||||
name: "The Undying",
|
name: "The Undying",
|
||||||
prestigeRequirement: 23,
|
prestigeRequirement: 5,
|
||||||
status: "locked",
|
status: "locked",
|
||||||
upgradeRewards: [],
|
upgradeRewards: [],
|
||||||
zoneId: "eternal_throne",
|
zoneId: "eternal_throne",
|
||||||
@@ -862,7 +862,7 @@ export const defaultBosses: Array<Boss> = [
|
|||||||
id: "apex_sovereign",
|
id: "apex_sovereign",
|
||||||
maxHp: 8e23,
|
maxHp: 8e23,
|
||||||
name: "The Apex Sovereign",
|
name: "The Apex Sovereign",
|
||||||
prestigeRequirement: 24,
|
prestigeRequirement: 5,
|
||||||
status: "locked",
|
status: "locked",
|
||||||
upgradeRewards: [],
|
upgradeRewards: [],
|
||||||
zoneId: "eternal_throne",
|
zoneId: "eternal_throne",
|
||||||
@@ -880,7 +880,7 @@ export const defaultBosses: Array<Boss> = [
|
|||||||
id: "the_apex",
|
id: "the_apex",
|
||||||
maxHp: 3e24,
|
maxHp: 3e24,
|
||||||
name: "The Apex",
|
name: "The Apex",
|
||||||
prestigeRequirement: 25,
|
prestigeRequirement: 6,
|
||||||
status: "locked",
|
status: "locked",
|
||||||
upgradeRewards: [],
|
upgradeRewards: [],
|
||||||
zoneId: "eternal_throne",
|
zoneId: "eternal_throne",
|
||||||
@@ -899,7 +899,7 @@ export const defaultBosses: Array<Boss> = [
|
|||||||
id: "chaos_wyrm",
|
id: "chaos_wyrm",
|
||||||
maxHp: 1e26,
|
maxHp: 1e26,
|
||||||
name: "The Chaos Wyrm",
|
name: "The Chaos Wyrm",
|
||||||
prestigeRequirement: 26,
|
prestigeRequirement: 6,
|
||||||
status: "locked",
|
status: "locked",
|
||||||
upgradeRewards: [],
|
upgradeRewards: [],
|
||||||
zoneId: "primordial_chaos",
|
zoneId: "primordial_chaos",
|
||||||
@@ -917,7 +917,7 @@ export const defaultBosses: Array<Boss> = [
|
|||||||
id: "creation_engine",
|
id: "creation_engine",
|
||||||
maxHp: 5e27,
|
maxHp: 5e27,
|
||||||
name: "The Creation Engine",
|
name: "The Creation Engine",
|
||||||
prestigeRequirement: 27,
|
prestigeRequirement: 6,
|
||||||
status: "locked",
|
status: "locked",
|
||||||
upgradeRewards: [ "aether_weaver_1" ],
|
upgradeRewards: [ "aether_weaver_1" ],
|
||||||
zoneId: "primordial_chaos",
|
zoneId: "primordial_chaos",
|
||||||
@@ -935,7 +935,7 @@ export const defaultBosses: Array<Boss> = [
|
|||||||
id: "entropy_avatar",
|
id: "entropy_avatar",
|
||||||
maxHp: 2e29,
|
maxHp: 2e29,
|
||||||
name: "The Entropy Avatar",
|
name: "The Entropy Avatar",
|
||||||
prestigeRequirement: 29,
|
prestigeRequirement: 7,
|
||||||
status: "locked",
|
status: "locked",
|
||||||
upgradeRewards: [],
|
upgradeRewards: [],
|
||||||
zoneId: "primordial_chaos",
|
zoneId: "primordial_chaos",
|
||||||
@@ -953,7 +953,7 @@ export const defaultBosses: Array<Boss> = [
|
|||||||
id: "primordial_titan",
|
id: "primordial_titan",
|
||||||
maxHp: 8e30,
|
maxHp: 8e30,
|
||||||
name: "The Primordial Titan",
|
name: "The Primordial Titan",
|
||||||
prestigeRequirement: 31,
|
prestigeRequirement: 7,
|
||||||
status: "locked",
|
status: "locked",
|
||||||
upgradeRewards: [],
|
upgradeRewards: [],
|
||||||
zoneId: "primordial_chaos",
|
zoneId: "primordial_chaos",
|
||||||
@@ -972,7 +972,7 @@ export const defaultBosses: Array<Boss> = [
|
|||||||
id: "expanse_drifter",
|
id: "expanse_drifter",
|
||||||
maxHp: 3e33,
|
maxHp: 3e33,
|
||||||
name: "The Expanse Drifter",
|
name: "The Expanse Drifter",
|
||||||
prestigeRequirement: 33,
|
prestigeRequirement: 8,
|
||||||
status: "locked",
|
status: "locked",
|
||||||
upgradeRewards: [ "titan_warrior_1" ],
|
upgradeRewards: [ "titan_warrior_1" ],
|
||||||
zoneId: "infinite_expanse",
|
zoneId: "infinite_expanse",
|
||||||
@@ -990,9 +990,9 @@ export const defaultBosses: Array<Boss> = [
|
|||||||
id: "horizon_beast",
|
id: "horizon_beast",
|
||||||
maxHp: 1e37,
|
maxHp: 1e37,
|
||||||
name: "The Horizon Beast",
|
name: "The Horizon Beast",
|
||||||
prestigeRequirement: 35,
|
prestigeRequirement: 8,
|
||||||
status: "locked",
|
status: "locked",
|
||||||
upgradeRewards: [],
|
upgradeRewards: [ "oblivion_paladin_1" ],
|
||||||
zoneId: "infinite_expanse",
|
zoneId: "infinite_expanse",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1008,7 +1008,7 @@ export const defaultBosses: Array<Boss> = [
|
|||||||
id: "infinity_construct",
|
id: "infinity_construct",
|
||||||
maxHp: 5e40,
|
maxHp: 5e40,
|
||||||
name: "The Infinity Construct",
|
name: "The Infinity Construct",
|
||||||
prestigeRequirement: 37,
|
prestigeRequirement: 8,
|
||||||
status: "locked",
|
status: "locked",
|
||||||
upgradeRewards: [],
|
upgradeRewards: [],
|
||||||
zoneId: "infinite_expanse",
|
zoneId: "infinite_expanse",
|
||||||
@@ -1026,7 +1026,7 @@ export const defaultBosses: Array<Boss> = [
|
|||||||
id: "expanse_sovereign",
|
id: "expanse_sovereign",
|
||||||
maxHp: 2e44,
|
maxHp: 2e44,
|
||||||
name: "The Expanse Sovereign",
|
name: "The Expanse Sovereign",
|
||||||
prestigeRequirement: 39,
|
prestigeRequirement: 9,
|
||||||
status: "locked",
|
status: "locked",
|
||||||
upgradeRewards: [],
|
upgradeRewards: [],
|
||||||
zoneId: "infinite_expanse",
|
zoneId: "infinite_expanse",
|
||||||
@@ -1045,7 +1045,7 @@ export const defaultBosses: Array<Boss> = [
|
|||||||
id: "forge_guardian",
|
id: "forge_guardian",
|
||||||
maxHp: 8e47,
|
maxHp: 8e47,
|
||||||
name: "The Forge Guardian",
|
name: "The Forge Guardian",
|
||||||
prestigeRequirement: 41,
|
prestigeRequirement: 9,
|
||||||
status: "locked",
|
status: "locked",
|
||||||
upgradeRewards: [ "nexus_sage_1" ],
|
upgradeRewards: [ "nexus_sage_1" ],
|
||||||
zoneId: "reality_forge",
|
zoneId: "reality_forge",
|
||||||
@@ -1063,7 +1063,7 @@ export const defaultBosses: Array<Boss> = [
|
|||||||
id: "reality_shaper",
|
id: "reality_shaper",
|
||||||
maxHp: 4e52,
|
maxHp: 4e52,
|
||||||
name: "The Reality Shaper",
|
name: "The Reality Shaper",
|
||||||
prestigeRequirement: 44,
|
prestigeRequirement: 10,
|
||||||
status: "locked",
|
status: "locked",
|
||||||
upgradeRewards: [],
|
upgradeRewards: [],
|
||||||
zoneId: "reality_forge",
|
zoneId: "reality_forge",
|
||||||
@@ -1081,7 +1081,7 @@ export const defaultBosses: Array<Boss> = [
|
|||||||
id: "creation_prime",
|
id: "creation_prime",
|
||||||
maxHp: 2e57,
|
maxHp: 2e57,
|
||||||
name: "The Creation Prime",
|
name: "The Creation Prime",
|
||||||
prestigeRequirement: 47,
|
prestigeRequirement: 11,
|
||||||
status: "locked",
|
status: "locked",
|
||||||
upgradeRewards: [],
|
upgradeRewards: [],
|
||||||
zoneId: "reality_forge",
|
zoneId: "reality_forge",
|
||||||
@@ -1099,7 +1099,7 @@ export const defaultBosses: Array<Boss> = [
|
|||||||
id: "reality_architect",
|
id: "reality_architect",
|
||||||
maxHp: 8e61,
|
maxHp: 8e61,
|
||||||
name: "The Reality Architect",
|
name: "The Reality Architect",
|
||||||
prestigeRequirement: 49,
|
prestigeRequirement: 11,
|
||||||
status: "locked",
|
status: "locked",
|
||||||
upgradeRewards: [],
|
upgradeRewards: [],
|
||||||
zoneId: "reality_forge",
|
zoneId: "reality_forge",
|
||||||
@@ -1118,7 +1118,7 @@ export const defaultBosses: Array<Boss> = [
|
|||||||
id: "storm_colossus",
|
id: "storm_colossus",
|
||||||
maxHp: 4e65,
|
maxHp: 4e65,
|
||||||
name: "The Storm Colossus",
|
name: "The Storm Colossus",
|
||||||
prestigeRequirement: 51,
|
prestigeRequirement: 12,
|
||||||
status: "locked",
|
status: "locked",
|
||||||
upgradeRewards: [],
|
upgradeRewards: [],
|
||||||
zoneId: "cosmic_maelstrom",
|
zoneId: "cosmic_maelstrom",
|
||||||
@@ -1136,7 +1136,7 @@ export const defaultBosses: Array<Boss> = [
|
|||||||
id: "force_prime",
|
id: "force_prime",
|
||||||
maxHp: 2e71,
|
maxHp: 2e71,
|
||||||
name: "The Force Prime",
|
name: "The Force Prime",
|
||||||
prestigeRequirement: 54,
|
prestigeRequirement: 12,
|
||||||
status: "locked",
|
status: "locked",
|
||||||
upgradeRewards: [],
|
upgradeRewards: [],
|
||||||
zoneId: "cosmic_maelstrom",
|
zoneId: "cosmic_maelstrom",
|
||||||
@@ -1154,9 +1154,9 @@ export const defaultBosses: Array<Boss> = [
|
|||||||
id: "maelstrom_god",
|
id: "maelstrom_god",
|
||||||
maxHp: 1e77,
|
maxHp: 1e77,
|
||||||
name: "The Maelstrom God",
|
name: "The Maelstrom God",
|
||||||
prestigeRequirement: 57,
|
prestigeRequirement: 13,
|
||||||
status: "locked",
|
status: "locked",
|
||||||
upgradeRewards: [],
|
upgradeRewards: [ "transcendent_rogue_1" ],
|
||||||
zoneId: "cosmic_maelstrom",
|
zoneId: "cosmic_maelstrom",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1172,7 +1172,7 @@ export const defaultBosses: Array<Boss> = [
|
|||||||
id: "cosmic_annihilator",
|
id: "cosmic_annihilator",
|
||||||
maxHp: 5e82,
|
maxHp: 5e82,
|
||||||
name: "The Cosmic Annihilator",
|
name: "The Cosmic Annihilator",
|
||||||
prestigeRequirement: 59,
|
prestigeRequirement: 13,
|
||||||
status: "locked",
|
status: "locked",
|
||||||
upgradeRewards: [],
|
upgradeRewards: [],
|
||||||
zoneId: "cosmic_maelstrom",
|
zoneId: "cosmic_maelstrom",
|
||||||
@@ -1191,7 +1191,7 @@ export const defaultBosses: Array<Boss> = [
|
|||||||
id: "ancient_sentinel",
|
id: "ancient_sentinel",
|
||||||
maxHp: 2e88,
|
maxHp: 2e88,
|
||||||
name: "The Ancient Sentinel",
|
name: "The Ancient Sentinel",
|
||||||
prestigeRequirement: 61,
|
prestigeRequirement: 14,
|
||||||
status: "locked",
|
status: "locked",
|
||||||
upgradeRewards: [ "astral_sovereign_1" ],
|
upgradeRewards: [ "astral_sovereign_1" ],
|
||||||
zoneId: "primeval_sanctum",
|
zoneId: "primeval_sanctum",
|
||||||
@@ -1209,7 +1209,7 @@ export const defaultBosses: Array<Boss> = [
|
|||||||
id: "time_elder",
|
id: "time_elder",
|
||||||
maxHp: 1e95,
|
maxHp: 1e95,
|
||||||
name: "The Time Elder",
|
name: "The Time Elder",
|
||||||
prestigeRequirement: 65,
|
prestigeRequirement: 15,
|
||||||
status: "locked",
|
status: "locked",
|
||||||
upgradeRewards: [],
|
upgradeRewards: [],
|
||||||
zoneId: "primeval_sanctum",
|
zoneId: "primeval_sanctum",
|
||||||
@@ -1227,7 +1227,7 @@ export const defaultBosses: Array<Boss> = [
|
|||||||
id: "origin_beast",
|
id: "origin_beast",
|
||||||
maxHp: 8e101,
|
maxHp: 8e101,
|
||||||
name: "The Origin Beast",
|
name: "The Origin Beast",
|
||||||
prestigeRequirement: 69,
|
prestigeRequirement: 16,
|
||||||
status: "locked",
|
status: "locked",
|
||||||
upgradeRewards: [],
|
upgradeRewards: [],
|
||||||
zoneId: "primeval_sanctum",
|
zoneId: "primeval_sanctum",
|
||||||
@@ -1245,7 +1245,7 @@ export const defaultBosses: Array<Boss> = [
|
|||||||
id: "primeval_god",
|
id: "primeval_god",
|
||||||
maxHp: 5e108,
|
maxHp: 5e108,
|
||||||
name: "The Primeval God",
|
name: "The Primeval God",
|
||||||
prestigeRequirement: 74,
|
prestigeRequirement: 17,
|
||||||
status: "locked",
|
status: "locked",
|
||||||
upgradeRewards: [],
|
upgradeRewards: [],
|
||||||
zoneId: "primeval_sanctum",
|
zoneId: "primeval_sanctum",
|
||||||
@@ -1264,7 +1264,7 @@ export const defaultBosses: Array<Boss> = [
|
|||||||
id: "absolute_herald",
|
id: "absolute_herald",
|
||||||
maxHp: 2e116,
|
maxHp: 2e116,
|
||||||
name: "The Absolute Herald",
|
name: "The Absolute Herald",
|
||||||
prestigeRequirement: 76,
|
prestigeRequirement: 17,
|
||||||
status: "locked",
|
status: "locked",
|
||||||
upgradeRewards: [ "primordial_mage_1" ],
|
upgradeRewards: [ "primordial_mage_1" ],
|
||||||
zoneId: "the_absolute",
|
zoneId: "the_absolute",
|
||||||
@@ -1282,7 +1282,7 @@ export const defaultBosses: Array<Boss> = [
|
|||||||
id: "void_convergence",
|
id: "void_convergence",
|
||||||
maxHp: 1e125,
|
maxHp: 1e125,
|
||||||
name: "The Void Convergence",
|
name: "The Void Convergence",
|
||||||
prestigeRequirement: 79,
|
prestigeRequirement: 18,
|
||||||
status: "locked",
|
status: "locked",
|
||||||
upgradeRewards: [],
|
upgradeRewards: [],
|
||||||
zoneId: "the_absolute",
|
zoneId: "the_absolute",
|
||||||
@@ -1300,9 +1300,9 @@ export const defaultBosses: Array<Boss> = [
|
|||||||
id: "eternal_end",
|
id: "eternal_end",
|
||||||
maxHp: 5e134,
|
maxHp: 5e134,
|
||||||
name: "The Eternal End",
|
name: "The Eternal End",
|
||||||
prestigeRequirement: 83,
|
prestigeRequirement: 19,
|
||||||
status: "locked",
|
status: "locked",
|
||||||
upgradeRewards: [],
|
upgradeRewards: [ "omniversal_champion_1" ],
|
||||||
zoneId: "the_absolute",
|
zoneId: "the_absolute",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1318,7 +1318,7 @@ export const defaultBosses: Array<Boss> = [
|
|||||||
id: "the_absolute_one",
|
id: "the_absolute_one",
|
||||||
maxHp: 2e145,
|
maxHp: 2e145,
|
||||||
name: "The Absolute One",
|
name: "The Absolute One",
|
||||||
prestigeRequirement: 88,
|
prestigeRequirement: 20,
|
||||||
status: "locked",
|
status: "locked",
|
||||||
upgradeRewards: [],
|
upgradeRewards: [],
|
||||||
zoneId: "the_absolute",
|
zoneId: "the_absolute",
|
||||||
|
|||||||
+51
-16
@@ -157,6 +157,21 @@ export const defaultQuests: Array<Quest> = [
|
|||||||
status: "locked",
|
status: "locked",
|
||||||
zoneId: "frozen_peaks",
|
zoneId: "frozen_peaks",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
combatPowerRequired: 200_000,
|
||||||
|
description:
|
||||||
|
"A tomb sealed within a glacier for millennia. The soldiers interred here died guarding something that no longer exists — but their treasures remain.",
|
||||||
|
durationSeconds: 150 * 60,
|
||||||
|
id: "glacier_tomb",
|
||||||
|
name: "The Glacier Tomb",
|
||||||
|
prerequisiteIds: [ "frozen_wastes" ],
|
||||||
|
rewards: [
|
||||||
|
{ amount: 10_000_000, type: "gold" },
|
||||||
|
{ amount: 3000, type: "essence" },
|
||||||
|
],
|
||||||
|
status: "locked",
|
||||||
|
zoneId: "frozen_peaks",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
combatPowerRequired: 400_000,
|
combatPowerRequired: 400_000,
|
||||||
description:
|
description:
|
||||||
@@ -164,7 +179,7 @@ export const defaultQuests: Array<Quest> = [
|
|||||||
durationSeconds: 3 * 60 * 60,
|
durationSeconds: 3 * 60 * 60,
|
||||||
id: "ice_caves",
|
id: "ice_caves",
|
||||||
name: "The Ice Caves",
|
name: "The Ice Caves",
|
||||||
prerequisiteIds: [ "frozen_wastes" ],
|
prerequisiteIds: [ "glacier_tomb" ],
|
||||||
rewards: [
|
rewards: [
|
||||||
{ amount: 5000, type: "essence" },
|
{ amount: 5000, type: "essence" },
|
||||||
{ amount: 200, type: "crystals" },
|
{ amount: 200, type: "crystals" },
|
||||||
@@ -188,6 +203,22 @@ export const defaultQuests: Array<Quest> = [
|
|||||||
status: "locked",
|
status: "locked",
|
||||||
zoneId: "frozen_peaks",
|
zoneId: "frozen_peaks",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
combatPowerRequired: 3_000_000,
|
||||||
|
description:
|
||||||
|
"Deep in the peaks lies the throne room of an ancient frost king, long dead, whose dominion over cold and storm was absolute. His crown still waits.",
|
||||||
|
durationSeconds: 7 * 60 * 60,
|
||||||
|
id: "frozen_throne",
|
||||||
|
name: "The Frozen Throne",
|
||||||
|
prerequisiteIds: [ "storm_citadel" ],
|
||||||
|
rewards: [
|
||||||
|
{ amount: 60_000_000, type: "gold" },
|
||||||
|
{ amount: 25_000, type: "essence" },
|
||||||
|
{ amount: 400, type: "crystals" },
|
||||||
|
],
|
||||||
|
status: "locked",
|
||||||
|
zoneId: "frozen_peaks",
|
||||||
|
},
|
||||||
// ── Shadow Marshes ────────────────────────────────────────────────────────
|
// ── Shadow Marshes ────────────────────────────────────────────────────────
|
||||||
{
|
{
|
||||||
combatPowerRequired: 5_000_000,
|
combatPowerRequired: 5_000_000,
|
||||||
@@ -198,7 +229,8 @@ export const defaultQuests: Array<Quest> = [
|
|||||||
name: "The Shadow Mere",
|
name: "The Shadow Mere",
|
||||||
prerequisiteIds: [],
|
prerequisiteIds: [],
|
||||||
rewards: [
|
rewards: [
|
||||||
{ amount: 150, type: "essence" },
|
{ amount: 5_000_000, type: "gold" },
|
||||||
|
{ amount: 5000, type: "essence" },
|
||||||
],
|
],
|
||||||
status: "locked",
|
status: "locked",
|
||||||
zoneId: "shadow_marshes",
|
zoneId: "shadow_marshes",
|
||||||
@@ -212,7 +244,8 @@ export const defaultQuests: Array<Quest> = [
|
|||||||
name: "The Witch Coven",
|
name: "The Witch Coven",
|
||||||
prerequisiteIds: [ "shadow_mere" ],
|
prerequisiteIds: [ "shadow_mere" ],
|
||||||
rewards: [
|
rewards: [
|
||||||
{ amount: 500, type: "essence" },
|
{ amount: 20_000_000, type: "gold" },
|
||||||
|
{ amount: 20_000, type: "essence" },
|
||||||
{ targetId: "shadow_assassin", type: "adventurer" },
|
{ targetId: "shadow_assassin", type: "adventurer" },
|
||||||
],
|
],
|
||||||
status: "locked",
|
status: "locked",
|
||||||
@@ -245,9 +278,9 @@ export const defaultQuests: Array<Quest> = [
|
|||||||
name: "The Plague Ruins",
|
name: "The Plague Ruins",
|
||||||
prerequisiteIds: [ "sunken_temple" ],
|
prerequisiteIds: [ "sunken_temple" ],
|
||||||
rewards: [
|
rewards: [
|
||||||
{ amount: 8_000_000, type: "gold" },
|
{ amount: 100_000_000, type: "gold" },
|
||||||
{ amount: 2000, type: "essence" },
|
{ amount: 30_000, type: "essence" },
|
||||||
{ amount: 150, type: "crystals" },
|
{ amount: 500, type: "crystals" },
|
||||||
{ targetId: "dark_templar", type: "adventurer" },
|
{ targetId: "dark_templar", type: "adventurer" },
|
||||||
],
|
],
|
||||||
status: "locked",
|
status: "locked",
|
||||||
@@ -329,8 +362,9 @@ export const defaultQuests: Array<Quest> = [
|
|||||||
name: "Void Rift",
|
name: "Void Rift",
|
||||||
prerequisiteIds: [],
|
prerequisiteIds: [],
|
||||||
rewards: [
|
rewards: [
|
||||||
{ amount: 500, type: "crystals" },
|
{ amount: 2_000_000_000, type: "gold" },
|
||||||
{ amount: 5000, type: "essence" },
|
{ amount: 300_000, type: "essence" },
|
||||||
|
{ amount: 1000, type: "crystals" },
|
||||||
],
|
],
|
||||||
status: "locked",
|
status: "locked",
|
||||||
zoneId: "astral_void",
|
zoneId: "astral_void",
|
||||||
@@ -344,9 +378,9 @@ export const defaultQuests: Array<Quest> = [
|
|||||||
name: "The Star Graveyard",
|
name: "The Star Graveyard",
|
||||||
prerequisiteIds: [ "void_rift" ],
|
prerequisiteIds: [ "void_rift" ],
|
||||||
rewards: [
|
rewards: [
|
||||||
{ amount: 1_000_000_000, type: "gold" },
|
{ amount: 8_000_000_000, type: "gold" },
|
||||||
{ amount: 100_000, type: "essence" },
|
{ amount: 800_000, type: "essence" },
|
||||||
{ amount: 1000, type: "crystals" },
|
{ amount: 3000, type: "crystals" },
|
||||||
],
|
],
|
||||||
status: "locked",
|
status: "locked",
|
||||||
zoneId: "astral_void",
|
zoneId: "astral_void",
|
||||||
@@ -360,8 +394,9 @@ export const defaultQuests: Array<Quest> = [
|
|||||||
name: "Between Worlds",
|
name: "Between Worlds",
|
||||||
prerequisiteIds: [ "star_graveyard" ],
|
prerequisiteIds: [ "star_graveyard" ],
|
||||||
rewards: [
|
rewards: [
|
||||||
{ amount: 250_000, type: "essence" },
|
{ amount: 25_000_000_000, type: "gold" },
|
||||||
{ amount: 2000, type: "crystals" },
|
{ amount: 2_000_000, type: "essence" },
|
||||||
|
{ amount: 8000, type: "crystals" },
|
||||||
{ targetId: "divine_champion", type: "adventurer" },
|
{ targetId: "divine_champion", type: "adventurer" },
|
||||||
],
|
],
|
||||||
status: "locked",
|
status: "locked",
|
||||||
@@ -376,9 +411,9 @@ export const defaultQuests: Array<Quest> = [
|
|||||||
name: "The End of All Things",
|
name: "The End of All Things",
|
||||||
prerequisiteIds: [ "between_worlds" ],
|
prerequisiteIds: [ "between_worlds" ],
|
||||||
rewards: [
|
rewards: [
|
||||||
{ amount: 10_000_000_000, type: "gold" },
|
{ amount: 80_000_000_000, type: "gold" },
|
||||||
{ amount: 1_000_000, type: "essence" },
|
{ amount: 5_000_000, type: "essence" },
|
||||||
{ amount: 10_000, type: "crystals" },
|
{ amount: 20_000, type: "crystals" },
|
||||||
],
|
],
|
||||||
status: "locked",
|
status: "locked",
|
||||||
zoneId: "astral_void",
|
zoneId: "astral_void",
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export const defaultTranscendenceUpgrades: Array<TranscendenceUpgrade> = [
|
|||||||
// ── Income multipliers ──────────────────────────────────────────────────────
|
// ── Income multipliers ──────────────────────────────────────────────────────
|
||||||
{
|
{
|
||||||
category: "income",
|
category: "income",
|
||||||
cost: 5,
|
cost: 2,
|
||||||
description:
|
description:
|
||||||
"The echoes of past runs linger, amplifying your guild's income by 25%.",
|
"The echoes of past runs linger, amplifying your guild's income by 25%.",
|
||||||
id: "echo_income_1",
|
id: "echo_income_1",
|
||||||
@@ -20,7 +20,7 @@ export const defaultTranscendenceUpgrades: Array<TranscendenceUpgrade> = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
category: "income",
|
category: "income",
|
||||||
cost: 10,
|
cost: 4,
|
||||||
description:
|
description:
|
||||||
"Your transcendent experience resonates through your guild, boosting income by 50%.",
|
"Your transcendent experience resonates through your guild, boosting income by 50%.",
|
||||||
id: "echo_income_2",
|
id: "echo_income_2",
|
||||||
@@ -29,7 +29,7 @@ export const defaultTranscendenceUpgrades: Array<TranscendenceUpgrade> = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
category: "income",
|
category: "income",
|
||||||
cost: 20,
|
cost: 8,
|
||||||
description:
|
description:
|
||||||
"The harmony of multiple timelines surges through your guild, doubling its income.",
|
"The harmony of multiple timelines surges through your guild, doubling its income.",
|
||||||
id: "echo_income_3",
|
id: "echo_income_3",
|
||||||
@@ -38,7 +38,7 @@ export const defaultTranscendenceUpgrades: Array<TranscendenceUpgrade> = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
category: "income",
|
category: "income",
|
||||||
cost: 40,
|
cost: 16,
|
||||||
description:
|
description:
|
||||||
"Ethereal energy overflows from your transcendence, tripling your guild's income.",
|
"Ethereal energy overflows from your transcendence, tripling your guild's income.",
|
||||||
id: "echo_income_4",
|
id: "echo_income_4",
|
||||||
@@ -47,7 +47,7 @@ export const defaultTranscendenceUpgrades: Array<TranscendenceUpgrade> = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
category: "income",
|
category: "income",
|
||||||
cost: 80,
|
cost: 32,
|
||||||
description:
|
description:
|
||||||
"The infinite chorus of every run you've ever played amplifies your guild fivefold.",
|
"The infinite chorus of every run you've ever played amplifies your guild fivefold.",
|
||||||
id: "echo_income_5",
|
id: "echo_income_5",
|
||||||
@@ -58,7 +58,7 @@ export const defaultTranscendenceUpgrades: Array<TranscendenceUpgrade> = [
|
|||||||
// ── Combat multipliers ──────────────────────────────────────────────────────
|
// ── Combat multipliers ──────────────────────────────────────────────────────
|
||||||
{
|
{
|
||||||
category: "combat",
|
category: "combat",
|
||||||
cost: 5,
|
cost: 2,
|
||||||
description:
|
description:
|
||||||
"Memories of countless battles harden your adventurers, increasing party DPS by 25%.",
|
"Memories of countless battles harden your adventurers, increasing party DPS by 25%.",
|
||||||
id: "echo_combat_1",
|
id: "echo_combat_1",
|
||||||
@@ -67,7 +67,7 @@ export const defaultTranscendenceUpgrades: Array<TranscendenceUpgrade> = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
category: "combat",
|
category: "combat",
|
||||||
cost: 15,
|
cost: 6,
|
||||||
description:
|
description:
|
||||||
"Veterans of transcendence know how to fight smarter, boosting party DPS by 50%.",
|
"Veterans of transcendence know how to fight smarter, boosting party DPS by 50%.",
|
||||||
id: "echo_combat_2",
|
id: "echo_combat_2",
|
||||||
@@ -76,7 +76,7 @@ export const defaultTranscendenceUpgrades: Array<TranscendenceUpgrade> = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
category: "combat",
|
category: "combat",
|
||||||
cost: 35,
|
cost: 12,
|
||||||
description:
|
description:
|
||||||
"Your warriors carry the strength of every fallen timeline, doubling party DPS.",
|
"Your warriors carry the strength of every fallen timeline, doubling party DPS.",
|
||||||
id: "echo_combat_3",
|
id: "echo_combat_3",
|
||||||
@@ -87,7 +87,7 @@ export const defaultTranscendenceUpgrades: Array<TranscendenceUpgrade> = [
|
|||||||
// ── Prestige threshold reductions ──────────────────────────────────────────
|
// ── Prestige threshold reductions ──────────────────────────────────────────
|
||||||
{
|
{
|
||||||
category: "prestige_threshold",
|
category: "prestige_threshold",
|
||||||
cost: 8,
|
cost: 3,
|
||||||
description:
|
description:
|
||||||
"Experience from past lives shortens the road to prestige — threshold reduced by 10%.",
|
"Experience from past lives shortens the road to prestige — threshold reduced by 10%.",
|
||||||
id: "echo_prestige_threshold_1",
|
id: "echo_prestige_threshold_1",
|
||||||
@@ -96,7 +96,7 @@ export const defaultTranscendenceUpgrades: Array<TranscendenceUpgrade> = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
category: "prestige_threshold",
|
category: "prestige_threshold",
|
||||||
cost: 20,
|
cost: 6,
|
||||||
description:
|
description:
|
||||||
"You've walked this path so many times you know every shortcut — threshold reduced by 20%.",
|
"You've walked this path so many times you know every shortcut — threshold reduced by 20%.",
|
||||||
id: "echo_prestige_threshold_2",
|
id: "echo_prestige_threshold_2",
|
||||||
@@ -107,7 +107,7 @@ export const defaultTranscendenceUpgrades: Array<TranscendenceUpgrade> = [
|
|||||||
// ── Prestige runestone multipliers ─────────────────────────────────────────
|
// ── Prestige runestone multipliers ─────────────────────────────────────────
|
||||||
{
|
{
|
||||||
category: "prestige_runestones",
|
category: "prestige_runestones",
|
||||||
cost: 8,
|
cost: 3,
|
||||||
description:
|
description:
|
||||||
"Transcendent insight attunes you to the runestones, earning 50% more per prestige.",
|
"Transcendent insight attunes you to the runestones, earning 50% more per prestige.",
|
||||||
id: "echo_prestige_runestones_1",
|
id: "echo_prestige_runestones_1",
|
||||||
@@ -116,7 +116,7 @@ export const defaultTranscendenceUpgrades: Array<TranscendenceUpgrade> = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
category: "prestige_runestones",
|
category: "prestige_runestones",
|
||||||
cost: 20,
|
cost: 6,
|
||||||
description:
|
description:
|
||||||
"You have mastered the art of runestone crafting, doubling your prestige runestone yield.",
|
"You have mastered the art of runestone crafting, doubling your prestige runestone yield.",
|
||||||
id: "echo_prestige_runestones_2",
|
id: "echo_prestige_runestones_2",
|
||||||
@@ -127,7 +127,7 @@ export const defaultTranscendenceUpgrades: Array<TranscendenceUpgrade> = [
|
|||||||
// ── Echo meta multipliers ───────────────────────────────────────────────────
|
// ── Echo meta multipliers ───────────────────────────────────────────────────
|
||||||
{
|
{
|
||||||
category: "echo_meta",
|
category: "echo_meta",
|
||||||
cost: 50,
|
cost: 25,
|
||||||
description:
|
description:
|
||||||
"Your transcendence resonates deeper, amplifying future echo yields by 25%.",
|
"Your transcendence resonates deeper, amplifying future echo yields by 25%.",
|
||||||
id: "echo_meta_1",
|
id: "echo_meta_1",
|
||||||
@@ -136,7 +136,7 @@ export const defaultTranscendenceUpgrades: Array<TranscendenceUpgrade> = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
category: "echo_meta",
|
category: "echo_meta",
|
||||||
cost: 150,
|
cost: 75,
|
||||||
description:
|
description:
|
||||||
"Each loop of existence makes the next more powerful — future echo yields +50%.",
|
"Each loop of existence makes the next more powerful — future echo yields +50%.",
|
||||||
id: "echo_meta_2",
|
id: "echo_meta_2",
|
||||||
@@ -145,7 +145,7 @@ export const defaultTranscendenceUpgrades: Array<TranscendenceUpgrade> = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
category: "echo_meta",
|
category: "echo_meta",
|
||||||
cost: 400,
|
cost: 200,
|
||||||
description:
|
description:
|
||||||
"You have mastered the infinite spiral of transcendence, doubling all future echo yields.",
|
"You have mastered the infinite spiral of transcendence, doubling all future echo yields.",
|
||||||
id: "echo_meta_3",
|
id: "echo_meta_3",
|
||||||
|
|||||||
@@ -15,14 +15,21 @@ import type {
|
|||||||
} from "@elysium/types";
|
} from "@elysium/types";
|
||||||
|
|
||||||
const basePrestigeGoldThreshold = 1_000_000;
|
const basePrestigeGoldThreshold = 1_000_000;
|
||||||
const thresholdScaleFactor = 5;
|
|
||||||
const runestonesPerPrestigeLevel = 10;
|
const runestonesPerPrestigeLevel = 10;
|
||||||
const milestoneInterval = 5;
|
const milestoneInterval = 5;
|
||||||
const milestoneRunestonesPerInterval = 25;
|
const milestoneRunestonesPerInterval = 25;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Hard cap on the base runestone yield (before multipliers) to prevent
|
||||||
|
* extreme AFK accumulation from producing game-breaking runestone counts.
|
||||||
|
* With all upgrades (5.625× max) this caps out at ~1,125 per prestige.
|
||||||
|
*/
|
||||||
|
const maxBaseRunestones = 200;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates the gold threshold required for the next prestige.
|
* Calculates the gold threshold required for the next prestige.
|
||||||
* Formula: BASE * SCALE_FACTOR^prestigeCount — each prestige makes the next threshold harder.
|
* Formula: BASE * (count + 1)^2 — polynomial growth that peaks around prestige 8–10
|
||||||
|
* then gets easier as the production multiplier overtakes it.
|
||||||
* @param prestigeCount - The current number of prestiges completed.
|
* @param prestigeCount - The current number of prestiges completed.
|
||||||
* @param thresholdMultiplier - An optional echo-upgrade multiplier applied to the threshold.
|
* @param thresholdMultiplier - An optional echo-upgrade multiplier applied to the threshold.
|
||||||
* @returns The gold amount required to prestige.
|
* @returns The gold amount required to prestige.
|
||||||
@@ -33,7 +40,7 @@ const calculatePrestigeThreshold = (
|
|||||||
): number => {
|
): number => {
|
||||||
return (
|
return (
|
||||||
basePrestigeGoldThreshold
|
basePrestigeGoldThreshold
|
||||||
* Math.pow(thresholdScaleFactor, prestigeCount)
|
* Math.pow(prestigeCount + 1, 2)
|
||||||
* thresholdMultiplier
|
* thresholdMultiplier
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -107,7 +114,9 @@ interface RunestoneParameters {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates how many runestones the player earns from a prestige.
|
* Calculates how many runestones the player earns from a prestige.
|
||||||
* Formula: floor(sqrt(totalGoldEarned / threshold)) * RUNESTONES_PER_PRESTIGE_LEVEL * runestoneMultiplier.
|
* Formula: min(floor(cbrt(totalGoldEarned / threshold)) * RUNESTONES_PER_PRESTIGE_LEVEL, MAX_BASE) * multipliers.
|
||||||
|
* Uses cube root for stronger diminishing returns than sqrt, and caps the base before multipliers
|
||||||
|
* to prevent extended AFK sessions from producing runestone windfalls.
|
||||||
* @param parameters - The parameters for the runestone calculation.
|
* @param parameters - The parameters for the runestone calculation.
|
||||||
* @param parameters.totalGoldEarned - The total gold earned in the current run.
|
* @param parameters.totalGoldEarned - The total gold earned in the current run.
|
||||||
* @param parameters.prestigeCount - The current prestige count.
|
* @param parameters.prestigeCount - The current prestige count.
|
||||||
@@ -123,9 +132,11 @@ const calculateRunestones = (parameters: RunestoneParameters): number => {
|
|||||||
echoRunestoneMultiplier = 1,
|
echoRunestoneMultiplier = 1,
|
||||||
} = parameters;
|
} = parameters;
|
||||||
const threshold = calculatePrestigeThreshold(prestigeCount);
|
const threshold = calculatePrestigeThreshold(prestigeCount);
|
||||||
const base
|
const base = Math.min(
|
||||||
= Math.floor(Math.sqrt(totalGoldEarned / threshold))
|
Math.floor(Math.cbrt(totalGoldEarned / threshold))
|
||||||
* runestonesPerPrestigeLevel;
|
* runestonesPerPrestigeLevel,
|
||||||
|
maxBaseRunestones,
|
||||||
|
);
|
||||||
const runestoneMult = getCategoryMultiplier(
|
const runestoneMult = getCategoryMultiplier(
|
||||||
purchasedUpgradeIds,
|
purchasedUpgradeIds,
|
||||||
"runestones",
|
"runestones",
|
||||||
@@ -135,14 +146,15 @@ const calculateRunestones = (parameters: RunestoneParameters): number => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates the new prestige production multiplier.
|
* Calculates the new prestige production multiplier.
|
||||||
* Formula: 1.15^prestigeCount — exponential scaling per prestige.
|
* Formula: 1.25^prestigeCount — exponential scaling per prestige that eventually
|
||||||
|
* overtakes the polynomial threshold growth, making late prestiges progressively easier.
|
||||||
* @param prestigeCount - The new prestige count.
|
* @param prestigeCount - The new prestige count.
|
||||||
* @returns The production multiplier for the new prestige level.
|
* @returns The production multiplier for the new prestige level.
|
||||||
*/
|
*/
|
||||||
const calculateProductionMultiplier = (
|
const calculateProductionMultiplier = (
|
||||||
prestigeCount: number,
|
prestigeCount: number,
|
||||||
): number => {
|
): number => {
|
||||||
return Math.pow(1.15, prestigeCount);
|
return Math.pow(1.25, prestigeCount);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ const finalBossId = "the_absolute_one";
|
|||||||
/**
|
/**
|
||||||
* Base constant used in the echo yield formula.
|
* Base constant used in the echo yield formula.
|
||||||
*/
|
*/
|
||||||
const echoFormulaConstant = 853;
|
const echoFormulaConstant = 224;
|
||||||
|
|
||||||
const getCategoryMultiplier = (
|
const getCategoryMultiplier = (
|
||||||
purchasedIds: Array<string>,
|
purchasedIds: Array<string>,
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ describe("transcendence route", () => {
|
|||||||
const res = await post("/buy-upgrade", { upgradeId: "echo_income_1" });
|
const res = await post("/buy-upgrade", { upgradeId: "echo_income_1" });
|
||||||
expect(res.status).toBe(200);
|
expect(res.status).toBe(200);
|
||||||
const body = await res.json() as { echoesRemaining: number; purchasedUpgradeIds: string[] };
|
const body = await res.json() as { echoesRemaining: number; purchasedUpgradeIds: string[] };
|
||||||
expect(body.echoesRemaining).toBe(95); // 100 - 5
|
expect(body.echoesRemaining).toBe(98); // 100 - 2
|
||||||
expect(body.purchasedUpgradeIds).toContain("echo_income_1");
|
expect(body.purchasedUpgradeIds).toContain("echo_income_1");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -55,15 +55,18 @@ const makeMinimalState = (overrides: Partial<GameState> = {}): GameState =>
|
|||||||
|
|
||||||
describe("calculatePrestigeThreshold", () => {
|
describe("calculatePrestigeThreshold", () => {
|
||||||
it("returns base threshold at count 0", () => {
|
it("returns base threshold at count 0", () => {
|
||||||
|
// base × (0+1)^2 = 1_000_000 × 1 = 1_000_000
|
||||||
expect(calculatePrestigeThreshold(0)).toBe(1_000_000);
|
expect(calculatePrestigeThreshold(0)).toBe(1_000_000);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("returns 5× at count 1", () => {
|
it("returns 4× base at count 1", () => {
|
||||||
expect(calculatePrestigeThreshold(1)).toBe(5_000_000);
|
// base × (1+1)^2 = 1_000_000 × 4 = 4_000_000
|
||||||
|
expect(calculatePrestigeThreshold(1)).toBe(4_000_000);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("returns 25× at count 2", () => {
|
it("returns 9× base at count 2", () => {
|
||||||
expect(calculatePrestigeThreshold(2)).toBe(25_000_000);
|
// base × (2+1)^2 = 1_000_000 × 9 = 9_000_000
|
||||||
|
expect(calculatePrestigeThreshold(2)).toBe(9_000_000);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("applies threshold multiplier correctly", () => {
|
it("applies threshold multiplier correctly", () => {
|
||||||
@@ -99,21 +102,27 @@ describe("isEligibleForPrestige", () => {
|
|||||||
|
|
||||||
describe("calculateRunestones", () => {
|
describe("calculateRunestones", () => {
|
||||||
it("calculates basic runestones formula", () => {
|
it("calculates basic runestones formula", () => {
|
||||||
// floor(sqrt(4_000_000 / 1_000_000)) × 10 = floor(2) × 10 = 20
|
// floor(cbrt(4_000_000 / 1_000_000)) × 10 = floor(cbrt(4)) × 10 = 1 × 10 = 10
|
||||||
const result = calculateRunestones({ totalGoldEarned: 4_000_000, prestigeCount: 0, purchasedUpgradeIds: [] });
|
const result = calculateRunestones({ totalGoldEarned: 4_000_000, prestigeCount: 0, purchasedUpgradeIds: [] });
|
||||||
expect(result).toBe(20);
|
expect(result).toBe(10);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("applies echo runestone multiplier", () => {
|
it("applies echo runestone multiplier", () => {
|
||||||
// floor(sqrt(4) × 10) = 20; × 2 = 40
|
// floor(cbrt(4)) × 10 = 10; × 2 = 20
|
||||||
const result = calculateRunestones({ totalGoldEarned: 4_000_000, prestigeCount: 0, purchasedUpgradeIds: [], echoRunestoneMultiplier: 2 });
|
const result = calculateRunestones({ totalGoldEarned: 4_000_000, prestigeCount: 0, purchasedUpgradeIds: [], echoRunestoneMultiplier: 2 });
|
||||||
expect(result).toBe(40);
|
expect(result).toBe(20);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("applies purchased runestone upgrade multiplier", () => {
|
it("applies purchased runestone upgrade multiplier", () => {
|
||||||
// With "runestones_1" purchased (multiplier 1.25): floor(20 × 1.25) = 25
|
// With "runestone_gain_1" purchased (multiplier 1.25): floor(10 × 1.25) = 12
|
||||||
const result = calculateRunestones({ totalGoldEarned: 4_000_000, prestigeCount: 0, purchasedUpgradeIds: ["runestone_gain_1"] });
|
const result = calculateRunestones({ totalGoldEarned: 4_000_000, prestigeCount: 0, purchasedUpgradeIds: ["runestone_gain_1"] });
|
||||||
expect(result).toBeGreaterThan(20);
|
expect(result).toBe(12);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("caps base runestones before multipliers", () => {
|
||||||
|
// cbrt(9_261_000_000 / 1_000_000) = cbrt(9261) = 21 → 21 × 10 = 210, capped at 200
|
||||||
|
const result = calculateRunestones({ totalGoldEarned: 9_261_000_000, prestigeCount: 0, purchasedUpgradeIds: [] });
|
||||||
|
expect(result).toBe(200);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -122,12 +131,12 @@ describe("calculateProductionMultiplier", () => {
|
|||||||
expect(calculateProductionMultiplier(0)).toBe(1);
|
expect(calculateProductionMultiplier(0)).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("returns 1.15 at count 1", () => {
|
it("returns 1.25 at count 1", () => {
|
||||||
expect(calculateProductionMultiplier(1)).toBeCloseTo(1.15);
|
expect(calculateProductionMultiplier(1)).toBeCloseTo(1.25);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("scales exponentially", () => {
|
it("scales exponentially", () => {
|
||||||
expect(calculateProductionMultiplier(10)).toBeCloseTo(Math.pow(1.15, 10));
|
expect(calculateProductionMultiplier(10)).toBeCloseTo(Math.pow(1.25, 10));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -97,20 +97,21 @@ describe("isEligibleForTranscendence", () => {
|
|||||||
|
|
||||||
describe("calculateEchoes", () => {
|
describe("calculateEchoes", () => {
|
||||||
it("handles prestige count of 0 by treating it as 1", () => {
|
it("handles prestige count of 0 by treating it as 1", () => {
|
||||||
// safeCount = max(0, 1) = 1; floor(853 / sqrt(1)) = 853
|
// safeCount = max(0, 1) = 1; floor(224 / sqrt(1)) = 224
|
||||||
expect(calculateEchoes(0, 1)).toBe(853);
|
expect(calculateEchoes(0, 1)).toBe(224);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("calculates echoes at count 1", () => {
|
it("calculates echoes at count 1", () => {
|
||||||
expect(calculateEchoes(1, 1)).toBe(853);
|
// floor(224 / sqrt(1)) = 224
|
||||||
|
expect(calculateEchoes(1, 1)).toBe(224);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("decreases echoes with higher prestige count", () => {
|
it("decreases echoes with higher prestige count", () => {
|
||||||
const echoesAt1 = calculateEchoes(1, 1);
|
const echoesAt1 = calculateEchoes(1, 1);
|
||||||
const echoesAt4 = calculateEchoes(4, 1);
|
const echoesAt4 = calculateEchoes(4, 1);
|
||||||
expect(echoesAt4).toBeLessThan(echoesAt1);
|
expect(echoesAt4).toBeLessThan(echoesAt1);
|
||||||
// floor(853 / sqrt(4)) = floor(853 / 2) = 426
|
// floor(224 / sqrt(4)) = floor(224 / 2) = 112
|
||||||
expect(echoesAt4).toBe(426);
|
expect(echoesAt4).toBe(112);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("applies echoMetaMultiplier", () => {
|
it("applies echoMetaMultiplier", () => {
|
||||||
@@ -118,6 +119,11 @@ describe("calculateEchoes", () => {
|
|||||||
const withMult = calculateEchoes(1, 2);
|
const withMult = calculateEchoes(1, 2);
|
||||||
expect(withMult).toBe(base * 2);
|
expect(withMult).toBe(base * 2);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("returns 50 echoes at the target prestige 20", () => {
|
||||||
|
// floor(224 / sqrt(20)) = floor(224 / 4.472) = floor(50.09) = 50
|
||||||
|
expect(calculateEchoes(20, 1)).toBe(50);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("buildPostTranscendenceState", () => {
|
describe("buildPostTranscendenceState", () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user