"use client" import * as React from "react" import { cva, type VariantProps } from "class-variance-authority" import { cn } from "@/lib/utils" /* ═══════════════════════════════════════════════════════════════════════════ STAT BAR — HP / MP / XP Style ═══════════════════════════════════════════════════════════════════════════ */ const statBarVariants = cva( "relative h-6 rounded-sm border-2 overflow-hidden bg-[var(--void-900)]", { variants: { variant: { hp: "border-[var(--hp-red)]", mp: "border-[var(--mp-blue)]", xp: "border-[var(--xp-gold)]", shield: "border-[var(--shield-purple)]", }, size: { sm: "h-4 text-[10px]", default: "h-6 text-xs", lg: "h-8 text-sm", }, }, defaultVariants: { variant: "xp", size: "default", }, } ) const statBarFillVariants = cva( "h-full transition-all duration-500 ease-out relative", { variants: { variant: { hp: "bg-gradient-to-b from-[var(--hp-red)] to-[#990000] shadow-[inset_0_-2px_4px_rgba(0,0,0,0.3),0_0_10px_rgba(255,51,51,0.5)]", mp: "bg-gradient-to-b from-[var(--mp-blue)] to-[#0066cc] shadow-[inset_0_-2px_4px_rgba(0,0,0,0.3),0_0_10px_rgba(51,153,255,0.5)]", xp: "bg-gradient-to-b from-[var(--xp-gold)] to-[#cc9900] shadow-[inset_0_-2px_4px_rgba(0,0,0,0.3),0_0_10px_rgba(255,215,0,0.5)]", shield: "bg-gradient-to-b from-[var(--shield-purple)] to-[#6a1b9a] shadow-[inset_0_-2px_4px_rgba(0,0,0,0.3),0_0_10px_rgba(156,39,176,0.5)]", }, }, defaultVariants: { variant: "xp", }, } ) interface StatBarProps extends React.HTMLAttributes, VariantProps { value: number max?: number showLabel?: boolean label?: string animated?: boolean } const StatBar = React.forwardRef( ({ className, variant, size, value, max = 100, showLabel = true, label, animated = false, ...props }, ref) => { const percentage = Math.min(100, Math.max(0, (value / max) * 100)) return (
{showLabel && (
{label || `${value}/${max}`}
)}
) } ) StatBar.displayName = "StatBar" /* ═══════════════════════════════════════════════════════════════════════════ LEVEL BADGE ═══════════════════════════════════════════════════════════════════════════ */ interface LevelBadgeProps extends React.HTMLAttributes { level: number size?: "sm" | "default" | "lg" animated?: boolean } const LevelBadge = React.forwardRef( ({ className, level, size = "default", animated = false, ...props }, ref) => { const sizes = { sm: "w-10 h-10 text-sm", default: "w-12 h-12 text-xl", lg: "w-16 h-16 text-2xl", } return (
{level}
) } ) LevelBadge.displayName = "LevelBadge" /* ═══════════════════════════════════════════════════════════════════════════ ACHIEVEMENT POPUP ═══════════════════════════════════════════════════════════════════════════ */ interface AchievementProps extends React.HTMLAttributes { icon: React.ReactNode title: string name: string xp?: number animated?: boolean } const Achievement = React.forwardRef( ({ className, icon, title, name, xp, animated = true, ...props }, ref) => { return (
{icon}

{title}

{name}

{xp && (

+{xp} XP

)}
) } ) Achievement.displayName = "Achievement" /* ═══════════════════════════════════════════════════════════════════════════ SKILL TREE NODE ═══════════════════════════════════════════════════════════════════════════ */ interface SkillNodeProps extends React.HTMLAttributes { icon: React.ReactNode name: string unlocked?: boolean active?: boolean level?: number maxLevel?: number } const SkillNode = React.forwardRef( ({ className, icon, name, unlocked = false, active = false, level = 0, maxLevel = 5, ...props }, ref) => { return (
{icon}

{name}

{maxLevel > 1 && (
{Array.from({ length: maxLevel }).map((_, i) => (
))}
)}
) } ) SkillNode.displayName = "SkillNode" /* ═══════════════════════════════════════════════════════════════════════════ STATS PANEL ═══════════════════════════════════════════════════════════════════════════ */ interface StatsPanelProps extends React.HTMLAttributes { stats: Array<{ label: string value: number | string icon?: React.ReactNode change?: number }> } const StatsPanel = React.forwardRef( ({ className, stats, ...props }, ref) => { return (
{/* Gaming-style header bar */}
{stats.map((stat, i) => (
{stat.icon && {stat.icon}} {stat.label}
{stat.value} {stat.change !== undefined && ( 0 ? "text-[var(--success)]" : stat.change < 0 ? "text-[var(--hp-red)]" : "text-muted-foreground" )}> {stat.change > 0 ? "+" : ""}{stat.change} )}
))}
) } ) StatsPanel.displayName = "StatsPanel" /* ═══════════════════════════════════════════════════════════════════════════ INVENTORY SLOT ═══════════════════════════════════════════════════════════════════════════ */ interface InventorySlotProps extends React.HTMLAttributes { item?: { icon: React.ReactNode rarity?: "common" | "uncommon" | "rare" | "epic" | "legendary" quantity?: number } size?: "sm" | "default" | "lg" selected?: boolean } const rarityColors = { common: "border-[var(--void-400)]", uncommon: "border-[var(--success)]", rare: "border-[var(--mp-blue)]", epic: "border-[var(--shield-purple)]", legendary: "border-[var(--xp-gold)] glow-gold", } const InventorySlot = React.forwardRef( ({ className, item, size = "default", selected = false, ...props }, ref) => { const sizes = { sm: "w-10 h-10", default: "w-14 h-14", lg: "w-20 h-20", } return (
{item && ( <>
{item.icon}
{item.quantity && item.quantity > 1 && (
{item.quantity}
)} )}
) } ) InventorySlot.displayName = "InventorySlot" export { StatBar, LevelBadge, Achievement, SkillNode, StatsPanel, InventorySlot, }