blacktrigram 0.7.48 → 0.7.49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/components/screens/combat/CombatScreen3D.d.ts.map +1 -1
- package/lib/components/screens/combat/CombatScreen3D.js +7 -14
- package/lib/components/screens/combat/CombatScreen3D.js.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatBottomHUD.d.ts.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatBottomHUD.js +2 -2
- package/lib/components/screens/combat/components/hud/CombatBottomHUD.js.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatPortraitStatusStrip.js +1 -1
- package/lib/components/screens/combat/components/hud/CombatTopHUD.d.ts.map +1 -1
- package/lib/components/screens/combat/components/hud/CombatTopHUD.js +2 -1
- package/lib/components/screens/combat/components/hud/CombatTopHUD.js.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatLayout.d.ts.map +1 -1
- package/lib/components/screens/combat/hooks/useCombatLayout.js +11 -5
- package/lib/components/screens/combat/hooks/useCombatLayout.js.map +1 -1
- package/lib/components/screens/controls/ControlsScreen3D.js +1 -1
- package/lib/components/screens/intro/IntroScreen3D.js +1 -1
- package/lib/components/screens/philosophy/PhilosophyScreen3D.js +1 -1
- package/lib/components/screens/training/TrainingScreen3D.d.ts.map +1 -1
- package/lib/components/screens/training/TrainingScreen3D.js +2 -11
- package/lib/components/screens/training/TrainingScreen3D.js.map +1 -1
- package/lib/components/screens/training/components/hud/TrainingBottomHUD.d.ts.map +1 -1
- package/lib/components/screens/training/components/hud/TrainingBottomHUD.js +2 -2
- package/lib/components/screens/training/components/hud/TrainingBottomHUD.js.map +1 -1
- package/lib/components/screens/training/hooks/useTrainingLayout.d.ts.map +1 -1
- package/lib/components/screens/training/hooks/useTrainingLayout.js +11 -5
- package/lib/components/screens/training/hooks/useTrainingLayout.js.map +1 -1
- package/lib/components/shared/ui/SplashScreen.js +2 -2
- package/lib/hooks/useHUDLayout.d.ts.map +1 -1
- package/lib/hooks/useHUDLayout.js +3 -2
- package/lib/hooks/useHUDLayout.js.map +1 -1
- package/lib/types/constants/layout.d.ts +21 -0
- package/lib/types/constants/layout.d.ts.map +1 -1
- package/lib/types/constants/layout.js +22 -1
- package/lib/types/constants/layout.js.map +1 -1
- package/lib/utils/responsiveLayoutHelpers.d.ts +7 -0
- package/lib/utils/responsiveLayoutHelpers.d.ts.map +1 -1
- package/lib/utils/responsiveLayoutHelpers.js +16 -2
- package/lib/utils/responsiveLayoutHelpers.js.map +1 -1
- package/package.json +5 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CombatBottomHUD.d.ts","sourceRoot":"","sources":["../../../../../../src/components/screens/combat/components/hud/CombatBottomHUD.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"CombatBottomHUD.d.ts","sourceRoot":"","sources":["../../../../../../src/components/screens/combat/components/hud/CombatBottomHUD.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAwBjD,MAAM,WAAW,oBAAoB;IACnC,2CAA2C;IAC3C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,4CAA4C;IAC5C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,+DAA+D;IAC/D,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,mDAAmD;IACnD,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,8CAA8C;IAC9C,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,iDAAiD;IACjD,QAAQ,CAAC,UAAU,EAAE,SAAS,SAAS,EAAE,CAAC;IAC1C,qDAAqD;IACrD,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,yCAAyC;IACzC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,iCAAiC;IACjC,QAAQ,CAAC,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,sCAAsC;IACtC,QAAQ,CAAC,iBAAiB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACpD,iCAAiC;IACjC,QAAQ,CAAC,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC7C;AAED;;;;;GAKG;AACH,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,oBAAoB,CA8L1D,CAAC;AAEF,eAAe,eAAe,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BORDERS, BORDER_RADIUS, COMBAT_UI_DIMENSIONS, COMBAT_UI_DIMENSIONS_NUMERIC, FONT_SIZE_MULTIPLIERS, GRADIENTS, HIERARCHY, HUD_STYLE, OPACITY, SPACING, SPACING_ADJUSTMENTS, TEXT_EFFECTS, TYPOGRAPHY, TYPOGRAPHY_NUMERIC } from "../../../../../types/constants/designSystem.js";
|
|
2
2
|
import { Z_INDEX } from "../../../../../types/LayoutTypes.js";
|
|
3
|
-
import { HUD_SIDE_CONTROL_RESERVES } from "../../../../../types/constants/layout.js";
|
|
3
|
+
import { COMBAT_BOTTOM_HUD_HEIGHT_PERCENT, HUD_SIDE_CONTROL_RESERVES } from "../../../../../types/constants/layout.js";
|
|
4
4
|
import { BREAKPOINTS, getHUDHeight, getResponsiveFontSize, getResponsivePadding, parsePercentageToRatio, shouldShowMobileControls } from "../../../../../utils/responsiveLayout.js";
|
|
5
5
|
import TechniqueBar from "../../../../shared/three/ui/TechniqueBar.js";
|
|
6
6
|
import { VolumeControl } from "../../../../shared/ui/VolumeControl.js";
|
|
@@ -30,7 +30,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
30
30
|
var CombatBottomHUD = ({ width, height, isMobile = false, positionScale, visible, techniques, player, selectedIndex, cooldowns, onTechniqueSelect, combatMessages = [] }) => {
|
|
31
31
|
const showMobileControls = shouldShowMobileControls(width, isMobile);
|
|
32
32
|
const layout = React.useMemo(() => {
|
|
33
|
-
const hudHeight = getHUDHeight(height,
|
|
33
|
+
const hudHeight = getHUDHeight(height, COMBAT_BOTTOM_HUD_HEIGHT_PERCENT) * positionScale;
|
|
34
34
|
const padding = getResponsivePadding(width) * positionScale;
|
|
35
35
|
const baseFontSize = getResponsiveFontSize(width);
|
|
36
36
|
const titleFontSize = Math.max(TYPOGRAPHY_NUMERIC.nano, baseFontSize * FONT_SIZE_MULTIPLIERS.titleSmall);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CombatBottomHUD.js","names":[],"sources":["../../../../../../src/components/screens/combat/components/hud/CombatBottomHUD.tsx"],"sourcesContent":["/**\n * CombatBottomHUD - Bottom bar for combat screen\n *\n * Contains:\n * - Technique Bar (centered)\n * - Volume Control (bottom-right, compact)\n * - Combat Messages (above technique bar)\n *\n * Gaming Layout Best Practice:\n * - Width: 100% of screen\n * - Height: Resolution-based ~10% of screen height (40-120px range)\n *\n * @korean 전투화면 하단 바 - 기술 바, 음량, 전투 메시지\n */\n\nimport React from \"react\";\nimport { PlayerState } from \"../../../../../systems\";\nimport { Technique } from \"../../../../../types\";\nimport { HUD_SIDE_CONTROL_RESERVES } from \"../../../../../types/constants/layout\";\nimport { Z_INDEX } from \"../../../../../types/LayoutTypes\";\nimport { SPACING, SPACING_ADJUSTMENTS, BORDER_RADIUS, TYPOGRAPHY, TYPOGRAPHY_NUMERIC, HIERARCHY, BORDERS, GRADIENTS, HUD_STYLE ,\n OPACITY,\n COMBAT_UI_DIMENSIONS,\n COMBAT_UI_DIMENSIONS_NUMERIC,\n TEXT_EFFECTS,\n FONT_SIZE_MULTIPLIERS,\n} from \"../../../../../types/constants/designSystem\";\nimport {\n BREAKPOINTS,\n getHUDHeight,\n getResponsiveFontSize,\n getResponsivePadding,\n parsePercentageToRatio,\n shouldShowMobileControls,\n} from \"../../../../../utils/responsiveLayout\";\nimport { TechniqueBar } from \"../../../../shared/three/ui/TechniqueBar\";\nimport { VolumeControl } from \"../../../../shared/ui/VolumeControl\";\n\nexport interface CombatBottomHUDProps {\n /** Screen width for layout calculations */\n readonly width: number;\n /** Screen height for layout calculations */\n readonly height: number;\n /** Whether mobile controls should be shown (NOT for sizing) */\n readonly isMobile?: boolean;\n /** Position scale multiplier for large displays */\n readonly positionScale: number;\n /** Whether technique bar should be visible */\n readonly visible: boolean;\n /** Available techniques for the technique bar */\n readonly techniques: readonly Technique[];\n /** Player state for technique availability checks */\n readonly player: PlayerState;\n /** Currently selected technique index */\n readonly selectedIndex: number;\n /** Active technique cooldowns */\n readonly cooldowns: Map<string, number>;\n /** Handler for technique selection */\n readonly onTechniqueSelect: (index: number) => void;\n /** Combat messages to display */\n readonly combatMessages?: readonly string[];\n}\n\n/**\n * CombatBottomHUD Component\n *\n * Compact bottom bar with centered technique bar, volume control,\n * and combat messages. Uses resolution-based sizing for all elements.\n */\nexport const CombatBottomHUD: React.FC<CombatBottomHUDProps> = ({\n width,\n height,\n isMobile = false,\n positionScale,\n visible,\n techniques,\n player,\n selectedIndex,\n cooldowns,\n onTechniqueSelect,\n combatMessages = [],\n}) => {\n const showMobileControls = shouldShowMobileControls(width, isMobile);\n\n const layout = React.useMemo(() => {\n const hudHeight = getHUDHeight(height, 0.1) * positionScale;\n \n const padding = getResponsivePadding(width) * positionScale;\n \n const baseFontSize = getResponsiveFontSize(width);\n const titleFontSize = Math.max(TYPOGRAPHY_NUMERIC.nano, baseFontSize * FONT_SIZE_MULTIPLIERS.titleSmall);\n const messageFontSize = Math.max(TYPOGRAPHY_NUMERIC.caption, baseFontSize * FONT_SIZE_MULTIPLIERS.messageSmall);\n \n const minMessageWidth = width < BREAKPOINTS.mobile \n ? COMBAT_UI_DIMENSIONS_NUMERIC.combatLogMinMobile\n : COMBAT_UI_DIMENSIONS_NUMERIC.combatLogMinDesktop;\n const maxMessageWidth = width < BREAKPOINTS.mobile \n ? width * COMBAT_UI_DIMENSIONS.combatLogMaxWidthPercentMobile\n : COMBAT_UI_DIMENSIONS_NUMERIC.combatLogMaxDesktop;\n const maxTechniqueBarWidth = width < BREAKPOINTS.mobile \n ? COMBAT_UI_DIMENSIONS.techniqueBarWidthMobile \n : COMBAT_UI_DIMENSIONS.techniqueBarWidthDesktop;\n \n const messagePadding = width < BREAKPOINTS.mobile \n ? `${SPACING_ADJUSTMENTS.compact} ${SPACING.sm}` \n : `${SPACING.xs} ${SPACING.md}`;\n\n const usableWidth = width - padding * 2;\n const maxBarWidthPx = usableWidth * parsePercentageToRatio(maxTechniqueBarWidth);\n const volumeReserve = showMobileControls\n ? HUD_SIDE_CONTROL_RESERVES.TECHNIQUE_BAR_MOBILE\n : HUD_SIDE_CONTROL_RESERVES.VOLUME_CONTROL;\n\n const techniqueBarContainerWidth = Math.max(\n 0,\n Math.min(\n maxBarWidthPx,\n usableWidth - volumeReserve,\n ),\n );\n\n return {\n hudHeight,\n padding,\n titleFontSize,\n messageFontSize,\n minMessageWidth,\n maxMessageWidth,\n maxTechniqueBarWidth,\n messagePadding,\n volumeReserve,\n techniqueBarContainerWidth,\n };\n }, [width, height, positionScale, showMobileControls]);\n\n const recentMessages = combatMessages.slice(-3);\n\n return (\n <div\n style={{\n position: \"absolute\",\n bottom: 0,\n left: 0,\n width: \"100%\",\n height: `${layout.hudHeight}px`,\n display: \"flex\",\n flexDirection: \"column\",\n justifyContent: \"flex-end\",\n alignItems: \"center\",\n pointerEvents: \"none\",\n padding: `${layout.padding}px`,\n boxSizing: \"border-box\",\n borderTop: BORDERS.default,\n background: GRADIENTS.verticalReverse(0.9),\n backdropFilter: HUD_STYLE.backdropFilter,\n }}\n data-testid=\"combat-bottom-hud\"\n >\n {/* Combat Messages - styled box above technique bar */}\n {recentMessages.length > 0 && (\n <div\n style={{\n position: \"absolute\",\n top: `${layout.padding}px`,\n left: \"50%\",\n transform: \"translateX(-50%)\",\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n gap: SPACING_ADJUSTMENTS.micro,\n zIndex: Z_INDEX.HUD,\n padding: layout.messagePadding,\n background: HUD_STYLE.background,\n border: BORDERS.muted,\n borderRadius: BORDER_RADIUS.md,\n boxShadow: HUD_STYLE.shadow,\n minWidth: `${layout.minMessageWidth}px`,\n maxWidth: typeof layout.maxMessageWidth === 'number' \n ? `${layout.maxMessageWidth}px` \n : layout.maxMessageWidth,\n }}\n data-testid=\"combat-bottom-hud-messages\"\n >\n <div\n style={{\n fontSize: `${layout.titleFontSize}px`,\n fontFamily: TYPOGRAPHY.caption.fontFamily,\n color: HIERARCHY.accent70.color,\n textTransform: \"uppercase\",\n letterSpacing: \"1px\",\n marginBottom: SPACING_ADJUSTMENTS.tiny,\n }}\n >\n 전투 기록 | Combat Log\n </div>\n {recentMessages.map((message, index) => (\n <div\n key={index}\n style={{\n fontSize: `${layout.messageFontSize}px`,\n fontFamily: TYPOGRAPHY.bodySmall.fontFamily,\n color: HIERARCHY.primary.color,\n textShadow: TEXT_EFFECTS.darkShadow,\n opacity: OPACITY.base + index * OPACITY.increment,\n textAlign: \"center\",\n }}\n >\n {message}\n </div>\n ))}\n </div>\n )}\n\n {/* Technique Bar - centered, embedded mode for proper containment.\n Reserve space on the right for the absolute Volume Control so cards\n never visually overlap the volume button. */}\n {visible && (\n <div\n style={{\n pointerEvents: \"all\",\n display: \"flex\",\n justifyContent: \"center\",\n alignItems: \"center\",\n width: \"100%\",\n maxWidth: layout.maxTechniqueBarWidth,\n marginRight: layout.volumeReserve,\n }}\n data-testid=\"combat-bottom-hud-technique-section\"\n >\n <TechniqueBar\n techniques={techniques as Technique[]}\n player={player}\n selectedIndex={selectedIndex}\n cooldowns={cooldowns}\n onTechniqueSelect={onTechniqueSelect}\n onTechniqueHover={(_tech) => {}}\n isMobile={showMobileControls}\n screenWidth={width}\n screenHeight={height}\n embedded={true}\n containerWidth={layout.techniqueBarContainerWidth}\n />\n </div>\n )}\n\n {/* Volume Control - bottom right corner */}\n <div\n style={{\n position: \"absolute\",\n right: `${layout.padding * 1.5}px`,\n bottom: `${layout.padding}px`,\n pointerEvents: \"all\",\n }}\n data-testid=\"combat-bottom-hud-volume-section\"\n >\n <VolumeControl position=\"custom\" compact={true} />\n </div>\n </div>\n );\n};\n\nexport default CombatBottomHUD;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CombatBottomHUD.js","names":[],"sources":["../../../../../../src/components/screens/combat/components/hud/CombatBottomHUD.tsx"],"sourcesContent":["/**\n * CombatBottomHUD - Bottom bar for combat screen\n *\n * Contains:\n * - Technique Bar (centered)\n * - Volume Control (bottom-right, compact)\n * - Combat Messages (above technique bar)\n *\n * Gaming Layout Best Practice:\n * - Width: 100% of screen\n * - Height: Resolution-based ~10% of screen height (40-120px range)\n *\n * @korean 전투화면 하단 바 - 기술 바, 음량, 전투 메시지\n */\n\nimport React from \"react\";\nimport { PlayerState } from \"../../../../../systems\";\nimport { Technique } from \"../../../../../types\";\nimport {\n COMBAT_BOTTOM_HUD_HEIGHT_PERCENT,\n HUD_SIDE_CONTROL_RESERVES,\n} from \"../../../../../types/constants/layout\";\nimport { Z_INDEX } from \"../../../../../types/LayoutTypes\";\nimport { SPACING, SPACING_ADJUSTMENTS, BORDER_RADIUS, TYPOGRAPHY, TYPOGRAPHY_NUMERIC, HIERARCHY, BORDERS, GRADIENTS, HUD_STYLE ,\n OPACITY,\n COMBAT_UI_DIMENSIONS,\n COMBAT_UI_DIMENSIONS_NUMERIC,\n TEXT_EFFECTS,\n FONT_SIZE_MULTIPLIERS,\n} from \"../../../../../types/constants/designSystem\";\nimport {\n BREAKPOINTS,\n getHUDHeight,\n getResponsiveFontSize,\n getResponsivePadding,\n parsePercentageToRatio,\n shouldShowMobileControls,\n} from \"../../../../../utils/responsiveLayout\";\nimport { TechniqueBar } from \"../../../../shared/three/ui/TechniqueBar\";\nimport { VolumeControl } from \"../../../../shared/ui/VolumeControl\";\n\nexport interface CombatBottomHUDProps {\n /** Screen width for layout calculations */\n readonly width: number;\n /** Screen height for layout calculations */\n readonly height: number;\n /** Whether mobile controls should be shown (NOT for sizing) */\n readonly isMobile?: boolean;\n /** Position scale multiplier for large displays */\n readonly positionScale: number;\n /** Whether technique bar should be visible */\n readonly visible: boolean;\n /** Available techniques for the technique bar */\n readonly techniques: readonly Technique[];\n /** Player state for technique availability checks */\n readonly player: PlayerState;\n /** Currently selected technique index */\n readonly selectedIndex: number;\n /** Active technique cooldowns */\n readonly cooldowns: Map<string, number>;\n /** Handler for technique selection */\n readonly onTechniqueSelect: (index: number) => void;\n /** Combat messages to display */\n readonly combatMessages?: readonly string[];\n}\n\n/**\n * CombatBottomHUD Component\n *\n * Compact bottom bar with centered technique bar, volume control,\n * and combat messages. Uses resolution-based sizing for all elements.\n */\nexport const CombatBottomHUD: React.FC<CombatBottomHUDProps> = ({\n width,\n height,\n isMobile = false,\n positionScale,\n visible,\n techniques,\n player,\n selectedIndex,\n cooldowns,\n onTechniqueSelect,\n combatMessages = [],\n}) => {\n const showMobileControls = shouldShowMobileControls(width, isMobile);\n\n const layout = React.useMemo(() => {\n const hudHeight = getHUDHeight(height, COMBAT_BOTTOM_HUD_HEIGHT_PERCENT) * positionScale;\n \n const padding = getResponsivePadding(width) * positionScale;\n \n const baseFontSize = getResponsiveFontSize(width);\n const titleFontSize = Math.max(TYPOGRAPHY_NUMERIC.nano, baseFontSize * FONT_SIZE_MULTIPLIERS.titleSmall);\n const messageFontSize = Math.max(TYPOGRAPHY_NUMERIC.caption, baseFontSize * FONT_SIZE_MULTIPLIERS.messageSmall);\n \n const minMessageWidth = width < BREAKPOINTS.mobile \n ? COMBAT_UI_DIMENSIONS_NUMERIC.combatLogMinMobile\n : COMBAT_UI_DIMENSIONS_NUMERIC.combatLogMinDesktop;\n const maxMessageWidth = width < BREAKPOINTS.mobile \n ? width * COMBAT_UI_DIMENSIONS.combatLogMaxWidthPercentMobile\n : COMBAT_UI_DIMENSIONS_NUMERIC.combatLogMaxDesktop;\n const maxTechniqueBarWidth = width < BREAKPOINTS.mobile \n ? COMBAT_UI_DIMENSIONS.techniqueBarWidthMobile \n : COMBAT_UI_DIMENSIONS.techniqueBarWidthDesktop;\n \n const messagePadding = width < BREAKPOINTS.mobile \n ? `${SPACING_ADJUSTMENTS.compact} ${SPACING.sm}` \n : `${SPACING.xs} ${SPACING.md}`;\n\n const usableWidth = width - padding * 2;\n const maxBarWidthPx = usableWidth * parsePercentageToRatio(maxTechniqueBarWidth);\n const volumeReserve = showMobileControls\n ? HUD_SIDE_CONTROL_RESERVES.TECHNIQUE_BAR_MOBILE\n : HUD_SIDE_CONTROL_RESERVES.VOLUME_CONTROL;\n\n const techniqueBarContainerWidth = Math.max(\n 0,\n Math.min(\n maxBarWidthPx,\n usableWidth - volumeReserve,\n ),\n );\n\n return {\n hudHeight,\n padding,\n titleFontSize,\n messageFontSize,\n minMessageWidth,\n maxMessageWidth,\n maxTechniqueBarWidth,\n messagePadding,\n volumeReserve,\n techniqueBarContainerWidth,\n };\n }, [width, height, positionScale, showMobileControls]);\n\n const recentMessages = combatMessages.slice(-3);\n\n return (\n <div\n style={{\n position: \"absolute\",\n bottom: 0,\n left: 0,\n width: \"100%\",\n height: `${layout.hudHeight}px`,\n display: \"flex\",\n flexDirection: \"column\",\n justifyContent: \"flex-end\",\n alignItems: \"center\",\n pointerEvents: \"none\",\n padding: `${layout.padding}px`,\n boxSizing: \"border-box\",\n borderTop: BORDERS.default,\n background: GRADIENTS.verticalReverse(0.9),\n backdropFilter: HUD_STYLE.backdropFilter,\n }}\n data-testid=\"combat-bottom-hud\"\n >\n {/* Combat Messages - styled box above technique bar */}\n {recentMessages.length > 0 && (\n <div\n style={{\n position: \"absolute\",\n top: `${layout.padding}px`,\n left: \"50%\",\n transform: \"translateX(-50%)\",\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n gap: SPACING_ADJUSTMENTS.micro,\n zIndex: Z_INDEX.HUD,\n padding: layout.messagePadding,\n background: HUD_STYLE.background,\n border: BORDERS.muted,\n borderRadius: BORDER_RADIUS.md,\n boxShadow: HUD_STYLE.shadow,\n minWidth: `${layout.minMessageWidth}px`,\n maxWidth: typeof layout.maxMessageWidth === 'number' \n ? `${layout.maxMessageWidth}px` \n : layout.maxMessageWidth,\n }}\n data-testid=\"combat-bottom-hud-messages\"\n >\n <div\n style={{\n fontSize: `${layout.titleFontSize}px`,\n fontFamily: TYPOGRAPHY.caption.fontFamily,\n color: HIERARCHY.accent70.color,\n textTransform: \"uppercase\",\n letterSpacing: \"1px\",\n marginBottom: SPACING_ADJUSTMENTS.tiny,\n }}\n >\n 전투 기록 | Combat Log\n </div>\n {recentMessages.map((message, index) => (\n <div\n key={index}\n style={{\n fontSize: `${layout.messageFontSize}px`,\n fontFamily: TYPOGRAPHY.bodySmall.fontFamily,\n color: HIERARCHY.primary.color,\n textShadow: TEXT_EFFECTS.darkShadow,\n opacity: OPACITY.base + index * OPACITY.increment,\n textAlign: \"center\",\n }}\n >\n {message}\n </div>\n ))}\n </div>\n )}\n\n {/* Technique Bar - centered, embedded mode for proper containment.\n Reserve space on the right for the absolute Volume Control so cards\n never visually overlap the volume button. */}\n {visible && (\n <div\n style={{\n pointerEvents: \"all\",\n display: \"flex\",\n justifyContent: \"center\",\n alignItems: \"center\",\n width: \"100%\",\n maxWidth: layout.maxTechniqueBarWidth,\n marginRight: layout.volumeReserve,\n }}\n data-testid=\"combat-bottom-hud-technique-section\"\n >\n <TechniqueBar\n techniques={techniques as Technique[]}\n player={player}\n selectedIndex={selectedIndex}\n cooldowns={cooldowns}\n onTechniqueSelect={onTechniqueSelect}\n onTechniqueHover={(_tech) => {}}\n isMobile={showMobileControls}\n screenWidth={width}\n screenHeight={height}\n embedded={true}\n containerWidth={layout.techniqueBarContainerWidth}\n />\n </div>\n )}\n\n {/* Volume Control - bottom right corner */}\n <div\n style={{\n position: \"absolute\",\n right: `${layout.padding * 1.5}px`,\n bottom: `${layout.padding}px`,\n pointerEvents: \"all\",\n }}\n data-testid=\"combat-bottom-hud-volume-section\"\n >\n <VolumeControl position=\"custom\" compact={true} />\n </div>\n </div>\n );\n};\n\nexport default CombatBottomHUD;\n"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { KOREAN_COLORS } from "../../../../../types/constants/colors.js";
|
|
2
2
|
import { BORDERS, FONT_SIZE_MULTIPLIERS, GRADIENTS, HIERARCHY, HUD_STYLE, TYPOGRAPHY, TYPOGRAPHY_NUMERIC } from "../../../../../types/constants/designSystem.js";
|
|
3
|
-
import { hexToRgbaString } from "../../../../../utils/colorUtils.js";
|
|
4
3
|
import { getResponsiveFontSize, getResponsivePadding } from "../../../../../utils/responsiveLayout.js";
|
|
4
|
+
import { hexToRgbaString } from "../../../../../utils/colorUtils.js";
|
|
5
5
|
import { useMemo } from "react";
|
|
6
6
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
7
|
//#region src/components/screens/combat/components/hud/CombatPortraitStatusStrip.tsx
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CombatTopHUD.d.ts","sourceRoot":"","sources":["../../../../../../src/components/screens/combat/components/hud/CombatTopHUD.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,oBAAoB,EAAE,MAAM,qCAAqC,CAAC;
|
|
1
|
+
{"version":3,"file":"CombatTopHUD.d.ts","sourceRoot":"","sources":["../../../../../../src/components/screens/combat/components/hud/CombatTopHUD.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,oBAAoB,EAAE,MAAM,qCAAqC,CAAC;AAqB3E,MAAM,WAAW,iBAAiB;IAChC,2CAA2C;IAC3C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,4CAA4C;IAC5C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,+DAA+D;IAC/D,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,mDAAmD;IACnD,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,2BAA2B;IAC3B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,0BAA0B;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,2CAA2C;IAC3C,QAAQ,CAAC,UAAU,EAAE,oBAAoB,CAAC;IAC1C,4BAA4B;IAC5B,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,oCAAoC;IACpC,QAAQ,CAAC,cAAc,EAAE,MAAM,IAAI,CAAC;IACpC,iCAAiC;IACjC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC5B;AAED;;;;;GAKG;AACH,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAwIpD,CAAC;AAEF,eAAe,YAAY,CAAC"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { BORDERS, FONT_SIZE_MULTIPLIERS, GRADIENTS, HIERARCHY, HUD_STYLE, LAYOUT_MULTIPLIERS, TYPOGRAPHY, TYPOGRAPHY_NUMERIC } from "../../../../../types/constants/designSystem.js";
|
|
2
|
+
import { COMBAT_TOP_HUD_HEIGHT_PERCENT } from "../../../../../types/constants/layout.js";
|
|
2
3
|
import { getHUDHeight, getResponsiveFontSize, getResponsivePadding, shouldShowMobileControls } from "../../../../../utils/responsiveLayout.js";
|
|
3
4
|
import CombatTimer from "../../../../shared/ui/CombatTimer.js";
|
|
4
5
|
import CombatReturnToMenuButton from "../controls/CombatButtons.js";
|
|
@@ -28,7 +29,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
28
29
|
var CombatTopHUD = ({ width, height, isMobile = false, positionScale, currentRound, totalRounds, timerState, showTimer, onReturnToMenu, isPaused: _isPaused }) => {
|
|
29
30
|
const showMobileControls = shouldShowMobileControls(width, isMobile);
|
|
30
31
|
const layout = React.useMemo(() => {
|
|
31
|
-
const hudHeight = getHUDHeight(height,
|
|
32
|
+
const hudHeight = getHUDHeight(height, COMBAT_TOP_HUD_HEIGHT_PERCENT) * positionScale;
|
|
32
33
|
const padding = getResponsivePadding(width) * positionScale;
|
|
33
34
|
const gap = padding * LAYOUT_MULTIPLIERS.gapToPadding;
|
|
34
35
|
const baseFontSize = getResponsiveFontSize(width) * positionScale;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CombatTopHUD.js","names":[],"sources":["../../../../../../src/components/screens/combat/components/hud/CombatTopHUD.tsx"],"sourcesContent":["/**\n * CombatTopHUD - Slim top bar for combat screen\n *\n * Gaming Best Practice - Minimal Top Bar:\n * - Round indicator (left)\n * - Timer (center)\n * - Return to Menu button (right)\n *\n * Layout:\n * - Width: 100% of screen\n * - Height: Resolution-based ~6% of screen height (40-80px)\n *\n * @korean 전투화면 상단 바 - 라운드, 타이머, 메뉴 복귀\n */\n\nimport React from \"react\";\nimport { UseCombatTimerReturn } from \"../../../../../hooks/useCombatTimer\";\nimport {\n BORDERS,\n FONT_SIZE_MULTIPLIERS,\n GRADIENTS,\n HIERARCHY,\n HUD_STYLE,\n LAYOUT_MULTIPLIERS,\n TYPOGRAPHY,\n TYPOGRAPHY_NUMERIC,\n} from \"../../../../../types/constants/designSystem\";\nimport {\n getHUDHeight,\n getResponsiveFontSize,\n getResponsivePadding,\n shouldShowMobileControls,\n} from \"../../../../../utils/responsiveLayout\";\nimport { CombatTimer } from \"../../../../shared/ui/CombatTimer\";\nimport { CombatReturnToMenuButton } from \"../controls/CombatButtons\";\n\nexport interface CombatTopHUDProps {\n /** Screen width for layout calculations */\n readonly width: number;\n /** Screen height for layout calculations */\n readonly height: number;\n /** Whether mobile controls should be shown (NOT for sizing) */\n readonly isMobile?: boolean;\n /** Position scale multiplier for large displays */\n readonly positionScale: number;\n /** Current round number */\n readonly currentRound: number;\n /** Max rounds in match */\n readonly totalRounds: number;\n /** Timer state from useCombatTimer hook */\n readonly timerState: UseCombatTimerReturn;\n /** Whether to show timer */\n readonly showTimer: boolean;\n /** Handler for returning to menu */\n readonly onReturnToMenu: () => void;\n /** Whether the game is paused */\n readonly isPaused: boolean;\n}\n\n/**\n * CombatTopHUD Component\n *\n * Slim top bar containing round info, timer, and return to menu button.\n * Uses resolution-based sizing for all dimensions.\n */\nexport const CombatTopHUD: React.FC<CombatTopHUDProps> = ({\n width,\n height,\n isMobile = false,\n positionScale,\n currentRound,\n totalRounds,\n timerState,\n showTimer,\n onReturnToMenu,\n isPaused: _isPaused, // Reserved for future pause indicator\n}) => {\n const showMobileControls = shouldShowMobileControls(width, isMobile);\n\n const layout = React.useMemo(() => {\n const hudHeight = getHUDHeight(height,
|
|
1
|
+
{"version":3,"file":"CombatTopHUD.js","names":[],"sources":["../../../../../../src/components/screens/combat/components/hud/CombatTopHUD.tsx"],"sourcesContent":["/**\n * CombatTopHUD - Slim top bar for combat screen\n *\n * Gaming Best Practice - Minimal Top Bar:\n * - Round indicator (left)\n * - Timer (center)\n * - Return to Menu button (right)\n *\n * Layout:\n * - Width: 100% of screen\n * - Height: Resolution-based ~6% of screen height (40-80px)\n *\n * @korean 전투화면 상단 바 - 라운드, 타이머, 메뉴 복귀\n */\n\nimport React from \"react\";\nimport { UseCombatTimerReturn } from \"../../../../../hooks/useCombatTimer\";\nimport { COMBAT_TOP_HUD_HEIGHT_PERCENT } from \"../../../../../types/constants/layout\";\nimport {\n BORDERS,\n FONT_SIZE_MULTIPLIERS,\n GRADIENTS,\n HIERARCHY,\n HUD_STYLE,\n LAYOUT_MULTIPLIERS,\n TYPOGRAPHY,\n TYPOGRAPHY_NUMERIC,\n} from \"../../../../../types/constants/designSystem\";\nimport {\n getHUDHeight,\n getResponsiveFontSize,\n getResponsivePadding,\n shouldShowMobileControls,\n} from \"../../../../../utils/responsiveLayout\";\nimport { CombatTimer } from \"../../../../shared/ui/CombatTimer\";\nimport { CombatReturnToMenuButton } from \"../controls/CombatButtons\";\n\nexport interface CombatTopHUDProps {\n /** Screen width for layout calculations */\n readonly width: number;\n /** Screen height for layout calculations */\n readonly height: number;\n /** Whether mobile controls should be shown (NOT for sizing) */\n readonly isMobile?: boolean;\n /** Position scale multiplier for large displays */\n readonly positionScale: number;\n /** Current round number */\n readonly currentRound: number;\n /** Max rounds in match */\n readonly totalRounds: number;\n /** Timer state from useCombatTimer hook */\n readonly timerState: UseCombatTimerReturn;\n /** Whether to show timer */\n readonly showTimer: boolean;\n /** Handler for returning to menu */\n readonly onReturnToMenu: () => void;\n /** Whether the game is paused */\n readonly isPaused: boolean;\n}\n\n/**\n * CombatTopHUD Component\n *\n * Slim top bar containing round info, timer, and return to menu button.\n * Uses resolution-based sizing for all dimensions.\n */\nexport const CombatTopHUD: React.FC<CombatTopHUDProps> = ({\n width,\n height,\n isMobile = false,\n positionScale,\n currentRound,\n totalRounds,\n timerState,\n showTimer,\n onReturnToMenu,\n isPaused: _isPaused, // Reserved for future pause indicator\n}) => {\n const showMobileControls = shouldShowMobileControls(width, isMobile);\n\n const layout = React.useMemo(() => {\n const hudHeight = getHUDHeight(height, COMBAT_TOP_HUD_HEIGHT_PERCENT) * positionScale;\n\n const padding = getResponsivePadding(width) * positionScale;\n \n const gap = padding * LAYOUT_MULTIPLIERS.gapToPadding;\n \n const baseFontSize = getResponsiveFontSize(width) * positionScale;\n const fontSize = Math.max(TYPOGRAPHY_NUMERIC.bodySmall, baseFontSize * FONT_SIZE_MULTIPLIERS.bodySmall);\n const titleSize = Math.max(TYPOGRAPHY_NUMERIC.body, baseFontSize * FONT_SIZE_MULTIPLIERS.titleLarge);\n\n\n return {\n hudHeight,\n padding,\n gap,\n fontSize,\n titleSize,\n hudWidth: width,\n };\n }, [width, height, positionScale]);\n\n return (\n <div\n style={{\n position: \"absolute\",\n top: 0,\n left: 0,\n width: \"100%\",\n height: `${layout.hudHeight}px`,\n display: \"flex\",\n flexDirection: \"row\",\n justifyContent: \"space-between\",\n alignItems: \"center\",\n padding: `${layout.padding}px`,\n pointerEvents: \"none\",\n boxSizing: \"border-box\",\n borderBottom: BORDERS.default,\n background: GRADIENTS.vertical(0.9),\n backdropFilter: HUD_STYLE.backdropFilter,\n }}\n data-testid=\"combat-top-hud\"\n >\n {/* Left Section - Round Info */}\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n gap: \"2px\",\n pointerEvents: \"none\",\n alignItems: \"flex-start\",\n }}\n data-testid=\"combat-top-hud-left-section\"\n >\n {/* Title */}\n <div\n style={{\n fontSize: `${layout.titleSize}px`,\n fontWeight: 600,\n fontFamily: TYPOGRAPHY.heading2.fontFamily,\n color: HIERARCHY.gold.color,\n textShadow: HUD_STYLE.accentGlow,\n }}\n >\n 전투 | Combat\n </div>\n\n {/* Round indicator */}\n <div\n style={{\n display: \"flex\",\n gap: `${layout.gap}px`,\n alignItems: \"center\",\n fontSize: `${layout.fontSize}px`,\n fontFamily: TYPOGRAPHY.body.fontFamily,\n color: HIERARCHY.accent.color,\n }}\n >\n <span>\n 라운드 {currentRound}/{totalRounds}\n </span>\n </div>\n </div>\n\n {/* Center Section - Timer */}\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n gap: \"2px\",\n }}\n data-testid=\"combat-top-hud-center-section\"\n >\n {showTimer && (\n <CombatTimer\n formattedTime={timerState.formattedTime}\n warningLevel={timerState.warningLevel}\n isTimeUp={timerState.isTimeUp}\n isMobile={showMobileControls}\n style={{ position: \"relative\", top: 0 }}\n />\n )}\n </div>\n\n {/* Right Section - Return to Menu */}\n <div\n style={{\n display: \"flex\",\n flexDirection: \"row\",\n alignItems: \"center\",\n pointerEvents: \"all\",\n }}\n data-testid=\"combat-top-hud-right-section\"\n >\n <CombatReturnToMenuButton\n onClick={onReturnToMenu}\n isMobile={showMobileControls}\n />\n </div>\n </div>\n );\n};\n\nexport default CombatTopHUD;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkEA,IAAa,gBAA6C,EACxD,OACA,QACA,WAAW,OACX,eACA,cACA,aACA,YACA,WACA,gBACA,UAAU,gBACN;CACJ,MAAM,qBAAqB,yBAAyB,OAAO,QAAQ;CAEnE,MAAM,SAAS,MAAM,cAAc;EACjC,MAAM,YAAY,aAAa,QAAQ,6BAA6B,IAAI;EAExE,MAAM,UAAU,qBAAqB,KAAK,IAAI;EAE9C,MAAM,MAAM,UAAU,mBAAmB;EAEzC,MAAM,eAAe,sBAAsB,KAAK,IAAI;EAKpD,OAAO;GACL;GACA;GACA;GACA,UARe,KAAK,IAAI,mBAAmB,WAAW,eAAe,sBAAsB,SAQ3F;GACA,WARgB,KAAK,IAAI,mBAAmB,MAAM,eAAe,sBAAsB,UAQvF;GACA,UAAU;EACZ;CACF,GAAG;EAAC;EAAO;EAAQ;CAAa,CAAC;CAEjC,OACE,qBAAC,OAAD;EACE,OAAO;GACL,UAAU;GACV,KAAK;GACL,MAAM;GACN,OAAO;GACP,QAAQ,GAAG,OAAO,UAAU;GAC5B,SAAS;GACT,eAAe;GACf,gBAAgB;GAChB,YAAY;GACZ,SAAS,GAAG,OAAO,QAAQ;GAC3B,eAAe;GACf,WAAW;GACX,cAAc,QAAQ;GACtB,YAAY,UAAU,SAAS,EAAG;GAClC,gBAAgB,UAAU;EAC5B;EACA,eAAY;YAlBd;GAqBE,qBAAC,OAAD;IACE,OAAO;KACL,SAAS;KACT,eAAe;KACf,KAAK;KACL,eAAe;KACf,YAAY;IACd;IACA,eAAY;cARd,CAWE,oBAAC,OAAD;KACE,OAAO;MACL,UAAU,GAAG,OAAO,UAAU;MAC9B,YAAY;MACZ,YAAY,WAAW,SAAS;MAChC,OAAO,UAAU,KAAK;MACtB,YAAY,UAAU;KACxB;eACD;IAEI,CAAA,GAGL,oBAAC,OAAD;KACE,OAAO;MACL,SAAS;MACT,KAAK,GAAG,OAAO,IAAI;MACnB,YAAY;MACZ,UAAU,GAAG,OAAO,SAAS;MAC7B,YAAY,WAAW,KAAK;MAC5B,OAAO,UAAU,OAAO;KAC1B;eAEA,qBAAC,QAAD,EAAA,UAAA;MAAM;MACC;MAAa;MAAE;KAChB,EAAA,CAAA;IACH,CAAA,CACF;;GAGL,oBAAC,OAAD;IACE,OAAO;KACL,SAAS;KACT,eAAe;KACf,YAAY;KACZ,KAAK;IACP;IACA,eAAY;cAEX,aACC,oBAAC,aAAD;KACE,eAAe,WAAW;KAC1B,cAAc,WAAW;KACzB,UAAU,WAAW;KACrB,UAAU;KACV,OAAO;MAAE,UAAU;MAAY,KAAK;KAAE;IACvC,CAAA;GAEA,CAAA;GAGL,oBAAC,OAAD;IACE,OAAO;KACL,SAAS;KACT,eAAe;KACf,YAAY;KACZ,eAAe;IACjB;IACA,eAAY;cAEZ,oBAAC,0BAAD;KACE,SAAS;KACT,UAAU;IACX,CAAA;GACE,CAAA;EACF;;AAET"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useCombatLayout.d.ts","sourceRoot":"","sources":["../../../../../src/components/screens/combat/hooks/useCombatLayout.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;
|
|
1
|
+
{"version":3,"file":"useCombatLayout.d.ts","sourceRoot":"","sources":["../../../../../src/components/screens/combat/hooks/useCombatLayout.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAuBH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uCAAuC,CAAC;AAExE,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;CAClC;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;CACnC;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,eAAe,EAAE,eAAe,CAAC;IAC1C,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAClC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;CACjC;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,YAAY,CAkG3E"}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { getScreenSize } from "../../../../systems/ResponsiveScaling.js";
|
|
2
|
+
import { COMBAT_BOTTOM_HUD_HEIGHT_PERCENT, COMBAT_TOP_HUD_HEIGHT_PERCENT } from "../../../../types/constants/layout.js";
|
|
3
|
+
import { getHUDHeight } from "../../../../utils/responsiveLayout.js";
|
|
4
|
+
import { getCombatLayoutConstants, getDesktopArenaWidthBudget, getHUDPositionScale } from "../../../../utils/responsiveLayoutHelpers.js";
|
|
2
5
|
import { calculateArenaWorldDimensions } from "../../../../utils/arenaWorldDimensions.js";
|
|
3
6
|
import { shouldUseMobileControls } from "../../../../utils/deviceDetection.js";
|
|
4
7
|
import { calculateMobileAreaBounds } from "../../../../utils/mobileLayoutHelpers.js";
|
|
5
8
|
import { PORTRAIT_HYSTERESIS_FACTOR, mobileControlsBottomClearance } from "../../../../utils/responsiveOrientationConstants.js";
|
|
6
|
-
import { getCombatLayoutConstants, getDesktopArenaWidthBudget } from "../../../../utils/responsiveLayoutHelpers.js";
|
|
7
9
|
import { useMemo } from "react";
|
|
8
10
|
//#region src/components/screens/combat/hooks/useCombatLayout.ts
|
|
9
11
|
/**
|
|
@@ -47,18 +49,21 @@ function useCombatLayout(width, height) {
|
|
|
47
49
|
const isPortrait = height > width * PORTRAIT_HYSTERESIS_FACTOR;
|
|
48
50
|
const isMobile = shouldUseMobileControls() || isPortrait && width < 1024;
|
|
49
51
|
const layoutConstants = useMemo(() => getCombatLayoutConstants(width, isMobile), [width, isMobile]);
|
|
52
|
+
const positionScale = useMemo(() => getHUDPositionScale(screenSize, isMobile), [screenSize, isMobile]);
|
|
50
53
|
return {
|
|
51
54
|
layoutConstants,
|
|
52
55
|
arenaBounds: useMemo(() => {
|
|
53
56
|
const isExtraSmallWidth = width < 380;
|
|
54
57
|
const portraitStatusStripHeight = isMobile && isPortrait ? Math.max(isExtraSmallWidth ? 28 : 36, Math.round(height * .055)) : 0;
|
|
55
|
-
const
|
|
58
|
+
const topHudHeight = getHUDHeight(height, COMBAT_TOP_HUD_HEIGHT_PERCENT) * positionScale;
|
|
59
|
+
const bottomHudHeight = getHUDHeight(height, COMBAT_BOTTOM_HUD_HEIGHT_PERCENT) * positionScale;
|
|
60
|
+
const arenaY = topHudHeight + portraitStatusStripHeight + layoutConstants.padding;
|
|
56
61
|
const worldDimensions = calculateArenaWorldDimensions(width);
|
|
57
62
|
if (isMobile) {
|
|
58
63
|
const isExtraSmall = isExtraSmallWidth;
|
|
59
|
-
return calculateMobileAreaBounds(width, height,
|
|
64
|
+
return calculateMobileAreaBounds(width, height, topHudHeight + layoutConstants.padding + portraitStatusStripHeight, mobileControlsBottomClearance(layoutConstants.controlsHeight, Math.max(layoutConstants.footerHeight, bottomHudHeight), isExtraSmall, isPortrait, "combat"), arenaY, isPortrait ? "portrait" : "landscape");
|
|
60
65
|
}
|
|
61
|
-
const totalReservedHeight =
|
|
66
|
+
const totalReservedHeight = topHudHeight + bottomHudHeight;
|
|
62
67
|
const totalPadding = layoutConstants.padding * 3;
|
|
63
68
|
const availableHeight = height - totalReservedHeight - totalPadding;
|
|
64
69
|
let arenaWidth = getDesktopArenaWidthBudget(width);
|
|
@@ -82,7 +87,8 @@ function useCombatLayout(width, height) {
|
|
|
82
87
|
height,
|
|
83
88
|
layoutConstants,
|
|
84
89
|
isMobile,
|
|
85
|
-
isPortrait
|
|
90
|
+
isPortrait,
|
|
91
|
+
positionScale
|
|
86
92
|
]),
|
|
87
93
|
isMobile,
|
|
88
94
|
isPortrait,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useCombatLayout.js","names":[],"sources":["../../../../../src/components/screens/combat/hooks/useCombatLayout.ts"],"sourcesContent":["/**\n * useCombatLayout Hook - Enhanced Responsive Combat Layout\n *\n * Custom hook for managing responsive combat screen layout calculations with\n * comprehensive support for all screen sizes from mobile to ultra-wide displays.\n *\n * Enhanced Features:\n * - Five screen size categories (mobile, tablet, desktop, large, xlarge)\n * - Proportional scaling for consistent sizing across devices\n * - Optimized arena sizing for each device category\n * - Smooth transitions for resize operations\n * - 60fps performance maintained\n *\n * Uses robust device detection combining user-agent and screen size to ensure\n * mobile controls are shown on all mobile devices, including high-resolution phones.\n *\n * Performance:\n * - Reduces recalculations by checking only breakpoint changes, not exact dimensions\n * - Memoizes arena bounds to prevent cascading re-renders\n * - Targets <1ms execution time for layout calculations\n *\n * @param width - Screen width\n * @param height - Screen height\n *\n * @returns Layout constants and arena bounds\n *\n * @example\n * ```typescript\n * const { layoutConstants, arenaBounds, isMobile, screenSize } = useCombatLayout(1200, 800);\n * ```\n */\n\nimport { useMemo } from \"react\";\nimport { getScreenSize } from \"../../../../systems/ResponsiveScaling\";\nimport { calculateArenaWorldDimensions } from \"../../../../utils/arenaWorldDimensions\";\nimport { shouldUseMobileControls } from \"../../../../utils/deviceDetection\";\nimport { calculateMobileAreaBounds } from \"../../../../utils/mobileLayoutHelpers\";\nimport {\n mobileControlsBottomClearance,\n PORTRAIT_FORCE_MAX_WIDTH_PX,\n PORTRAIT_HYSTERESIS_FACTOR,\n} from \"../../../../utils/responsiveOrientationConstants\";\nimport {\n getCombatLayoutConstants,\n getDesktopArenaWidthBudget,\n} from \"../../../../utils/responsiveLayoutHelpers\";\n\nimport type { ScreenSize } from \"../../../../systems/ResponsiveScaling\";\n\nexport interface LayoutConstants {\n readonly padding: number;\n readonly hudHeight: number;\n readonly controlsHeight: number;\n readonly footerHeight: number;\n readonly healthBarHeight: number;\n}\n\nexport interface ArenaBounds {\n readonly x: number;\n readonly y: number;\n readonly width: number;\n readonly height: number;\n readonly scale: number; // 3D scale factor for arena (1.0 = desktop, <1.0 = mobile)\n readonly worldWidthMeters: number; // Physical arena width in meters\n readonly worldDepthMeters: number; // Physical arena depth in meters\n}\n\nexport interface CombatLayout {\n readonly layoutConstants: LayoutConstants;\n readonly arenaBounds: ArenaBounds;\n readonly isMobile: boolean;\n readonly isPortrait: boolean;\n readonly screenSize: ScreenSize;\n}\n\n/**\n * Custom hook for combat screen layout calculations\n * Enhanced with centralized responsive scaling system\n * Optimized to reduce recalculations and improve 60fps performance\n */\nexport function useCombatLayout(width: number, height: number): CombatLayout {\n const screenSize = useMemo(() => getScreenSize(width), [width]);\n\n const isPortrait = height > width * PORTRAIT_HYSTERESIS_FACTOR;\n\n const isMobile =\n shouldUseMobileControls() ||\n (isPortrait && width < PORTRAIT_FORCE_MAX_WIDTH_PX);\n\n const layoutConstants = useMemo<LayoutConstants>(\n () => getCombatLayoutConstants(width, isMobile),\n [width, isMobile],\n );\n\n const arenaBounds = useMemo<ArenaBounds>(() => {\n const isExtraSmallWidth = width < 380;\n const portraitStatusStripHeight =\n isMobile && isPortrait\n ? Math.max(isExtraSmallWidth ? 28 : 36, Math.round(height * 0.055))\n : 0;\n\n const arenaY =\n
|
|
1
|
+
{"version":3,"file":"useCombatLayout.js","names":[],"sources":["../../../../../src/components/screens/combat/hooks/useCombatLayout.ts"],"sourcesContent":["/**\n * useCombatLayout Hook - Enhanced Responsive Combat Layout\n *\n * Custom hook for managing responsive combat screen layout calculations with\n * comprehensive support for all screen sizes from mobile to ultra-wide displays.\n *\n * Enhanced Features:\n * - Five screen size categories (mobile, tablet, desktop, large, xlarge)\n * - Proportional scaling for consistent sizing across devices\n * - Optimized arena sizing for each device category\n * - Smooth transitions for resize operations\n * - 60fps performance maintained\n *\n * Uses robust device detection combining user-agent and screen size to ensure\n * mobile controls are shown on all mobile devices, including high-resolution phones.\n *\n * Performance:\n * - Reduces recalculations by checking only breakpoint changes, not exact dimensions\n * - Memoizes arena bounds to prevent cascading re-renders\n * - Targets <1ms execution time for layout calculations\n *\n * @param width - Screen width\n * @param height - Screen height\n *\n * @returns Layout constants and arena bounds\n *\n * @example\n * ```typescript\n * const { layoutConstants, arenaBounds, isMobile, screenSize } = useCombatLayout(1200, 800);\n * ```\n */\n\nimport { useMemo } from \"react\";\nimport { getScreenSize } from \"../../../../systems/ResponsiveScaling\";\nimport { calculateArenaWorldDimensions } from \"../../../../utils/arenaWorldDimensions\";\nimport { shouldUseMobileControls } from \"../../../../utils/deviceDetection\";\nimport { calculateMobileAreaBounds } from \"../../../../utils/mobileLayoutHelpers\";\nimport {\n mobileControlsBottomClearance,\n PORTRAIT_FORCE_MAX_WIDTH_PX,\n PORTRAIT_HYSTERESIS_FACTOR,\n} from \"../../../../utils/responsiveOrientationConstants\";\nimport {\n getCombatLayoutConstants,\n getDesktopArenaWidthBudget,\n getHUDPositionScale,\n} from \"../../../../utils/responsiveLayoutHelpers\";\nimport {\n COMBAT_BOTTOM_HUD_HEIGHT_PERCENT,\n COMBAT_TOP_HUD_HEIGHT_PERCENT,\n} from \"../../../../types/constants/layout\";\nimport { getHUDHeight } from \"../../../../utils/responsiveLayout\";\n\nimport type { ScreenSize } from \"../../../../systems/ResponsiveScaling\";\n\nexport interface LayoutConstants {\n readonly padding: number;\n readonly hudHeight: number;\n readonly controlsHeight: number;\n readonly footerHeight: number;\n readonly healthBarHeight: number;\n}\n\nexport interface ArenaBounds {\n readonly x: number;\n readonly y: number;\n readonly width: number;\n readonly height: number;\n readonly scale: number; // 3D scale factor for arena (1.0 = desktop, <1.0 = mobile)\n readonly worldWidthMeters: number; // Physical arena width in meters\n readonly worldDepthMeters: number; // Physical arena depth in meters\n}\n\nexport interface CombatLayout {\n readonly layoutConstants: LayoutConstants;\n readonly arenaBounds: ArenaBounds;\n readonly isMobile: boolean;\n readonly isPortrait: boolean;\n readonly screenSize: ScreenSize;\n}\n\n/**\n * Custom hook for combat screen layout calculations\n * Enhanced with centralized responsive scaling system\n * Optimized to reduce recalculations and improve 60fps performance\n */\nexport function useCombatLayout(width: number, height: number): CombatLayout {\n const screenSize = useMemo(() => getScreenSize(width), [width]);\n\n const isPortrait = height > width * PORTRAIT_HYSTERESIS_FACTOR;\n\n const isMobile =\n shouldUseMobileControls() ||\n (isPortrait && width < PORTRAIT_FORCE_MAX_WIDTH_PX);\n\n const layoutConstants = useMemo<LayoutConstants>(\n () => getCombatLayoutConstants(width, isMobile),\n [width, isMobile],\n );\n\n const positionScale = useMemo(\n () => getHUDPositionScale(screenSize, isMobile),\n [screenSize, isMobile],\n );\n\n const arenaBounds = useMemo<ArenaBounds>(() => {\n const isExtraSmallWidth = width < 380;\n const portraitStatusStripHeight =\n isMobile && isPortrait\n ? Math.max(isExtraSmallWidth ? 28 : 36, Math.round(height * 0.055))\n : 0;\n\n const topHudHeight =\n getHUDHeight(height, COMBAT_TOP_HUD_HEIGHT_PERCENT) * positionScale;\n const bottomHudHeight =\n getHUDHeight(height, COMBAT_BOTTOM_HUD_HEIGHT_PERCENT) * positionScale;\n const arenaY =\n topHudHeight +\n portraitStatusStripHeight +\n layoutConstants.padding;\n\n const worldDimensions = calculateArenaWorldDimensions(width);\n\n if (isMobile) {\n const isExtraSmall = isExtraSmallWidth;\n const minTopClearance =\n topHudHeight + layoutConstants.padding + portraitStatusStripHeight;\n\n const minBottomClearance = mobileControlsBottomClearance(\n layoutConstants.controlsHeight,\n Math.max(layoutConstants.footerHeight, bottomHudHeight),\n isExtraSmall,\n isPortrait,\n \"combat\",\n );\n\n const mobileBounds = calculateMobileAreaBounds(\n width,\n height,\n minTopClearance,\n minBottomClearance,\n arenaY,\n isPortrait ? \"portrait\" : \"landscape\",\n );\n\n return mobileBounds;\n }\n\n const totalReservedHeight =\n topHudHeight + bottomHudHeight;\n const totalPadding = layoutConstants.padding * 3;\n const availableHeight = height - totalReservedHeight - totalPadding;\n const availableWidth = getDesktopArenaWidthBudget(width);\n\n let arenaWidth = availableWidth;\n let arenaHeight = arenaWidth * (3 / 4); // 4:3 aspect ratio\n\n if (arenaHeight > availableHeight) {\n arenaHeight = availableHeight;\n arenaWidth = arenaHeight * (4 / 3);\n }\n\n const pixelsPerMeter = arenaWidth / worldDimensions.widthMeters;\n const referencePixelsPerMeter = 100;\n const scale = pixelsPerMeter / referencePixelsPerMeter;\n\n return {\n x: (width - arenaWidth) / 2, // Center horizontally\n y: arenaY,\n width: arenaWidth,\n height: arenaHeight, // 4:3 aspect ratio\n scale,\n worldWidthMeters: worldDimensions.widthMeters,\n worldDepthMeters: worldDimensions.depthMeters,\n };\n }, [width, height, layoutConstants, isMobile, isPortrait, positionScale]);\n\n return {\n layoutConstants,\n arenaBounds,\n isMobile,\n isPortrait,\n screenSize,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsFA,SAAgB,gBAAgB,OAAe,QAA8B;CAC3E,MAAM,aAAa,cAAc,cAAc,KAAK,GAAG,CAAC,KAAK,CAAC;CAE9D,MAAM,aAAa,SAAS,QAAQ;CAEpC,MAAM,WACJ,wBAAwB,KACvB,cAAc,QAAA;CAEjB,MAAM,kBAAkB,cAChB,yBAAyB,OAAO,QAAQ,GAC9C,CAAC,OAAO,QAAQ,CAClB;CAEA,MAAM,gBAAgB,cACd,oBAAoB,YAAY,QAAQ,GAC9C,CAAC,YAAY,QAAQ,CACvB;CA0EA,OAAO;EACL;EACA,aA1EkB,cAA2B;GAC7C,MAAM,oBAAoB,QAAQ;GAClC,MAAM,4BACJ,YAAY,aACR,KAAK,IAAI,oBAAoB,KAAK,IAAI,KAAK,MAAM,SAAS,IAAK,CAAC,IAChE;GAEN,MAAM,eACJ,aAAa,QAAQ,6BAA6B,IAAI;GACxD,MAAM,kBACJ,aAAa,QAAQ,gCAAgC,IAAI;GAC3D,MAAM,SACJ,eACA,4BACA,gBAAgB;GAElB,MAAM,kBAAkB,8BAA8B,KAAK;GAE3D,IAAI,UAAU;IACZ,MAAM,eAAe;IAqBrB,OATqB,0BACnB,OACA,QAZA,eAAe,gBAAgB,UAAU,2BAEhB,8BACzB,gBAAgB,gBAChB,KAAK,IAAI,gBAAgB,cAAc,eAAe,GACtD,cACA,YACA,QAOA,GACA,QACA,aAAa,aAAa,WAGrB;GACT;GAEA,MAAM,sBACJ,eAAe;GACjB,MAAM,eAAe,gBAAgB,UAAU;GAC/C,MAAM,kBAAkB,SAAS,sBAAsB;GAGvD,IAAI,aAFmB,2BAA2B,KAEjC;GACjB,IAAI,cAAc,cAAc,IAAI;GAEpC,IAAI,cAAc,iBAAiB;IACjC,cAAc;IACd,aAAa,eAAe,IAAI;GAClC;GAIA,MAAM,QAFiB,aAAa,gBAAgB,cAErB;GAE/B,OAAO;IACL,IAAI,QAAQ,cAAc;IAC1B,GAAG;IACH,OAAO;IACP,QAAQ;IACR;IACA,kBAAkB,gBAAgB;IAClC,kBAAkB,gBAAgB;GACpC;EACF,GAAG;GAAC;GAAO;GAAQ;GAAiB;GAAU;GAAY;EAAa,CAIrE;EACA;EACA;EACA;CACF;AACF"}
|
|
@@ -3,11 +3,11 @@ import { FONT_FAMILY } from "../../../types/constants/typography.js";
|
|
|
3
3
|
import { Z_INDEX } from "../../../types/LayoutTypes.js";
|
|
4
4
|
import { COMBAT_CONTROLS } from "../../../systems/types.js";
|
|
5
5
|
import { useWebGLContextLossHandler } from "../../../hooks/useWebGLContextLossHandler.js";
|
|
6
|
+
import { getLayoutConstants } from "../../../utils/responsiveLayoutHelpers.js";
|
|
6
7
|
import { hexToRgbaString } from "../../../utils/colorUtils.js";
|
|
7
8
|
import { useKoreanTheme } from "../../shared/base/useKoreanTheme.js";
|
|
8
9
|
import { VolumeControl } from "../../shared/ui/VolumeControl.js";
|
|
9
10
|
import { shouldUseMobileControls } from "../../../utils/deviceDetection.js";
|
|
10
|
-
import { getLayoutConstants } from "../../../utils/responsiveLayoutHelpers.js";
|
|
11
11
|
import BackgroundScene3D from "../../shared/three/scene/BackgroundScene3D.js";
|
|
12
12
|
import useWindowSize from "../../../hooks/useWindowSize.js";
|
|
13
13
|
import { BackButton } from "../../shared/ui/BackButton.js";
|
|
@@ -21,7 +21,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
|
21
21
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
22
22
|
import { Canvas } from "@react-three/fiber";
|
|
23
23
|
//#region src/components/screens/intro/IntroScreen3D.tsx
|
|
24
|
-
var APP_VERSION = "0.7.
|
|
24
|
+
var APP_VERSION = "0.7.49";
|
|
25
25
|
var MENU_ITEMS = [
|
|
26
26
|
{
|
|
27
27
|
mode: GameMode.VERSUS,
|
|
@@ -4,11 +4,11 @@ import { TRIGRAM_DATA } from "../../../systems/trigram/types.js";
|
|
|
4
4
|
import { PLAYER_ARCHETYPES_DATA } from "../../../systems/types.js";
|
|
5
5
|
import { useWebGLContextLossHandler } from "../../../hooks/useWebGLContextLossHandler.js";
|
|
6
6
|
import { KoreanCulture } from "../../../systems/trigram/KoreanCulture.js";
|
|
7
|
+
import { getLayoutConstants } from "../../../utils/responsiveLayoutHelpers.js";
|
|
7
8
|
import { hexToRgbaString } from "../../../utils/colorUtils.js";
|
|
8
9
|
import { useKoreanTheme } from "../../shared/base/useKoreanTheme.js";
|
|
9
10
|
import { VolumeControl } from "../../shared/ui/VolumeControl.js";
|
|
10
11
|
import { shouldUseMobileControls } from "../../../utils/deviceDetection.js";
|
|
11
|
-
import { getLayoutConstants } from "../../../utils/responsiveLayoutHelpers.js";
|
|
12
12
|
import BackgroundScene3D from "../../shared/three/scene/BackgroundScene3D.js";
|
|
13
13
|
import useWindowSize from "../../../hooks/useWindowSize.js";
|
|
14
14
|
import { BackButton, LinkButton } from "../../shared/ui/BackButton.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TrainingScreen3D.d.ts","sourceRoot":"","sources":["../../../../src/components/screens/training/TrainingScreen3D.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAeH,OAAO,KAMN,MAAM,OAAO,CAAC;AAQf,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAc/C,OAAO,EAEL,eAAe,EAIhB,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"TrainingScreen3D.d.ts","sourceRoot":"","sources":["../../../../src/components/screens/training/TrainingScreen3D.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAeH,OAAO,KAMN,MAAM,OAAO,CAAC;AAQf,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAc/C,OAAO,EAEL,eAAe,EAIhB,MAAM,gBAAgB,CAAC;AAqExB;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,sCAAsC;IACtC,QAAQ,CAAC,cAAc,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,WAAW,CAAC,KAAK,IAAI,CAAC;IACjE,sCAAsC;IACtC,QAAQ,CAAC,cAAc,EAAE,MAAM,IAAI,CAAC;IACpC,+CAA+C;IAC/C,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,+CAA+C;IAC/C,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,qEAAqE;IACrE,QAAQ,CAAC,gBAAgB,CAAC,EAAE,eAAe,CAAC;CAC7C;AAED;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CAwpC5D,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
|
|
@@ -17,6 +17,7 @@ import { physicalReachCalculator } from "../../../systems/physics/PhysicalReachC
|
|
|
17
17
|
import { getArchetypePhysicalAttributes } from "../../../data/archetypePhysicalAttributes.js";
|
|
18
18
|
import { getMobileControlsBottom } from "../../../types/constants/layout.js";
|
|
19
19
|
import { usePlayerMovement } from "../../../utils/inputSystem.js";
|
|
20
|
+
import { getHUDPositionScale } from "../../../utils/responsiveLayoutHelpers.js";
|
|
20
21
|
import { createCameraConfig } from "../../../utils/sharedPhysicsConfig.js";
|
|
21
22
|
import { useKoreanTheme } from "../../shared/base/useKoreanTheme.js";
|
|
22
23
|
import VitalPointMarkers3D from "../../shared/three/effects/VitalPointMarkers3D.js";
|
|
@@ -95,17 +96,7 @@ var TrainingScreen3D = ({ onPlayerUpdate, onReturnToMenu, width = 1200, height =
|
|
|
95
96
|
size: "md",
|
|
96
97
|
isMobile
|
|
97
98
|
});
|
|
98
|
-
const positionScale = React.useMemo(() =>
|
|
99
|
-
if (isMobile) return 1;
|
|
100
|
-
switch (screenSize) {
|
|
101
|
-
case "mobile": return 1;
|
|
102
|
-
case "tablet": return 1;
|
|
103
|
-
case "desktop": return 1;
|
|
104
|
-
case "large": return 1.25;
|
|
105
|
-
case "xlarge": return 1.5;
|
|
106
|
-
default: return 1;
|
|
107
|
-
}
|
|
108
|
-
}, [isMobile, screenSize]);
|
|
99
|
+
const positionScale = React.useMemo(() => getHUDPositionScale(screenSize, isMobile), [screenSize, isMobile]);
|
|
109
100
|
const difficulty = "normal";
|
|
110
101
|
const vitalPointCount = 70;
|
|
111
102
|
const [selectedArchetype, setSelectedArchetype] = React.useState(initialArchetype);
|