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
+3
View File
@@ -0,0 +1,3 @@
import { nhcarrigan } from "@nhcarrigan/eslint-config";
export default [...(await nhcarrigan())];
+18
View File
@@ -0,0 +1,18 @@
{
"name": "@elysium/types",
"version": "1.0.0",
"private": true,
"main": "./prod/src/index.js",
"types": "./prod/src/index.d.ts",
"scripts": {
"build": "tsc -p tsconfig.json",
"lint": "eslint --max-warnings 0 src",
"test": "echo \"No tests for types package\""
},
"devDependencies": {
"@nhcarrigan/eslint-config": "5.2.0",
"@nhcarrigan/typescript-config": "4.0.0",
"eslint": "9.22.0",
"typescript": "5.8.2"
}
}
+28
View File
@@ -0,0 +1,28 @@
export type { Adventurer, AdventurerClass } from "./interfaces/Adventurer.js";
export type {
ApiError,
AuthResponse,
BossDamageRequest,
BossDamageResponse,
LoadResponse,
PrestigeRequest,
PrestigeResponse,
PublicProfileResponse,
SaveRequest,
SaveResponse,
} from "./interfaces/Api.js";
export type { Boss, BossStatus } from "./interfaces/Boss.js";
export type { GameState } from "./interfaces/GameState.js";
export type { Player } from "./interfaces/Player.js";
export type { PrestigeData } from "./interfaces/Prestige.js";
export type {
Quest,
QuestReward,
QuestRewardType,
QuestStatus,
} from "./interfaces/Quest.js";
export type { Resource } from "./interfaces/Resource.js";
export type {
Upgrade,
UpgradeTarget,
} from "./interfaces/Upgrade.js";
@@ -0,0 +1,20 @@
export type AdventurerClass =
| "warrior"
| "mage"
| "rogue"
| "cleric"
| "ranger"
| "paladin";
export interface Adventurer {
id: string;
name: string;
class: AdventurerClass;
level: number;
/** Base gold generated per second */
goldPerSecond: number;
/** Base essence generated per second */
essencePerSecond: number;
count: number;
unlocked: boolean;
}
+63
View File
@@ -0,0 +1,63 @@
import type { GameState } from "./GameState.js";
import type { Player } from "./Player.js";
export interface AuthResponse {
token: string;
player: Player;
isNew: boolean;
}
export interface SaveRequest {
state: GameState;
}
export interface SaveResponse {
savedAt: number;
}
export interface LoadResponse {
state: GameState;
/** Offline gold earned since last save (server-calculated) */
offlineGold: number;
/** Seconds the player was offline (capped at 8 hours) */
offlineSeconds: number;
}
export interface BossDamageRequest {
bossId: string;
damage: number;
}
export interface BossDamageResponse {
currentHp: number;
defeated: boolean;
rewards?: {
gold: number;
essence: number;
crystals: number;
upgradeIds: string[];
};
}
export interface PrestigeRequest {
characterName: string;
}
export interface PrestigeResponse {
runestones: number;
newPrestigeCount: number;
}
export interface PublicProfileResponse {
characterName: string;
username: string;
avatar: string | null;
prestigeCount: number;
totalGoldEarned: number;
totalClicks: number;
createdAt: number;
}
export interface ApiError {
error: string;
}
+22
View File
@@ -0,0 +1,22 @@
export type BossStatus = "locked" | "available" | "in_progress" | "defeated";
export interface Boss {
id: string;
name: string;
description: string;
status: BossStatus;
maxHp: number;
currentHp: number;
/** Damage dealt to adventurers per second whilst the fight is active */
damagePerSecond: number;
/** Gold reward on defeat */
goldReward: number;
/** Essence reward on defeat */
essenceReward: number;
/** Crystal reward on defeat */
crystalReward: number;
/** IDs of upgrades unlocked on defeat */
upgradeRewards: string[];
/** Minimum prestige level required to access this boss */
prestigeRequirement: number;
}
@@ -0,0 +1,21 @@
import type { Adventurer } from "./Adventurer.js";
import type { Boss } from "./Boss.js";
import type { Player } from "./Player.js";
import type { PrestigeData } from "./Prestige.js";
import type { Quest } from "./Quest.js";
import type { Resource } from "./Resource.js";
import type { Upgrade } from "./Upgrade.js";
export interface GameState {
player: Player;
resources: Resource;
adventurers: Adventurer[];
upgrades: Upgrade[];
quests: Quest[];
bosses: Boss[];
prestige: PrestigeData;
/** Click power (gold per click, before upgrades) */
baseClickPower: number;
/** Unix timestamp of the last client-side tick */
lastTickAt: number;
}
+16
View File
@@ -0,0 +1,16 @@
export interface Player {
discordId: string;
username: string;
discriminator: string;
avatar: string | null;
/** Player's chosen in-game character name */
characterName: string;
/** Unix timestamp when the account was created */
createdAt: number;
/** Unix timestamp of the last server-side save */
lastSavedAt: number;
/** Total lifetime gold ever collected (for prestige eligibility) */
totalGoldEarned: number;
/** Total lifetime clicks */
totalClicks: number;
}
+12
View File
@@ -0,0 +1,12 @@
export interface PrestigeData {
/** Number of times the player has prestiged */
count: number;
/** Runestones carried over between prestiges */
runestones: number;
/** Multiplier applied to all production (based on prestige count) */
productionMultiplier: number;
/** IDs of prestige upgrades purchased with runestones */
purchasedUpgradeIds: string[];
/** Unix timestamp of last prestige */
lastPrestigedAt?: number;
}
+24
View File
@@ -0,0 +1,24 @@
export type QuestStatus = "locked" | "available" | "active" | "completed";
export type QuestRewardType = "gold" | "essence" | "crystals" | "upgrade" | "adventurer";
export interface QuestReward {
type: QuestRewardType;
amount?: number;
/** ID of the upgrade or adventurer to unlock (if applicable) */
targetId?: string;
}
export interface Quest {
id: string;
name: string;
description: string;
status: QuestStatus;
/** Unix timestamp when quest was started (if active) */
startedAt?: number;
/** Duration in seconds */
durationSeconds: number;
rewards: QuestReward[];
/** IDs of quests that must be completed before this one unlocks */
prerequisiteIds: string[];
}
@@ -0,0 +1,6 @@
export interface Resource {
gold: number;
essence: number;
crystals: number;
runestones: number;
}
+21
View File
@@ -0,0 +1,21 @@
export type UpgradeTarget =
| "click"
| "adventurer"
| "global"
| "prestige"
| "boss";
export interface Upgrade {
id: string;
name: string;
description: string;
target: UpgradeTarget;
/** ID of the adventurer this applies to (if target is "adventurer") */
adventurerId?: string;
/** Multiplier applied to the target's output */
multiplier: number;
costGold: number;
costEssence: number;
purchased: boolean;
unlocked: boolean;
}
+8
View File
@@ -0,0 +1,8 @@
{
"extends": "@nhcarrigan/typescript-config",
"compilerOptions": {
"outDir": "./prod",
"rootDir": "."
},
"exclude": ["test/**/*.ts"]
}