'use client'; import { useMemo } from 'react'; import { fmt } from '@/lib/game/stores'; import { ELEMENT_OPPOSITES, FLOOR_ELEM_CYCLE } from '@/lib/game/constants'; import { getGuardianForFloor, getAllGuardianFloors } from '@/lib/game/data/guardian-encounters'; import type { GuardianDef } from '@/lib/game/types'; import { Card, CardContent } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { SectionHeader } from '@/components/ui/section-header'; const GUARDIAN_FLOORS = getAllGuardianFloors(); // ─── Helper: Get Counter Element ───────────────────────────────────────────── export function getCounterElement(element: string): string | null { return ELEMENT_OPPOSITES[element] || null; } export function getElementColor(element: string): string { const colors: Record = { fire: '#FF6B35', water: '#4ECDC4', air: '#00D4FF', earth: '#F4A261', light: '#FFD700', dark: '#9B59B6', death: '#778CA3', void: '#4A235A', stellar: '#F0E68C', }; return colors[element] || '#9CA3AF'; } // ─── Guardian Stat Formatters ──────────────────────────────────────────────── export function fmtArmor(armor: number | undefined): React.ReactNode { if (!armor || armor <= 0) return null; return ( {'Armor: '}{Math.round(armor * 100)}{'%'} ); } export function fmtShield(shield: number | undefined): React.ReactNode { if (!shield || shield <= 0) return null; return ( {'Shield: '}{fmt(shield)} ); } export function fmtBarrier(barrier: number | undefined): React.ReactNode { if (!barrier || barrier <= 0) return null; return ( {'Barrier: '}{Math.round(barrier * 100)}{'%'} ); } export function fmtRegen(regen: number | undefined, isPercent: boolean | undefined): React.ReactNode { if (!regen || regen <= 0) return null; return ( {'Regen: '}{isPercent ? `${regen}%/tick` : `${regen}/tick`} ); } // ─── Preparation Tips ──────────────────────────────────────────────────────── export function PreparationTips({ counterElement, nextFloorElement, hasHighArmor }: { counterElement: string | null; nextFloorElement: string | null; hasHighArmor: boolean; }) { return (
Recommended Preparation:
{counterElement && (
Use {counterElement} spells for super effective damage (+50%)
)} {nextFloorElement && (
🔄 Floor element: {nextFloorElement}
)} {hasHighArmor && (
🛡️ High armor — consider armor-piercing or raw damage spells
)}
💡 Ensure mana pools are full before attempting
); } // ─── Guardian Roster ───────────────────────────────────────────────────────── export function GuardianRoster({ clearedFloors }: { clearedFloors: Record }) { return (
{GUARDIAN_FLOORS.map((floor) => { const guardian = getGuardianForFloor(floor); return guardian ? ( ) : null; })}
); } function GuardianRosterItem({ floor, guardian, isDefeated }: { floor: number; guardian: GuardianDef; isDefeated: boolean }) { return (
{floor}
{guardian.name}
{guardian.element.join(' + ')} Health: {fmt(guardian.hp)}
{isDefeated ? ( ✓ Defeated ) : ( Undefeated )}
); } // ─── Floor Progress Bar ──────────────────────────────────────────────────────── export function FloorProgressBar({ maxFloor, clearedFloors }: { maxFloor: number; clearedFloors: Record }) { const totalFloors = Math.min(maxFloor, 100); const clearedSet = new Set(Object.entries(clearedFloors).filter(([, v]) => v).map(([k]) => Number(k))); const rows: number[][] = []; for (let i = 0; i < totalFloors; i += 10) { rows.push(Array.from({ length: 10 }, (_, j) => i + j + 1).filter((f) => f <= totalFloors)); } return (
{rows.reverse().map((row) => (
{row.map((floor) => { const isCleared = clearedSet.has(floor); const isGuardian = !!getGuardianForFloor(floor); const isCurrent = floor === maxFloor; let bgClass = 'bg-gray-800'; if (isCleared) bgClass = 'bg-emerald-600/60'; else if (isCurrent) bgClass = 'bg-amber-600/60'; const borderClass = isGuardian ? 'border-amber-500' : isCurrent ? 'border-amber-400' : 'border-gray-700'; return (
{floor}
); })}
))}
); } function FloorLegend() { return (
Cleared
Uncleared
Guardian
Current
); }