Files
blitzkit/packages/website/src/components/Tankopedia/HeroSection/index.tsx
T

156 lines
4.7 KiB
TypeScript

import { Box, Flex } from "@radix-ui/themes";
import { times } from "lodash-es";
import { useEffect, useMemo, useRef } from "react";
import { api } from "../../../api/dynamic";
import { defaultEqualizer } from "../../../core/blitzkit/tankToDuelMember";
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 "../../StaticArmor";
import { Options } from "./components/Options";
import { TankSandbox } from "./components/TankSandbox";
import { Title } from "./components/TankSandbox/Title";
const tankDefinitions = await api.tanks();
export function HeroSection({ skeleton }: MaybeSkeletonComponentProps) {
const equalize = Duel.use((state) => state.equalize);
const disturbed = Tankopedia.use((state) => state.disturbed);
const canvas = useRef<HTMLCanvasElement>(null!);
const isFullScreen = useFullScreen();
const protagonist = Duel.use((state) => state.protagonist.tank);
const modelRequested = Tankopedia.use((state) => state.modelRequested);
const thicknessRange = useMemo(() => {
const entries = Object.values(tankDefinitions.tanks);
const filtered = entries.filter(
(thisTank) => thisTank.tier === protagonist.tier,
);
const value =
(filtered.reduce((accumulator, thisTank) => {
return (
accumulator +
thisTank.turrets.at(-1)!.guns.at(-1)!.shells[0].penetration!.near *
((equalize ? thisTank.equalizer : undefined) ?? defaultEqualizer)
.penetration
);
}, 0) /
filtered.length) *
(3 / 4);
return { value } satisfies ThicknessRange;
}, [protagonist, equalize]);
useEffect(() => {
if (disturbed) {
document.body.classList.remove("no-navbar");
}
}, [disturbed]);
useEffect(() => {
function handleKeyDown(event: KeyboardEvent) {
times(3, (index) => {
if (
event.key === `${index + 1}` &&
Duel.state.antagonist.gun.shells.length > index
) {
Duel.mutate((draft) => {
draft.antagonist.shell = Duel.state.antagonist.gun.shells[index];
});
Tankopedia.mutate((draft) => {
draft.customShell = undefined;
});
}
});
}
window.addEventListener("keydown", handleKeyDown);
return () => window.removeEventListener("keydown", handleKeyDown);
}, []);
return (
<Flex
overflow="hidden"
justify="center"
style={{
backgroundColor: "black",
}}
position="relative"
>
<Box
position="absolute"
width="100%"
height="100%"
top="0"
left="0"
style={{
opacity: disturbed ? 0 : 1,
background: "black",
transitionDuration: "1s",
}}
/>
{/* <ScrollHint /> */}
<Flex
direction={{ initial: "column-reverse", md: "row" }}
style={{
zIndex: isFullScreen ? 2 : undefined,
transitionDuration: "1s",
background: isFullScreen ? "black" : undefined,
}}
height={
isFullScreen
? "100vh"
: disturbed
? "calc(100svh - 8rem)"
: {
initial: "28rem",
md: "calc(100svh - 14rem)",
}
}
maxWidth={isFullScreen ? undefined : "120rem"}
flexGrow="1"
width={isFullScreen ? "100vw" : undefined}
position={isFullScreen ? "fixed" : "relative"}
top={isFullScreen ? "0" : undefined}
left={isFullScreen ? "0" : undefined}
>
<Box
className="tank-sandbox-container"
flexGrow="1"
flexBasis="0"
flexShrink="0"
position="relative"
>
<Box position="absolute" width="100%" height="100%" overflow="hidden">
<Box width="100%" height="100%" position="relative">
<Box
position="absolute"
width="100%"
height="100%"
top="0"
left="0"
>
{!skeleton && modelRequested && (
<Suspense>
<TankSandbox ref={canvas} thicknessRange={thicknessRange} />
</Suspense>
)}
</Box>
</Box>
<Title skeleton={skeleton} />
<Options
skeleton={skeleton}
canvas={canvas}
thicknessRange={thicknessRange}
/>
</Box>
</Box>
</Flex>
</Flex>
);
}