mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-11-15 13:46:09 +01:00
feat: renaming
This commit is contained in:
1 parent
8b527f2333
commit
96482d4268
38 files changed
+931
-943
No files matched your search
+34
-34
@@ -1,5 +1,5 @@
|
||||
import { ShellType } from '@blitzkit/core';
|
||||
import { literals } from '@blitzkit/i18n/src/literals';
|
||||
import { ShellType } from "@blitzkit/core";
|
||||
import { literals } from "@blitzkit/i18n/src/literals";
|
||||
import {
|
||||
Button,
|
||||
Flex,
|
||||
@@ -9,15 +9,15 @@ import {
|
||||
Slider,
|
||||
Text,
|
||||
TextField,
|
||||
} from '@radix-ui/themes';
|
||||
import { throttle } from 'lodash-es';
|
||||
import { type ChangeEvent } from 'react';
|
||||
import { useLocale } from '../../../../../../hooks/useLocale';
|
||||
import { Duel } from '../../../../../../stores/duel';
|
||||
import { TankopediaEphemeral } from '../../../../../../stores/tankopediaEphemeral';
|
||||
} from "@radix-ui/themes";
|
||||
import { throttle } from "lodash-es";
|
||||
import { type ChangeEvent } from "react";
|
||||
import { useLocale } from "../../../../../../hooks/useLocale";
|
||||
import { Duel } from "../../../../../../stores/duel";
|
||||
import { Tankopedia } from "../../../../../../stores/tankopedia";
|
||||
|
||||
export function CustomShellButton() {
|
||||
const customShell = TankopediaEphemeral.use((state) => state.customShell);
|
||||
const customShell = Tankopedia.use((state) => state.customShell);
|
||||
const gun = Duel.use((state) => state.protagonist.gun);
|
||||
const antagonistShell = Duel.use((state) => state.antagonist.shell);
|
||||
const { strings } = useLocale();
|
||||
@@ -27,24 +27,24 @@ export function CustomShellButton() {
|
||||
onOpenChange={(open) => {
|
||||
if (!open || customShell) return;
|
||||
|
||||
TankopediaEphemeral.mutate((draft) => {
|
||||
Tankopedia.mutate((draft) => {
|
||||
draft.customShell = antagonistShell;
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Popover.Trigger>
|
||||
<IconButton
|
||||
color={customShell ? undefined : 'gray'}
|
||||
color={customShell ? undefined : "gray"}
|
||||
variant="soft"
|
||||
size={{ initial: '2', sm: '3' }}
|
||||
size={{ initial: "2", sm: "3" }}
|
||||
radius="none"
|
||||
>
|
||||
<img
|
||||
alt="custom shell"
|
||||
src="/assets/images/icons/custom-shell.png"
|
||||
style={{
|
||||
width: '50%',
|
||||
height: '50%',
|
||||
width: "50%",
|
||||
height: "50%",
|
||||
}}
|
||||
/>
|
||||
</IconButton>
|
||||
@@ -90,13 +90,13 @@ export function CustomShellButton() {
|
||||
modified.ricochet = undefined;
|
||||
modified.explosion_radius =
|
||||
gun.shells.find(
|
||||
(shell) => shell.type === ShellType.HE,
|
||||
(shell) => shell.type === ShellType.HE
|
||||
)?.explosion_radius ?? 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
TankopediaEphemeral.mutate((draft) => {
|
||||
Tankopedia.mutate((draft) => {
|
||||
draft.customShell = modified;
|
||||
});
|
||||
}}
|
||||
@@ -129,11 +129,11 @@ export function CustomShellButton() {
|
||||
</Text>
|
||||
<TextField.Root
|
||||
variant="classic"
|
||||
style={{ maxWidth: '6rem' }}
|
||||
style={{ maxWidth: "6rem" }}
|
||||
defaultValue={customShell.caliber}
|
||||
type="number"
|
||||
onChange={(event) => {
|
||||
TankopediaEphemeral.mutate((draft) => {
|
||||
Tankopedia.mutate((draft) => {
|
||||
draft.customShell!.caliber = event.target.valueAsNumber;
|
||||
});
|
||||
}}
|
||||
@@ -141,9 +141,9 @@ export function CustomShellButton() {
|
||||
onKeyDown={(event) => {
|
||||
event.stopPropagation();
|
||||
|
||||
if (event.key === 'Enter') {
|
||||
if (event.key === "Enter") {
|
||||
(event.target as HTMLInputElement).blur();
|
||||
} else if (event.key === 'Escape') {
|
||||
} else if (event.key === "Escape") {
|
||||
(event.target as HTMLInputElement).valueAsNumber =
|
||||
customShell.caliber;
|
||||
(event.target as HTMLInputElement).blur();
|
||||
@@ -151,7 +151,7 @@ export function CustomShellButton() {
|
||||
}}
|
||||
>
|
||||
<TextField.Slot side="right">
|
||||
{literals(strings.common.units.mm, [''])}
|
||||
{literals(strings.common.units.mm, [""])}
|
||||
</TextField.Slot>
|
||||
</TextField.Root>
|
||||
</Flex>
|
||||
@@ -165,11 +165,11 @@ export function CustomShellButton() {
|
||||
</Text>
|
||||
<TextField.Root
|
||||
variant="classic"
|
||||
style={{ maxWidth: '6rem' }}
|
||||
style={{ maxWidth: "6rem" }}
|
||||
defaultValue={customShell.penetration.near}
|
||||
type="number"
|
||||
onChange={(event: ChangeEvent<HTMLInputElement>) => {
|
||||
TankopediaEphemeral.mutate((draft) => {
|
||||
Tankopedia.mutate((draft) => {
|
||||
draft.customShell!.penetration.near =
|
||||
event.target.valueAsNumber;
|
||||
});
|
||||
@@ -178,9 +178,9 @@ export function CustomShellButton() {
|
||||
onKeyDown={(event) => {
|
||||
event.stopPropagation();
|
||||
|
||||
if (event.key === 'Enter') {
|
||||
if (event.key === "Enter") {
|
||||
(event.target as HTMLInputElement).blur();
|
||||
} else if (event.key === 'Escape') {
|
||||
} else if (event.key === "Escape") {
|
||||
(event.target as HTMLInputElement).valueAsNumber =
|
||||
customShell.penetration.near;
|
||||
(event.target as HTMLInputElement).blur();
|
||||
@@ -188,7 +188,7 @@ export function CustomShellButton() {
|
||||
}}
|
||||
>
|
||||
<TextField.Slot side="right">
|
||||
{literals(strings.common.units.mm, [''])}
|
||||
{literals(strings.common.units.mm, [""])}
|
||||
</TextField.Slot>
|
||||
</TextField.Root>
|
||||
</Flex>
|
||||
@@ -223,9 +223,9 @@ export function CustomShellButton() {
|
||||
step={Number.EPSILON}
|
||||
defaultValue={[customShell.normalization ?? 0]}
|
||||
onValueChange={([value]) => {
|
||||
console.log('asd');
|
||||
console.log("asd");
|
||||
|
||||
TankopediaEphemeral.mutate((draft) => {
|
||||
Tankopedia.mutate((draft) => {
|
||||
draft.customShell!.normalization = value;
|
||||
});
|
||||
}}
|
||||
@@ -254,7 +254,7 @@ export function CustomShellButton() {
|
||||
step={Number.EPSILON}
|
||||
defaultValue={[customShell.ricochet ?? 90]}
|
||||
onValueChange={throttle(([value]) => {
|
||||
TankopediaEphemeral.mutate((draft) => {
|
||||
Tankopedia.mutate((draft) => {
|
||||
draft.customShell!.ricochet = value;
|
||||
});
|
||||
}, 1000)}
|
||||
@@ -269,11 +269,11 @@ export function CustomShellButton() {
|
||||
<Text>Explosion radius</Text>
|
||||
<TextField.Root
|
||||
variant="classic"
|
||||
style={{ maxWidth: '6rem' }}
|
||||
style={{ maxWidth: "6rem" }}
|
||||
defaultValue={customShell.explosion_radius ?? 0}
|
||||
type="number"
|
||||
onChange={(event: ChangeEvent<HTMLInputElement>) => {
|
||||
TankopediaEphemeral.mutate((draft) => {
|
||||
Tankopedia.mutate((draft) => {
|
||||
draft.customShell!.explosion_radius =
|
||||
event.target.valueAsNumber;
|
||||
});
|
||||
@@ -282,9 +282,9 @@ export function CustomShellButton() {
|
||||
onKeyDown={(event) => {
|
||||
event.stopPropagation();
|
||||
|
||||
if (event.key === 'Enter') {
|
||||
if (event.key === "Enter") {
|
||||
(event.target as HTMLInputElement).blur();
|
||||
} else if (event.key === 'Escape') {
|
||||
} else if (event.key === "Escape") {
|
||||
(event.target as HTMLInputElement).valueAsNumber =
|
||||
customShell.explosion_radius ?? 1;
|
||||
(event.target as HTMLInputElement).blur();
|
||||
@@ -309,7 +309,7 @@ export function CustomShellButton() {
|
||||
<Button
|
||||
color="red"
|
||||
onClick={() => {
|
||||
TankopediaEphemeral.mutate((draft) => {
|
||||
Tankopedia.mutate((draft) => {
|
||||
draft.customShell = antagonistShell;
|
||||
});
|
||||
}}
|
||||
|
||||
+12
-12
@@ -1,14 +1,14 @@
|
||||
import { asset } from '@blitzkit/core';
|
||||
import { IconButton } from '@radix-ui/themes';
|
||||
import { useArmor } from '../../../../../../hooks/useArmor';
|
||||
import { Duel } from '../../../../../../stores/duel';
|
||||
import { TankopediaEphemeral } from '../../../../../../stores/tankopediaEphemeral';
|
||||
import { asset } from "@blitzkit/core";
|
||||
import { IconButton } from "@radix-ui/themes";
|
||||
import { useArmor } from "../../../../../../hooks/useArmor";
|
||||
import { Duel } from "../../../../../../stores/duel";
|
||||
import { Tankopedia } from "../../../../../../stores/tankopedia";
|
||||
|
||||
export function DynamicArmorSwitcher() {
|
||||
const tank = Duel.use((state) => state.protagonist.tank);
|
||||
const mutateDuel = Duel.useMutation();
|
||||
const isDynamicArmorActive = Duel.use((state) =>
|
||||
state.protagonist.consumables.includes(73),
|
||||
state.protagonist.consumables.includes(73)
|
||||
);
|
||||
const { hasDynamicArmor } = useArmor(tank.id);
|
||||
|
||||
@@ -16,9 +16,9 @@ export function DynamicArmorSwitcher() {
|
||||
|
||||
return (
|
||||
<IconButton
|
||||
color={isDynamicArmorActive ? undefined : 'gray'}
|
||||
color={isDynamicArmorActive ? undefined : "gray"}
|
||||
variant="soft"
|
||||
size={{ initial: '2', sm: '3' }}
|
||||
size={{ initial: "2", sm: "3" }}
|
||||
onClick={() => {
|
||||
mutateDuel((draft) => {
|
||||
if (draft.protagonist.consumables.includes(73)) {
|
||||
@@ -32,17 +32,17 @@ export function DynamicArmorSwitcher() {
|
||||
}
|
||||
}
|
||||
});
|
||||
TankopediaEphemeral.mutate((draft) => {
|
||||
Tankopedia.mutate((draft) => {
|
||||
draft.shot = undefined;
|
||||
});
|
||||
}}
|
||||
>
|
||||
<img
|
||||
alt="Dynamic Armor"
|
||||
src={asset('icons/consumables/73.webp')}
|
||||
src={asset("icons/consumables/73.webp")}
|
||||
style={{
|
||||
width: '50%',
|
||||
height: '50%',
|
||||
width: "50%",
|
||||
height: "50%",
|
||||
}}
|
||||
/>
|
||||
</IconButton>
|
||||
|
||||
+41
-41
@@ -1,44 +1,44 @@
|
||||
import { literals } from '@blitzkit/i18n/src/literals';
|
||||
import { EyeClosedIcon, EyeOpenIcon, ReloadIcon } from '@radix-ui/react-icons';
|
||||
import { Button, Checkbox, Flex, Text } from '@radix-ui/themes';
|
||||
import { Suspense } from 'react';
|
||||
import { useLocale } from '../../../../../../hooks/useLocale';
|
||||
import { TankopediaEphemeral } from '../../../../../../stores/tankopediaEphemeral';
|
||||
import { TankopediaPersistent } from '../../../../../../stores/tankopediaPersistent';
|
||||
import { TankopediaDisplay } from '../../../../../../stores/tankopediaPersistent/constants';
|
||||
import type { MaybeSkeletonComponentProps } from '../../../../../../types/maybeSkeletonComponentProps';
|
||||
import type { ThicknessRange } from '../../../../../Armor/components/StaticArmor';
|
||||
import { DynamicArmorSwitcher } from './DynamicArmorSwitcher';
|
||||
import { literals } from "@blitzkit/i18n/src/literals";
|
||||
import { EyeClosedIcon, EyeOpenIcon, ReloadIcon } from "@radix-ui/react-icons";
|
||||
import { Button, Checkbox, Flex, Text } from "@radix-ui/themes";
|
||||
import { Suspense } from "react";
|
||||
import { useLocale } from "../../../../../../hooks/useLocale";
|
||||
import { Tankopedia } from "../../../../../../stores/tankopedia";
|
||||
import { TankopediaPersistent } from "../../../../../../stores/tankopediaPersistent";
|
||||
import { TankopediaDisplay } from "../../../../../../stores/tankopediaPersistent/constants";
|
||||
import type { MaybeSkeletonComponentProps } from "../../../../../../types/maybeSkeletonComponentProps";
|
||||
import type { ThicknessRange } from "../../../../../Armor/components/StaticArmor";
|
||||
import { DynamicArmorSwitcher } from "./DynamicArmorSwitcher";
|
||||
|
||||
type ThicknessesProps = MaybeSkeletonComponentProps & {
|
||||
thicknessRange: ThicknessRange;
|
||||
};
|
||||
|
||||
export function Thicknesses({ thicknessRange, skeleton }: ThicknessesProps) {
|
||||
const display = TankopediaEphemeral.use((state) => state.display);
|
||||
const display = Tankopedia.use((state) => state.display);
|
||||
const showExternalModules = TankopediaPersistent.use(
|
||||
(state) => state.showExternalModules,
|
||||
(state) => state.showExternalModules
|
||||
);
|
||||
const showSpacedArmor = TankopediaPersistent.use(
|
||||
(state) => state.showSpacedArmor,
|
||||
(state) => state.showSpacedArmor
|
||||
);
|
||||
const showPrimaryArmor = TankopediaPersistent.use(
|
||||
(state) => state.showPrimaryArmor,
|
||||
(state) => state.showPrimaryArmor
|
||||
);
|
||||
const mutateTankopediaPersistent = TankopediaPersistent.useMutation();
|
||||
const editStatic = TankopediaEphemeral.use((state) => state.editStatic);
|
||||
const editStatic = Tankopedia.use((state) => state.editStatic);
|
||||
const { strings } = useLocale();
|
||||
|
||||
return (
|
||||
<Flex
|
||||
position="absolute"
|
||||
right={display === TankopediaDisplay.StaticArmor ? '0' : '-6rem'}
|
||||
right={display === TankopediaDisplay.StaticArmor ? "0" : "-6rem"}
|
||||
top="50%"
|
||||
mr="4"
|
||||
style={{
|
||||
transform: 'translateY(-50%)',
|
||||
userSelect: 'none',
|
||||
transitionDuration: '200ms',
|
||||
transform: "translateY(-50%)",
|
||||
userSelect: "none",
|
||||
transitionDuration: "200ms",
|
||||
}}
|
||||
direction="column"
|
||||
gap="5"
|
||||
@@ -48,12 +48,12 @@ export function Thicknesses({ thicknessRange, skeleton }: ThicknessesProps) {
|
||||
<Flex
|
||||
height="64px"
|
||||
gap="2"
|
||||
style={{ opacity: showPrimaryArmor ? 1 : 0.5, cursor: 'pointer' }}
|
||||
style={{ opacity: showPrimaryArmor ? 1 : 0.5, cursor: "pointer" }}
|
||||
onClick={() => {
|
||||
mutateTankopediaPersistent((draft) => {
|
||||
draft.showPrimaryArmor = !draft.showPrimaryArmor;
|
||||
});
|
||||
TankopediaEphemeral.mutate((draft) => {
|
||||
Tankopedia.mutate((draft) => {
|
||||
draft.highlightArmor = undefined;
|
||||
});
|
||||
}}
|
||||
@@ -68,7 +68,7 @@ export function Thicknesses({ thicknessRange, skeleton }: ThicknessesProps) {
|
||||
{strings.website.tools.tankopedia.sandbox.static.primary}
|
||||
</Text>
|
||||
<Text color="gray" size="1">
|
||||
{literals(strings.common.units.mm, ['0'])}
|
||||
{literals(strings.common.units.mm, ["0"])}
|
||||
</Text>
|
||||
</Flex>
|
||||
|
||||
@@ -76,10 +76,10 @@ export function Thicknesses({ thicknessRange, skeleton }: ThicknessesProps) {
|
||||
align="center"
|
||||
width="1rem"
|
||||
style={{
|
||||
borderRadius: 'var(--radius-full)',
|
||||
overflow: 'hidden',
|
||||
borderRadius: "var(--radius-full)",
|
||||
overflow: "hidden",
|
||||
background: `linear-gradient(rgb(64, 0, 0), rgb(255, 0, 0) 33%, rgb(255, 255, 0) 66%, rgb(0, 255, 0))`,
|
||||
color: 'black',
|
||||
color: "black",
|
||||
}}
|
||||
>
|
||||
{showPrimaryArmor ? <EyeOpenIcon /> : <EyeClosedIcon />}
|
||||
@@ -89,12 +89,12 @@ export function Thicknesses({ thicknessRange, skeleton }: ThicknessesProps) {
|
||||
<Flex
|
||||
height="64px"
|
||||
gap="2"
|
||||
style={{ opacity: showSpacedArmor ? 1 : 0.5, cursor: 'pointer' }}
|
||||
style={{ opacity: showSpacedArmor ? 1 : 0.5, cursor: "pointer" }}
|
||||
onClick={() => {
|
||||
mutateTankopediaPersistent((draft) => {
|
||||
draft.showSpacedArmor = !draft.showSpacedArmor;
|
||||
});
|
||||
TankopediaEphemeral.mutate((draft) => {
|
||||
Tankopedia.mutate((draft) => {
|
||||
draft.highlightArmor = undefined;
|
||||
});
|
||||
}}
|
||||
@@ -109,7 +109,7 @@ export function Thicknesses({ thicknessRange, skeleton }: ThicknessesProps) {
|
||||
{strings.website.tools.tankopedia.sandbox.static.spaced}
|
||||
</Text>
|
||||
<Text color="gray" size="1">
|
||||
{literals(strings.common.units.mm, ['0'])}
|
||||
{literals(strings.common.units.mm, ["0"])}
|
||||
</Text>
|
||||
</Flex>
|
||||
|
||||
@@ -117,8 +117,8 @@ export function Thicknesses({ thicknessRange, skeleton }: ThicknessesProps) {
|
||||
align="center"
|
||||
width="1rem"
|
||||
style={{
|
||||
borderRadius: 'var(--radius-full)',
|
||||
overflow: 'hidden',
|
||||
borderRadius: "var(--radius-full)",
|
||||
overflow: "hidden",
|
||||
background: `linear-gradient(rgb(32, 0, 225), rgb(255, 0, 255))`,
|
||||
}}
|
||||
>
|
||||
@@ -128,12 +128,12 @@ export function Thicknesses({ thicknessRange, skeleton }: ThicknessesProps) {
|
||||
|
||||
<Flex
|
||||
gap="2"
|
||||
style={{ opacity: showExternalModules ? 1 : 0.5, cursor: 'pointer' }}
|
||||
style={{ opacity: showExternalModules ? 1 : 0.5, cursor: "pointer" }}
|
||||
onClick={() => {
|
||||
mutateTankopediaPersistent((draft) => {
|
||||
draft.showExternalModules = !draft.showExternalModules;
|
||||
});
|
||||
TankopediaEphemeral.mutate((draft) => {
|
||||
Tankopedia.mutate((draft) => {
|
||||
draft.highlightArmor = undefined;
|
||||
});
|
||||
}}
|
||||
@@ -149,10 +149,10 @@ export function Thicknesses({ thicknessRange, skeleton }: ThicknessesProps) {
|
||||
width="1rem"
|
||||
height="1rem"
|
||||
style={{
|
||||
borderRadius: 'var(--radius-full)',
|
||||
overflow: 'hidden',
|
||||
backgroundColor: '#c0c0c0',
|
||||
color: 'black',
|
||||
borderRadius: "var(--radius-full)",
|
||||
overflow: "hidden",
|
||||
backgroundColor: "#c0c0c0",
|
||||
color: "black",
|
||||
}}
|
||||
>
|
||||
{showExternalModules ? <EyeOpenIcon /> : <EyeClosedIcon />}
|
||||
@@ -164,9 +164,9 @@ export function Thicknesses({ thicknessRange, skeleton }: ThicknessesProps) {
|
||||
<Flex align="center" gap="2">
|
||||
<Flex
|
||||
gap="2"
|
||||
style={{ cursor: 'pointer' }}
|
||||
style={{ cursor: "pointer" }}
|
||||
onClick={() => {
|
||||
TankopediaEphemeral.mutate((draft) => {
|
||||
Tankopedia.mutate((draft) => {
|
||||
draft.editStatic = !draft.editStatic;
|
||||
draft.highlightArmor = undefined;
|
||||
});
|
||||
@@ -184,9 +184,9 @@ export function Thicknesses({ thicknessRange, skeleton }: ThicknessesProps) {
|
||||
size="1"
|
||||
variant="ghost"
|
||||
onClick={() => {
|
||||
TankopediaEphemeral.mutate((draft) => {
|
||||
Tankopedia.mutate((draft) => {
|
||||
draft.editStatic = false;
|
||||
draft.model = TankopediaEphemeral.initial.model;
|
||||
draft.model = Tankopedia.initial.model;
|
||||
});
|
||||
}}
|
||||
>
|
||||
|
||||
+81
-81
@@ -3,8 +3,8 @@ import {
|
||||
resolvePenetrationCoefficient,
|
||||
TIER_ROMAN_NUMERALS,
|
||||
uniqueGuns,
|
||||
} from '@blitzkit/core';
|
||||
import { literals } from '@blitzkit/i18n/src/literals';
|
||||
} from "@blitzkit/core";
|
||||
import { literals } from "@blitzkit/i18n/src/literals";
|
||||
import {
|
||||
CameraIcon,
|
||||
CopyIcon,
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
EnterFullScreenIcon,
|
||||
ExitFullScreenIcon,
|
||||
EyeOpenIcon,
|
||||
} from '@radix-ui/react-icons';
|
||||
} from "@radix-ui/react-icons";
|
||||
import {
|
||||
Button,
|
||||
Checkbox,
|
||||
@@ -25,27 +25,27 @@ import {
|
||||
Tabs,
|
||||
Text,
|
||||
Tooltip,
|
||||
} from '@radix-ui/themes';
|
||||
import { Suspense, useState, type RefObject } from 'react';
|
||||
import { Pose, poseEvent } from '../../../../../core/blitzkit/pose';
|
||||
import { useEquipment } from '../../../../../hooks/useEquipment';
|
||||
import { useFullScreen } from '../../../../../hooks/useFullScreen';
|
||||
import { useFullscreenAvailability } from '../../../../../hooks/useFullscreenAvailability';
|
||||
import { useLocale } from '../../../../../hooks/useLocale';
|
||||
import { Duel } from '../../../../../stores/duel';
|
||||
import { TankopediaEphemeral } from '../../../../../stores/tankopediaEphemeral';
|
||||
import { TankopediaPersistent } from '../../../../../stores/tankopediaPersistent';
|
||||
import { TankopediaDisplay } from '../../../../../stores/tankopediaPersistent/constants';
|
||||
import type { MaybeSkeletonComponentProps } from '../../../../../types/maybeSkeletonComponentProps';
|
||||
import type { ThicknessRange } from '../../../../Armor/components/StaticArmor';
|
||||
import { ModuleButton } from '../../../../ModuleButtons/ModuleButton';
|
||||
import { SmallTankIcon } from '../../../../SmallTankIcon';
|
||||
import { TankSearch } from '../../../../TankSearch';
|
||||
import { screenshotReadyEvent } from '../TankSandbox/components/SceneProps';
|
||||
import { CustomShellButton } from './components/CustomShellButton';
|
||||
import { DynamicArmorSwitcher } from './components/DynamicArmorSwitcher';
|
||||
import { QuickInputs } from './components/QuickInputs';
|
||||
import { Thicknesses } from './components/Thicknesses';
|
||||
} from "@radix-ui/themes";
|
||||
import { Suspense, useState, type RefObject } from "react";
|
||||
import { Pose, poseEvent } from "../../../../../core/blitzkit/pose";
|
||||
import { useEquipment } from "../../../../../hooks/useEquipment";
|
||||
import { useFullScreen } from "../../../../../hooks/useFullScreen";
|
||||
import { useFullscreenAvailability } from "../../../../../hooks/useFullscreenAvailability";
|
||||
import { useLocale } from "../../../../../hooks/useLocale";
|
||||
import { Duel } from "../../../../../stores/duel";
|
||||
import { Tankopedia } from "../../../../../stores/tankopedia";
|
||||
import { TankopediaPersistent } from "../../../../../stores/tankopediaPersistent";
|
||||
import { TankopediaDisplay } from "../../../../../stores/tankopediaPersistent/constants";
|
||||
import type { MaybeSkeletonComponentProps } from "../../../../../types/maybeSkeletonComponentProps";
|
||||
import type { ThicknessRange } from "../../../../Armor/components/StaticArmor";
|
||||
import { ModuleButton } from "../../../../ModuleButtons/ModuleButton";
|
||||
import { SmallTankIcon } from "../../../../SmallTankIcon";
|
||||
import { TankSearch } from "../../../../TankSearch";
|
||||
import { screenshotReadyEvent } from "../TankSandbox/components/SceneProps";
|
||||
import { CustomShellButton } from "./components/CustomShellButton";
|
||||
import { DynamicArmorSwitcher } from "./components/DynamicArmorSwitcher";
|
||||
import { QuickInputs } from "./components/QuickInputs";
|
||||
import { Thicknesses } from "./components/Thicknesses";
|
||||
|
||||
type OptionsProps = MaybeSkeletonComponentProps & {
|
||||
thicknessRange: ThicknessRange;
|
||||
@@ -53,13 +53,13 @@ type OptionsProps = MaybeSkeletonComponentProps & {
|
||||
};
|
||||
|
||||
export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
|
||||
const hasCustomShell = TankopediaEphemeral.use(
|
||||
(state) => state.customShell !== undefined,
|
||||
const hasCustomShell = Tankopedia.use(
|
||||
(state) => state.customShell !== undefined
|
||||
);
|
||||
const display = TankopediaEphemeral.use((state) => state.display);
|
||||
const display = Tankopedia.use((state) => state.display);
|
||||
const isFullScreen = useFullScreen();
|
||||
const advancedHighlighting = TankopediaPersistent.use(
|
||||
(state) => state.advancedHighlighting,
|
||||
(state) => state.advancedHighlighting
|
||||
);
|
||||
const fullScreenAvailable = useFullscreenAvailability(true);
|
||||
const protagonistTank = Duel.use((state) => state.protagonist.tank);
|
||||
@@ -68,14 +68,14 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
|
||||
const [antagonistSelectorOpen, setAntagonistSelectorOpen] = useState(false);
|
||||
const antagonistTank = Duel.use((state) => state.antagonist.tank);
|
||||
const hasCalibratedShells = useEquipment(103, true);
|
||||
const [tab, setTab] = useState('search');
|
||||
const [tab, setTab] = useState("search");
|
||||
const mutateDuel = Duel.useMutation();
|
||||
const hasEnhancedArmor = useEquipment(110);
|
||||
const antagonistUniqueGuns = uniqueGuns(antagonistTank.turrets);
|
||||
const mutateTankopediaPersistent = TankopediaPersistent.useMutation();
|
||||
const { strings, unwrap } = useLocale();
|
||||
const revealed = TankopediaEphemeral.use((state) => state.revealed);
|
||||
const disturbed = TankopediaEphemeral.use((state) => state.disturbed);
|
||||
const revealed = Tankopedia.use((state) => state.revealed);
|
||||
const disturbed = Tankopedia.use((state) => state.disturbed);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -87,21 +87,21 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
|
||||
gap="2"
|
||||
direction="column"
|
||||
top="50%"
|
||||
right={display === TankopediaDisplay.DynamicArmor ? '3' : '-4rem'}
|
||||
right={display === TankopediaDisplay.DynamicArmor ? "3" : "-4rem"}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
transform: 'translateY(-50%)',
|
||||
transitionDuration: '200ms',
|
||||
position: "absolute",
|
||||
transform: "translateY(-50%)",
|
||||
transitionDuration: "200ms",
|
||||
}}
|
||||
align="end"
|
||||
>
|
||||
{!hasCustomShell && (
|
||||
<Text color="gray" size={{ initial: '1', sm: '2' }}>
|
||||
<Text color="gray" size={{ initial: "1", sm: "2" }}>
|
||||
{literals(strings.common.units.mm, [
|
||||
(
|
||||
resolvePenetrationCoefficient(
|
||||
hasCalibratedShells,
|
||||
antagonistShell.type,
|
||||
antagonistShell.type
|
||||
) * antagonistShell.penetration.near
|
||||
).toFixed(0),
|
||||
])}
|
||||
@@ -115,14 +115,14 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
|
||||
radius="full"
|
||||
variant="soft"
|
||||
color="gray"
|
||||
size={{ initial: '2', sm: '3' }}
|
||||
style={{ position: 'relative' }}
|
||||
size={{ initial: "2", sm: "3" }}
|
||||
style={{ position: "relative" }}
|
||||
>
|
||||
<Text
|
||||
size="1"
|
||||
color="gray"
|
||||
style={{
|
||||
position: 'absolute',
|
||||
position: "absolute",
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
}}
|
||||
@@ -134,11 +134,11 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
|
||||
|
||||
<img
|
||||
alt="Antagonist Gun"
|
||||
src={asset('icons/modules/gun.webp')}
|
||||
src={asset("icons/modules/gun.webp")}
|
||||
style={{
|
||||
width: '65%',
|
||||
height: '65%',
|
||||
objectFit: 'contain',
|
||||
width: "65%",
|
||||
height: "65%",
|
||||
objectFit: "contain",
|
||||
}}
|
||||
/>
|
||||
</IconButton>
|
||||
@@ -160,7 +160,7 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
|
||||
selected={gun.id === antagonistGun.id}
|
||||
discriminator={TIER_ROMAN_NUMERALS[gun.tier]}
|
||||
secondaryDiscriminator={
|
||||
<Text style={{ fontSize: '0.75em' }}>
|
||||
<Text style={{ fontSize: "0.75em" }}>
|
||||
{literals(strings.common.units.mm, [
|
||||
gun.shells[0].caliber.toFixed(0),
|
||||
])}
|
||||
@@ -176,7 +176,7 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
|
||||
<Flex
|
||||
direction="column"
|
||||
style={{
|
||||
borderRadius: 'var(--radius-full)',
|
||||
borderRadius: "var(--radius-full)",
|
||||
}}
|
||||
overflow="hidden"
|
||||
>
|
||||
@@ -185,17 +185,17 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
|
||||
color={
|
||||
thisShell.id === antagonistShell.id && !hasCustomShell
|
||||
? undefined
|
||||
: 'gray'
|
||||
: "gray"
|
||||
}
|
||||
variant="soft"
|
||||
key={thisShell.id}
|
||||
size={{ initial: '2', sm: '3' }}
|
||||
size={{ initial: "2", sm: "3" }}
|
||||
radius="none"
|
||||
onClick={() => {
|
||||
mutateDuel((draft) => {
|
||||
draft.antagonist.shell = thisShell;
|
||||
});
|
||||
TankopediaEphemeral.mutate((draft) => {
|
||||
Tankopedia.mutate((draft) => {
|
||||
draft.shot = undefined;
|
||||
draft.customShell = undefined;
|
||||
});
|
||||
@@ -205,8 +205,8 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
|
||||
alt={unwrap(thisShell.name)}
|
||||
src={asset(`icons/shells/${thisShell.icon}.webp`)}
|
||||
style={{
|
||||
width: '50%',
|
||||
height: '50%',
|
||||
width: "50%",
|
||||
height: "50%",
|
||||
}}
|
||||
/>
|
||||
</IconButton>
|
||||
@@ -218,15 +218,15 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
|
||||
<Flex
|
||||
direction="column"
|
||||
style={{
|
||||
pointerEvents: 'auto',
|
||||
borderRadius: 'var(--radius-full)',
|
||||
pointerEvents: "auto",
|
||||
borderRadius: "var(--radius-full)",
|
||||
}}
|
||||
overflow="hidden"
|
||||
>
|
||||
<IconButton
|
||||
color={hasCalibratedShells ? undefined : 'gray'}
|
||||
color={hasCalibratedShells ? undefined : "gray"}
|
||||
variant="soft"
|
||||
size={{ initial: '2', sm: '3' }}
|
||||
size={{ initial: "2", sm: "3" }}
|
||||
radius="none"
|
||||
onClick={() => {
|
||||
mutateDuel((draft) => {
|
||||
@@ -234,24 +234,24 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
|
||||
? 0
|
||||
: 1;
|
||||
});
|
||||
TankopediaEphemeral.mutate((draft) => {
|
||||
Tankopedia.mutate((draft) => {
|
||||
draft.shot = undefined;
|
||||
});
|
||||
}}
|
||||
>
|
||||
<img
|
||||
alt="Calibrated Shells"
|
||||
src={asset('icons/equipment/103.webp')}
|
||||
src={asset("icons/equipment/103.webp")}
|
||||
style={{
|
||||
width: '50%',
|
||||
height: '50%',
|
||||
width: "50%",
|
||||
height: "50%",
|
||||
}}
|
||||
/>
|
||||
</IconButton>
|
||||
<IconButton
|
||||
color={hasEnhancedArmor ? undefined : 'gray'}
|
||||
color={hasEnhancedArmor ? undefined : "gray"}
|
||||
variant="soft"
|
||||
size={{ initial: '2', sm: '3' }}
|
||||
size={{ initial: "2", sm: "3" }}
|
||||
radius="none"
|
||||
onClick={() => {
|
||||
mutateDuel((draft) => {
|
||||
@@ -259,17 +259,17 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
|
||||
? 0
|
||||
: -1;
|
||||
});
|
||||
TankopediaEphemeral.mutate((draft) => {
|
||||
Tankopedia.mutate((draft) => {
|
||||
draft.shot = undefined;
|
||||
});
|
||||
}}
|
||||
>
|
||||
<img
|
||||
alt="Enhanced Armor"
|
||||
src={asset('icons/equipment/110.webp')}
|
||||
src={asset("icons/equipment/110.webp")}
|
||||
style={{
|
||||
width: '50%',
|
||||
height: '50%',
|
||||
width: "50%",
|
||||
height: "50%",
|
||||
}}
|
||||
/>
|
||||
</IconButton>
|
||||
@@ -286,22 +286,22 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
|
||||
direction="column"
|
||||
align="center"
|
||||
position="absolute"
|
||||
bottom={revealed ? '4' : '-100%'}
|
||||
bottom={revealed ? "4" : "-100%"}
|
||||
left="50%"
|
||||
style={{ transform: 'translateX(-50%)', transitionDuration: '200ms' }}
|
||||
style={{ transform: "translateX(-50%)", transitionDuration: "200ms" }}
|
||||
>
|
||||
<Flex
|
||||
direction="column"
|
||||
align="center"
|
||||
style={{ transitionDuration: '200ms' }}
|
||||
style={{ transitionDuration: "200ms" }}
|
||||
position="relative"
|
||||
bottom={display === TankopediaDisplay.DynamicArmor ? '0' : '-7rem'}
|
||||
bottom={display === TankopediaDisplay.DynamicArmor ? "0" : "-7rem"}
|
||||
>
|
||||
<Flex
|
||||
align="center"
|
||||
gap="2"
|
||||
mb="1"
|
||||
style={{ cursor: 'pointer' }}
|
||||
style={{ cursor: "pointer" }}
|
||||
onClick={() => {
|
||||
mutateTankopediaPersistent((draft) => {
|
||||
draft.advancedHighlighting = !draft.advancedHighlighting;
|
||||
@@ -338,7 +338,7 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
|
||||
<Tabs.Root
|
||||
value={tab}
|
||||
onValueChange={setTab}
|
||||
style={{ position: 'relative' }}
|
||||
style={{ position: "relative" }}
|
||||
>
|
||||
<TankSearch
|
||||
compact
|
||||
@@ -366,7 +366,7 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
|
||||
variant="classic"
|
||||
value={`${disturbed ? display : -1}`}
|
||||
onValueChange={(value) => {
|
||||
TankopediaEphemeral.mutate((draft) => {
|
||||
Tankopedia.mutate((draft) => {
|
||||
draft.disturbed = true;
|
||||
draft.display = Number(value);
|
||||
});
|
||||
@@ -379,7 +379,7 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
|
||||
<Flex height="100%" align="center">
|
||||
<img
|
||||
src="/assets/images/icons/tankopedia-model.png"
|
||||
style={{ height: '1.25em' }}
|
||||
style={{ height: "1.25em" }}
|
||||
/>
|
||||
</Flex>
|
||||
</Tooltip>
|
||||
@@ -391,7 +391,7 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
|
||||
<Flex height="100%" align="center">
|
||||
<img
|
||||
src="/assets/images/icons/tankopedia-dynamic-armor.png"
|
||||
style={{ height: '1.25em' }}
|
||||
style={{ height: "1.25em" }}
|
||||
/>
|
||||
</Flex>
|
||||
</Tooltip>
|
||||
@@ -403,7 +403,7 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
|
||||
<Flex height="100%" align="center">
|
||||
<img
|
||||
src="/assets/images/icons/tankopedia-static-armor.png"
|
||||
style={{ height: '1.25em' }}
|
||||
style={{ height: "1.25em" }}
|
||||
/>
|
||||
</Flex>
|
||||
</Tooltip>
|
||||
@@ -457,15 +457,15 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
|
||||
requestAnimationFrame(() => {
|
||||
if (!canvas.current) return;
|
||||
|
||||
const anchor = document.createElement('a');
|
||||
const anchor = document.createElement("a");
|
||||
|
||||
anchor.setAttribute(
|
||||
'download',
|
||||
`${protagonistTank.name}.png`,
|
||||
"download",
|
||||
`${protagonistTank.name}.png`
|
||||
);
|
||||
anchor.setAttribute(
|
||||
'href',
|
||||
canvas.current.toDataURL('image/png'),
|
||||
"href",
|
||||
canvas.current.toDataURL("image/png")
|
||||
);
|
||||
anchor.click();
|
||||
|
||||
@@ -493,7 +493,7 @@ export function Options({ thicknessRange, canvas, skeleton }: OptionsProps) {
|
||||
if (!blob) return;
|
||||
|
||||
navigator.clipboard.write([
|
||||
new ClipboardItem({ 'image/png': blob }),
|
||||
new ClipboardItem({ "image/png": blob }),
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
+10
-10
@@ -1,10 +1,10 @@
|
||||
import { Box } from '@radix-ui/themes';
|
||||
import { Var } from '../../../../../core/radix/var';
|
||||
import { TankopediaEphemeral } from '../../../../../stores/tankopediaEphemeral';
|
||||
import './index.css';
|
||||
import { Box } from "@radix-ui/themes";
|
||||
import { Var } from "../../../../../core/radix/var";
|
||||
import { Tankopedia } from "../../../../../stores/tankopedia";
|
||||
import "./index.css";
|
||||
|
||||
export function ScrollHint() {
|
||||
const disturbed = TankopediaEphemeral.use((state) => state.disturbed);
|
||||
const disturbed = Tankopedia.use((state) => state.disturbed);
|
||||
|
||||
if (disturbed) return null;
|
||||
|
||||
@@ -17,9 +17,9 @@ export function ScrollHint() {
|
||||
height="2rem"
|
||||
width="1rem"
|
||||
style={{
|
||||
borderRadius: '0.5rem',
|
||||
outline: `2px solid ${Var('gray-11')}`,
|
||||
transform: 'translateX(-50%)',
|
||||
borderRadius: "0.5rem",
|
||||
outline: `2px solid ${Var("gray-11")}`,
|
||||
transform: "translateX(-50%)",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
@@ -28,8 +28,8 @@ export function ScrollHint() {
|
||||
height="0.75rem"
|
||||
m="0.125rem"
|
||||
style={{
|
||||
borderRadius: '50%',
|
||||
backgroundColor: Var('gray-12'),
|
||||
borderRadius: "50%",
|
||||
backgroundColor: Var("gray-12"),
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
+18
-18
@@ -1,17 +1,17 @@
|
||||
import { Flex, Heading } from '@radix-ui/themes';
|
||||
import { useLocale } from '../../../../../../hooks/useLocale';
|
||||
import { Duel } from '../../../../../../stores/duel';
|
||||
import { TankopediaEphemeral } from '../../../../../../stores/tankopediaEphemeral';
|
||||
import { Tracker } from './components/Tracker';
|
||||
import { Flex, Heading } from "@radix-ui/themes";
|
||||
import { useLocale } from "../../../../../../hooks/useLocale";
|
||||
import { Duel } from "../../../../../../stores/duel";
|
||||
import { Tankopedia } from "../../../../../../stores/tankopedia";
|
||||
import { Tracker } from "./components/Tracker";
|
||||
|
||||
export function Title() {
|
||||
const { unwrap } = useLocale();
|
||||
const protagonist = Duel.use((state) => state.protagonist.tank);
|
||||
const revealed = TankopediaEphemeral.use((state) => state.revealed);
|
||||
const revealed = Tankopedia.use((state) => state.revealed);
|
||||
const name = unwrap(protagonist.name);
|
||||
const fontSize = revealed
|
||||
? revealed
|
||||
? '2rem'
|
||||
? "2rem"
|
||||
: `min(16vh, ${125 / name.length}vw)`
|
||||
: `min(12vh, ${75 / name.length}vw)`;
|
||||
|
||||
@@ -20,7 +20,7 @@ export function Title() {
|
||||
onPointerDown={(event) => {
|
||||
event.preventDefault();
|
||||
|
||||
TankopediaEphemeral.mutate((draft) => {
|
||||
Tankopedia.mutate((draft) => {
|
||||
draft.revealed = true;
|
||||
});
|
||||
}}
|
||||
@@ -28,27 +28,27 @@ export function Title() {
|
||||
position="absolute"
|
||||
align="center"
|
||||
justify="center"
|
||||
top={revealed ? '6rem' : '50%'}
|
||||
top={revealed ? "6rem" : "50%"}
|
||||
left="50%"
|
||||
width="100%"
|
||||
height={revealed ? fontSize : '100%'}
|
||||
height={revealed ? fontSize : "100%"}
|
||||
style={{
|
||||
pointerEvents: revealed ? 'none' : undefined,
|
||||
transitionDuration: '1s',
|
||||
transform: 'translate(-50%, -50%)',
|
||||
pointerEvents: revealed ? "none" : undefined,
|
||||
transitionDuration: "1s",
|
||||
transform: "translate(-50%, -50%)",
|
||||
}}
|
||||
>
|
||||
<Heading
|
||||
style={{
|
||||
fontWeight: 900,
|
||||
userSelect: 'none',
|
||||
pointerEvents: 'none',
|
||||
userSelect: "none",
|
||||
pointerEvents: "none",
|
||||
fontSize,
|
||||
whiteSpace: 'nowrap',
|
||||
whiteSpace: "nowrap",
|
||||
opacity: revealed ? 1 : 0.5,
|
||||
letterSpacing: revealed || !revealed ? 0 : '-0.03em',
|
||||
letterSpacing: revealed || !revealed ? 0 : "-0.03em",
|
||||
transition: `
|
||||
letter-spacing 1.5s ${revealed ? '' : 'cubic-bezier(0.81, -2, 0.68, 1)'},
|
||||
letter-spacing 1.5s ${revealed ? "" : "cubic-bezier(0.81, -2, 0.68, 1)"},
|
||||
font-size 1s,
|
||||
-webkit-text-stroke 2s,
|
||||
opacity 1s
|
||||
|
||||
+39
-39
@@ -1,15 +1,15 @@
|
||||
import { I_HAT, J_HAT } from '@blitzkit/core';
|
||||
import { OrbitControls } from '@react-three/drei';
|
||||
import { invalidate, useFrame, useThree } from '@react-three/fiber';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { PerspectiveCamera, Vector3 } from 'three';
|
||||
import { OrbitControls as OrbitControlsClass } from 'three-stdlib';
|
||||
import { awaitableModelDefinitions } from '../../../../../../core/awaitables/modelDefinitions';
|
||||
import { applyPitchYawLimits } from '../../../../../../core/blitz/applyPitchYawLimits';
|
||||
import { hasEquipment } from '../../../../../../core/blitzkit/hasEquipment';
|
||||
import { Pose, poseEvent } from '../../../../../../core/blitzkit/pose';
|
||||
import { Duel } from '../../../../../../stores/duel';
|
||||
import { TankopediaEphemeral } from '../../../../../../stores/tankopediaEphemeral';
|
||||
import { I_HAT, J_HAT } from "@blitzkit/core";
|
||||
import { OrbitControls } from "@react-three/drei";
|
||||
import { invalidate, useFrame, useThree } from "@react-three/fiber";
|
||||
import { useEffect, useRef } from "react";
|
||||
import { PerspectiveCamera, Vector3 } from "three";
|
||||
import { OrbitControls as OrbitControlsClass } from "three-stdlib";
|
||||
import { awaitableModelDefinitions } from "../../../../../../core/awaitables/modelDefinitions";
|
||||
import { applyPitchYawLimits } from "../../../../../../core/blitz/applyPitchYawLimits";
|
||||
import { hasEquipment } from "../../../../../../core/blitzkit/hasEquipment";
|
||||
import { Pose, poseEvent } from "../../../../../../core/blitzkit/pose";
|
||||
import { Duel } from "../../../../../../stores/duel";
|
||||
import { Tankopedia } from "../../../../../../stores/tankopedia";
|
||||
|
||||
const poseDistances: Record<Pose, number> = {
|
||||
[Pose.HullDown]: 15,
|
||||
@@ -56,23 +56,23 @@ export function Controls({
|
||||
const protagonistHullOrigin = new Vector3(
|
||||
protagonistTrackModelDefinition.origin.x,
|
||||
protagonistTrackModelDefinition.origin.y,
|
||||
-protagonistTrackModelDefinition.origin.z,
|
||||
-protagonistTrackModelDefinition.origin.z
|
||||
);
|
||||
const protagonistTurretOrigin = new Vector3(
|
||||
protagonistModelDefinition.turret_origin.x,
|
||||
protagonistModelDefinition.turret_origin.y,
|
||||
-protagonistModelDefinition.turret_origin.z,
|
||||
-protagonistModelDefinition.turret_origin.z
|
||||
);
|
||||
const protagonistGunOrigin = new Vector3(
|
||||
protagonistTurretModelDefinition.gun_origin.x,
|
||||
protagonistTurretModelDefinition.gun_origin.y,
|
||||
-protagonistTurretModelDefinition.gun_origin.z,
|
||||
-protagonistTurretModelDefinition.gun_origin.z
|
||||
);
|
||||
const antagonistGunHeight =
|
||||
protagonistTrackModelDefinition.origin.y +
|
||||
antagonistModelDefinition.turret_origin.y +
|
||||
antagonistTurretModelDefinition.gun_origin.y;
|
||||
const disturbed = TankopediaEphemeral.use((state) => state.disturbed);
|
||||
const disturbed = Tankopedia.use((state) => state.disturbed);
|
||||
const doAutoRotate = autoRotate && !disturbed;
|
||||
const gunHeight =
|
||||
protagonistHullOrigin.y +
|
||||
@@ -81,7 +81,7 @@ export function Controls({
|
||||
const inspectModeInitialPosition = new Vector3(
|
||||
-8,
|
||||
naked ? gunHeight + 10 : 2,
|
||||
-13,
|
||||
-13
|
||||
);
|
||||
|
||||
const t0 = useRef(Date.now() / 1000);
|
||||
@@ -100,11 +100,11 @@ export function Controls({
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribeTankopediaEphemeral = TankopediaEphemeral.on(
|
||||
const unsubscribeTankopediaEphemeral = Tankopedia.on(
|
||||
(state) => state.controlsEnabled,
|
||||
(enabled) => {
|
||||
if (orbitControls.current) orbitControls.current.enabled = enabled;
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
function handlePoseEvent(event: Pose) {
|
||||
@@ -112,12 +112,12 @@ export function Controls({
|
||||
const hasImprovedVerticalStabilizer = hasEquipment(
|
||||
122,
|
||||
duel.protagonist.tank.equipment_preset,
|
||||
duel.protagonist.equipmentMatrix,
|
||||
duel.protagonist.equipmentMatrix
|
||||
);
|
||||
const hasDownImprovedVerticalStabilizer = hasEquipment(
|
||||
124,
|
||||
duel.protagonist.tank.equipment_preset,
|
||||
duel.protagonist.equipmentMatrix,
|
||||
duel.protagonist.equipmentMatrix
|
||||
);
|
||||
|
||||
switch (event) {
|
||||
@@ -128,7 +128,7 @@ export function Controls({
|
||||
protagonistGunModelDefinition.pitch,
|
||||
protagonistTurretModelDefinition.yaw,
|
||||
hasImprovedVerticalStabilizer,
|
||||
hasDownImprovedVerticalStabilizer,
|
||||
hasDownImprovedVerticalStabilizer
|
||||
);
|
||||
|
||||
camera.position
|
||||
@@ -140,14 +140,14 @@ export function Controls({
|
||||
new Vector3(
|
||||
0,
|
||||
poseDistances[event] * Math.sin(pitch),
|
||||
poseDistances[event] * -Math.cos(pitch),
|
||||
),
|
||||
poseDistances[event] * -Math.cos(pitch)
|
||||
)
|
||||
);
|
||||
camera.lookAt(
|
||||
protagonistHullOrigin
|
||||
.clone()
|
||||
.add(protagonistTurretOrigin)
|
||||
.add(protagonistGunOrigin),
|
||||
.add(protagonistGunOrigin)
|
||||
);
|
||||
orbitControls.current?.target.set(0, antagonistGunHeight, 0);
|
||||
|
||||
@@ -161,7 +161,7 @@ export function Controls({
|
||||
protagonistGunModelDefinition.pitch,
|
||||
protagonistTurretModelDefinition.yaw,
|
||||
hasImprovedVerticalStabilizer,
|
||||
hasDownImprovedVerticalStabilizer,
|
||||
hasDownImprovedVerticalStabilizer
|
||||
);
|
||||
|
||||
camera.position
|
||||
@@ -173,8 +173,8 @@ export function Controls({
|
||||
new Vector3(
|
||||
0,
|
||||
poseDistances[event] * Math.sin(pitch),
|
||||
poseDistances[event] * -Math.cos(pitch),
|
||||
),
|
||||
poseDistances[event] * -Math.cos(pitch)
|
||||
)
|
||||
);
|
||||
orbitControls.current?.target
|
||||
.set(0, 0, 0)
|
||||
@@ -185,8 +185,8 @@ export function Controls({
|
||||
new Vector3(
|
||||
0,
|
||||
0.5 * poseDistances[event] * Math.sin(pitch),
|
||||
0.5 * poseDistances[event] * -Math.cos(pitch),
|
||||
),
|
||||
0.5 * poseDistances[event] * -Math.cos(pitch)
|
||||
)
|
||||
);
|
||||
|
||||
break;
|
||||
@@ -220,15 +220,15 @@ export function Controls({
|
||||
}
|
||||
|
||||
function disturb() {
|
||||
TankopediaEphemeral.mutate((draft) => {
|
||||
Tankopedia.mutate((draft) => {
|
||||
draft.disturbed = true;
|
||||
});
|
||||
}
|
||||
|
||||
poseEvent.on(disturb);
|
||||
canvas.addEventListener('pointerdown', disturb);
|
||||
canvas.addEventListener('wheel', handleWheel);
|
||||
document.body.addEventListener('scroll', handleScroll);
|
||||
canvas.addEventListener("pointerdown", disturb);
|
||||
canvas.addEventListener("wheel", handleWheel);
|
||||
document.body.addEventListener("scroll", handleScroll);
|
||||
|
||||
function updateCamera() {
|
||||
if (!orbitControls.current) return;
|
||||
@@ -239,7 +239,7 @@ export function Controls({
|
||||
orbitControls.current.target.set(
|
||||
0,
|
||||
gunHeight * (naked ? 1 / 4 : 3 / 4),
|
||||
0,
|
||||
0
|
||||
);
|
||||
orbitControls.current.enablePan = true;
|
||||
orbitControls.current.enableZoom = true;
|
||||
@@ -251,10 +251,10 @@ export function Controls({
|
||||
updateCamera();
|
||||
|
||||
return () => {
|
||||
canvas.removeEventListener('pointerdown', disturb);
|
||||
canvas.removeEventListener("pointerdown", disturb);
|
||||
poseEvent.off(disturb);
|
||||
canvas.removeEventListener('wheel', handleWheel);
|
||||
document.body.removeEventListener('scroll', handleScroll);
|
||||
canvas.removeEventListener("wheel", handleWheel);
|
||||
document.body.removeEventListener("scroll", handleScroll);
|
||||
};
|
||||
}, []);
|
||||
|
||||
@@ -266,7 +266,7 @@ export function Controls({
|
||||
enableZoom={zoomable}
|
||||
zoomSpeed={zoomable ? undefined : 0}
|
||||
ref={orbitControls}
|
||||
enabled={TankopediaEphemeral.state.controlsEnabled}
|
||||
enabled={Tankopedia.state.controlsEnabled}
|
||||
enableDamping={false}
|
||||
autoRotateSpeed={1 / 4}
|
||||
/>
|
||||
|
||||
+6
-6
@@ -1,14 +1,14 @@
|
||||
import { invalidate, useThree } from '@react-three/fiber';
|
||||
import { useEffect } from 'react';
|
||||
import type { Fog } from 'three';
|
||||
import { fogAnimationTime, fogFar0, fogFar1, forNear0, forNear1 } from '..';
|
||||
import { TankopediaEphemeral } from '../../../../../../stores/tankopediaEphemeral';
|
||||
import { invalidate, useThree } from "@react-three/fiber";
|
||||
import { useEffect } from "react";
|
||||
import type { Fog } from "three";
|
||||
import { fogAnimationTime, fogFar0, fogFar1, forNear0, forNear1 } from "..";
|
||||
import { Tankopedia } from "../../../../../../stores/tankopedia";
|
||||
|
||||
export function InitialFogReveal() {
|
||||
const fog = useThree((state) => state.scene.fog as Fog | null);
|
||||
|
||||
useEffect(() => {
|
||||
TankopediaEphemeral.mutate((draft) => {
|
||||
Tankopedia.mutate((draft) => {
|
||||
draft.revealed = true;
|
||||
});
|
||||
|
||||
|
||||
+48
-51
@@ -1,17 +1,17 @@
|
||||
import { invalidate, useThree, type ThreeEvent } from '@react-three/fiber';
|
||||
import { useRef } from 'react';
|
||||
import { Group, Mesh, MeshStandardMaterial, Vector2 } from 'three';
|
||||
import { applyPitchYawLimits } from '../../../../../../core/blitz/applyPitchYawLimits';
|
||||
import { hasEquipment } from '../../../../../../core/blitzkit/hasEquipment';
|
||||
import { jsxTree } from '../../../../../../core/blitzkit/jsxTree';
|
||||
import { modelTransformEvent } from '../../../../../../core/blitzkit/modelTransform';
|
||||
import { useModel } from '../../../../../../hooks/useModel';
|
||||
import { useTankModelDefinition } from '../../../../../../hooks/useTankModelDefinition';
|
||||
import { useTankTransform } from '../../../../../../hooks/useTankTransform';
|
||||
import { Duel } from '../../../../../../stores/duel';
|
||||
import { TankopediaEphemeral } from '../../../../../../stores/tankopediaEphemeral';
|
||||
import { TankopediaDisplay } from '../../../../../../stores/tankopediaPersistent/constants';
|
||||
import { ModelTankWrapper } from '../../../../../Armor/components/ModelTankWrapper';
|
||||
import { invalidate, useThree, type ThreeEvent } from "@react-three/fiber";
|
||||
import { useRef } from "react";
|
||||
import { Group, Mesh, MeshStandardMaterial, Vector2 } from "three";
|
||||
import { applyPitchYawLimits } from "../../../../../../core/blitz/applyPitchYawLimits";
|
||||
import { hasEquipment } from "../../../../../../core/blitzkit/hasEquipment";
|
||||
import { jsxTree } from "../../../../../../core/blitzkit/jsxTree";
|
||||
import { modelTransformEvent } from "../../../../../../core/blitzkit/modelTransform";
|
||||
import { useModel } from "../../../../../../hooks/useModel";
|
||||
import { useTankModelDefinition } from "../../../../../../hooks/useTankModelDefinition";
|
||||
import { useTankTransform } from "../../../../../../hooks/useTankTransform";
|
||||
import { Duel } from "../../../../../../stores/duel";
|
||||
import { Tankopedia } from "../../../../../../stores/tankopedia";
|
||||
import { TankopediaDisplay } from "../../../../../../stores/tankopediaPersistent/constants";
|
||||
import { ModelTankWrapper } from "../../../../../Armor/components/ModelTankWrapper";
|
||||
|
||||
export function TankModel() {
|
||||
const duelStore = Duel.useStore();
|
||||
@@ -34,9 +34,9 @@ export function TankModel() {
|
||||
return (
|
||||
<ModelTankWrapper ref={hullContainer}>
|
||||
{nodes.map((node) => {
|
||||
const isHull = node.name === 'hull';
|
||||
const isWheel = node.name.startsWith('chassis_wheel_');
|
||||
const isTrack = node.name.startsWith('chassis_track_');
|
||||
const isHull = node.name === "hull";
|
||||
const isWheel = node.name.startsWith("chassis_wheel_");
|
||||
const isTrack = node.name.startsWith("chassis_track_");
|
||||
const isVisible = isHull || isWheel || isTrack;
|
||||
const position = new Vector2();
|
||||
const delta = new Vector2();
|
||||
@@ -59,19 +59,16 @@ export function TankModel() {
|
||||
}
|
||||
|
||||
function onPointerDown(event: ThreeEvent<PointerEvent>) {
|
||||
if (
|
||||
isTrack &&
|
||||
TankopediaEphemeral.state.display === TankopediaDisplay.Model
|
||||
) {
|
||||
if (isTrack && Tankopedia.state.display === TankopediaDisplay.Model) {
|
||||
position.set(event.clientX, event.clientY);
|
||||
event.stopPropagation();
|
||||
|
||||
TankopediaEphemeral.mutate((draft) => {
|
||||
Tankopedia.mutate((draft) => {
|
||||
draft.controlsEnabled = false;
|
||||
});
|
||||
|
||||
window.addEventListener('pointermove', handlePointerMove);
|
||||
window.addEventListener('pointerup', handlePointerUp);
|
||||
window.addEventListener("pointermove", handlePointerMove);
|
||||
window.addEventListener("pointerup", handlePointerUp);
|
||||
} else {
|
||||
event.stopPropagation();
|
||||
}
|
||||
@@ -85,12 +82,12 @@ export function TankModel() {
|
||||
translateTexture(deltaX + deltaY);
|
||||
}
|
||||
function handlePointerUp() {
|
||||
TankopediaEphemeral.mutate((draft) => {
|
||||
Tankopedia.mutate((draft) => {
|
||||
draft.controlsEnabled = true;
|
||||
});
|
||||
|
||||
window.removeEventListener('pointermove', handlePointerMove);
|
||||
window.removeEventListener('pointerup', handlePointerUp);
|
||||
window.removeEventListener("pointermove", handlePointerMove);
|
||||
window.removeEventListener("pointerup", handlePointerUp);
|
||||
}
|
||||
|
||||
return jsxTree(node, {
|
||||
@@ -110,10 +107,10 @@ export function TankModel() {
|
||||
|
||||
<group ref={turretContainer}>
|
||||
{nodes.map((node) => {
|
||||
const isTurret = node.name.startsWith('turret_');
|
||||
const isTurret = node.name.startsWith("turret_");
|
||||
const isCurrentTurret =
|
||||
node.name ===
|
||||
`turret_${turretModelDefinition.model_id.toString().padStart(2, '0')}`;
|
||||
`turret_${turretModelDefinition.model_id.toString().padStart(2, "0")}`;
|
||||
const isVisible = isCurrentTurret;
|
||||
const position = new Vector2();
|
||||
const delta = new Vector2();
|
||||
@@ -127,27 +124,27 @@ export function TankModel() {
|
||||
|
||||
position.set(event.clientX, event.clientY);
|
||||
|
||||
TankopediaEphemeral.mutate((draft) => {
|
||||
Tankopedia.mutate((draft) => {
|
||||
draft.controlsEnabled = false;
|
||||
});
|
||||
TankopediaEphemeral.mutate((draft) => {
|
||||
Tankopedia.mutate((draft) => {
|
||||
draft.shot = undefined;
|
||||
draft.highlightArmor = undefined;
|
||||
});
|
||||
window.addEventListener('pointermove', handlePointerMove);
|
||||
window.addEventListener('pointerup', handlePointerUp);
|
||||
window.addEventListener("pointermove", handlePointerMove);
|
||||
window.addEventListener("pointerup", handlePointerUp);
|
||||
}
|
||||
function handlePointerMove(event: PointerEvent) {
|
||||
const duel = duelStore.getState();
|
||||
const hasImprovedVerticalStabilizer = hasEquipment(
|
||||
122,
|
||||
duel.protagonist.tank.equipment_preset,
|
||||
duel.protagonist.equipmentMatrix,
|
||||
duel.protagonist.equipmentMatrix
|
||||
);
|
||||
const hasDownImprovedVerticalStabilizer = hasEquipment(
|
||||
124,
|
||||
duel.protagonist.tank.equipment_preset,
|
||||
duel.protagonist.equipmentMatrix,
|
||||
duel.protagonist.equipmentMatrix
|
||||
);
|
||||
const boundingRect = canvas.getBoundingClientRect();
|
||||
|
||||
@@ -161,16 +158,16 @@ export function TankModel() {
|
||||
gunModelDefinition.pitch,
|
||||
turretModelDefinition.yaw,
|
||||
hasImprovedVerticalStabilizer,
|
||||
hasDownImprovedVerticalStabilizer,
|
||||
hasDownImprovedVerticalStabilizer
|
||||
);
|
||||
modelTransformEvent.dispatch({ pitch, yaw });
|
||||
}
|
||||
function handlePointerUp() {
|
||||
TankopediaEphemeral.mutate((draft) => {
|
||||
Tankopedia.mutate((draft) => {
|
||||
draft.controlsEnabled = true;
|
||||
});
|
||||
window.removeEventListener('pointermove', handlePointerMove);
|
||||
window.removeEventListener('pointerup', handlePointerUp);
|
||||
window.removeEventListener("pointermove", handlePointerMove);
|
||||
window.removeEventListener("pointerup", handlePointerUp);
|
||||
}
|
||||
|
||||
return jsxTree(node, {
|
||||
@@ -194,10 +191,10 @@ export function TankModel() {
|
||||
node.name ===
|
||||
`gun_${gunModelDefinition.model_id
|
||||
.toString()
|
||||
.padStart(2, '0')}_mask`;
|
||||
.padStart(2, "0")}_mask`;
|
||||
const isCurrentGun =
|
||||
node.name ===
|
||||
`gun_${gunModelDefinition.model_id.toString().padStart(2, '0')}`;
|
||||
`gun_${gunModelDefinition.model_id.toString().padStart(2, "0")}`;
|
||||
const isVisible = isCurrentGun || isCurrentMantlet;
|
||||
const position = new Vector2();
|
||||
const delta = new Vector2();
|
||||
@@ -210,10 +207,10 @@ export function TankModel() {
|
||||
function onPointerDown(event: ThreeEvent<PointerEvent>) {
|
||||
event.stopPropagation();
|
||||
|
||||
TankopediaEphemeral.mutate((draft) => {
|
||||
Tankopedia.mutate((draft) => {
|
||||
draft.controlsEnabled = false;
|
||||
});
|
||||
TankopediaEphemeral.mutate((draft) => {
|
||||
Tankopedia.mutate((draft) => {
|
||||
draft.shot = undefined;
|
||||
draft.highlightArmor = undefined;
|
||||
});
|
||||
@@ -222,20 +219,20 @@ export function TankModel() {
|
||||
pitch = modelTransformEvent.last!.pitch;
|
||||
yaw = modelTransformEvent.last!.yaw;
|
||||
|
||||
window.addEventListener('pointermove', handlePointerMove);
|
||||
window.addEventListener('pointerup', handlePointerUp);
|
||||
window.addEventListener("pointermove", handlePointerMove);
|
||||
window.addEventListener("pointerup", handlePointerUp);
|
||||
}
|
||||
function handlePointerMove(event: PointerEvent) {
|
||||
const duel = duelStore.getState();
|
||||
const hasImprovedVerticalStabilizer = hasEquipment(
|
||||
122,
|
||||
duel.protagonist.tank.equipment_preset,
|
||||
duel.protagonist.equipmentMatrix,
|
||||
duel.protagonist.equipmentMatrix
|
||||
);
|
||||
const hasDownImprovedVerticalStabilizer = hasEquipment(
|
||||
124,
|
||||
duel.protagonist.tank.equipment_preset,
|
||||
duel.protagonist.equipmentMatrix,
|
||||
duel.protagonist.equipmentMatrix
|
||||
);
|
||||
const boundingRect = canvas.getBoundingClientRect();
|
||||
delta.set(event.clientX, event.clientY).sub(position);
|
||||
@@ -247,16 +244,16 @@ export function TankModel() {
|
||||
gunModelDefinition.pitch,
|
||||
turretModelDefinition.yaw,
|
||||
hasImprovedVerticalStabilizer,
|
||||
hasDownImprovedVerticalStabilizer,
|
||||
hasDownImprovedVerticalStabilizer
|
||||
);
|
||||
modelTransformEvent.dispatch({ pitch, yaw });
|
||||
}
|
||||
function handlePointerUp() {
|
||||
TankopediaEphemeral.mutate((draft) => {
|
||||
Tankopedia.mutate((draft) => {
|
||||
draft.controlsEnabled = true;
|
||||
});
|
||||
window.removeEventListener('pointermove', handlePointerMove);
|
||||
window.removeEventListener('pointerup', handlePointerUp);
|
||||
window.removeEventListener("pointermove", handlePointerMove);
|
||||
window.removeEventListener("pointerup", handlePointerUp);
|
||||
}
|
||||
|
||||
return jsxTree(node, {
|
||||
|
||||
+18
-18
@@ -1,14 +1,14 @@
|
||||
import { useRef } from 'react';
|
||||
import { Group, ShaderMaterial, UniformsLib, UniformsUtils } from 'three';
|
||||
import { jsxTree } from '../../../../../../../core/blitzkit/jsxTree';
|
||||
import { useModel } from '../../../../../../../hooks/useModel';
|
||||
import { useTankModelDefinition } from '../../../../../../../hooks/useTankModelDefinition';
|
||||
import { useTankTransform } from '../../../../../../../hooks/useTankTransform';
|
||||
import { Duel } from '../../../../../../../stores/duel';
|
||||
import { TankopediaEphemeral } from '../../../../../../../stores/tankopediaEphemeral';
|
||||
import { ModelTankWrapper } from '../../../../../../Armor/components/ModelTankWrapper';
|
||||
import fragmentShader from './shaders/fragment.glsl?raw';
|
||||
import vertexShader from './shaders/vertex.glsl?raw';
|
||||
import { useRef } from "react";
|
||||
import { Group, ShaderMaterial, UniformsLib, UniformsUtils } from "three";
|
||||
import { jsxTree } from "../../../../../../../core/blitzkit/jsxTree";
|
||||
import { useModel } from "../../../../../../../hooks/useModel";
|
||||
import { useTankModelDefinition } from "../../../../../../../hooks/useTankModelDefinition";
|
||||
import { useTankTransform } from "../../../../../../../hooks/useTankTransform";
|
||||
import { Duel } from "../../../../../../../stores/duel";
|
||||
import { Tankopedia } from "../../../../../../../stores/tankopedia";
|
||||
import { ModelTankWrapper } from "../../../../../../Armor/components/ModelTankWrapper";
|
||||
import fragmentShader from "./shaders/fragment.glsl?raw";
|
||||
import vertexShader from "./shaders/vertex.glsl?raw";
|
||||
|
||||
const skeletonMaterial = new ShaderMaterial({
|
||||
vertexShader,
|
||||
@@ -33,16 +33,16 @@ export function TransitionSkeleton() {
|
||||
const gunModelDefinition = turretModelDefinition.guns[protagonist.gun.id];
|
||||
const { gltf } = useModel(protagonist.tank.id);
|
||||
const nodes = Object.values(gltf.nodes);
|
||||
const revealed = TankopediaEphemeral.use((state) => state.revealed);
|
||||
const revealed = Tankopedia.use((state) => state.revealed);
|
||||
|
||||
useTankTransform(track, turret, turretContainer, gunContainer);
|
||||
|
||||
return (
|
||||
<ModelTankWrapper ref={hullContainer} visible={revealed}>
|
||||
{nodes.map((node) => {
|
||||
const isHull = node.name === 'hull';
|
||||
const isWheel = node.name.startsWith('chassis_wheel_');
|
||||
const isTrack = node.name.startsWith('chassis_track_');
|
||||
const isHull = node.name === "hull";
|
||||
const isWheel = node.name.startsWith("chassis_wheel_");
|
||||
const isTrack = node.name.startsWith("chassis_track_");
|
||||
const isVisible = isHull || isWheel || isTrack;
|
||||
|
||||
if (!isVisible) return null;
|
||||
@@ -58,7 +58,7 @@ export function TransitionSkeleton() {
|
||||
{nodes.map((node) => {
|
||||
const isCurrentTurret =
|
||||
node.name ===
|
||||
`turret_${turretModelDefinition.model_id.toString().padStart(2, '0')}`;
|
||||
`turret_${turretModelDefinition.model_id.toString().padStart(2, "0")}`;
|
||||
const isVisible = isCurrentTurret;
|
||||
|
||||
if (!isVisible) return null;
|
||||
@@ -76,10 +76,10 @@ export function TransitionSkeleton() {
|
||||
node.name ===
|
||||
`gun_${gunModelDefinition.model_id
|
||||
.toString()
|
||||
.padStart(2, '0')}_mask`;
|
||||
.padStart(2, "0")}_mask`;
|
||||
const isCurrentGun =
|
||||
node.name ===
|
||||
`gun_${gunModelDefinition.model_id.toString().padStart(2, '0')}`;
|
||||
`gun_${gunModelDefinition.model_id.toString().padStart(2, "0")}`;
|
||||
const isVisible = isCurrentGun || isCurrentMantlet;
|
||||
|
||||
if (!isVisible) return null;
|
||||
|
||||
+45
-45
@@ -1,5 +1,5 @@
|
||||
import { BLITZKIT_TANK_ICON_SIZE } from '@blitzkit/core';
|
||||
import { invalidate } from '@react-three/fiber';
|
||||
import { BLITZKIT_TANK_ICON_SIZE } from "@blitzkit/core";
|
||||
import { invalidate } from "@react-three/fiber";
|
||||
import {
|
||||
forwardRef,
|
||||
Suspense,
|
||||
@@ -7,33 +7,33 @@ import {
|
||||
useImperativeHandle,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { Fog } from 'three';
|
||||
import { applyPitchYawLimits } from '../../../../../core/blitz/applyPitchYawLimits';
|
||||
import { modelTransformEvent } from '../../../../../core/blitzkit/modelTransform';
|
||||
import { Pose, poseEvent } from '../../../../../core/blitzkit/pose';
|
||||
import { useEquipment } from '../../../../../hooks/useEquipment';
|
||||
import { useTankModelDefinition } from '../../../../../hooks/useTankModelDefinition';
|
||||
import { Duel } from '../../../../../stores/duel';
|
||||
import { TankopediaEphemeral } from '../../../../../stores/tankopediaEphemeral';
|
||||
import { TankopediaPersistent } from '../../../../../stores/tankopediaPersistent';
|
||||
import { TankopediaDisplay } from '../../../../../stores/tankopediaPersistent/constants';
|
||||
import { Armor } from '../../../../Armor';
|
||||
import { ArmorPlateDisplay } from '../../../../Armor/components/ArmorPlateDisplay';
|
||||
import { ShotDisplay } from '../../../../Armor/components/ShotDisplay';
|
||||
} from "react";
|
||||
import { Fog } from "three";
|
||||
import { applyPitchYawLimits } from "../../../../../core/blitz/applyPitchYawLimits";
|
||||
import { modelTransformEvent } from "../../../../../core/blitzkit/modelTransform";
|
||||
import { Pose, poseEvent } from "../../../../../core/blitzkit/pose";
|
||||
import { useEquipment } from "../../../../../hooks/useEquipment";
|
||||
import { useTankModelDefinition } from "../../../../../hooks/useTankModelDefinition";
|
||||
import { Duel } from "../../../../../stores/duel";
|
||||
import { Tankopedia } from "../../../../../stores/tankopedia";
|
||||
import { TankopediaPersistent } from "../../../../../stores/tankopediaPersistent";
|
||||
import { TankopediaDisplay } from "../../../../../stores/tankopediaPersistent/constants";
|
||||
import { Armor } from "../../../../Armor";
|
||||
import { ArmorPlateDisplay } from "../../../../Armor/components/ArmorPlateDisplay";
|
||||
import { ShotDisplay } from "../../../../Armor/components/ShotDisplay";
|
||||
import {
|
||||
StaticArmor,
|
||||
type ThicknessRange,
|
||||
} from '../../../../Armor/components/StaticArmor';
|
||||
import { SmartCanvas } from '../../../../SmartCanvas';
|
||||
import { AutoClear } from './components/AutoClear';
|
||||
import { Controls } from './components/Control';
|
||||
import { InitialAligner } from './components/InitialAligner';
|
||||
import { InitialFogReveal } from './components/InitialFogReveal';
|
||||
import { Lighting } from './components/Lighting';
|
||||
import { SceneProps } from './components/SceneProps';
|
||||
import { TankModel } from './components/TankModel';
|
||||
import { TransitionSkeleton } from './components/TransitionSkeleton';
|
||||
} from "../../../../Armor/components/StaticArmor";
|
||||
import { SmartCanvas } from "../../../../SmartCanvas";
|
||||
import { AutoClear } from "./components/AutoClear";
|
||||
import { Controls } from "./components/Control";
|
||||
import { InitialAligner } from "./components/InitialAligner";
|
||||
import { InitialFogReveal } from "./components/InitialFogReveal";
|
||||
import { Lighting } from "./components/Lighting";
|
||||
import { SceneProps } from "./components/SceneProps";
|
||||
import { TankModel } from "./components/TankModel";
|
||||
import { TransitionSkeleton } from "./components/TransitionSkeleton";
|
||||
|
||||
interface TankSandboxProps {
|
||||
thicknessRange: ThicknessRange;
|
||||
@@ -48,7 +48,7 @@ export const fogAnimationTime = 2.5;
|
||||
|
||||
export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
|
||||
({ thicknessRange, naked }, ref) => {
|
||||
const fog = useRef(new Fog('black', forNear1, fogFar1));
|
||||
const fog = useRef(new Fog("black", forNear1, fogFar1));
|
||||
const canvas = useRef<HTMLCanvasElement>(null);
|
||||
const hasImprovedVerticalStabilizer = useEquipment(122);
|
||||
const hasDownImprovedVerticalStabilizer = useEquipment(124);
|
||||
@@ -58,10 +58,10 @@ export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
|
||||
const turretModelDefinition =
|
||||
tankModelDefinition.turrets[protagonistTurret.id];
|
||||
const gunModelDefinition = turretModelDefinition.guns[protagonistGun.id];
|
||||
const rawDisplay = TankopediaEphemeral.use((state) => state.display);
|
||||
const rawDisplay = Tankopedia.use((state) => state.display);
|
||||
const [display, setDisplay] = useState(rawDisplay);
|
||||
const hideTankModelUnderArmor = TankopediaPersistent.use(
|
||||
(state) => state.hideTankModelUnderArmor,
|
||||
(state) => state.hideTankModelUnderArmor
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -95,8 +95,8 @@ export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
|
||||
useImperativeHandle(ref, () => canvas.current!, []);
|
||||
|
||||
function handlePointerDown() {
|
||||
window.addEventListener('pointermove', handlePointerMove);
|
||||
window.addEventListener('pointerup', handlePointerUp);
|
||||
window.addEventListener("pointermove", handlePointerMove);
|
||||
window.addEventListener("pointerup", handlePointerUp);
|
||||
}
|
||||
function handlePointerMove(event: PointerEvent) {
|
||||
event.preventDefault();
|
||||
@@ -104,8 +104,8 @@ export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
|
||||
function handlePointerUp(event: PointerEvent) {
|
||||
event.preventDefault();
|
||||
|
||||
window.removeEventListener('pointermove', handlePointerMove);
|
||||
window.removeEventListener('pointerup', handlePointerUp);
|
||||
window.removeEventListener("pointermove", handlePointerMove);
|
||||
window.removeEventListener("pointerup", handlePointerUp);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
@@ -118,7 +118,7 @@ export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
|
||||
gunModelDefinition.pitch,
|
||||
turretModelDefinition.yaw,
|
||||
hasImprovedVerticalStabilizer,
|
||||
hasDownImprovedVerticalStabilizer,
|
||||
hasDownImprovedVerticalStabilizer
|
||||
);
|
||||
|
||||
modelTransformEvent.dispatch({ pitch, yaw });
|
||||
@@ -132,7 +132,7 @@ export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
|
||||
0,
|
||||
gunModelDefinition.pitch,
|
||||
turretModelDefinition.yaw,
|
||||
hasImprovedVerticalStabilizer,
|
||||
hasImprovedVerticalStabilizer
|
||||
);
|
||||
|
||||
modelTransformEvent.dispatch({ pitch, yaw });
|
||||
@@ -146,7 +146,7 @@ export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
|
||||
0,
|
||||
gunModelDefinition.pitch,
|
||||
turretModelDefinition.yaw,
|
||||
hasImprovedVerticalStabilizer,
|
||||
hasImprovedVerticalStabilizer
|
||||
);
|
||||
|
||||
modelTransformEvent.dispatch({ pitch, yaw });
|
||||
@@ -168,7 +168,7 @@ export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
|
||||
modelTransformEvent.last!.yaw,
|
||||
gunModelDefinition.pitch,
|
||||
turretModelDefinition.yaw,
|
||||
hasImprovedVerticalStabilizer,
|
||||
hasImprovedVerticalStabilizer
|
||||
);
|
||||
|
||||
modelTransformEvent.dispatch({ pitch, yaw });
|
||||
@@ -176,13 +176,13 @@ export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
|
||||
|
||||
useEffect(() => {
|
||||
if (display !== TankopediaDisplay.DynamicArmor) {
|
||||
TankopediaEphemeral.mutate((draft) => {
|
||||
Tankopedia.mutate((draft) => {
|
||||
draft.shot = undefined;
|
||||
});
|
||||
}
|
||||
|
||||
if (display !== TankopediaDisplay.StaticArmor) {
|
||||
TankopediaEphemeral.mutate((draft) => {
|
||||
Tankopedia.mutate((draft) => {
|
||||
draft.highlightArmor = undefined;
|
||||
});
|
||||
}
|
||||
@@ -200,16 +200,16 @@ export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
|
||||
shadows="soft"
|
||||
onPointerDown={handlePointerDown}
|
||||
onPointerMissed={() => {
|
||||
TankopediaEphemeral.mutate((draft) => {
|
||||
Tankopedia.mutate((draft) => {
|
||||
draft.shot = undefined;
|
||||
draft.highlightArmor = undefined;
|
||||
});
|
||||
}}
|
||||
style={{
|
||||
userSelect: 'none',
|
||||
width: naked ? BLITZKIT_TANK_ICON_SIZE.width : '100%',
|
||||
height: naked ? BLITZKIT_TANK_ICON_SIZE.height : '100%',
|
||||
outline: naked ? '1rem red solid' : undefined,
|
||||
userSelect: "none",
|
||||
width: naked ? BLITZKIT_TANK_ICON_SIZE.width : "100%",
|
||||
height: naked ? BLITZKIT_TANK_ICON_SIZE.height : "100%",
|
||||
outline: naked ? "1rem red solid" : undefined,
|
||||
}}
|
||||
>
|
||||
{!naked && <SceneProps />}
|
||||
@@ -236,5 +236,5 @@ export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
|
||||
<InitialAligner />
|
||||
</SmartCanvas>
|
||||
);
|
||||
},
|
||||
}
|
||||
);
|
||||
@@ -1,28 +1,28 @@
|
||||
import { Box, Flex } from '@radix-ui/themes';
|
||||
import { times } from 'lodash-es';
|
||||
import { Suspense, useEffect, useMemo, useRef } from 'react';
|
||||
import { awaitableTankDefinitions } from '../../../core/awaitables/tankDefinitions';
|
||||
import { useFullScreen } from '../../../hooks/useFullScreen';
|
||||
import { Duel } from '../../../stores/duel';
|
||||
import { TankopediaEphemeral } from '../../../stores/tankopediaEphemeral';
|
||||
import type { MaybeSkeletonComponentProps } from '../../../types/maybeSkeletonComponentProps';
|
||||
import type { ThicknessRange } from '../../Armor/components/StaticArmor';
|
||||
import { Options } from './components/Options';
|
||||
import { TankSandbox } from './components/TankSandbox';
|
||||
import { Title } from './components/TankSandbox/Title';
|
||||
import { Box, Flex } from "@radix-ui/themes";
|
||||
import { times } from "lodash-es";
|
||||
import { Suspense, useEffect, useMemo, useRef } from "react";
|
||||
import { awaitableTankDefinitions } from "../../../core/awaitables/tankDefinitions";
|
||||
import { useFullScreen } from "../../../hooks/useFullScreen";
|
||||
import { Duel } from "../../../stores/duel";
|
||||
import { Tankopedia } from "../../../stores/tankopedia";
|
||||
import type { MaybeSkeletonComponentProps } from "../../../types/maybeSkeletonComponentProps";
|
||||
import type { ThicknessRange } from "../../Armor/components/StaticArmor";
|
||||
import { Options } from "./components/Options";
|
||||
import { TankSandbox } from "./components/TankSandbox";
|
||||
import { Title } from "./components/TankSandbox/Title";
|
||||
|
||||
const tankDefinitions = await awaitableTankDefinitions;
|
||||
|
||||
export function HeroSection({ skeleton }: MaybeSkeletonComponentProps) {
|
||||
const revealed = TankopediaEphemeral.use((state) => state.revealed);
|
||||
const disturbed = TankopediaEphemeral.use((state) => state.disturbed);
|
||||
const revealed = Tankopedia.use((state) => state.revealed);
|
||||
const disturbed = Tankopedia.use((state) => state.disturbed);
|
||||
const canvas = useRef<HTMLCanvasElement>(null);
|
||||
const isFullScreen = useFullScreen();
|
||||
const protagonist = Duel.use((state) => state.protagonist.tank);
|
||||
const thicknessRange = useMemo(() => {
|
||||
const entries = Object.values(tankDefinitions.tanks);
|
||||
const filtered = entries.filter(
|
||||
(thisTank) => thisTank.tier === protagonist.tier,
|
||||
(thisTank) => thisTank.tier === protagonist.tier
|
||||
);
|
||||
const value =
|
||||
(filtered.reduce((accumulator, thisTank) => {
|
||||
@@ -41,7 +41,7 @@ export function HeroSection({ skeleton }: MaybeSkeletonComponentProps) {
|
||||
|
||||
useEffect(() => {
|
||||
if (disturbed) {
|
||||
document.body.classList.remove('no-navbar');
|
||||
document.body.classList.remove("no-navbar");
|
||||
}
|
||||
}, [disturbed]);
|
||||
|
||||
@@ -54,15 +54,15 @@ export function HeroSection({ skeleton }: MaybeSkeletonComponentProps) {
|
||||
mutateDuel((draft) => {
|
||||
draft.antagonist.shell = shells[index];
|
||||
});
|
||||
TankopediaEphemeral.mutate((draft) => {
|
||||
Tankopedia.mutate((draft) => {
|
||||
draft.customShell = undefined;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
return () => window.removeEventListener('keydown', handleKeyDown);
|
||||
window.addEventListener("keydown", handleKeyDown);
|
||||
return () => window.removeEventListener("keydown", handleKeyDown);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
@@ -70,7 +70,7 @@ export function HeroSection({ skeleton }: MaybeSkeletonComponentProps) {
|
||||
overflow="hidden"
|
||||
justify="center"
|
||||
style={{
|
||||
backgroundColor: 'black',
|
||||
backgroundColor: "black",
|
||||
}}
|
||||
position="relative"
|
||||
>
|
||||
@@ -82,36 +82,36 @@ export function HeroSection({ skeleton }: MaybeSkeletonComponentProps) {
|
||||
left="0"
|
||||
style={{
|
||||
opacity: disturbed ? 0 : 1,
|
||||
background: 'black',
|
||||
transitionDuration: '1s',
|
||||
background: "black",
|
||||
transitionDuration: "1s",
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* <ScrollHint /> */}
|
||||
|
||||
<Flex
|
||||
direction={{ initial: 'column-reverse', md: 'row' }}
|
||||
direction={{ initial: "column-reverse", md: "row" }}
|
||||
style={{
|
||||
zIndex: isFullScreen ? 2 : undefined,
|
||||
transitionDuration: '1s',
|
||||
background: isFullScreen ? 'black' : undefined,
|
||||
transitionDuration: "1s",
|
||||
background: isFullScreen ? "black" : undefined,
|
||||
}}
|
||||
height={
|
||||
isFullScreen
|
||||
? '100vh'
|
||||
? "100vh"
|
||||
: disturbed
|
||||
? 'calc(100svh - 6rem)'
|
||||
? "calc(100svh - 6rem)"
|
||||
: {
|
||||
initial: '28rem',
|
||||
md: 'calc(100svh - 12rem)',
|
||||
initial: "28rem",
|
||||
md: "calc(100svh - 12rem)",
|
||||
}
|
||||
}
|
||||
maxWidth={isFullScreen ? undefined : '120rem'}
|
||||
maxWidth={isFullScreen ? undefined : "120rem"}
|
||||
flexGrow="1"
|
||||
width={isFullScreen ? '100vw' : undefined}
|
||||
position={isFullScreen ? 'fixed' : 'relative'}
|
||||
top={isFullScreen ? '0' : undefined}
|
||||
left={isFullScreen ? '0' : undefined}
|
||||
width={isFullScreen ? "100vw" : undefined}
|
||||
position={isFullScreen ? "fixed" : "relative"}
|
||||
top={isFullScreen ? "0" : undefined}
|
||||
left={isFullScreen ? "0" : undefined}
|
||||
>
|
||||
<Box
|
||||
className="tank-sandbox-container"
|
||||
@@ -126,14 +126,14 @@ export function HeroSection({ skeleton }: MaybeSkeletonComponentProps) {
|
||||
height="100%"
|
||||
position="relative"
|
||||
style={{
|
||||
transitionDuration: '1s',
|
||||
transitionDuration: "1s",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
transitionDuration: '2s',
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
transitionDuration: "2s",
|
||||
opacity: revealed ? 1 : 0,
|
||||
filter: disturbed
|
||||
? undefined
|
||||
|
||||
Reference in new issue
Block a user