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 | 2x 30x 30x 2x | /**
* PlayerStateOverlayHtml Component - Unified player state visual indicators
*
* Combines all player state visual effects into a single overlay:
* - Pain vignette
* - Balance indicator
* - Consciousness blur
* - Blood loss warning
* - Stamina warning
*
* @module components/combat/PlayerStateOverlayHtml
* @category Combat UI
* @korean 플레이어상태오버레이
*/
import React from "react";
import { PainVignette } from "../effects/PainVignette";
import { BalanceIndicator } from "../indicators/BalanceIndicator";
import { ConsciousnessBlur } from "../effects/ConsciousnessBlur";
import { BloodLossOverlayHtml } from "../effects/BloodLossOverlayHtml";
import { StaminaWarning } from "../indicators/StaminaWarning";
import type { BalanceState } from "../../../../../types/player-visual";
export interface PlayerStateOverlayProps {
/**
* Current pain level (0-100)
* @korean 통증
*/
readonly pain: number;
/**
* Current balance state
* @korean 균형상태
*/
readonly balanceState: BalanceState;
/**
* Player position ('left' or 'right')
* @korean 플레이어위치
*/
readonly position: "left" | "right";
/**
* Current consciousness level (0-100)
* @korean 의식
*/
readonly consciousness: number;
/**
* Current blood loss (0-100, optional)
* @korean 출혈
*/
readonly bloodLoss?: number;
/**
* Current stamina (0-100)
* @korean 체력
*/
readonly stamina: number;
/**
* Mobile responsive mode
* @korean 모바일여부
*/
readonly isMobile: boolean;
/**
* Multiplier applied to every fullscreen effect's visual weight (0.0-1.0).
*
* Used by the parent screen to attenuate fullscreen vignette / blur / flash
* effects when the 3D arena is already visually compressed (e.g. portrait
* mobile, where the arena is rendered in a 3:4 aspect ratio and consumes
* the majority of the viewport height). Default is `1.0` (no attenuation).
*
* Cascades to `PainVignette`, `ConsciousnessBlur`, `BloodLossOverlayHtml`
* and `StaminaWarning`. Does not affect `BalanceIndicator`, which is an
* informational indicator rather than a fullscreen overlay.
*
* @korean 효과강도배수
*/
readonly intensityScale?: number;
}
/**
* PlayerStateOverlayHtml - Unified visual effects for player state
*
* Combines all player state visual indicators into a single component
* with optimal performance and consistent rendering. All effects use
* smooth 0.5s transitions and are optimized for 60fps.
*
* Optimized with React.memo for performance:
* - Prevents re-renders when props haven't changed
* - Custom comparison function for precise control
* - Reduces DOM updates for 60fps target
*
* @example
* ```tsx
* <PlayerStateOverlayHtml
* pain={65}
* balanceState="SHAKEN"
* position="left"
* consciousness={80}
* bloodLoss={45}
* stamina={15}
* isMobile={false}
* />
* ```
*/
export const PlayerStateOverlayHtml = React.memo<PlayerStateOverlayProps>(
({
pain,
balanceState,
position,
consciousness,
bloodLoss = 0,
stamina,
isMobile,
intensityScale = 1,
}) => {
return (
<div data-testid="player-state-overlay" style={{ display: 'contents' }}>
{/* Pain vignette - shows when pain >= 5 (see PainVignette.tsx) */}
<PainVignette
pain={pain}
isMobile={isMobile}
intensityScale={intensityScale}
/>
{/* Balance indicator - always visible, color-coded by state (see BalanceIndicator.tsx) */}
<BalanceIndicator
balanceState={balanceState}
position={position}
isMobile={isMobile}
/>
{/* Consciousness blur - shows when consciousness <= 90 (see ConsciousnessBlur.tsx) */}
<ConsciousnessBlur
consciousness={consciousness}
isMobile={isMobile}
intensityScale={intensityScale}
/>
{/* Blood loss warning - pulses when bloodLoss >= 50 (see BloodLossOverlayHtml.tsx) */}
<BloodLossOverlayHtml
bloodLoss={bloodLoss}
isMobile={isMobile}
intensityScale={intensityScale}
/>
{/* Stamina warning - flashes when stamina < 20 (see StaminaWarning.tsx) */}
<StaminaWarning
stamina={stamina}
isMobile={isMobile}
intensityScale={intensityScale}
/>
</div>
);
},
(prevProps, nextProps) => {
return (
prevProps.pain === nextProps.pain &&
prevProps.balanceState === nextProps.balanceState &&
prevProps.position === nextProps.position &&
prevProps.consciousness === nextProps.consciousness &&
prevProps.bloodLoss === nextProps.bloodLoss &&
prevProps.stamina === nextProps.stamina &&
prevProps.isMobile === nextProps.isMobile &&
prevProps.intensityScale === nextProps.intensityScale
);
},
);
PlayerStateOverlayHtml.displayName = "PlayerStateOverlayHtml";
|