mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-11-13 20:56:11 +01:00
flatten armor dir
This commit is contained in:
1 parent
e18d6f58f9
commit
e5b44d4eb8
28 files changed
+90
-101
No files matched your search
@@ -0,0 +1,259 @@
|
||||
import { literals } from "@blitzkit/i18n/src/literals";
|
||||
import {
|
||||
Card,
|
||||
Flex,
|
||||
Inset,
|
||||
Separator,
|
||||
Text,
|
||||
type CardProps,
|
||||
} from "@radix-ui/themes";
|
||||
import type { ComponentProps } from "react";
|
||||
import { radToDeg } from "three/src/math/MathUtils.js";
|
||||
import { useLocale } from "../hooks/useLocale";
|
||||
import type { Shot, ShotLayer } from "../stores/tankopedia";
|
||||
import { shotStatusColors } from "./ShotDisplay";
|
||||
import { ArmorType } from "./SpacedArmorScene";
|
||||
|
||||
export const layerTypeNames = {
|
||||
[ArmorType.Primary]: "primary",
|
||||
[ArmorType.Spaced]: "spaced",
|
||||
[ArmorType.External]: "external",
|
||||
null: "gap",
|
||||
} as const;
|
||||
|
||||
function LayerEntry({
|
||||
layerIndex,
|
||||
shotStatusColor,
|
||||
layerName,
|
||||
layer,
|
||||
angle,
|
||||
}: {
|
||||
layerIndex?: string;
|
||||
shotStatusColor: ComponentProps<typeof Text>["color"];
|
||||
layerName: string;
|
||||
layer: ShotLayer;
|
||||
angle?: number;
|
||||
}) {
|
||||
const { locale, strings } = useLocale();
|
||||
|
||||
return (
|
||||
<tr>
|
||||
<td>
|
||||
<Text size="1" color="gray">
|
||||
{layerIndex}
|
||||
</Text>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<Text size="2" color={shotStatusColor}>
|
||||
{layerName}
|
||||
</Text>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
{layer.type === null && (
|
||||
<Text size="2" color={shotStatusColor}>
|
||||
{literals(strings.common.units.mm, {
|
||||
value: Math.round(layer.distance * 1000).toLocaleString(locale),
|
||||
})}
|
||||
</Text>
|
||||
)}
|
||||
|
||||
{layer.type === ArmorType.External && (
|
||||
<Text size="2" color={shotStatusColor}>
|
||||
{literals(strings.common.units.mm, {
|
||||
value: Math.round(layer.thickness).toLocaleString(locale),
|
||||
})}
|
||||
</Text>
|
||||
)}
|
||||
{(layer.type === ArmorType.Primary ||
|
||||
layer.type === ArmorType.Spaced) && (
|
||||
<Text size="2" color={shotStatusColor}>
|
||||
{angle === undefined
|
||||
? literals(strings.common.units.mm, {
|
||||
value: Math.round(layer.thicknessAngled).toLocaleString(
|
||||
locale
|
||||
),
|
||||
})
|
||||
: literals(
|
||||
strings.website.tools.tankopedia.sandbox.dynamic.shot_card
|
||||
.stats.thickness_and_angle,
|
||||
{
|
||||
thickness: Math.round(layer.thicknessAngled).toLocaleString(
|
||||
locale
|
||||
),
|
||||
angle: radToDeg(angle ?? 0).toFixed(0),
|
||||
}
|
||||
)}
|
||||
</Text>
|
||||
)}
|
||||
</td>
|
||||
|
||||
<td>
|
||||
{layer.type === null && (
|
||||
<Text size="2" color="gray">
|
||||
{literals(
|
||||
strings.website.tools.tankopedia.sandbox.dynamic.shot_card.stats
|
||||
.ricochet_loss,
|
||||
{ loss: Math.max(-100, -50 * layer.distance).toFixed(0) }
|
||||
)}
|
||||
</Text>
|
||||
)}
|
||||
|
||||
{(layer.type === ArmorType.Primary ||
|
||||
layer.type === ArmorType.Spaced) && (
|
||||
<Text size="2" color="gray">
|
||||
{literals(
|
||||
strings.website.tools.tankopedia.sandbox.dynamic.shot_card.stats
|
||||
.nominal,
|
||||
{ thickness: Math.round(layer.thickness).toLocaleString(locale) }
|
||||
)}
|
||||
</Text>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
interface ShotDisplayCardProps extends CardProps {
|
||||
shot: Shot;
|
||||
}
|
||||
|
||||
export function ShotDisplayCard({ shot, ...props }: ShotDisplayCardProps) {
|
||||
const { locale, strings } = useLocale();
|
||||
|
||||
if (!shot) return null;
|
||||
|
||||
const outTitleColor = shot.out
|
||||
? shotStatusColors[shot.out.status]
|
||||
: undefined;
|
||||
const inTitleColor = shotStatusColors[shot.in.status];
|
||||
|
||||
return (
|
||||
<Card variant="classic" {...props}>
|
||||
<Flex direction="column" gap="2">
|
||||
<Flex direction="column" gap="1">
|
||||
<Text color={inTitleColor} weight="bold">
|
||||
{literals(
|
||||
strings.website.tools.tankopedia.sandbox.dynamic.shot_card.status[
|
||||
shot.in.status
|
||||
],
|
||||
{ damage: Math.round(shot.damage).toLocaleString(locale) }
|
||||
)}
|
||||
</Text>
|
||||
|
||||
<table
|
||||
style={{
|
||||
whiteSpace: "nowrap",
|
||||
borderSpacing: "var(--space-1) 0",
|
||||
}}
|
||||
>
|
||||
{shot.in.layers.map((layer, index) => {
|
||||
const layerName =
|
||||
strings.website.tools.tankopedia.sandbox.dynamic.shot_card
|
||||
.element[
|
||||
layer.type === ArmorType.External
|
||||
? layer.variant
|
||||
: layerTypeNames[`${layer.type}`]
|
||||
];
|
||||
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={
|
||||
layer.type === ArmorType.Primary ||
|
||||
layer.type === ArmorType.Spaced
|
||||
? layer.angle
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</table>
|
||||
</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">
|
||||
{literals(
|
||||
strings.website.tools.tankopedia.sandbox.dynamic.shot_card
|
||||
.status[shot.out.status],
|
||||
{ damage: Math.round(shot.damage).toLocaleString(locale) }
|
||||
)}
|
||||
</Text>
|
||||
|
||||
<Flex direction="column">
|
||||
{shot.out.layers.map((layer, index) => {
|
||||
const layerName =
|
||||
strings.website.tools.tankopedia.sandbox.dynamic.shot_card
|
||||
.element[
|
||||
layer.type === ArmorType.External
|
||||
? layer.variant
|
||||
: layerTypeNames[`${layer.type}`]
|
||||
];
|
||||
const shiftedIndex = index + shot.out!.layers.length;
|
||||
const layerIndex =
|
||||
layer.type === null
|
||||
? undefined
|
||||
: `${
|
||||
(shot.containsGaps ? shiftedIndex / 2 : shiftedIndex) +
|
||||
1
|
||||
}.`;
|
||||
const shotStatusColor =
|
||||
shot.out!.status === "splash"
|
||||
? shotStatusColors.splash
|
||||
: shotStatusColors[layer.status];
|
||||
|
||||
return (
|
||||
<LayerEntry
|
||||
key={index}
|
||||
layer={layer}
|
||||
layerIndex={layerIndex}
|
||||
layerName={layerName}
|
||||
shotStatusColor={shotStatusColor}
|
||||
angle={
|
||||
layer.type === ArmorType.Primary ||
|
||||
layer.type === ArmorType.Spaced
|
||||
? layer.angle
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Flex>
|
||||
</>
|
||||
)}
|
||||
</Flex>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user