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 | 30x 30x 30x 30x 4x 60x 30x 30x 30x 30x 30x 60x 60x 4x | /**
* BalanceIndicator Component - Visual indicator for player balance state
*
* Displays a color-coded border around the player representing their
* combat balance state: READY (green), SHAKEN (yellow), VULNERABLE (orange), HELPLESS (red).
*
* NOTE: This component is rendered OUTSIDE the Canvas as part of the HTML overlay.
* It does NOT use Html from drei - it's a standard React component.
*
* @module components/combat/BalanceIndicator
* @category Combat UI
* @korean 균형표시기
*/
import React, { useMemo } from "react";
import { KOREAN_COLORS } from "../../../../../types/constants";
import type { BalanceState } from "../../../../../types/player-visual";
export interface BalanceIndicatorProps {
/**
* Current balance state
* @korean 균형상태
*/
readonly balanceState: BalanceState;
/**
* Player position ('left' or 'right' side of screen)
* @korean 플레이어위치
*/
readonly position: "left" | "right";
/**
* Mobile responsive mode (thinner borders)
* @korean 모바일여부
*/
readonly isMobile: boolean;
}
/**
* Get color for balance state
*/
function getBalanceColor(state: BalanceState): number {
switch (state) {
case "READY":
return KOREAN_COLORS.POSITIVE_GREEN; // 🟢 Green
case "SHAKEN":
return KOREAN_COLORS.WARNING_YELLOW; // 🟡 Yellow
case "VULNERABLE":
return KOREAN_COLORS.WARNING_ORANGE; // 🟠 Orange
case "HELPLESS":
return KOREAN_COLORS.ACCENT_RED; // 🔴 Red
}
}
/**
* Get Korean label for balance state
*/
function getBalanceLabel(state: BalanceState): {
korean: string;
english: string;
} {
switch (state) {
case "READY":
return { korean: "준비완료", english: "READY" };
case "SHAKEN":
return { korean: "동요상태", english: "SHAKEN" };
case "VULNERABLE":
return { korean: "취약상태", english: "VULNERABLE" };
case "HELPLESS":
return { korean: "무력상태", english: "HELPLESS" };
}
}
/**
* BalanceIndicator - Color-coded border indicator for player balance state
*
* Renders a border around the player HUD area with color matching the
* current balance state. Uses smooth transitions for state changes.
*
* @example
* ```tsx
* <BalanceIndicator
* balanceState="SHAKEN"
* position="left"
* isMobile={false}
* />
* ```
*/
export const BalanceIndicator: React.FC<BalanceIndicatorProps> = ({
balanceState,
position,
isMobile,
}) => {
const indicatorStyle = useMemo(() => {
const color = getBalanceColor(balanceState);
const colorHex = `#${color.toString(16).padStart(6, "0")}`;
// Mobile uses thinner border
const borderWidth = isMobile ? "3px" : "4px";
// Position based on player side
const isLeft = position === "left";
return {
position: "absolute" as const,
top: isMobile ? "8px" : "12px",
left: isLeft ? (isMobile ? "8px" : "12px") : "auto",
right: isLeft ? "auto" : isMobile ? "8px" : "12px",
width: isMobile ? "180px" : "220px",
height: isMobile ? "80px" : "100px",
border: `${borderWidth} solid ${colorHex}`,
borderRadius: "8px",
boxShadow: `0 0 12px ${colorHex}`,
pointerEvents: "none" as const,
transition: "border-color 0.5s ease-out, box-shadow 0.5s ease-out",
zIndex: 90, // Below HUD text but above game content
};
}, [balanceState, position, isMobile]);
const label = useMemo(() => getBalanceLabel(balanceState), [balanceState]);
return (
<div
data-testid={`balance-indicator-${position}`}
style={indicatorStyle}
aria-label={`${label.korean} | ${label.english}`}
role="status"
aria-live="polite"
/>
);
};
BalanceIndicator.displayName = "BalanceIndicator";
|