All checks were successful
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m45s
117 lines
4.0 KiB
TypeScript
Executable File
117 lines
4.0 KiB
TypeScript
Executable File
'use client';
|
|
|
|
import { Button } from '@/components/ui/button';
|
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
|
import { ELEMENTS } from '@/lib/game/constants';
|
|
|
|
interface LabTabProps {
|
|
store: {
|
|
rawMana: number;
|
|
elements: Record<string, { current: number; max: number; unlocked: boolean }>;
|
|
skills: Record<string, number>;
|
|
craftComposite: (target: string) => void;
|
|
};
|
|
}
|
|
|
|
export function LabTab({ store }: LabTabProps) {
|
|
// Render elemental mana grid
|
|
const renderElementsGrid = () => (
|
|
<div className="grid grid-cols-3 sm:grid-cols-4 md:grid-cols-6 lg:grid-cols-8 gap-2">
|
|
{Object.entries(store.elements)
|
|
.filter(([, state]) => state.unlocked)
|
|
.map(([id, state]) => {
|
|
const def = ELEMENTS[id];
|
|
return (
|
|
<div
|
|
key={id}
|
|
className="p-2 rounded border border-gray-700 bg-gray-800/50"
|
|
>
|
|
<div className="text-lg text-center">{def?.sym}</div>
|
|
<div className="text-xs font-semibold text-center" style={{ color: def?.color }}>{def?.name}</div>
|
|
<div className="text-xs text-gray-400 game-mono text-center">{state.current}/{state.max}</div>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
);
|
|
|
|
// Render composite crafting
|
|
const renderCompositeCrafting = () => {
|
|
const compositeElements = Object.entries(ELEMENTS)
|
|
.filter(([, def]) => def.recipe)
|
|
.filter(([id]) => store.elements[id]?.unlocked);
|
|
|
|
if (compositeElements.length === 0) return null;
|
|
|
|
return (
|
|
<Card className="bg-gray-900/80 border-gray-700">
|
|
<CardHeader className="pb-2">
|
|
<CardTitle className="text-amber-400 text-sm">Composite Crafting</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="space-y-2">
|
|
{compositeElements.map(([id, def]) => {
|
|
const recipe = def.recipe || [];
|
|
const canCraft = recipe.every(r => (store.elements[r]?.current || 0) >= 1);
|
|
const craftBonus = 1 + (store.skills.elemCrafting || 0) * 0.25;
|
|
const output = Math.floor(craftBonus);
|
|
|
|
return (
|
|
<div key={id} className="p-2 rounded border border-gray-700 bg-gray-800/50 flex items-center justify-between">
|
|
<div className="flex items-center gap-2">
|
|
<span className="text-lg">{def.sym}</span>
|
|
<span className="text-sm" style={{ color: def.color }}>{def.name}</span>
|
|
<span className="text-xs text-gray-500">
|
|
({recipe.map(r => ELEMENTS[r]?.sym).join(' + ')})
|
|
</span>
|
|
</div>
|
|
<Button
|
|
size="sm"
|
|
variant="outline"
|
|
disabled={!canCraft}
|
|
onClick={() => store.craftComposite(id)}
|
|
>
|
|
Craft ({output})
|
|
</Button>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
};
|
|
|
|
// Check if there are any unlocked elements
|
|
const hasUnlockedElements = Object.values(store.elements).some(e => e.unlocked);
|
|
|
|
if (!hasUnlockedElements) {
|
|
return (
|
|
<Card className="bg-gray-900/80 border-gray-700">
|
|
<CardContent className="pt-6">
|
|
<div className="text-center text-gray-500">
|
|
No elemental mana available. Elements are unlocked through gameplay.
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
|
{/* Elemental Mana Display */}
|
|
<Card className="bg-gray-900/80 border-gray-700 lg:col-span-2">
|
|
<CardHeader className="pb-2">
|
|
<CardTitle className="text-amber-400 text-sm">Elemental Mana</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
{renderElementsGrid()}
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* Composite Crafting */}
|
|
{renderCompositeCrafting()}
|
|
</div>
|
|
);
|
|
}
|