import type { PrestigeUpgradeCategory } from "@elysium/types"; import { useState } from "react"; import { prestige } from "../../api/client.js"; import { useGame } from "../../context/GameContext.js"; import { PRESTIGE_UPGRADES, PRESTIGE_UPGRADE_CATEGORY_LABELS, } from "../../data/prestigeUpgrades.js"; const BASE_THRESHOLD = 1_000_000; const THRESHOLD_SCALE = 5; const RUNESTONES_PER_LEVEL = 10; const calculateThreshold = (prestigeCount: number): number => BASE_THRESHOLD * Math.pow(THRESHOLD_SCALE, prestigeCount); const calculateProductionMultiplier = (prestigeCount: number): number => Math.pow(1.15, prestigeCount); const calculateRunestonePreview = ( totalGoldEarned: number, prestigeCount: number, purchasedUpgradeIds: string[], ): number => { const threshold = calculateThreshold(prestigeCount); const base = Math.floor(Math.sqrt(totalGoldEarned / threshold)) * RUNESTONES_PER_LEVEL; const runestoneMult = PRESTIGE_UPGRADES .filter((u) => u.category === "runestones" && purchasedUpgradeIds.includes(u.id)) .reduce((mult, u) => mult * u.multiplier, 1); return Math.floor(base * runestoneMult); }; const CATEGORY_ORDER: PrestigeUpgradeCategory[] = [ "income", "click", "essence", "crystals", "runestones", "utility", ]; export const PrestigePanel = (): React.JSX.Element => { const { state, reload, formatNumber, buyPrestigeUpgrade, toggleAutoPrestige } = useGame(); const [isPending, setIsPending] = useState(false); const [result, setResult] = useState<{ runestones: number; count: number; milestoneRunestones: number } | null>(null); const [prestigeError, setPrestigeError] = useState(null); const [buyingId, setBuyingId] = useState(null); const [activeTab, setActiveTab] = useState<"prestige" | "shop">("prestige"); if (!state) return

Loading...

; const { prestige: prestigeData, player } = state; const threshold = calculateThreshold(prestigeData.count); const isEligible = player.totalGoldEarned >= threshold; const runestonePreview = calculateRunestonePreview( player.totalGoldEarned, prestigeData.count, prestigeData.purchasedUpgradeIds, ); const nextMultiplier = calculateProductionMultiplier(prestigeData.count + 1); const handlePrestige = async (): Promise => { setIsPending(true); setPrestigeError(null); try { const data = await prestige({}); setResult({ runestones: data.runestones, count: data.newPrestigeCount, milestoneRunestones: data.milestoneRunestones }); await reload(); } catch (err) { setPrestigeError(err instanceof Error ? err.message : "Prestige failed"); } finally { setIsPending(false); } }; const handleBuyUpgrade = async (upgradeId: string): Promise => { setBuyingId(upgradeId); try { await buyPrestigeUpgrade(upgradeId); } finally { setBuyingId(null); } }; const upgradesByCategory = CATEGORY_ORDER.map((category) => ({ category, label: PRESTIGE_UPGRADE_CATEGORY_LABELS[category] ?? category, upgrades: PRESTIGE_UPGRADES.filter((u) => u.category === category), })); return (

⭐ Prestige

{activeTab === "prestige" && ( <>

Prestige resets your progress but grants Runestones — permanent currency used for powerful upgrades. Each prestige multiplies your global production by ×1.15 (compounding each run).

Total gold this run:{" "} {formatNumber(player.totalGoldEarned)}

Required to prestige: {formatNumber(threshold)}

Prestige count: {prestigeData.count}

Current production multiplier:{" "} ×{prestigeData.productionMultiplier.toFixed(2)}

After next prestige:{" "} ×{nextMultiplier.toFixed(2)}

Runestones: {formatNumber(prestigeData.runestones)}

{isEligible && (

Runestones on prestige: +{formatNumber(runestonePreview)}

)} {!isEligible && (

Progress: {formatNumber(player.totalGoldEarned)} / {formatNumber(threshold)}{" "} ({((player.totalGoldEarned / threshold) * 100).toFixed(1)}%)

)}
{isEligible ? (

You are ready to prestige!

{prestigeError &&

{prestigeError}

} {result && (

Ascended to Prestige {result.count}! Earned {formatNumber(result.runestones)} Runestones. {result.milestoneRunestones > 0 && ( <> 🎉 Milestone bonus: +{formatNumber(result.milestoneRunestones)} Runestones! )}

)}
) : (

Earn {formatNumber(threshold - player.totalGoldEarned)} more gold to unlock prestige.

)} )} {activeTab === "shop" && (

Balance: {formatNumber(prestigeData.runestones)} Runestones

{upgradesByCategory.map(({ category, label, upgrades }) => (

{label}

{upgrades.map((upgrade) => { const purchased = prestigeData.purchasedUpgradeIds.includes(upgrade.id); const canAfford = prestigeData.runestones >= upgrade.runestonesCost; const isLoading = buyingId === upgrade.id; const isAutoPrestigeToggle = upgrade.id === "auto_prestige" && purchased; const autoPrestigeEnabled = prestigeData.autoPrestigeEnabled ?? false; return (

{upgrade.name}

{upgrade.description}

{purchased ? "✅ Purchased" : `🔮 ${formatNumber(upgrade.runestonesCost)} Runestones`}

{isAutoPrestigeToggle && ( )} {!purchased && ( )}
); })}
))}
)}
); };