feat: list

This commit is contained in:
TrèsAbhi committed 2024-02-29 11:16:00 -06:00
1 parent fa8922e0a6
commit c0a3628b3b
3 files changed
+138 -35

No files matched your search

+116 -11
View File
@@ -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<Shot['type'], string> = {
// ricochet: 'Ricochet',
@@ -13,12 +21,19 @@ import {
// block: 'Blocked',
// };
// export const SHOT_ICONS: Record<Shot['type'], ReactNode> = {
// export const SHOT_ICONS: Record<ShotLayerBase[''], ReactNode> = {
// ricochet: <Ricochet width={24} height={24} />,
// block: <Block width={24} height={24} />,
// penetration: <NominalPenetration width={24} height={24} />,
// };
const layerTypeNames: Record<ArmorType | 'null', string> = {
[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}
>
<Html center>
<Html
center
style={{
pointerEvents: 'none',
}}
>
<Card
style={{
backgroundColor:
@@ -86,7 +106,7 @@ export function ShotDisplay() {
let length = LENGTH_INFINITY;
if (layer.index !== 0) {
const gap = shot[index - 1] as ShotLayerGap;
const gap = shot.layers[index - 1] as ShotLayerGap;
length = gap.distance;
}
@@ -100,7 +120,10 @@ export function ShotDisplay() {
</group>
{(() => {
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 (
<Html
position={shot.point}
style={{
transform: 'translateY(-50%)',
marginLeft: 16,
pointerEvents: 'none',
}}
>
<Card style={{ width: 256 }}>
<Flex direction="column">
<Text>
{lastStatus[0].toUpperCase()}
{lastStatus.slice(1)}
</Text>
{shot.layers.map((layer) => {
if (layer.type === null && !explosive) return null;
return (
<Flex align="center" gap="2">
<Flex>
{hasMultipleLayers && (
<Text size="2" style={{ width: 16 }}>
{layer.type === null
? null
: `${layer.index + 1}.`}
</Text>
)}
<Text size="2">
{layerTypeNames[`${layer.type}`]}
</Text>
</Flex>
{layer.type !== null && (
<>
<Flex align="center" style={{ width: 64 }}>
<NominalPenetration width={16} height={16} />
<Text size="2">
{Math.round(layer.thickness)}mm
</Text>
</Flex>
{layer.type !== ArmorType.External && (
<Flex align="center">
{layer.status === 'ricochet' ? (
<Ricochet width={16} height={16} />
) : layer.status === 'blocked' ? (
<Block width={16} height={16} />
) : (
<AngledPenetration width={16} height={16} />
)}
<Text size="2">
{Math.round(layer.thicknessAngled)}mm (
{Math.round(radToDeg(layer.angle))}°)
</Text>
</Flex>
)}
</>
)}
{layer.type === null && (
<Text size="2">
{Math.round(layer.distance * 1000)}mm (-
{Math.round(Math.min(100, layer.distance * 50))}%
penetration)
</Text>
)}
</Flex>
);
})}
</Flex>
</Card>
</Html>
);
})()}
</>
);
}
@@ -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 });
},
})}
+6 -3
View File
@@ -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 =
| {