Files
Mana-Loop/src/components/game/ManaDisplay.tsx
T
n8n-gitea b402b8f56e
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m20s
refactor: cleanup codebase — remove hydration guards, extract constants, fix bugs
2026-05-26 11:20:36 +02:00

142 lines
5.3 KiB
TypeScript
Executable File

'use client';
import { Button } from '@/components/ui/button';
import { Progress } from '@/components/ui/progress';
import { Card, CardContent } from '@/components/ui/card';
import { Zap, ChevronDown, ChevronUp } from 'lucide-react';
import { fmt, fmtDec } from '@/lib/game/stores';
import { ELEMENTS } from '@/lib/game/constants';
import { useState } from 'react';
interface ManaDisplayProps {
rawMana: number;
maxMana: number;
effectiveRegen: number;
meditationMultiplier: number;
clickMana: number;
isGathering: boolean;
onGatherStart: () => void;
onGatherEnd: () => void;
elements: Record<string, { current: number; max: number; unlocked: boolean }>;
}
export function ManaDisplay({
rawMana,
maxMana,
effectiveRegen,
meditationMultiplier,
clickMana,
isGathering,
onGatherStart,
onGatherEnd,
elements,
}: ManaDisplayProps) {
const [expanded, setExpanded] = useState(true);
// Get unlocked elements with current > 0, sorted by current amount
const unlockedElements = Object.entries(elements)
.filter(([, state]) => state.unlocked && state.current > 0)
.sort((a, b) => b[1].current - a[1].current);
return (
<Card className="bg-[var(--bg-panel)] border-[var(--border-subtle)]">
<CardContent className="pt-4 space-y-3">
{/* Raw Mana - Main Display */}
<div>
<div className="flex items-baseline gap-1">
<span className="text-3xl font-bold game-mono" style={{ color: 'var(--mana-raw)' }}>{fmt(rawMana)}</span>
<span className="text-sm" style={{ color: 'var(--text-muted)' }}>/ {fmt(maxMana)}</span>
</div>
<div className="text-xs" style={{ color: 'var(--text-muted)' }}>
+{fmtDec(effectiveRegen)} mana/hr {meditationMultiplier > 1.01 && <span style={{ color: 'var(--mana-light)' }}>({fmtDec(meditationMultiplier, 1)}x med)</span>}
</div>
</div>
<Progress
value={(rawMana / maxMana) * 100}
className="h-2 bg-[var(--bg-sunken)]"
style={{ '--progress-bg': 'var(--mana-raw)' } as React.CSSProperties}
/>
<Button
className={`w-full transition-all text-[var(--font-display)] tracking-wider
${isGathering
? 'animate-gather-glow'
: 'hover:scale-[1.02]'}
`}
style={{
background: 'var(--mana-raw)',
border: '1px solid var(--border-accent)',
color: 'var(--bg-gather-btn)',
fontWeight: 600,
}}
onMouseDown={onGatherStart}
onMouseUp={onGatherEnd}
onMouseLeave={onGatherEnd}
onTouchStart={onGatherStart}
onTouchEnd={onGatherEnd}
>
<Zap className="w-4 h-4 mr-2" />
Gather +{clickMana} Mana
{isGathering && <span className="ml-2 text-xs" style={{ opacity: 0.8 }}>(Holding...)</span>}
</Button>
{/* Elemental Mana Pools */}
{unlockedElements.length > 0 && (
<div className="border-t border-[var(--border-subtle)] pt-3 mt-3">
<button
onClick={() => setExpanded(!expanded)}
className="flex items-center justify-between w-full text-xs transition-colors"
style={{ color: 'var(--text-muted)' }}
>
<span style={{ fontFamily: 'var(--font-display)', letterSpacing: '0.5px' }}>ELEMENTAL MANA ({unlockedElements.length})</span>
{expanded ? <ChevronUp className="w-3 h-3" /> : <ChevronDown className="w-3 h-3" />}
</button>
{expanded && (
<div className="grid grid-cols-2 gap-2 mt-2">
{unlockedElements.map(([id, state]) => {
const elem = ELEMENTS[id];
if (!elem) return null;
return (
<div
key={id}
className="p-2 transition-all border rounded-sm"
style={{
background: 'var(--bg-sunken)/30',
borderColor: `${elem.color}30`,
}}
>
<div className="flex items-center gap-1 mb-1">
<span style={{ color: elem.color }}>{elem.sym}</span>
<span className="text-xs font-medium" style={{ color: elem.color }}>
{elem.name}
</span>
</div>
<div className="h-1.5 rounded-full overflow-hidden" style={{ background: 'var(--bg-void)' }}>
<div
className="h-full transition-all rounded-full"
style={{
width: `${Math.min(100, (state.current / state.max) * 100)}%`,
backgroundColor: elem.color
}}
/>
</div>
<div className="text-xs game-mono" style={{ color: 'var(--text-muted)' }}>
{fmt(state.current)}/{fmt(state.max)}
</div>
</div>
);
})}
</div>
)}
</div>
)}
</CardContent>
</Card>
);
}
ManaDisplay.displayName = "ManaDisplay";