From c12fb0d668ce1c8c186df33e0aa4ad84f9dd6158 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tr=C3=A8sAbhi?= <43380238+tresabhi@users.noreply.github.com> Date: Mon, 29 Jul 2024 13:22:20 -0500 Subject: [PATCH] feat: card --- .../[id]/components/Model/TankSandbox.tsx | 6 ++- .../Armor/components/ArmorPlateDisplay.tsx | 43 +++++++++++++++++++ .../Armor/components/ShotDisplayCard.tsx | 2 +- .../SpacedArmorSceneComponent/index.tsx | 31 +++++++++++-- src/stores/tankopediaEphemeral.ts | 14 +++++- 5 files changed, 88 insertions(+), 8 deletions(-) create mode 100644 src/components/Armor/components/ArmorPlateDisplay.tsx diff --git a/src/app/tools/tankopedia/[id]/components/Model/TankSandbox.tsx b/src/app/tools/tankopedia/[id]/components/Model/TankSandbox.tsx index 3dda8bec2..ffd7a967b 100644 --- a/src/app/tools/tankopedia/[id]/components/Model/TankSandbox.tsx +++ b/src/app/tools/tankopedia/[id]/components/Model/TankSandbox.tsx @@ -1,6 +1,7 @@ import { Canvas } from '@react-three/fiber'; import { Suspense, use, useEffect, useRef } from 'react'; import { Armor } from '../../../../../../components/Armor'; +import { ArmorPlateDisplay } from '../../../../../../components/Armor/components/ArmorPlateDisplay'; import { ShotDisplay } from '../../../../../../components/Armor/components/ShotDisplay'; import { StaticArmor } from '../../../../../../components/StaticArmor'; import { applyPitchYawLimits } from '../../../../../../core/blitz/applyPitchYawLimits'; @@ -157,6 +158,9 @@ export function TankSandbox() { {display !== TankopediaDisplay.StaticArmor && } + + + {/* idk why the shot display doesn't work without suspense here lol */} }> {display === TankopediaDisplay.DynamicArmor && } @@ -165,8 +169,6 @@ export function TankSandbox() { )} - - ); } diff --git a/src/components/Armor/components/ArmorPlateDisplay.tsx b/src/components/Armor/components/ArmorPlateDisplay.tsx new file mode 100644 index 000000000..07370c593 --- /dev/null +++ b/src/components/Armor/components/ArmorPlateDisplay.tsx @@ -0,0 +1,43 @@ +import { Card, Flex, Text } from '@radix-ui/themes'; +import { Html } from '@react-three/drei'; +import { radToDeg } from 'three/src/math/MathUtils'; +import * as TankopediaEphemeral from '../../../stores/tankopediaEphemeral'; +import { layerTypeNames } from './ShotDisplayCard'; +import { ArmorType } from './SpacedArmorScene'; + +export function ArmorPlateDisplay() { + const highlightArmor = TankopediaEphemeral.use( + (state) => state.highlightArmor, + ); + + if (highlightArmor === undefined) return null; + + return ( + + + + + + {layerTypeNames[highlightArmor.type]}{' '} + {highlightArmor.thickness.toFixed(0)} + + mm + + + {highlightArmor.type !== ArmorType.External && ( + + {highlightArmor.thicknessAngled.toFixed(0)} + + mm {radToDeg(highlightArmor.angle).toFixed(0)}° + + + )} + + + + + ); +} diff --git a/src/components/Armor/components/ShotDisplayCard.tsx b/src/components/Armor/components/ShotDisplayCard.tsx index b7b29cbaf..313eee2f3 100644 --- a/src/components/Armor/components/ShotDisplayCard.tsx +++ b/src/components/Armor/components/ShotDisplayCard.tsx @@ -6,7 +6,7 @@ import { shotStatusColors } from './ShotDisplay'; import { ArmorType } from './SpacedArmorScene'; import { ExternalModuleVariant } from './SpacedArmorSceneComponent'; -const layerTypeNames: Record = { +export const layerTypeNames: Record = { [ArmorType.Core]: 'Core', [ArmorType.Spaced]: 'Spaced', [ArmorType.External]: 'External', diff --git a/src/components/Armor/components/SpacedArmorSceneComponent/index.tsx b/src/components/Armor/components/SpacedArmorSceneComponent/index.tsx index 5456ac513..14c433d95 100644 --- a/src/components/Armor/components/SpacedArmorSceneComponent/index.tsx +++ b/src/components/Armor/components/SpacedArmorSceneComponent/index.tsx @@ -3,6 +3,8 @@ import { clamp } from 'lodash'; import { useCallback, useEffect, useMemo } from 'react'; import { Color, + DoubleSide, + FrontSide, Intersection, MeshBasicMaterial, MeshStandardMaterial, @@ -74,6 +76,7 @@ const omitMaterial = new MeshBasicMaterial({ depthWrite: true, }); const unselectedColor = new Color(0x404040); +const externalModuleColor = new Color(0, 1 / 4, 1 / 2); /** * Render orders allocations: @@ -395,7 +398,7 @@ export function SpacedArmorSceneComponent({ break; case ArmorType.External: - color = new Color(0, 1, 1); + color = externalModuleColor; opacity = 1 / 8; renderOrder = 0; depthWrite = false; @@ -427,9 +430,10 @@ export function SpacedArmorSceneComponent({ if (selectedName === undefined) { // nothing selected, go back to defaults material.opacity = opacity; - material.transparent = true; + material.transparent = opacity < 1; material.color = color; material.depthWrite = props.type !== ArmorType.External; + material.side = FrontSide; } else if ( selectedName === props.name || (props.name.startsWith('chassis_') && @@ -444,17 +448,19 @@ export function SpacedArmorSceneComponent({ material.transparent = false; material.color = color; material.depthWrite = true; + material.side = DoubleSide; } else { // something else selected, become background material.opacity = 1 / 4; material.transparent = true; material.color = unselectedColor; material.depthWrite = props.type !== ArmorType.External; + material.side = FrontSide; } } const unsubscribe = tankopediaEphemeralStore.subscribe( - (state) => state.highlightArmor, + (state) => state.highlightArmor?.name, handleHighlightArmor, ); @@ -474,8 +480,25 @@ export function SpacedArmorSceneComponent({ onClick(event) { event.stopPropagation(); + + const { point } = event; + const cameraNormal = camera.position.clone().sub(point).normalize(); + const surfaceNormal = event + .normal!.clone() + .applyQuaternion(event.object.getWorldQuaternion(new Quaternion())); + const angle = surfaceNormal.angleTo(cameraNormal); + const thicknessAngled = thickness / Math.sin(Math.PI / 2 - angle); + mutateTankopediaEphemeralStore((draft) => { - draft.highlightArmor = props.name; + draft.highlightArmor = { + type: props.type, + name: props.name, + point, + thickness, + thicknessAngled, + angle, + color: `#${color.getHexString()}`, + }; }); }, }); diff --git a/src/stores/tankopediaEphemeral.ts b/src/stores/tankopediaEphemeral.ts index 0e94e02c5..5960620ab 100644 --- a/src/stores/tankopediaEphemeral.ts +++ b/src/stores/tankopediaEphemeral.ts @@ -11,7 +11,19 @@ interface TankopediaEphemeral { shot?: Shot; skills: Record; controlsEnabled: boolean; - highlightArmor?: string; + highlightArmor?: { + name: string; + point: Vector3; + thickness: number; + color: string; + } & ( + | { + type: ArmorType.Core | ArmorType.Spaced; + thicknessAngled: number; + angle: number; + } + | { type: ArmorType.External } + ); } export interface ShotLayerBase {