Files
Mana-Loop/src/components/game/tabs/SpireCombatPage/RoomDisplay.tsx
T

202 lines
7.0 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client';
import type { FloorState, EnemyState, RoomType } from '@/lib/game/types';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import { Progress } from '@/components/ui/progress';
import { getSpireRoomTypeDisplay } from '@/lib/game/utils/spire-utils';
import { getModifierDisplay, getModifierDescription } from '@/lib/game/utils/enemy-generator';
import { ELEMENTS } from '@/lib/game/constants';
import { fmt } from '@/lib/game/stores';
interface RoomDisplayProps {
floorState: FloorState;
floor: number;
}
function EnemyRow({ enemy, floor }: { enemy: EnemyState; floor: number }) {
const elemDef = ELEMENTS[enemy.element];
const hpPercent = enemy.maxHP > 0 ? (enemy.hp / enemy.maxHP) * 100 : 0;
const barrierVal = enemy.barrier ?? 0;
const hasBarrier = barrierVal > 0;
const barrierPercent = hasBarrier ? barrierVal * 100 : 0;
return (
<div className="space-y-1 p-2 bg-gray-800/50 rounded border border-gray-700/50">
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
{elemDef && (
<span style={{ color: elemDef.color }}>{elemDef.sym}</span>
)}
<span className="text-sm font-medium text-gray-200">{enemy.name}</span>
</div>
<span className="text-xs text-gray-500">
{fmt(enemy.hp)} / {fmt(enemy.maxHP)}
</span>
</div>
{/* HP bar */}
<div className="relative">
<Progress
value={hpPercent}
className="h-2 bg-gray-700"
style={{ '--progress-bg': elemDef?.color || '#EF4444' } as React.CSSProperties}
/>
{hasBarrier && (
<div
className="absolute top-0 left-0 h-2 rounded-full bg-blue-400/40"
style={{ width: `${barrierPercent}%` }}
/>
)}
</div>
{/* Enemy stats */}
<div className="flex items-center gap-2 flex-wrap">
{enemy.armor > 0 && (
<Badge variant="outline" className="text-[10px] border-amber-600 text-amber-400">
{Math.round(enemy.armor * 100)}% armor
</Badge>
)}
{enemy.dodgeChance > 0 && (
<Badge variant="outline" className="text-[10px] border-green-600 text-green-400">
💨 {Math.round(enemy.dodgeChance * 100)}% dodge
</Badge>
)}
{hasBarrier && (
<Badge variant="outline" className="text-[10px] border-blue-600 text-blue-400">
🛡 Barrier
</Badge>
)}
</div>
</div>
);
}
export function RoomDisplay({ floorState, floor }: RoomDisplayProps) {
// Guard against null/undefined/stale floorState
if (!floorState || !floorState.roomType) {
return (
<Card className="bg-gray-900/80 border-gray-700">
<CardHeader className="pb-2">
<CardTitle className="text-sm text-gray-400">Loading room...</CardTitle>
</CardHeader>
</Card>
);
}
const roomDisplay = getSpireRoomTypeDisplay(floorState.roomType as RoomType);
// Handle special room types (cast to string for extended types)
const rt = floorState.roomType as string;
if (rt === 'recovery') {
const progress = floorState.recoveryProgress || 0;
const required = floorState.recoveryRequired || 1;
return (
<Card className="bg-gray-900/80 border-green-800/40">
<CardHeader className="pb-2">
<CardTitle className="text-sm flex items-center gap-2" style={{ color: '#10B981' }}>
💚 Recovery Room
</CardTitle>
</CardHeader>
<CardContent>
<p className="text-xs text-gray-400 mb-2">
Rest and recover. Spend 1 hour to gain 5x mana regen & conversion rates.
</p>
<Progress
value={(progress / required) * 100}
className="h-2 bg-gray-800"
style={{ '--progress-bg': '#10B981' } as React.CSSProperties}
/>
</CardContent>
</Card>
);
}
if (rt === 'library') {
return (
<Card className="bg-gray-900/80 border-indigo-800/40">
<CardHeader className="pb-2">
<CardTitle className="text-sm flex items-center gap-2" style={{ color: '#6366F1' }}>
📚 Ancient Library
</CardTitle>
</CardHeader>
<CardContent>
<p className="text-xs text-gray-400">
Study a random discipline at 10x XP speed (no mana cost). Spend 1 hour to gain knowledge.
</p>
</CardContent>
</Card>
);
}
if (rt === 'treasure') {
return (
<Card className="bg-gray-900/80 border-amber-800/40">
<CardHeader className="pb-2">
<CardTitle className="text-sm flex items-center gap-2" style={{ color: '#F59E0B' }}>
💎 Treasure Room
</CardTitle>
</CardHeader>
<CardContent>
<p className="text-xs text-gray-400">
A hidden cache of resources awaits. Claim your reward!
</p>
</CardContent>
</Card>
);
}
if (floorState.roomType === 'puzzle') {
const puzzleId = floorState.puzzleId || 'unknown';
const progress = floorState.puzzleProgress || 0;
const required = floorState.puzzleRequired || 1;
return (
<Card className="bg-gray-900/80 border-purple-800/40">
<CardHeader className="pb-2">
<CardTitle className="text-sm flex items-center gap-2" style={{ color: '#8B5CF6' }}>
🧩 Puzzle Room {puzzleId.replace(/_/g, ' ')}
</CardTitle>
</CardHeader>
<CardContent>
<p className="text-xs text-gray-400 mb-2">
Solve the puzzle. Higher attunement levels speed up progress.
</p>
<Progress
value={(progress / required) * 100}
className="h-2 bg-gray-800"
style={{ '--progress-bg': '#8B5CF6' } as React.CSSProperties}
/>
<div className="text-xs text-gray-500 mt-1">
Progress: {Math.round(progress * 100)} / {Math.round(required * 100)}
</div>
</CardContent>
</Card>
);
}
// Combat rooms (combat, swarm, speed, guardian)
const enemies = floorState.enemies || [];
const isGuardian = floorState.roomType === 'guardian';
return (
<Card className={`bg-gray-900/80 ${isGuardian ? 'border-red-800/40' : 'border-gray-700'}`}>
<CardHeader className="pb-2">
<CardTitle className="text-sm flex items-center gap-2" style={{ color: roomDisplay.color }}>
{roomDisplay.icon} {roomDisplay.label}
{isGuardian && <Badge className="bg-red-900/50 text-red-300 text-xs">BOSS</Badge>}
</CardTitle>
</CardHeader>
<CardContent className="space-y-2">
{enemies.length === 0 ? (
<div className="text-xs text-gray-500 italic">Room cleared!</div>
) : (
enemies.map((enemy) => (
<EnemyRow key={enemy.id} enemy={enemy} floor={floor} />
))
)}
</CardContent>
</Card>
);
}