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 | 2x 88x 88x 88x | /**
* TrainingLeftHUD - Left side HUD for training screen
*
* Contains:
* - Anatomy Display controls
* - Guard Indicator
*
* Gaming Layout Best Practice:
* - Width: Resolution-based 14-18% of screen
* - Height: 100% minus top/bottom HUD heights
* - When combined with the right HUD, leaves roughly 64–72% center width for the arena depending on resolution
*
* Responsible for sizing and positioning all left-side UI elements.
* Now uses shared HUD utilities with resolution-based sizing.
*
* @korean 훈련화면 왼쪽 HUD - 해부학 표시 및 가드 표시기
*/
import React from "react";
import { useHUDLayout } from "../../../../../hooks/useHUDLayout";
import { TRIGRAM_STANCES_ORDER } from "../../../../../systems/trigram/types";
import { TrigramStance } from "../../../../../types/common";
import { BaseHUDContainer } from "../../../../shared/ui/BaseHUDContainer";
import { GuardIndicator } from "../../../../shared/three/indicators/GuardIndicator";
import AnatomyControlsOverlayHtml from "../AnatomyControlsOverlayHtml";
import type { AnatomyLayer } from "../AnatomyOverlay3D";
export interface TrainingLeftHUDProps {
/** Screen width for layout calculations */
readonly width: number;
/** Screen height for layout calculations */
readonly height: number;
/** Whether mobile controls should be shown (NOT for sizing) */
readonly isMobile?: boolean;
/** Position scale multiplier for large displays */
readonly positionScale: number;
/** Currently visible anatomy layers */
readonly visibleAnatomyLayers: readonly AnatomyLayer[];
/** Handler for toggling anatomy layers */
readonly onAnatomyLayerToggle: (layer: AnatomyLayer) => void;
/** Current stance index (0-7) */
readonly currentStanceIndex: number;
/** Whether player is in guard stance */
readonly isInGuard: boolean;
}
/**
* TrainingLeftHUD Component
*
* Left side of the training screen containing anatomy controls and guard indicator.
* Uses resolution-based sizing for smooth scaling across all screen sizes.
* Uses shared HUD utilities for consistent layout and styling.
*/
export const TrainingLeftHUD: React.FC<TrainingLeftHUDProps> = ({
width,
height,
isMobile = false,
positionScale,
visibleAnatomyLayers,
onAnatomyLayerToggle,
currentStanceIndex,
isInGuard,
}) => {
// Use shared HUD layout hook
const layout = useHUDLayout(
width,
height,
positionScale,
'left',
'training'
);
const currentStance: TrigramStance =
TRIGRAM_STANCES_ORDER[currentStanceIndex];
return (
<BaseHUDContainer
position="left"
width={layout.hudWidth}
height={layout.availableHeight}
topOffset={layout.topOffset}
padding={layout.padding}
gap={layout.gap}
dataTestId="training-left-hud"
>
{/* Anatomy Controls */}
<div
style={{
pointerEvents: "all",
display: "flex",
flexDirection: "column",
gap: `${layout.gap}px`,
maxWidth: "100%",
}}
data-testid="training-left-hud-anatomy-section"
>
<AnatomyControlsOverlayHtml
visibleLayers={visibleAnatomyLayers as AnatomyLayer[]}
onLayerToggle={onAnatomyLayerToggle}
isMobile={isMobile}
/>
</div>
{/* Guard Indicator */}
<div
style={{ pointerEvents: "none", maxWidth: "100%" }}
data-testid="training-left-hud-guard-section"
>
<GuardIndicator
currentStance={currentStance}
isInGuard={isInGuard}
position="left"
isMobile={isMobile}
/>
</div>
</BaseHUDContainer>
);
};
export default TrainingLeftHUD;
|