feat: initial elysium idle game prototype

Sets up the full monorepo with pnpm workspaces. Includes shared types
package, Hono API with Discord OAuth/JWT auth, Prisma v6 + MongoDB
Atlas, and React + Vite frontend with game loop, five tabs, and
Discord-linked save/load.
This commit is contained in:
2026-03-06 11:26:19 -08:00
committed by Naomi Carrigan
parent c69e155de3
commit a3daed1683
64 changed files with 9011 additions and 0 deletions
+62
View File
@@ -0,0 +1,62 @@
import type { GameState, SaveRequest } from "@elysium/types";
import { Hono } from "hono";
import { prisma } from "../db/client.js";
import { authMiddleware } from "../middleware/auth.js";
import { calculateOfflineGold } from "../services/offlineProgress.js";
export const gameRouter = new Hono();
gameRouter.use("*", authMiddleware);
gameRouter.get("/load", async (context) => {
const discordId = context.get("discordId") as string;
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 now = Date.now();
const { offlineGold, offlineSeconds } = calculateOfflineGold(state, now);
if (offlineGold > 0) {
state.resources.gold += offlineGold;
state.player.totalGoldEarned += offlineGold;
}
state.lastTickAt = now;
return context.json({ state, offlineGold, offlineSeconds });
});
gameRouter.post("/save", async (context) => {
const discordId = context.get("discordId") as string;
const body = await context.req.json<SaveRequest>();
if (!body.state) {
return context.json({ error: "Missing state in request body" }, 400);
}
const now = Date.now();
await prisma.player.update({
where: { discordId },
data: {
lastSavedAt: now,
totalGoldEarned: body.state.player.totalGoldEarned,
totalClicks: body.state.player.totalClicks,
characterName: body.state.player.characterName,
},
});
await prisma.gameState.upsert({
where: { discordId },
create: { discordId, state: body.state, updatedAt: now },
update: { state: body.state, updatedAt: now },
});
return context.json({ savedAt: now });
});