2 Commits

Author SHA1 Message Date
hikari 7b81f6cb33 fix: resolve auto-boss signature mismatch, expose full CP, cap auto-buy, show unlock hints
Security Scan and Upload / Security & DefectDojo Upload (pull_request) Successful in 1m6s
CI / Lint, Build & Test (pull_request) Failing after 1m9s
Closes #148: clear stale signature after each boss fight so subsequent
auto-boss pre-saves don't send a mismatched HMAC.

Closes #151: auto-buy skips non-max-tier adventurers once they reach 100,
keeping gold flowing to the highest-unlocked tier.

Closes #152: introduce computePartyCombatPower() in tick.ts mirroring the
server-side formula (global upgrades, prestige, equipment, set bonuses,
echo, crafted, companion). Resource bar, auto-quest gate, and boss panel
all now use the same multiplier-accurate value.

Closes #146: tick engine auto-unlocks adventurer-specific upgrades when
their adventurer is first recruited; upgrade panel shows a recruit hint
for locked entries with no boss/quest source.
2026-03-25 16:38:42 -07:00
hikari ad4fcc2811 fix: resolve sync count inflation, add essence/s display, sort auto-buy by level
Closes #147: patch functions now detect actual changes before incrementing
the patched counter, preventing inflated sync reports.

Closes #149: computeEssencePerSecond exported from tick engine and shown
in the resource bar dropdown alongside Gold/s.

Closes #150: auto-buy now sorts adventurers by level descending for
semantic clarity, ensuring highest-tier units are purchased first.
2026-03-25 16:16:21 -07:00
14 changed files with 162 additions and 382 deletions
+6 -6
View File
@@ -26,7 +26,7 @@ export const defaultAdventurers: Array<Adventurer> = [
combatPower: 3, combatPower: 3,
count: 0, count: 0,
essencePerSecond: 0, essencePerSecond: 0,
goldPerSecond: 0.7, goldPerSecond: 0.5,
id: "militia", id: "militia",
level: 2, level: 2,
name: "Militia", name: "Militia",
@@ -129,7 +129,7 @@ export const defaultAdventurers: Array<Adventurer> = [
unlocked: false, unlocked: false,
}, },
{ {
baseCost: 2_850_000_000, baseCost: 2_600_000_000,
class: "mage", class: "mage",
combatPower: 13_000, combatPower: 13_000,
count: 0, count: 0,
@@ -141,7 +141,7 @@ export const defaultAdventurers: Array<Adventurer> = [
unlocked: false, unlocked: false,
}, },
{ {
baseCost: 13_500_000_000, baseCost: 11_000_000_000,
class: "rogue", class: "rogue",
combatPower: 28_000, combatPower: 28_000,
count: 0, count: 0,
@@ -153,7 +153,7 @@ export const defaultAdventurers: Array<Adventurer> = [
unlocked: false, unlocked: false,
}, },
{ {
baseCost: 64_000_000_000, baseCost: 47_000_000_000,
class: "paladin", class: "paladin",
combatPower: 60_000, combatPower: 60_000,
count: 0, count: 0,
@@ -165,7 +165,7 @@ export const defaultAdventurers: Array<Adventurer> = [
unlocked: false, unlocked: false,
}, },
{ {
baseCost: 300_000_000_000, baseCost: 200_000_000_000,
class: "rogue", class: "rogue",
combatPower: 130_000, combatPower: 130_000,
count: 0, count: 0,
@@ -177,7 +177,7 @@ export const defaultAdventurers: Array<Adventurer> = [
unlocked: false, unlocked: false,
}, },
{ {
baseCost: 1_800_000_000_000, baseCost: 1_400_000_000_000,
class: "paladin", class: "paladin",
combatPower: 400_000, combatPower: 400_000,
count: 0, count: 0,
+67 -67
View File
@@ -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: [ "dark_templar_1" ], upgradeRewards: [],
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: 1, prestigeRequirement: 6,
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: 2, prestigeRequirement: 7,
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: 2, prestigeRequirement: 8,
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: 2, prestigeRequirement: 9,
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: 2, prestigeRequirement: 10,
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: 2, prestigeRequirement: 9,
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: 2, prestigeRequirement: 10,
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: 2, prestigeRequirement: 11,
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: 3, prestigeRequirement: 12,
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: 3, prestigeRequirement: 13,
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: 3, prestigeRequirement: 12,
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: 3, prestigeRequirement: 13,
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: 3, prestigeRequirement: 14,
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: 3, prestigeRequirement: 15,
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: 4, prestigeRequirement: 16,
status: "locked", status: "locked",
upgradeRewards: [], upgradeRewards: [],
zoneId: "infernal_court", zoneId: "infernal_court",
@@ -626,9 +626,9 @@ export const defaultBosses: Array<Boss> = [
id: "prism_golem", id: "prism_golem",
maxHp: 2e16, maxHp: 2e16,
name: "The Prism Golem", name: "The Prism Golem",
prestigeRequirement: 3, prestigeRequirement: 15,
status: "locked", status: "locked",
upgradeRewards: [ "crystal_sage_1" ], 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: 4, prestigeRequirement: 16,
status: "locked", status: "locked",
upgradeRewards: [ "void_ascendancy" ], upgradeRewards: [ "void_ascendancy" ],
zoneId: "crystalline_spire", zoneId: "crystalline_spire",
@@ -662,9 +662,9 @@ export const defaultBosses: Array<Boss> = [
id: "the_faceted", id: "the_faceted",
maxHp: 3e17, maxHp: 3e17,
name: "The Faceted", name: "The Faceted",
prestigeRequirement: 4, prestigeRequirement: 17,
status: "locked", status: "locked",
upgradeRewards: [ "void_sentinel_1" ], upgradeRewards: [],
zoneId: "crystalline_spire", zoneId: "crystalline_spire",
}, },
{ {
@@ -680,9 +680,9 @@ export const defaultBosses: Array<Boss> = [
id: "diamond_colossus", id: "diamond_colossus",
maxHp: 1e18, maxHp: 1e18,
name: "The Diamond Colossus", name: "The Diamond Colossus",
prestigeRequirement: 4, prestigeRequirement: 18,
status: "locked", status: "locked",
upgradeRewards: [ "eternal_champion_1" ], upgradeRewards: [],
zoneId: "crystalline_spire", zoneId: "crystalline_spire",
}, },
{ {
@@ -698,9 +698,9 @@ export const defaultBosses: Array<Boss> = [
id: "crystal_sovereign", id: "crystal_sovereign",
maxHp: 4e18, maxHp: 4e18,
name: "The Crystal Sovereign", name: "The Crystal Sovereign",
prestigeRequirement: 4, prestigeRequirement: 19,
status: "locked", status: "locked",
upgradeRewards: [ "cosmos_knight_1" ], upgradeRewards: [],
zoneId: "crystalline_spire", zoneId: "crystalline_spire",
}, },
// ── Void Sanctum ────────────────────────────────────────────────────────── // ── Void Sanctum ──────────────────────────────────────────────────────────
@@ -717,9 +717,9 @@ export const defaultBosses: Array<Boss> = [
id: "void_herald", id: "void_herald",
maxHp: 1e19, maxHp: 1e19,
name: "The Void Herald", name: "The Void Herald",
prestigeRequirement: 4, prestigeRequirement: 18,
status: "locked", status: "locked",
upgradeRewards: [ "seraph_knight_1" ], 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: 4, prestigeRequirement: 19,
status: "locked", status: "locked",
upgradeRewards: [ "divine_harmony" ], upgradeRewards: [ "divine_harmony" ],
zoneId: "void_sanctum", zoneId: "void_sanctum",
@@ -753,9 +753,9 @@ export const defaultBosses: Array<Boss> = [
id: "the_unmaker", id: "the_unmaker",
maxHp: 2e20, maxHp: 2e20,
name: "The Unmaker", name: "The Unmaker",
prestigeRequirement: 5, prestigeRequirement: 20,
status: "locked", status: "locked",
upgradeRewards: [ "abyss_diver_1" ], 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: 5, prestigeRequirement: 21,
status: "locked", status: "locked",
upgradeRewards: [], upgradeRewards: [],
zoneId: "void_sanctum", zoneId: "void_sanctum",
@@ -789,9 +789,9 @@ export const defaultBosses: Array<Boss> = [
id: "void_emperor", id: "void_emperor",
maxHp: 3e21, maxHp: 3e21,
name: "The Void Emperor", name: "The Void Emperor",
prestigeRequirement: 5, prestigeRequirement: 22,
status: "locked", status: "locked",
upgradeRewards: [ "infernal_warden_1" ], upgradeRewards: [],
zoneId: "void_sanctum", zoneId: "void_sanctum",
}, },
// ── Eternal Throne ──────────────────────────────────────────────────────── // ── Eternal Throne ────────────────────────────────────────────────────────
@@ -808,9 +808,9 @@ export const defaultBosses: Array<Boss> = [
id: "throne_warden", id: "throne_warden",
maxHp: 1e22, maxHp: 1e22,
name: "The Throne Warden", name: "The Throne Warden",
prestigeRequirement: 5, prestigeRequirement: 21,
status: "locked", status: "locked",
upgradeRewards: [ "infinity_ranger_1" ], 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: 5, prestigeRequirement: 22,
status: "locked", status: "locked",
upgradeRewards: [ "infernal_fury" ], upgradeRewards: [ "infernal_fury" ],
zoneId: "eternal_throne", zoneId: "eternal_throne",
@@ -844,9 +844,9 @@ export const defaultBosses: Array<Boss> = [
id: "the_undying", id: "the_undying",
maxHp: 2e23, maxHp: 2e23,
name: "The Undying", name: "The Undying",
prestigeRequirement: 5, prestigeRequirement: 23,
status: "locked", status: "locked",
upgradeRewards: [ "reality_warden_1" ], 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: 5, prestigeRequirement: 24,
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: 6, prestigeRequirement: 25,
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: 6, prestigeRequirement: 26,
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: 6, prestigeRequirement: 27,
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: 7, prestigeRequirement: 29,
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: 7, prestigeRequirement: 31,
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: 8, prestigeRequirement: 33,
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: 8, prestigeRequirement: 35,
status: "locked", status: "locked",
upgradeRewards: [ "oblivion_paladin_1" ], upgradeRewards: [],
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: 8, prestigeRequirement: 37,
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: 9, prestigeRequirement: 39,
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: 9, prestigeRequirement: 41,
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: 10, prestigeRequirement: 44,
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: 11, prestigeRequirement: 47,
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: 11, prestigeRequirement: 49,
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: 12, prestigeRequirement: 51,
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: 12, prestigeRequirement: 54,
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: 13, prestigeRequirement: 57,
status: "locked", status: "locked",
upgradeRewards: [ "transcendent_rogue_1" ], upgradeRewards: [],
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: 13, prestigeRequirement: 59,
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: 14, prestigeRequirement: 61,
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: 15, prestigeRequirement: 65,
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: 16, prestigeRequirement: 69,
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: 17, prestigeRequirement: 74,
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: 17, prestigeRequirement: 76,
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: 18, prestigeRequirement: 79,
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: 19, prestigeRequirement: 83,
status: "locked", status: "locked",
upgradeRewards: [ "omniversal_champion_1" ], upgradeRewards: [],
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: 20, prestigeRequirement: 88,
status: "locked", status: "locked",
upgradeRewards: [], upgradeRewards: [],
zoneId: "the_absolute", zoneId: "the_absolute",
+6 -6
View File
@@ -269,7 +269,7 @@ export const defaultEquipment: Array<Equipment> = [
type: "trinket", type: "trinket",
}, },
{ {
bonus: { clickMultiplier: 1.65, goldMultiplier: 1.2 }, bonus: { clickMultiplier: 1.55, goldMultiplier: 1.1 },
description: description:
"A fragment of the Mud Kraken's crystallised essence. Focuses raw power into devastating strikes.", "A fragment of the Mud Kraken's crystallised essence. Focuses raw power into devastating strikes.",
equipped: false, equipped: false,
@@ -305,9 +305,9 @@ export const defaultEquipment: Array<Equipment> = [
type: "trinket", type: "trinket",
}, },
{ {
bonus: { clickMultiplier: 2.5, goldMultiplier: 1.4 }, bonus: { clickMultiplier: 2.25, goldMultiplier: 1.25 },
description: description:
"The legendary stone that transmutes effort into wealth — every action fills the coffers.", "The legendary stone that grants mastery over gold and combat alike.",
equipped: false, equipped: false,
id: "philosophers_stone", id: "philosophers_stone",
name: "Philosopher's Stone", name: "Philosopher's Stone",
@@ -697,7 +697,7 @@ export const defaultEquipment: Array<Equipment> = [
}, },
// ── Purchasable endgame sinks ───────────────────────────────────────────── // ── Purchasable endgame sinks ─────────────────────────────────────────────
{ {
bonus: { clickMultiplier: 4.25 }, bonus: { clickMultiplier: 3 },
cost: { crystals: 0, essence: 20_000_000, gold: 0 }, cost: { crystals: 0, essence: 20_000_000, gold: 0 },
description: description:
"A lens of compressed celestial light that sharpens every strike with divine precision.", "A lens of compressed celestial light that sharpens every strike with divine precision.",
@@ -721,7 +721,7 @@ export const defaultEquipment: Array<Equipment> = [
type: "armour", type: "armour",
}, },
{ {
bonus: { combatMultiplier: 10.5 }, bonus: { combatMultiplier: 7 },
cost: { crystals: 0, essence: 100_000_000, gold: 0 }, cost: { crystals: 0, essence: 100_000_000, gold: 0 },
description: description:
"A weapon that channels void energy — the absence of resistance makes every strike devastating.", "A weapon that channels void energy — the absence of resistance makes every strike devastating.",
@@ -745,7 +745,7 @@ export const defaultEquipment: Array<Equipment> = [
type: "trinket", type: "trinket",
}, },
{ {
bonus: { goldMultiplier: 7.5 }, bonus: { goldMultiplier: 4.75 },
cost: { crystals: 20_000_000, essence: 0, gold: 0 }, cost: { crystals: 20_000_000, essence: 0, gold: 0 },
description: description:
"Armour structured around a crystalline lattice of optimal income calculations. Every gold piece finds you faster.", "Armour structured around a crystalline lattice of optimal income calculations. Every gold piece finds you faster.",
+16 -51
View File
@@ -157,21 +157,6 @@ 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:
@@ -179,7 +164,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: [ "glacier_tomb" ], prerequisiteIds: [ "frozen_wastes" ],
rewards: [ rewards: [
{ amount: 5000, type: "essence" }, { amount: 5000, type: "essence" },
{ amount: 200, type: "crystals" }, { amount: 200, type: "crystals" },
@@ -203,22 +188,6 @@ 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,
@@ -229,8 +198,7 @@ export const defaultQuests: Array<Quest> = [
name: "The Shadow Mere", name: "The Shadow Mere",
prerequisiteIds: [], prerequisiteIds: [],
rewards: [ rewards: [
{ amount: 5_000_000, type: "gold" }, { amount: 150, type: "essence" },
{ amount: 5000, type: "essence" },
], ],
status: "locked", status: "locked",
zoneId: "shadow_marshes", zoneId: "shadow_marshes",
@@ -244,8 +212,7 @@ export const defaultQuests: Array<Quest> = [
name: "The Witch Coven", name: "The Witch Coven",
prerequisiteIds: [ "shadow_mere" ], prerequisiteIds: [ "shadow_mere" ],
rewards: [ rewards: [
{ amount: 20_000_000, type: "gold" }, { amount: 500, type: "essence" },
{ amount: 20_000, type: "essence" },
{ targetId: "shadow_assassin", type: "adventurer" }, { targetId: "shadow_assassin", type: "adventurer" },
], ],
status: "locked", status: "locked",
@@ -278,9 +245,9 @@ export const defaultQuests: Array<Quest> = [
name: "The Plague Ruins", name: "The Plague Ruins",
prerequisiteIds: [ "sunken_temple" ], prerequisiteIds: [ "sunken_temple" ],
rewards: [ rewards: [
{ amount: 100_000_000, type: "gold" }, { amount: 8_000_000, type: "gold" },
{ amount: 30_000, type: "essence" }, { amount: 2000, type: "essence" },
{ amount: 500, type: "crystals" }, { amount: 150, type: "crystals" },
{ targetId: "dark_templar", type: "adventurer" }, { targetId: "dark_templar", type: "adventurer" },
], ],
status: "locked", status: "locked",
@@ -362,9 +329,8 @@ export const defaultQuests: Array<Quest> = [
name: "Void Rift", name: "Void Rift",
prerequisiteIds: [], prerequisiteIds: [],
rewards: [ rewards: [
{ amount: 2_000_000_000, type: "gold" }, { amount: 500, type: "crystals" },
{ amount: 300_000, type: "essence" }, { amount: 5000, type: "essence" },
{ amount: 1000, type: "crystals" },
], ],
status: "locked", status: "locked",
zoneId: "astral_void", zoneId: "astral_void",
@@ -378,9 +344,9 @@ export const defaultQuests: Array<Quest> = [
name: "The Star Graveyard", name: "The Star Graveyard",
prerequisiteIds: [ "void_rift" ], prerequisiteIds: [ "void_rift" ],
rewards: [ rewards: [
{ amount: 8_000_000_000, type: "gold" }, { amount: 1_000_000_000, type: "gold" },
{ amount: 800_000, type: "essence" }, { amount: 100_000, type: "essence" },
{ amount: 3000, type: "crystals" }, { amount: 1000, type: "crystals" },
], ],
status: "locked", status: "locked",
zoneId: "astral_void", zoneId: "astral_void",
@@ -394,9 +360,8 @@ export const defaultQuests: Array<Quest> = [
name: "Between Worlds", name: "Between Worlds",
prerequisiteIds: [ "star_graveyard" ], prerequisiteIds: [ "star_graveyard" ],
rewards: [ rewards: [
{ amount: 25_000_000_000, type: "gold" }, { amount: 250_000, type: "essence" },
{ amount: 2_000_000, type: "essence" }, { amount: 2000, type: "crystals" },
{ amount: 8000, type: "crystals" },
{ targetId: "divine_champion", type: "adventurer" }, { targetId: "divine_champion", type: "adventurer" },
], ],
status: "locked", status: "locked",
@@ -411,9 +376,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: 80_000_000_000, type: "gold" }, { amount: 10_000_000_000, type: "gold" },
{ amount: 5_000_000, type: "essence" }, { amount: 1_000_000, type: "essence" },
{ amount: 20_000, type: "crystals" }, { amount: 10_000, type: "crystals" },
], ],
status: "locked", status: "locked",
zoneId: "astral_void", zoneId: "astral_void",
+3 -28
View File
@@ -23,7 +23,7 @@ export const defaultRecipes: Array<CraftingRecipe> = [
zoneId: "verdant_vale", zoneId: "verdant_vale",
}, },
{ {
bonus: { type: "combat_power", value: 1.12 }, bonus: { type: "combat_power", value: 1.08 },
description: description:
"A ward fashioned from bark older than the kingdom. Its presence on the battlefield is not merely physical — something ancient watches through it.", "A ward fashioned from bark older than the kingdom. Its presence on the battlefield is not merely physical — something ancient watches through it.",
id: "elder_bark_shield", id: "elder_bark_shield",
@@ -75,7 +75,7 @@ export const defaultRecipes: Array<CraftingRecipe> = [
zoneId: "frozen_peaks", zoneId: "frozen_peaks",
}, },
{ {
bonus: { type: "gold_income", value: 1.15 }, bonus: { type: "gold_income", value: 1.1 },
description: description:
"The void shard set in frost crystal, creating something that should not be possible: a stable window into the spaces between spaces. Profitable beyond what physics suggests.", "The void shard set in frost crystal, creating something that should not be possible: a stable window into the spaces between spaces. Profitable beyond what physics suggests.",
id: "void_fragment_amulet", id: "void_fragment_amulet",
@@ -231,7 +231,7 @@ export const defaultRecipes: Array<CraftingRecipe> = [
zoneId: "infernal_court", zoneId: "infernal_court",
}, },
{ {
bonus: { type: "essence_income", value: 1.2 }, bonus: { type: "essence_income", value: 1.15 },
description: description:
"Soul residue and demon ichor worked into a catalyst that draws the essence from everything around it — gently, without drawing the court's attention. Usually.", "Soul residue and demon ichor worked into a catalyst that draws the essence from everything around it — gently, without drawing the court's attention. Usually.",
id: "soul_bound_catalyst", id: "soul_bound_catalyst",
@@ -492,19 +492,6 @@ export const defaultRecipes: Array<CraftingRecipe> = [
], ],
zoneId: "abyssal_trench", zoneId: "abyssal_trench",
}, },
{
bonus: { type: "click_power", value: 1.38 },
description:
"A primeval relic submerged at the absolute boundary of existence alongside omega crystals and boundary shards — the first and last thing, unified. Every action your guild takes through it is simultaneously the most ancient and most final thing that has ever happened. It does not miss.",
id: "primal_omega_lens",
name: "Primal Omega Lens",
requiredMaterials: [
{ materialId: "primeval_relic", quantity: 2 },
{ materialId: "boundary_shard", quantity: 4 },
{ materialId: "omega_crystal", quantity: 2 },
],
zoneId: "the_absolute",
},
{ {
bonus: { type: "combat_power", value: 1.4 }, bonus: { type: "combat_power", value: 1.4 },
description: description:
@@ -521,18 +508,6 @@ export const defaultRecipes: Array<CraftingRecipe> = [
}, },
// Zone 18: the_absolute // Zone 18: the_absolute
{
bonus: { type: "click_power", value: 1.28 },
description:
"Absolute fragments ground and set in an omega crystal lattice — an instrument of pure finality. Every action your guild takes through it carries the weight of an ending. It does not miss.",
id: "absolute_focus",
name: "Absolute Focus",
requiredMaterials: [
{ materialId: "absolute_fragment", quantity: 8 },
{ materialId: "omega_crystal", quantity: 3 },
],
zoneId: "the_absolute",
},
{ {
bonus: { type: "gold_income", value: 1.3 }, bonus: { type: "gold_income", value: 1.3 },
description: description:
+15 -15
View File
@@ -11,7 +11,7 @@ export const defaultTranscendenceUpgrades: Array<TranscendenceUpgrade> = [
// ── Income multipliers ────────────────────────────────────────────────────── // ── Income multipliers ──────────────────────────────────────────────────────
{ {
category: "income", category: "income",
cost: 2, cost: 5,
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: 4, cost: 10,
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: 8, cost: 20,
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: 16, cost: 40,
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: 32, cost: 80,
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: 2, cost: 5,
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: 6, cost: 15,
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: 12, cost: 35,
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: 3, cost: 8,
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: 6, cost: 20,
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: 3, cost: 8,
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: 6, cost: 20,
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: 25, cost: 50,
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: 75, cost: 150,
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: 200, cost: 400,
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",
+9 -22
View File
@@ -15,21 +15,14 @@ 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 * (count + 1)^2 — polynomial growth that peaks around prestige 8–10 * Formula: BASE * SCALE_FACTOR^prestigeCount — each prestige makes the next threshold harder.
* 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.
@@ -40,7 +33,7 @@ const calculatePrestigeThreshold = (
): number => { ): number => {
return ( return (
basePrestigeGoldThreshold basePrestigeGoldThreshold
* Math.pow(prestigeCount + 1, 2) * Math.pow(thresholdScaleFactor, prestigeCount)
* thresholdMultiplier * thresholdMultiplier
); );
}; };
@@ -114,9 +107,7 @@ interface RunestoneParameters {
/** /**
* Calculates how many runestones the player earns from a prestige. * Calculates how many runestones the player earns from a prestige.
* Formula: min(floor(cbrt(totalGoldEarned / threshold)) * RUNESTONES_PER_PRESTIGE_LEVEL, MAX_BASE) * multipliers. * Formula: floor(sqrt(totalGoldEarned / threshold)) * RUNESTONES_PER_PRESTIGE_LEVEL * runestoneMultiplier.
* 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.
@@ -132,11 +123,9 @@ const calculateRunestones = (parameters: RunestoneParameters): number => {
echoRunestoneMultiplier = 1, echoRunestoneMultiplier = 1,
} = parameters; } = parameters;
const threshold = calculatePrestigeThreshold(prestigeCount); const threshold = calculatePrestigeThreshold(prestigeCount);
const base = Math.min( const base
Math.floor(Math.cbrt(totalGoldEarned / threshold)) = Math.floor(Math.sqrt(totalGoldEarned / threshold))
* runestonesPerPrestigeLevel, * runestonesPerPrestigeLevel;
maxBaseRunestones,
);
const runestoneMult = getCategoryMultiplier( const runestoneMult = getCategoryMultiplier(
purchasedUpgradeIds, purchasedUpgradeIds,
"runestones", "runestones",
@@ -146,15 +135,14 @@ const calculateRunestones = (parameters: RunestoneParameters): number => {
/** /**
* Calculates the new prestige production multiplier. * Calculates the new prestige production multiplier.
* Formula: 1.25^prestigeCount — exponential scaling per prestige that eventually * Formula: 1.15^prestigeCount — exponential scaling per prestige.
* 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.25, prestigeCount); return Math.pow(1.15, prestigeCount);
}; };
/** /**
@@ -263,7 +251,6 @@ const buildPostPrestigeState = (
* Preserve automation preferences across prestige — the player explicitly * Preserve automation preferences across prestige — the player explicitly
* opted into these settings and would not expect them to silently reset. * opted into these settings and would not expect them to silently reset.
*/ */
autoAdventurer: currentState.autoAdventurer ?? false,
autoBoss: currentState.autoBoss ?? false, autoBoss: currentState.autoBoss ?? false,
autoQuest: currentState.autoQuest ?? false, autoQuest: currentState.autoQuest ?? false,
+1 -1
View File
@@ -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 = 224; const echoFormulaConstant = 853;
const getCategoryMultiplier = ( const getCategoryMultiplier = (
purchasedIds: Array<string>, purchasedIds: Array<string>,
+1 -1
View File
@@ -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(98); // 100 - 2 expect(body.echoesRemaining).toBe(95); // 100 - 5
expect(body.purchasedUpgradeIds).toContain("echo_income_1"); expect(body.purchasedUpgradeIds).toContain("echo_income_1");
}); });
+16 -25
View File
@@ -55,18 +55,15 @@ 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 4Ă— base at count 1", () => { it("returns 5Ă— at count 1", () => {
// base Ă— (1+1)^2 = 1_000_000 Ă— 4 = 4_000_000 expect(calculatePrestigeThreshold(1)).toBe(5_000_000);
expect(calculatePrestigeThreshold(1)).toBe(4_000_000);
}); });
it("returns 9Ă— base at count 2", () => { it("returns 25Ă— at count 2", () => {
// base Ă— (2+1)^2 = 1_000_000 Ă— 9 = 9_000_000 expect(calculatePrestigeThreshold(2)).toBe(25_000_000);
expect(calculatePrestigeThreshold(2)).toBe(9_000_000);
}); });
it("applies threshold multiplier correctly", () => { it("applies threshold multiplier correctly", () => {
@@ -102,27 +99,21 @@ describe("isEligibleForPrestige", () => {
describe("calculateRunestones", () => { describe("calculateRunestones", () => {
it("calculates basic runestones formula", () => { it("calculates basic runestones formula", () => {
// floor(cbrt(4_000_000 / 1_000_000)) Ă— 10 = floor(cbrt(4)) Ă— 10 = 1 Ă— 10 = 10 // floor(sqrt(4_000_000 / 1_000_000)) Ă— 10 = floor(2) Ă— 10 = 20
const result = calculateRunestones({ totalGoldEarned: 4_000_000, prestigeCount: 0, purchasedUpgradeIds: [] }); const result = calculateRunestones({ totalGoldEarned: 4_000_000, prestigeCount: 0, purchasedUpgradeIds: [] });
expect(result).toBe(10);
});
it("applies echo runestone multiplier", () => {
// floor(cbrt(4)) Ă— 10 = 10; Ă— 2 = 20
const result = calculateRunestones({ totalGoldEarned: 4_000_000, prestigeCount: 0, purchasedUpgradeIds: [], echoRunestoneMultiplier: 2 });
expect(result).toBe(20); expect(result).toBe(20);
}); });
it("applies purchased runestone upgrade multiplier", () => { it("applies echo runestone multiplier", () => {
// With "runestone_gain_1" purchased (multiplier 1.25): floor(10 Ă— 1.25) = 12 // floor(sqrt(4) Ă— 10) = 20; Ă— 2 = 40
const result = calculateRunestones({ totalGoldEarned: 4_000_000, prestigeCount: 0, purchasedUpgradeIds: ["runestone_gain_1"] }); const result = calculateRunestones({ totalGoldEarned: 4_000_000, prestigeCount: 0, purchasedUpgradeIds: [], echoRunestoneMultiplier: 2 });
expect(result).toBe(12); expect(result).toBe(40);
}); });
it("caps base runestones before multipliers", () => { it("applies purchased runestone upgrade multiplier", () => {
// cbrt(9_261_000_000 / 1_000_000) = cbrt(9261) = 21 → 21 × 10 = 210, capped at 200 // With "runestones_1" purchased (multiplier 1.25): floor(20 × 1.25) = 25
const result = calculateRunestones({ totalGoldEarned: 9_261_000_000, prestigeCount: 0, purchasedUpgradeIds: [] }); const result = calculateRunestones({ totalGoldEarned: 4_000_000, prestigeCount: 0, purchasedUpgradeIds: ["runestone_gain_1"] });
expect(result).toBe(200); expect(result).toBeGreaterThan(20);
}); });
}); });
@@ -131,12 +122,12 @@ describe("calculateProductionMultiplier", () => {
expect(calculateProductionMultiplier(0)).toBe(1); expect(calculateProductionMultiplier(0)).toBe(1);
}); });
it("returns 1.25 at count 1", () => { it("returns 1.15 at count 1", () => {
expect(calculateProductionMultiplier(1)).toBeCloseTo(1.25); expect(calculateProductionMultiplier(1)).toBeCloseTo(1.15);
}); });
it("scales exponentially", () => { it("scales exponentially", () => {
expect(calculateProductionMultiplier(10)).toBeCloseTo(Math.pow(1.25, 10)); expect(calculateProductionMultiplier(10)).toBeCloseTo(Math.pow(1.15, 10));
}); });
}); });
+5 -11
View File
@@ -97,21 +97,20 @@ 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(224 / sqrt(1)) = 224 // safeCount = max(0, 1) = 1; floor(853 / sqrt(1)) = 853
expect(calculateEchoes(0, 1)).toBe(224); expect(calculateEchoes(0, 1)).toBe(853);
}); });
it("calculates echoes at count 1", () => { it("calculates echoes at count 1", () => {
// floor(224 / sqrt(1)) = 224 expect(calculateEchoes(1, 1)).toBe(853);
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(224 / sqrt(4)) = floor(224 / 2) = 112 // floor(853 / sqrt(4)) = floor(853 / 2) = 426
expect(echoesAt4).toBe(112); expect(echoesAt4).toBe(426);
}); });
it("applies echoMetaMultiplier", () => { it("applies echoMetaMultiplier", () => {
@@ -119,11 +118,6 @@ 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", () => {
@@ -9,7 +9,6 @@
/* eslint-disable complexity -- Complex component with many render paths */ /* eslint-disable complexity -- Complex component with many render paths */
import { type JSX, useState } from "react"; import { type JSX, useState } from "react";
import { useGame } from "../../context/gameContext.js"; import { useGame } from "../../context/gameContext.js";
import { computeEffectiveAdventurerStats } from "../../engine/tick.js";
import { cdnImage } from "../../utils/cdn.js"; import { cdnImage } from "../../utils/cdn.js";
import { LockToggle } from "../ui/lockToggle.js"; import { LockToggle } from "../ui/lockToggle.js";
import type { Adventurer } from "@elysium/types"; import type { Adventurer } from "@elysium/types";
@@ -77,19 +76,12 @@ const computeMaxAffordable = (adventurer: Adventurer, gold: number): number => {
return quantity; return quantity;
}; };
interface EffectiveAdventurerStats {
readonly combatPower: number;
readonly essencePerSecond: number;
readonly goldPerSecond: number;
}
interface AdventurerCardProperties { interface AdventurerCardProperties {
readonly adventurer: Adventurer; readonly adventurer: Adventurer;
readonly currentGold: number; readonly currentGold: number;
readonly batchSize: BatchSize; readonly batchSize: BatchSize;
readonly unlockHint: string | undefined; readonly unlockHint: string | undefined;
readonly formatNumber: (n: number)=> string; readonly formatNumber: (n: number)=> string;
readonly effectiveStats: EffectiveAdventurerStats;
} }
/** /**
@@ -100,7 +92,6 @@ interface AdventurerCardProperties {
* @param props.batchSize - The selected batch size. * @param props.batchSize - The selected batch size.
* @param props.unlockHint - Optional quest name that unlocks this adventurer. * @param props.unlockHint - Optional quest name that unlocks this adventurer.
* @param props.formatNumber - The number formatting utility function. * @param props.formatNumber - The number formatting utility function.
* @param props.effectiveStats - The post-multiplier per-unit stats.
* @returns The JSX element. * @returns The JSX element.
*/ */
const AdventurerCard = ({ const AdventurerCard = ({
@@ -109,7 +100,6 @@ const AdventurerCard = ({
batchSize, batchSize,
unlockHint, unlockHint,
formatNumber, formatNumber,
effectiveStats,
}: AdventurerCardProperties): JSX.Element => { }: AdventurerCardProperties): JSX.Element => {
const { buyAdventurer } = useGame(); const { buyAdventurer } = useGame();
@@ -144,17 +134,17 @@ const AdventurerCard = ({
<div className="adventurer-info"> <div className="adventurer-info">
<h3>{adventurer.name}</h3> <h3>{adventurer.name}</h3>
<p> <p>
{formatNumber(effectiveStats.goldPerSecond)} {formatNumber(adventurer.goldPerSecond)}
{" gold/s each"} {" gold/s each"}
</p> </p>
{adventurer.essencePerSecond > 0 {adventurer.essencePerSecond > 0
&& <p> && <p>
{formatNumber(effectiveStats.essencePerSecond)} {formatNumber(adventurer.essencePerSecond)}
{" essence/s each"} {" essence/s each"}
</p> </p>
} }
<p> <p>
{formatNumber(effectiveStats.combatPower)} {formatNumber(adventurer.combatPower)}
{" combat power each"} {" combat power each"}
</p> </p>
</div> </div>
@@ -290,10 +280,6 @@ const AdventurerPanel = (): JSX.Element => {
adventurer={adventurer} adventurer={adventurer}
batchSize={batchSize} batchSize={batchSize}
currentGold={state.resources.gold} currentGold={state.resources.gold}
effectiveStats={computeEffectiveAdventurerStats(
state,
adventurer.id,
)}
formatNumber={formatNumber} formatNumber={formatNumber}
key={adventurer.id} key={adventurer.id}
unlockHint={adventurerUnlockHints.get(adventurer.id)} unlockHint={adventurerUnlockHints.get(adventurer.id)}
+7 -6
View File
@@ -11,10 +11,7 @@
/* eslint-disable max-statements -- Many local variables needed for quest state */ /* eslint-disable max-statements -- Many local variables needed for quest state */
import { useState, type JSX } from "react"; import { useState, type JSX } from "react";
import { useGame } from "../../context/gameContext.js"; import { useGame } from "../../context/gameContext.js";
import { import { zoneFailureChance } from "../../engine/tick.js";
computePartyCombatPower,
zoneFailureChance,
} from "../../engine/tick.js";
import { cdnImage } from "../../utils/cdn.js"; import { cdnImage } from "../../utils/cdn.js";
import { LockToggle } from "../ui/lockToggle.js"; import { LockToggle } from "../ui/lockToggle.js";
import { ZoneSelector } from "./zoneSelector.js"; import { ZoneSelector } from "./zoneSelector.js";
@@ -211,7 +208,7 @@ const QuestPanel = (): JSX.Element => {
); );
} }
const { autoQuest, bosses, quests, zones } = state; const { adventurers, autoQuest, bosses, quests, zones } = state;
const activeZone = zones.find((zone) => { const activeZone = zones.find((zone) => {
return zone.id === activeZoneId; return zone.id === activeZoneId;
@@ -229,7 +226,11 @@ const QuestPanel = (): JSX.Element => {
: quests.find((quest) => { : quests.find((quest) => {
return quest.id === activeZone.unlockQuestId; return quest.id === activeZone.unlockQuestId;
}); });
const partyCombatPower = computePartyCombatPower(state); let partyCombatPower = 0;
for (const adventurer of adventurers) {
const contribution = adventurer.combatPower * adventurer.count;
partyCombatPower = partyCombatPower + contribution;
}
const zoneQuests = quests.filter(({ zoneId }) => { const zoneQuests = quests.filter(({ zoneId }) => {
return zoneId === activeZoneId; return zoneId === activeZoneId;
}); });
+1 -120
View File
@@ -243,129 +243,10 @@ export const computeEssencePerSecond = (state: GameState): number => {
return essencePerSecond; return essencePerSecond;
}; };
/**
* Computes the effective per-unit stats for a single adventurer type,
* applying all active multipliers (upgrades, prestige, equipment, echo,
* crafted, companion). The returned values represent what a single
* adventurer of this type currently contributes per second, matching the
* per-unit contribution used by computeGoldPerSecond and
* computeEssencePerSecond.
* @param state - The current game state.
* @param adventurerId - The ID of the adventurer to compute stats for.
* @returns Effective per-unit goldPerSecond, essencePerSecond, and combatPower.
*/
export const computeEffectiveAdventurerStats = (
state: GameState,
adventurerId: string,
): { combatPower: number; essencePerSecond: number; goldPerSecond: number } => {
const adventurer = state.adventurers.find((a) => {
return a.id === adventurerId;
});
/* V8 ignore next 3 -- @preserve */
if (adventurer === undefined) {
return { combatPower: 0, essencePerSecond: 0, goldPerSecond: 0 };
}
const upgradeMultiplier = state.upgrades.
filter((upgrade) => {
const isGlobal = upgrade.target === "global";
const isThisAdventurer
= upgrade.target === "adventurer"
&& upgrade.adventurerId === adventurerId;
return upgrade.purchased && (isGlobal || isThisAdventurer);
}).
reduce((mult, upgrade) => {
return mult * upgrade.multiplier;
}, 1);
const equippedItems = state.equipment.filter((item) => {
return item.equipped;
});
const equipmentGoldMultiplier = equippedItems.reduce((mult, item) => {
return mult * (item.bonus.goldMultiplier ?? 1);
}, 1);
const equipmentCombatMultiplier = equippedItems.reduce((mult, item) => {
return mult * (item.bonus.combatMultiplier ?? 1);
}, 1);
const equippedItemIds = equippedItems.map((item) => {
return item.id;
});
const setBonuses = computeSetBonuses(equippedItemIds, EQUIPMENT_SETS);
const runestonesIncome = state.prestige.runestonesIncomeMultiplier ?? 1;
const runestonesEssence = state.prestige.runestonesEssenceMultiplier ?? 1;
// eslint-disable-next-line stylistic/no-mixed-operators -- prestige count * factor is clear
const prestigeCombatMultiplier = 1 + state.prestige.count * 0.1;
const echoIncome = state.transcendence?.echoIncomeMultiplier ?? 1;
const echoCombatMultiplier = state.transcendence?.echoCombatMultiplier ?? 1;
const craftedGoldMultiplier
= state.exploration?.craftedGoldMultiplier ?? 1;
const craftedEssenceMultiplier
= state.exploration?.craftedEssenceMultiplier ?? 1;
const craftedCombatMultiplier
= state.exploration?.craftedCombatMultiplier ?? 1;
const companionBonus = getActiveCompanionBonus(
state.companions?.activeCompanionId,
state.companions?.unlockedCompanionIds ?? [],
);
const companionGoldMult
= companionBonus?.type === "passiveGold"
? 1 + companionBonus.value
: 1;
const companionEssenceMult
= companionBonus?.type === "essenceIncome"
? 1 + companionBonus.value
: 1;
const companionCombatMult
= companionBonus?.type === "bossDamage"
? 1 + companionBonus.value
: 1;
const goldPerSecond
= adventurer.goldPerSecond
* upgradeMultiplier
* state.prestige.productionMultiplier
* runestonesIncome
* echoIncome
* equipmentGoldMultiplier
* setBonuses.goldMultiplier
* craftedGoldMultiplier
* companionGoldMult;
const essencePerSecond
= adventurer.essencePerSecond
* upgradeMultiplier
* state.prestige.productionMultiplier
* runestonesEssence
* craftedEssenceMultiplier
* companionEssenceMult;
const combatPower
= adventurer.combatPower
* upgradeMultiplier
* prestigeCombatMultiplier
* equipmentCombatMultiplier
* setBonuses.combatMultiplier
* echoCombatMultiplier
* craftedCombatMultiplier
* companionCombatMult;
return { combatPower, essencePerSecond, goldPerSecond };
};
/** /**
* Computes the party's total combat power, applying all active multipliers * Computes the party's total combat power, applying all active multipliers
* (upgrades, prestige, equipment, set bonuses, echo, crafted, companion). * (upgrades, prestige, equipment, set bonuses, echo, crafted, companion).
* This mirrors the server-side calculatePartyStats in boss.ts and is the * This mirrors the server-side calculatePartyStats in boss.ts.
* single source of truth for all combat-power checks in the client:
* - Displayed as "Combat Power" in the resource bar
* - Displayed as "Party DPS" in the boss panel
* - Used to gate quest availability
* Note: the active companion's bossDamage bonus is intentionally included
* here, as it applies to the full combat power calculation (boss fights and
* quest gating alike), matching the server-side behaviour.
* @param state - The current game state. * @param state - The current game state.
* @returns The total party combat power. * @returns The total party combat power.
*/ */