generated from nhcarrigan/template
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:
@@ -0,0 +1,3 @@
|
||||
import { nhcarrigan } from "@nhcarrigan/eslint-config";
|
||||
|
||||
export default [...(await nhcarrigan())];
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "@nhcarrigan/typescript-config",
|
||||
"compilerOptions": {
|
||||
"outDir": "./prod",
|
||||
"rootDir": "."
|
||||
},
|
||||
"exclude": ["test/**/*.ts"]
|
||||
}
|
||||
Reference in New Issue
Block a user