import type { BuyPrestigeUpgradeRequest, GameState, PrestigeRequest } from "@elysium/types"; import { Hono } from "hono"; import { prisma } from "../db/client.js"; import { authMiddleware } from "../middleware/auth.js"; import { DEFAULT_PRESTIGE_UPGRADES } from "../data/prestigeUpgrades.js"; import { updateChallengeProgress } from "../services/dailyChallenges.js"; import { buildPostPrestigeState, computeRunestoneMultipliers, isEligibleForPrestige, } from "../services/prestige.js"; export const prestigeRouter = new Hono(); prestigeRouter.use("*", authMiddleware); prestigeRouter.post("/", async (context) => { const discordId = context.get("discordId") as string; const body = await context.req.json(); const characterName = body.characterName?.trim(); if (!characterName) { return context.json({ error: "characterName is required" }, 400); } const record = await prisma.gameState.findUnique({ where: { discordId } }); if (!record) { return context.json({ error: "No save found" }, 404); } const state = record.state as unknown as GameState; if (!isEligibleForPrestige(state)) { return context.json( { error: "Not eligible for prestige — collect 1,000,000 total gold first" }, 400, ); } // Update daily prestige challenge progress before resetting the run let updatedDailyChallenges = state.dailyChallenges; let challengeCrystals = 0; if (updatedDailyChallenges) { const result = updateChallengeProgress(updatedDailyChallenges, "prestige", 1); updatedDailyChallenges = result.updatedChallenges; challengeCrystals = result.crystalsAwarded; } const { newState, newPrestigeData, runestonesEarned, milestoneRunestones } = buildPostPrestigeState( state, characterName, ); // Preserve daily challenges across the prestige reset and apply any crystal rewards const finalState: GameState = { ...newState, dailyChallenges: updatedDailyChallenges, resources: { ...newState.resources, crystals: newState.resources.crystals + challengeCrystals, }, }; const now = Date.now(); await prisma.gameState.update({ where: { discordId }, data: { state: finalState as object, updatedAt: now }, }); await prisma.player.update({ where: { discordId }, data: { characterName, totalGoldEarned: 0, totalClicks: 0, lastSavedAt: now, }, }); return context.json({ runestones: runestonesEarned, newPrestigeCount: newPrestigeData.count, milestoneRunestones, }); }); prestigeRouter.post("/buy-upgrade", async (context) => { const discordId = context.get("discordId") as string; const body = await context.req.json(); const { upgradeId } = body; if (!upgradeId) { return context.json({ error: "upgradeId is required" }, 400); } const upgrade = DEFAULT_PRESTIGE_UPGRADES.find((u) => u.id === upgradeId); if (!upgrade) { return context.json({ error: "Unknown prestige upgrade" }, 404); } const record = await prisma.gameState.findUnique({ where: { discordId } }); if (!record) { return context.json({ error: "No save found" }, 404); } const state = record.state as unknown as GameState; const { purchasedUpgradeIds, runestones } = state.prestige; if (purchasedUpgradeIds.includes(upgradeId)) { return context.json({ error: "Upgrade already purchased" }, 400); } if (runestones < upgrade.runestonesCost) { return context.json({ error: "Not enough runestones" }, 400); } const newRunestones = runestones - upgrade.runestonesCost; const newPurchasedUpgradeIds = [...purchasedUpgradeIds, upgradeId]; const newState: GameState = { ...state, prestige: { ...state.prestige, runestones: newRunestones, purchasedUpgradeIds: newPurchasedUpgradeIds, ...computeRunestoneMultipliers(newPurchasedUpgradeIds), }, }; await prisma.gameState.update({ where: { discordId }, data: { state: newState as object, updatedAt: Date.now() }, }); const multipliers = computeRunestoneMultipliers(newPurchasedUpgradeIds); return context.json({ runestonesRemaining: newRunestones, purchasedUpgradeIds: newPurchasedUpgradeIds, ...multipliers, }); });