Files
blitzkit/packages/website/src/components/Armor/components/ShotDisplayCard.tsx
T

239 lines
7.2 KiB
TypeScript
Raw Normal View History

2024-09-22 21:27:46 -05:00
import {
Card,
Flex,
Inset,
Separator,
Text,
type CardProps,
} from '@radix-ui/themes';
2024-09-14 19:43:14 -05:00
import type { ComponentProps } from 'react';
2024-09-22 21:27:46 -05:00
import { radToDeg } from 'three/src/math/MathUtils.js';
2025-01-13 20:56:30 -06:00
import { literals } from '../../../core/i18n/literals';
2025-01-07 12:47:55 -06:00
import { useLocale } from '../../../hooks/useLocale';
2025-01-13 20:56:30 -06:00
import type { Shot, ShotLayer } from '../../../stores/tankopediaEphemeral';
2024-09-22 21:27:46 -05:00
import { shotStatusColors } from './ShotDisplay';
2024-06-28 20:41:24 -05:00
import { ArmorType } from './SpacedArmorScene';
2025-01-13 20:56:30 -06:00
export const layerTypeNames = {
[ArmorType.Primary]: 'primary',
[ArmorType.Spaced]: 'spaced',
[ArmorType.External]: 'external',
null: 'gap',
} as const;
2024-06-28 20:41:24 -05:00
function LayerEntry({
layerIndex,
shotStatusColor,
layerName,
layer,
angle,
}: {
layerIndex?: string;
shotStatusColor: ComponentProps<typeof Text>['color'];
layerName: string;
2024-09-22 21:12:18 -05:00
layer: ShotLayer;
2024-06-28 20:41:24 -05:00
angle?: number;
}) {
2025-01-13 20:56:30 -06:00
const { locale, strings } = useLocale();
2025-01-07 12:47:55 -06:00
2024-06-28 20:41:24 -05:00
return (
<Flex align="center" gap="2">
2024-07-29 20:18:31 -05:00
<Text size="1" color="gray" style={{ width: 12 }}>
2024-06-28 20:41:24 -05:00
{layerIndex}
</Text>
2024-07-29 20:46:57 -05:00
<Text size="2" color={shotStatusColor} style={{ width: 48 }}>
2024-06-28 20:41:24 -05:00
{layerName}
</Text>
{layer.type === null && (
<>
2024-07-29 20:18:31 -05:00
<Text size="2" color={shotStatusColor} style={{ width: 64 }}>
2025-01-14 14:13:00 -06:00
{literals(strings.common.units.mm, [
2025-01-13 20:56:30 -06:00
Math.round(layer.distance * 1000).toLocaleString(locale),
])}
2024-06-28 20:41:24 -05:00
</Text>
<Text size="2" color="gray">
2025-01-13 20:56:30 -06:00
{literals(
strings.website.tools.tankopedia.sandbox.dynamic.shot_card.stats
.ricochet_loss,
[Math.max(-100, -50 * layer.distance).toFixed(0)],
)}
2024-06-28 20:41:24 -05:00
</Text>
</>
)}
{layer.type === ArmorType.External && (
2024-07-29 20:18:31 -05:00
<Text size="2" color={shotStatusColor} style={{ width: 64 }}>
2025-01-14 14:13:00 -06:00
{literals(strings.common.units.mm, [
2025-01-13 20:56:30 -06:00
Math.round(layer.thickness).toLocaleString(locale),
])}
2024-06-28 20:41:24 -05:00
</Text>
)}
2024-07-29 20:46:57 -05:00
{(layer.type === ArmorType.Primary ||
layer.type === ArmorType.Spaced) && (
2024-06-28 20:41:24 -05:00
<>
2024-07-29 20:18:31 -05:00
<Text size="2" color={shotStatusColor} style={{ width: 80 }}>
2025-01-13 20:56:30 -06:00
{literals(
angle === undefined
2025-01-14 14:13:00 -06:00
? strings.common.units.mm
2025-01-13 20:56:30 -06:00
: strings.website.tools.tankopedia.sandbox.dynamic.shot_card
.stats.thickness_and_angle,
[
Math.round(layer.thicknessAngled).toLocaleString(locale),
radToDeg(angle ?? 0).toFixed(0),
],
)}
2024-06-28 20:41:24 -05:00
</Text>
<Text size="2" color="gray">
2025-01-13 20:56:30 -06:00
{literals(
strings.website.tools.tankopedia.sandbox.dynamic.shot_card.stats
.nominal,
[Math.round(layer.thickness).toLocaleString(locale)],
)}
2024-06-28 20:41:24 -05:00
</Text>
</>
)}
</Flex>
);
}
2024-07-29 20:18:31 -05:00
interface ShotDisplayCardProps extends CardProps {
2024-09-22 21:12:18 -05:00
shot: Shot;
2024-07-13 19:49:00 -05:00
}
2024-06-28 20:41:24 -05:00
2024-07-29 20:18:31 -05:00
export function ShotDisplayCard({ shot, ...props }: ShotDisplayCardProps) {
2025-01-13 20:56:30 -06:00
const { locale, strings } = useLocale();
2025-01-07 12:47:55 -06:00
2024-06-28 20:41:24 -05:00
if (!shot) return null;
const outTitleColor = shot.out
? shotStatusColors[shot.out.status]
: undefined;
const inTitleColor = shotStatusColors[shot.in.status];
return (
2024-07-29 20:18:31 -05:00
<Card style={{ width: 300 }} {...props}>
2024-06-28 20:41:24 -05:00
<Flex direction="column" gap="2">
<Flex direction="column" gap="1">
<Text color={inTitleColor} weight="bold">
2025-01-13 20:56:30 -06:00
{literals(
strings.website.tools.tankopedia.sandbox.dynamic.shot_card.status[
shot.in.status
],
[Math.round(shot.damage).toLocaleString(locale)],
2024-06-28 20:41:24 -05:00
)}
</Text>
<Flex direction="column">
{shot.in.layers.map((layer, index) => {
const layerName =
2025-01-13 20:56:30 -06:00
strings.website.tools.tankopedia.sandbox.dynamic.shot_card
.element[
layer.type === ArmorType.External
? layer.variant
: layerTypeNames[`${layer.type}`]
];
2024-06-28 20:41:24 -05:00
const layerIndex =
layer.type === null
? undefined
: `${(shot.containsGaps ? index / 2 : index) + 1}.`;
const shotStatusColor =
shot.in.status === 'splash'
? shotStatusColors.splash
: shotStatusColors[layer.status];
return (
<LayerEntry
key={index}
layer={layer}
layerIndex={layerIndex}
layerName={layerName}
shotStatusColor={shotStatusColor}
angle={
2024-07-29 20:46:57 -05:00
layer.type === ArmorType.Primary ||
2024-06-28 20:41:24 -05:00
layer.type === ArmorType.Spaced
? layer.angle
: undefined
}
/>
);
})}
</Flex>
</Flex>
{shot.out && shot.out.layers.length > 0 && (
<>
<Inset side="x">
<Flex align="center" gap="2">
<Separator
style={{
flex: 1,
}}
/>
<Text size="1" color="gray">
ricochet (-25% penetration)
</Text>
<Separator
style={{
flex: 1,
}}
/>
</Flex>
</Inset>
<Text color={outTitleColor} weight="bold">
2025-01-13 20:56:30 -06:00
{literals(
strings.website.tools.tankopedia.sandbox.dynamic.shot_card
.status[shot.out.status],
[Math.round(shot.damage).toLocaleString(locale)],
)}
2024-06-28 20:41:24 -05:00
</Text>
<Flex direction="column">
{shot.out.layers.map((layer, index) => {
const layerName =
2025-01-13 20:56:30 -06:00
strings.website.tools.tankopedia.sandbox.dynamic.shot_card
.element[
layer.type === ArmorType.External
? layer.variant
: layerTypeNames[`${layer.type}`]
];
2024-06-28 20:41:24 -05:00
const shiftedIndex = index + shot.out!.layers.length;
const layerIndex =
layer.type === null
? undefined
2024-09-06 23:17:53 -05:00
: `${
(shot.containsGaps ? shiftedIndex / 2 : shiftedIndex) +
1
}.`;
2024-06-28 20:41:24 -05:00
const shotStatusColor =
shot.out!.status === 'splash'
? shotStatusColors.splash
: shotStatusColors[layer.status];
return (
<LayerEntry
key={index}
layer={layer}
layerIndex={layerIndex}
layerName={layerName}
shotStatusColor={shotStatusColor}
angle={
2024-07-29 20:46:57 -05:00
layer.type === ArmorType.Primary ||
2024-06-28 20:41:24 -05:00
layer.type === ArmorType.Spaced
? layer.angle
: undefined
}
/>
);
})}
</Flex>
</>
)}
</Flex>
</Card>
);
}