From c0a3628b3b7b258b714254d1afcfd8075856224a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tr=C3=A8sAbhi?= <43380238+tresabhi@users.noreply.github.com> Date: Thu, 29 Feb 2024 11:16:00 -0600 Subject: [PATCH] feat: list --- .../Armor/components/ShotDisplay.tsx | 127 ++++++++++++++++-- .../SpacedArmorSceneComponent/index.tsx | 37 +++-- src/stores/tankopedia.ts | 9 +- 3 files changed, 138 insertions(+), 35 deletions(-) diff --git a/src/components/Armor/components/ShotDisplay.tsx b/src/components/Armor/components/ShotDisplay.tsx index 147cb71cf..d7edac3fb 100644 --- a/src/components/Armor/components/ShotDisplay.tsx +++ b/src/components/Armor/components/ShotDisplay.tsx @@ -1,11 +1,19 @@ -import { Card, Inset, Text } from '@radix-ui/themes'; +import { Card, Flex, Inset, Text } from '@radix-ui/themes'; import { Html } from '@react-three/drei'; import { Euler, Quaternion, Vector3 } from 'three'; +import { radToDeg } from 'three/src/math/MathUtils'; import { J_HAT } from '../../../constants/axis'; +import { isExplosive } from '../../../core/blitz/isExplosive'; +import { AngledPenetration } from '../../../icons/AngledPenetration'; +import { Block } from '../../../icons/Block'; +import { NominalPenetration } from '../../../icons/NominalPenetration'; +import { Ricochet } from '../../../icons/Ricochet'; +import { useDuel } from '../../../stores/duel'; import { ShotLayerGap, useTankopediaTemporary, } from '../../../stores/tankopedia'; +import { ArmorType } from './SpacedArmorScene'; // export const SHOT_NAMES: Record = { // ricochet: 'Ricochet', @@ -13,12 +21,19 @@ import { // block: 'Blocked', // }; -// export const SHOT_ICONS: Record = { +// export const SHOT_ICONS: Record = { // ricochet: , // block: , // penetration: , // }; +const layerTypeNames: Record = { + [ArmorType.Core]: 'Core', + [ArmorType.Spaced]: 'Spaced', + [ArmorType.External]: 'External', + null: 'Gap', +}; + // const THICKNESS = 0.05; // const LENGTH = 4; @@ -26,15 +41,15 @@ const LENGTH_INFINITY = 4; export function ShotDisplay() { const shot = useTankopediaTemporary((state) => state.shot); - const hasMultipleLayers = shot?.some( + const hasMultipleLayers = shot?.layers.some( (layer, index) => index !== 0 && layer.type !== null, ); - - console.log(shot); + const { shell } = useDuel.getState().antagonist!; + const explosive = isExplosive(shell.type); return ( <> - {shot?.map((layer, index) => { + {shot?.layers.map((layer, index) => { if (layer.type === null) return; const shellRotation = new Euler().setFromQuaternion( @@ -43,9 +58,9 @@ export function ShotDisplay() { J_HAT.angleTo(layer.shellNormal), ), ); - const nextLayer = shot[index + 1] as ShotLayerGap | undefined; + const nextLayer = shot.layers[index + 1] as ShotLayerGap | undefined; const trueStatus = - nextLayer?.status === 'wasted' ? 'blocked' : layer.status; + nextLayer?.status === 'blocked' ? 'blocked' : layer.status; return ( <> @@ -54,7 +69,12 @@ export function ShotDisplay() { position={layer.point} rotation={shellRotation} > - + {(() => { - if (layer.status !== 'ricochet' || index !== shot.length - 1) + if ( + layer.status !== 'ricochet' || + index !== shot.layers.length - 1 + ) return null; const ricochetShellNormal = layer.shellNormal .clone() @@ -129,6 +152,88 @@ export function ShotDisplay() { ); })} + + {shot && + (() => { + const lastLayer = shot.layers.at(-1)!; + const lastStatus = lastLayer.status; + + return ( + + + + + {lastStatus[0].toUpperCase()} + {lastStatus.slice(1)} + + + {shot.layers.map((layer) => { + if (layer.type === null && !explosive) return null; + + return ( + + + {hasMultipleLayers && ( + + {layer.type === null + ? null + : `${layer.index + 1}.`} + + )} + + {layerTypeNames[`${layer.type}`]} + + + + {layer.type !== null && ( + <> + + + + {Math.round(layer.thickness)}mm + + + + {layer.type !== ArmorType.External && ( + + {layer.status === 'ricochet' ? ( + + ) : layer.status === 'blocked' ? ( + + ) : ( + + )} + + {Math.round(layer.thicknessAngled)}mm ( + {Math.round(radToDeg(layer.angle))}°) + + + )} + + )} + + {layer.type === null && ( + + {Math.round(layer.distance * 1000)}mm (- + {Math.round(Math.min(100, layer.distance * 50))}% + penetration) + + )} + + ); + })} + + + + ); + })()} ); } diff --git a/src/components/Armor/components/SpacedArmorSceneComponent/index.tsx b/src/components/Armor/components/SpacedArmorSceneComponent/index.tsx index 24eefb27b..7bc70448c 100644 --- a/src/components/Armor/components/SpacedArmorSceneComponent/index.tsx +++ b/src/components/Armor/components/SpacedArmorSceneComponent/index.tsx @@ -100,7 +100,10 @@ export function SpacedArmorSceneComponent({ ? 1.1 : 1.05 : 1); - const shot: Shot = []; // TODO: type this + const shot: Shot = { + layers: [], + point: event.point, + }; // TODO: type this let remainingPenetration = penetration; let penetrationChance = -1; let splashChance = -1; @@ -109,14 +112,8 @@ export function SpacedArmorSceneComponent({ .clone() .sub(event.point) .normalize(); - const cameraNormal = shellNormal.clone().multiplyScalar(-1); - pushLogs( - shellNormal, - cameraNormal, - filterIntersections(event.intersections), - 0, - ); + pushLogs(shellNormal, filterIntersections(event.intersections), 0); function filterIntersections(intersectionsRaw: Intersection[]) { const intersectionsAll = intersectionsRaw.filter( @@ -164,7 +161,6 @@ export function SpacedArmorSceneComponent({ function pushLogs( shellNormal: Vector3, - cameraNormal: Vector3, intersections: ArmorMeshIntersection[], startingIndex: number, ) { @@ -173,15 +169,15 @@ export function SpacedArmorSceneComponent({ if (layerIndex !== 0 && intersections.length > 0) { const distance = ( - shot.at(-1) as ShotLayerBase + shot.layers.at(-1) as ShotLayerBase ).point.distanceTo(intersections[0].point); if (explosive) remainingPenetration -= 0.5 * remainingPenetration * distance; const wasted = remainingPenetration < 0; - shot.push({ + shot.layers.push({ type: null, - status: wasted ? 'wasted' : 'nominal', + status: wasted ? 'blocked' : 'penetration', distance, }); @@ -206,9 +202,9 @@ export function SpacedArmorSceneComponent({ 0.5 * remainingPenetration * distance; const wasted = remainingPenetration < 0; - shot.push({ + shot.layers.push({ type: null, - status: wasted ? 'wasted' : 'nominal', + status: wasted ? 'blocked' : 'penetration', distance, }); @@ -219,7 +215,7 @@ export function SpacedArmorSceneComponent({ remainingPenetration -= thickness; const blocked = remainingPenetration < 0; - shot.push({ + shot.layers.push({ type: ArmorType.External, index: layerIndex, shellNormal, @@ -242,11 +238,13 @@ export function SpacedArmorSceneComponent({ ? (1.4 * normalization * shell.caliber) / (2.0 * thickness) : normalization; const finalThickness = - thickness / Math.cos(angle - finalNormalization); + thickness === 0 + ? 0 + : thickness / Math.cos(angle - finalNormalization); remainingPenetration -= finalThickness; if (!threeCalibersRule && angle >= ricochet) { - shot.push({ + shot.layers.push({ type, index: layerIndex, shellNormal, @@ -271,7 +269,6 @@ export function SpacedArmorSceneComponent({ raycaster.set(intersection.point, newCameraNormal); pushLogs( newShellNormal, - newCameraNormal, filterIntersections( raycaster.intersectObjects(scene.children, true), ), @@ -281,7 +278,7 @@ export function SpacedArmorSceneComponent({ break; } else { const blocked = remainingPenetration < 0; - shot.push({ + shot.layers.push({ type, index: layerIndex, shellNormal, @@ -300,8 +297,6 @@ export function SpacedArmorSceneComponent({ } } - console.log('setting', shot); - useTankopediaTemporary.setState({ shot }); }, })} diff --git a/src/stores/tankopedia.ts b/src/stores/tankopedia.ts index 0d9137415..43879b663 100644 --- a/src/stores/tankopedia.ts +++ b/src/stores/tankopedia.ts @@ -65,13 +65,16 @@ interface ShotLayerNonExternal extends ShotLayerBase { export interface ShotLayerGap { type: null; - status: 'nominal' | 'wasted'; + status: 'penetration' | 'blocked'; distance: number; } -type ShotLayer = ShotLayerExternal | ShotLayerNonExternal | ShotLayerGap; +export type ShotLayer = ShotLayerExternal | ShotLayerNonExternal | ShotLayerGap; -export type Shot = ShotLayer[]; +export type Shot = { + point: Vector3; + layers: ShotLayer[]; +}; export type ArmorPiercingLayer = | {