All files / components/screens/combat/components/hud DifficultyIndicator.tsx

92.59% Statements 25/27
88.88% Branches 16/18
100% Functions 6/6
92.59% Lines 25/27

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152                                                                  80x   63x   1x   12x   1x   3x                           80x   63x   1x   12x   1x   3x                     3x       80x 80x 80x     80x 50x     80x 50x     80x 50x         80x 80x   80x                                                                                              
/**
 * DifficultyIndicator Component - AI Difficulty Tier Display
 *
 * Shows the current AI difficulty tier with Korean-English bilingual text
 * and color-coded visual feedback based on tier level.
 *
 * **Korean Philosophy (난이도 표시)**:
 * Provides transparent feedback to player about AI challenge level,
 * helping them understand their skill progression.
 */
 
import React, { useMemo } from "react";
import { DifficultyTier } from "../../../../../systems/ai";
import {
  hexColorToCSS,
  hexToRgbaString,
} from "../../../../../utils/colorUtils";
import { useKoreanTheme } from "../../../../shared/base/useKoreanTheme";
 
export interface DifficultyIndicatorProps {
  /** Current difficulty tier (1-5) */
  readonly tier: DifficultyTier;
  /** Whether to use mobile-optimized sizing */
  readonly isMobile: boolean;
}
 
/**
 * Get tier display name with Korean and English
 */
function getTierName(tier: DifficultyTier): {
  korean: string;
  english: string;
} {
  switch (tier) {
    case DifficultyTier.BEGINNER:
      return { korean: "초보", english: "Beginner" };
    case DifficultyTier.NOVICE:
      return { korean: "입문", english: "Novice" };
    case DifficultyTier.INTERMEDIATE:
      return { korean: "중급", english: "Intermediate" };
    case DifficultyTier.ADVANCED:
      return { korean: "고급", english: "Advanced" };
    case DifficultyTier.EXPERT:
      return { korean: "전문", english: "Expert" };
    default:
      return { korean: "중급", english: "Intermediate" };
  }
}
 
/**
 * Get numeric color for difficulty tier using theme colors
 * Maps tiers to existing color scheme for consistency
 */
function getTierColorValue(
  tier: DifficultyTier,
  theme: ReturnType<typeof useKoreanTheme>,
): number {
  switch (tier) {
    case DifficultyTier.BEGINNER:
      return theme.colors.POSITIVE_GREEN; // Green - Easy
    case DifficultyTier.NOVICE:
      return theme.colors.ACCENT_GREEN; // Light Green
    case DifficultyTier.INTERMEDIATE:
      return theme.colors.ACCENT_GOLD; // Gold - Medium
    case DifficultyTier.ADVANCED:
      return theme.colors.SECONDARY_ORANGE; // Orange
    case DifficultyTier.EXPERT:
      return theme.colors.NEGATIVE_RED; // Red - Hard
    default:
      return theme.colors.ACCENT_GOLD; // Default to medium
  }
}
 
/**
 * DifficultyIndicator - Visual feedback for current AI difficulty tier
 *
 * Uses relative positioning for embedding in container HUDs
 */
export const DifficultyIndicator: React.FC<DifficultyIndicatorProps> = ({
  tier,
  isMobile,
}) => {
  const theme = useKoreanTheme({ variant: "primary", size: "small", isMobile });
  const tierName = getTierName(tier);
  const tierColorValue = getTierColorValue(tier, theme);
 
  // Memoize color calculations
  const tierColor = useMemo(
    () => hexColorToCSS(tierColorValue),
    [tierColorValue],
  );
  const tierBackground = useMemo(
    () => hexToRgbaString(tierColorValue, 0.13),
    [tierColorValue],
  );
  const tierBoxShadow = useMemo(
    () => hexToRgbaString(tierColorValue, 0.27),
    [tierColorValue],
  );
 
  // Responsive sizing
  const fontSize = isMobile ? 11 : 13;
  const padding = isMobile ? "6px 10px" : "8px 12px";
 
  return (
    <div
      data-testid="difficulty-indicator"
      style={{
        position: "relative",
        padding,
        background: tierBackground, // 13% opacity background
        border: `2px solid ${tierColor}`,
        borderRadius: "4px",
        fontFamily: theme.fontFamily.KOREAN,
        fontSize: `${fontSize}px`,
        color: tierColor,
        textShadow: "0 0 4px rgba(0,0,0,0.8)",
        pointerEvents: "none",
        display: "flex",
        flexDirection: "column",
        alignItems: "center",
        gap: "2px",
        transition: "all 0.3s ease-in-out", // Smooth color/border transitions
        boxShadow: `0 0 8px ${tierBoxShadow}`, // Subtle glow effect (27% opacity)
        width: "100%",
        boxSizing: "border-box" as const,
      }}
    >
      <div
        data-testid="difficulty-label"
        style={{
          fontSize: isMobile ? "9px" : "10px",
          opacity: 0.8,
          letterSpacing: "0.5px",
        }}
      >
        AI Difficulty
      </div>
      <div
        data-testid="difficulty-tier"
        style={{
          fontWeight: "bold",
          whiteSpace: "nowrap",
          letterSpacing: "0.5px",
        }}
      >
        {tierName.korean} | {tierName.english}
      </div>
    </div>
  );
};