All files / components/screens/combat/components/indicators TechniqueNameDisplay.tsx

4.54% Statements 1/22
0% Branches 0/28
0% Functions 0/5
4.54% Lines 1/22

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 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213                                                                                                                                                                                3x                                                                                                                                                                                                                                                        
/**
 * Technique Name Display Component
 * 
 * Shows Korean technique name during attack execution with animated overlay.
 * Implements the technique-to-animation link by displaying technique names
 * when attacks are performed.
 * 
 * @module components/combat/components/TechniqueNameDisplay
 * @category Combat UI
 * @korean 기술이름표시
 */
 
import React, { useEffect, useState } from "react";
import { Html } from "@react-three/drei";
import { KOREAN_COLORS } from "../../../../../types/constants";
import { toHexColor } from "../../../../../utils/colorHelpers";
 
/**
 * Props for TechniqueNameDisplay component
 * 
 * @public
 * @korean 기술이름표시속성
 */
export interface TechniqueNameDisplayProps {
  /**
   * Korean technique name to display
   * @korean 한글기술이름
   */
  readonly koreanName?: string;
 
  /**
   * English technique name (optional)
   * @korean 영어기술이름
   */
  readonly englishName?: string;
 
  /**
   * Duration to display in milliseconds
   * @korean 표시지속시간
   */
  readonly duration?: number;
 
  /**
   * Position in 3D space (relative to attacker)
   * @korean 3D공간위치
   */
  readonly position?: [number, number, number];
 
  /**
   * Whether technique is critical hit
   * @korean 치명타여부
   */
  readonly isCritical?: boolean;
 
  /**
   * Whether to show the technique name
   * @korean 표시여부
   */
  readonly visible?: boolean;
}
 
/**
 * Technique Name Display Component
 * 
 * Displays Korean and English technique names during attack execution.
 * Uses Html overlay from @react-three/drei for 3D positioning.
 * 
 * Features:
 * - Bilingual Korean-English display
 * - Animated fade-in/fade-out
 * - Critical hit styling with red glow
 * - Auto-hide after duration
 * 
 * @example
 * ```tsx
 * <TechniqueNameDisplay
 *   koreanName="경동맥격"
 *   englishName="Carotid Strike"
 *   duration={2000}
 *   position={[0, 2, 0]}
 *   isCritical={true}
 *   visible={true}
 * />
 * ```
 * 
 * @public
 * @korean 기술이름표시컴포넌트
 */
export const TechniqueNameDisplay: React.FC<TechniqueNameDisplayProps> = ({
  koreanName,
  englishName,
  duration = 2000,
  position = [0, 2, 0],
  isCritical = false,
  visible = true,
}) => {
  const [opacity, setOpacity] = useState(0);
  const [isVisible, setIsVisible] = useState(false);
 
  useEffect(() => {
    if (!visible || (!koreanName && !englishName)) {
      // Ensure overlay is hidden when not visible or no names provided
      setIsVisible(false);
      setOpacity(0);
      return;
    }
 
    // Fade in
    setIsVisible(true);
    setOpacity(1);
 
    // Fade out after duration
    const fadeOutTimer = setTimeout(() => {
      setOpacity(0);
    }, duration - 500); // Start fading 500ms before end
 
    // Hide completely after fade out
    const hideTimer = setTimeout(() => {
      setIsVisible(false);
    }, duration);
 
    return () => {
      clearTimeout(fadeOutTimer);
      clearTimeout(hideTimer);
    };
  }, [visible, koreanName, englishName, duration]);
 
  if (!isVisible || (!koreanName && !englishName)) {
    return null;
  }
 
  // Color based on critical hit
  const textColor = isCritical ? "#FF0055" : toHexColor(KOREAN_COLORS.ACCENT_GOLD);
  const glowColor = isCritical ? "#FF0055" : toHexColor(KOREAN_COLORS.PRIMARY_CYAN);
 
  return (
    <Html
      position={position}
      center
      distanceFactor={10}
      occlude={false}
      style={{
        transition: "opacity 0.5s ease-in-out",
        opacity,
        pointerEvents: "none",
        userSelect: "none",
      }}
      data-testid="technique-name-display"
    >
      <div
        style={{
          display: "flex",
          flexDirection: "column",
          alignItems: "center",
          gap: "4px",
          fontFamily: "'Nanum Gothic', 'Noto Sans KR', sans-serif",
          textShadow: `0 0 10px ${glowColor}, 0 0 20px ${glowColor}`,
        }}
      >
        {/* Korean name (primary) */}
        {koreanName && (
          <div
            style={{
              fontSize: isCritical ? "32px" : "28px",
              fontWeight: "bold",
              color: textColor,
              letterSpacing: "2px",
            }}
            data-testid="technique-name-korean"
          >
            {koreanName}
          </div>
        )}
 
        {/* English name (secondary) */}
        {englishName && (
          <div
            style={{
              fontSize: isCritical ? "18px" : "16px",
              fontWeight: "normal",
              color: toHexColor(KOREAN_COLORS.PRIMARY_CYAN),
              opacity: 0.8,
              letterSpacing: "1px",
            }}
            data-testid="technique-name-english"
          >
            {englishName}
          </div>
        )}
 
        {/* Critical hit indicator */}
        {isCritical && (
          <div
            style={{
              fontSize: "14px",
              fontWeight: "bold",
              color: "#FF0055",
              marginTop: "4px",
              textTransform: "uppercase",
              letterSpacing: "2px",
            }}
            data-testid="technique-critical-indicator"
          >
            치명타 | CRITICAL
          </div>
        )}
      </div>
    </Html>
  );
};
 
export default TechniqueNameDisplay;