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
@@ -0,0 +1,73 @@
import type { Upgrade } from "@elysium/types";
import { useGame } from "../../context/GameContext.js";
interface UpgradeCardProps {
upgrade: Upgrade;
currentGold: number;
currentEssence: number;
}
const UpgradeCard = ({ upgrade, currentGold, currentEssence }: UpgradeCardProps): React.JSX.Element => {
const { buyUpgrade } = useGame();
const canAfford =
currentGold >= upgrade.costGold && currentEssence >= upgrade.costEssence;
if (upgrade.purchased) {
return (
<div className="upgrade-card purchased">
<span className="upgrade-name"> {upgrade.name}</span>
<span className="upgrade-desc">{upgrade.description}</span>
</div>
);
}
return (
<div className="upgrade-card">
<div className="upgrade-info">
<h3>{upgrade.name}</h3>
<p>{upgrade.description}</p>
<p className="upgrade-multiplier">×{upgrade.multiplier} multiplier</p>
</div>
<div className="upgrade-cost">
{upgrade.costGold > 0 && <span>🪙 {upgrade.costGold.toLocaleString()}</span>}
{upgrade.costEssence > 0 && <span> {upgrade.costEssence.toLocaleString()}</span>}
</div>
<button
className="buy-button"
disabled={!canAfford}
onClick={() => { buyUpgrade(upgrade.id); }}
type="button"
>
Buy
</button>
</div>
);
};
export const UpgradePanel = (): React.JSX.Element => {
const { state } = useGame();
if (!state) return <section className="panel"><p>Loading...</p></section>;
const availableUpgrades = state.upgrades.filter((u) => u.unlocked);
return (
<section className="panel upgrade-panel">
<h2>Upgrades</h2>
{availableUpgrades.length === 0 ? (
<p className="empty-state">No upgrades available yet keep adventuring!</p>
) : (
<div className="upgrade-list">
{availableUpgrades.map((upgrade) => (
<UpgradeCard
key={upgrade.id}
upgrade={upgrade}
currentGold={state.resources.gold}
currentEssence={state.resources.essence}
/>
))}
</div>
)}
</section>
);
};