Files
blitzkit/packages/website/src/components/ArmorPlateDisplay.tsx
T

199 lines
7.8 KiB
TypeScript
Raw Normal View History

2025-09-01 22:56:39 -05:00
import { ReloadIcon } from "@radix-ui/react-icons";
2026-04-03 14:37:45 -05:00
import {
Card,
Code,
Flex,
IconButton,
Text,
TextField,
} from "@radix-ui/themes";
2025-09-01 22:56:39 -05:00
import { Html } from "@react-three/drei";
2026-03-25 11:18:53 +01:00
import { useRef } from "react";
2025-09-01 22:56:39 -05:00
import { radToDeg } from "three/src/math/MathUtils.js";
2026-09-27 16:46:22 -05:00
import { resolveArmorIndex } from "../core/blitzkit/resolveArmorIndex";
import { LocaleProvider, useLocale } from "../hooks/useLocale";
import { App } from "../stores/app";
import { Duel } from "../stores/duel";
import { Tankopedia } from "../stores/tankopedia";
2025-09-01 22:56:39 -05:00
import { layerTypeNames } from "./ShotDisplayCard";
import { ArmorType } from "./SpacedArmorScene";
2024-07-29 13:22:20 -05:00
export function ArmorPlateDisplay() {
2025-09-01 22:56:39 -05:00
const highlightArmor = Tankopedia.use((state) => state.highlightArmor);
2024-09-22 21:27:46 -05:00
const developerMode = App.useDeferred((state) => state.developerMode, false);
2024-08-01 14:00:43 -05:00
const input = useRef<HTMLInputElement>(null);
const { locale, strings } = useLocale();
2024-07-30 13:24:55 -05:00
2024-07-29 13:22:20 -05:00
if (highlightArmor === undefined) return null;
return (
<group position={highlightArmor.point}>
2024-08-01 14:00:43 -05:00
<Html
center
2025-09-01 22:56:39 -05:00
style={{ pointerEvents: highlightArmor.editingPlate ? "auto" : "none" }}
2024-08-01 14:00:43 -05:00
>
2026-04-03 14:37:45 -05:00
<LocaleProvider locale={locale}>
<Card
variant="classic"
mb="4"
style={{
whiteSpace: "nowrap",
color: highlightArmor.color,
transform: "translateY(-50%)",
}}
>
<Flex direction="column">
<Text weight="bold">
{
strings.website.tools.tankopedia.sandbox.dynamic.shot_card
.element[layerTypeNames[highlightArmor.type]]
}{" "}
2026-04-03 14:37:45 -05:00
{highlightArmor.thickness.toFixed(0)}
<Text size="1" weight="regular">
mm
2024-07-29 13:22:20 -05:00
</Text>
</Text>
2024-07-31 13:11:25 -05:00
2026-04-03 14:37:45 -05:00
{highlightArmor.type !== ArmorType.External && (
<Text size="2" color="gray">
{highlightArmor.thicknessAngled.toFixed(0)}
<Text size="1">
mm {radToDeg(highlightArmor.angle).toFixed(0)}°
</Text>
2024-07-30 13:24:55 -05:00
</Text>
2026-04-03 14:37:45 -05:00
)}
2024-08-01 14:00:43 -05:00
2026-04-03 14:37:45 -05:00
{developerMode && (
<>
<Text mt="2" color="gray" size="2">
<b>DEV:</b>{" "}
<Code variant="soft" highContrast color="gray">
{highlightArmor.name}
</Code>
</Text>
</>
)}
2024-08-01 14:00:43 -05:00
2026-04-03 14:37:45 -05:00
{highlightArmor.editingPlate && (
<Flex gap="2" align="center">
<TextField.Root
variant="classic"
style={{ width: 96 }}
mt="2"
ref={input}
key={highlightArmor.name}
type="number"
autoFocus
onFocus={(event) => event.target.select()}
onKeyDown={(event) => {
2024-08-01 14:06:45 -05:00
if (!highlightArmor.editingPlate) return;
const index = resolveArmorIndex(highlightArmor.name);
if (index === undefined) return;
2026-04-03 14:37:45 -05:00
const thickness = event.currentTarget.valueAsNumber;
2024-08-01 14:00:43 -05:00
2026-04-03 14:37:45 -05:00
if (isNaN(thickness) || thickness < 0) return;
if (event.key === "Enter") {
input.current?.blur();
Tankopedia.mutate((draft) => {
draft.highlightArmor = undefined;
const { protagonist } = Duel.state;
const tank = draft.model;
const turret = tank.turrets[protagonist.turret.id];
const gun = turret.guns[protagonist.gun.id];
const track = tank.tracks[protagonist.track.id];
if (highlightArmor.name.startsWith("hull_")) {
tank.armor!.thickness[index] = thickness;
} else if (
highlightArmor.name.startsWith("turret_")
) {
turret.armor!.thickness[index] = thickness;
} else if (highlightArmor.name.startsWith("gun_")) {
if (highlightArmor.name.includes("_armor_")) {
gun.armor!.thickness[index] = thickness;
} else {
gun.thickness = thickness;
}
} else if (
highlightArmor.name.startsWith("chassis_")
) {
track.thickness = thickness;
}
});
} else if (event.key === "Escape") {
input.current?.blur();
Tankopedia.mutate((draft) => {
draft.highlightArmor = undefined;
});
2024-08-01 14:00:43 -05:00
}
2026-04-03 14:37:45 -05:00
}}
onClick={(event) => {
event.stopPropagation();
event.preventDefault();
}}
defaultValue={highlightArmor.thickness.toFixed(0)}
>
<TextField.Slot side="right">mm</TextField.Slot>
</TextField.Root>
<IconButton
mt="2"
color="red"
onClick={() => {
Tankopedia.mutate((draft) => {
if (!highlightArmor.editingPlate) return;
const index = resolveArmorIndex(highlightArmor.name);
if (index === undefined) return;
draft.highlightArmor = undefined;
const initialState = Tankopedia.initial;
const { protagonist } = Duel.state;
const tank = draft.model;
const initialTank = initialState.model;
const turret = tank.turrets[protagonist.turret.id];
const initialTurret =
initialTank.turrets[protagonist.turret.id];
const gun = turret.guns[protagonist.gun.id];
const initialGun =
initialTurret.guns[protagonist.gun.id];
const track = tank.tracks[protagonist.track.id];
const initialTrack =
initialTank.tracks[protagonist.track.id];
if (highlightArmor.name.startsWith("hull_")) {
tank.armor!.thickness[index] =
initialTank.armor!.thickness[index];
} else if (highlightArmor.name.startsWith("turret_")) {
turret.armor!.thickness[index] =
initialTurret.armor!.thickness[index];
} else if (highlightArmor.name.startsWith("gun_")) {
if (highlightArmor.name.includes("_armor_")) {
gun.armor!.thickness[index] =
initialGun.armor!.thickness[index];
} else {
gun.thickness = initialGun.thickness;
}
} else if (highlightArmor.name.startsWith("chassis_")) {
track.thickness = initialTrack.thickness;
}
});
}}
>
<ReloadIcon />
</IconButton>
</Flex>
)}
</Flex>
</Card>
</LocaleProvider>
2024-07-29 13:22:20 -05:00
</Html>
</group>
);
}