mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-23 19:49:50 +02:00
feat: code split
This commit is contained in:
1 parent
857cba902a
commit
5208cf8678
6 files changed
+253
-251
No files matched your search
@@ -14,5 +14,7 @@ export function Controls() {
|
|||||||
orbitControls.current?.target.set(0, 1.5, 0);
|
orbitControls.current?.target.set(0, 1.5, 0);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return <OrbitControls ref={orbitControls} enabled={enabled} />;
|
return (
|
||||||
|
<OrbitControls ref={orbitControls} enabled={enabled} rotateSpeed={0.75} />
|
||||||
|
);
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,168 @@
|
|||||||
|
import {
|
||||||
|
EnterFullScreenIcon,
|
||||||
|
ExitFullScreenIcon,
|
||||||
|
FrameIcon,
|
||||||
|
} from '@radix-ui/react-icons';
|
||||||
|
import { Button, Card, Flex, Tabs, Theme } from '@radix-ui/themes';
|
||||||
|
import { Html } from '@react-three/drei';
|
||||||
|
import { Canvas } from '@react-three/fiber';
|
||||||
|
import { Suspense, useEffect, useRef, useState } from 'react';
|
||||||
|
import { Group } from 'three';
|
||||||
|
import { theme } from '../../../../../../stitches.config';
|
||||||
|
import mutateTankopedia, {
|
||||||
|
TankopediaMode,
|
||||||
|
useTankopedia,
|
||||||
|
} from '../../../../../../stores/tankopedia';
|
||||||
|
import { Loader } from '../../../../components/Loader';
|
||||||
|
import { Controls } from '../Control';
|
||||||
|
import { RotationInputs } from '../RotationInputs';
|
||||||
|
import { SceneProps } from '../SceneProps';
|
||||||
|
import { TankModel } from './components/TankModel';
|
||||||
|
|
||||||
|
interface TankDisplayProps {
|
||||||
|
tankId: number;
|
||||||
|
turretId: number;
|
||||||
|
gunId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function TankDisplay({ tankId, turretId, gunId }: TankDisplayProps) {
|
||||||
|
const canvas = useRef<HTMLCanvasElement>(null);
|
||||||
|
const canvasWrapper = useRef<HTMLDivElement>(null);
|
||||||
|
const [isFullScreen, setIsFullScreen] = useState(false);
|
||||||
|
const mode = useTankopedia((state) => state.mode);
|
||||||
|
const hullContainer = useRef<Group>(null);
|
||||||
|
const showGrid = useTankopedia((state) => state.model.showGrid);
|
||||||
|
|
||||||
|
function handlePointerDown() {
|
||||||
|
window.addEventListener('pointermove', handlePointerMove);
|
||||||
|
window.addEventListener('pointerup', handlePointerUp);
|
||||||
|
}
|
||||||
|
function handlePointerMove(event: PointerEvent) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
function handlePointerUp(event: PointerEvent) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
window.removeEventListener('pointermove', handlePointerMove);
|
||||||
|
window.removeEventListener('pointerup', handlePointerUp);
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
function handleFullScreenChange() {
|
||||||
|
setIsFullScreen(document.fullscreenElement !== null);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('fullscreenchange', handleFullScreenChange);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener('fullscreenchange', handleFullScreenChange);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Theme radius={isFullScreen ? 'none' : undefined}>
|
||||||
|
<Card
|
||||||
|
style={{
|
||||||
|
position: 'relative',
|
||||||
|
border: isFullScreen ? 'none' : 'unset',
|
||||||
|
}}
|
||||||
|
ref={canvasWrapper}
|
||||||
|
>
|
||||||
|
<Theme radius="full" style={{ height: '100%' }}>
|
||||||
|
<Flex
|
||||||
|
style={{
|
||||||
|
height: isFullScreen ? '100%' : '75vh',
|
||||||
|
maxHeight: isFullScreen ? 'unset' : 576,
|
||||||
|
}}
|
||||||
|
direction="column"
|
||||||
|
gap="2"
|
||||||
|
>
|
||||||
|
<Tabs.Root
|
||||||
|
value={mode}
|
||||||
|
onValueChange={(mode) => {
|
||||||
|
mutateTankopedia((draft) => {
|
||||||
|
draft.mode = mode as TankopediaMode;
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Tabs.List>
|
||||||
|
<Tabs.Trigger value="model">Model</Tabs.Trigger>
|
||||||
|
<Tabs.Trigger value="armor">Armor</Tabs.Trigger>
|
||||||
|
</Tabs.List>
|
||||||
|
</Tabs.Root>
|
||||||
|
|
||||||
|
<div style={{ height: '100%' }}>
|
||||||
|
<Canvas
|
||||||
|
shadows
|
||||||
|
ref={canvas}
|
||||||
|
camera={{ fov: 20 }}
|
||||||
|
onPointerDown={handlePointerDown}
|
||||||
|
>
|
||||||
|
<Controls />
|
||||||
|
<SceneProps />
|
||||||
|
|
||||||
|
<Suspense
|
||||||
|
fallback={
|
||||||
|
<Html center position={[0, 1.5, 0]}>
|
||||||
|
<Loader
|
||||||
|
naked
|
||||||
|
color={theme.colors.textHighContrast_purple}
|
||||||
|
/>
|
||||||
|
</Html>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<TankModel
|
||||||
|
gunId={gunId}
|
||||||
|
tankId={tankId}
|
||||||
|
turretId={turretId}
|
||||||
|
ref={hullContainer}
|
||||||
|
/>
|
||||||
|
</Suspense>
|
||||||
|
</Canvas>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Flex
|
||||||
|
gap="4"
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: 18,
|
||||||
|
right: 18,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
color={showGrid ? 'purple' : 'gray'}
|
||||||
|
variant="ghost"
|
||||||
|
onClick={() => {
|
||||||
|
mutateTankopedia((draft) => {
|
||||||
|
draft.model.showGrid = !draft.model.showGrid;
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<FrameIcon />
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
onClick={() => {
|
||||||
|
if (isFullScreen) {
|
||||||
|
document.exitFullscreen();
|
||||||
|
} else {
|
||||||
|
canvasWrapper.current?.requestFullscreen();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{isFullScreen ? (
|
||||||
|
<ExitFullScreenIcon />
|
||||||
|
) : (
|
||||||
|
<EnterFullScreenIcon />
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
|
||||||
|
<RotationInputs gunId={gunId} tankId={tankId} turretId={turretId} />
|
||||||
|
</Flex>
|
||||||
|
</Theme>
|
||||||
|
</Card>
|
||||||
|
</Theme>
|
||||||
|
);
|
||||||
|
}
|
||||||
+6
-6
@@ -8,17 +8,17 @@ import {
|
|||||||
} from 'react';
|
} from 'react';
|
||||||
import { Group, Vector3 } from 'three';
|
import { Group, Vector3 } from 'three';
|
||||||
import { GLTFLoader } from 'three-stdlib';
|
import { GLTFLoader } from 'three-stdlib';
|
||||||
import { X_AXIS } from '../../../../../constants/axis';
|
import { X_AXIS } from '../../../../../../../constants/axis';
|
||||||
import {
|
import {
|
||||||
ModelDefinitions,
|
ModelDefinitions,
|
||||||
modelDefinitions,
|
modelDefinitions,
|
||||||
} from '../../../../../core/blitzkrieg/modelDefinitions';
|
} from '../../../../../../../core/blitzkrieg/modelDefinitions';
|
||||||
import mutateTankopedia, {
|
import mutateTankopedia, {
|
||||||
useTankopedia,
|
useTankopedia,
|
||||||
} from '../../../../../stores/tankopedia';
|
} from '../../../../../../../stores/tankopedia';
|
||||||
import { GunContainer } from './GunContainer';
|
import { GunContainer } from '../../GunContainer';
|
||||||
import { HullContainer } from './HullContainer';
|
import { HullContainer } from '../../HullContainer';
|
||||||
import { TurretContainer } from './TurretContainer';
|
import { TurretContainer } from '../../TurretContainer';
|
||||||
|
|
||||||
interface TankModelProps {
|
interface TankModelProps {
|
||||||
tankId: number;
|
tankId: number;
|
||||||
@@ -1,22 +1,29 @@
|
|||||||
import { Color } from 'three';
|
import { Color } from 'three';
|
||||||
import InfiniteGridHelper from '../../../../../components/InfiniteGridHelper';
|
import InfiniteGridHelper from '../../../../../components/InfiniteGridHelper';
|
||||||
|
import { useTankopedia } from '../../../../../stores/tankopedia';
|
||||||
|
|
||||||
export function SceneProps() {
|
export function SceneProps() {
|
||||||
|
const showGrid = useTankopedia((state) => state.model.showGrid);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<InfiniteGridHelper
|
{showGrid && (
|
||||||
size1={1 / 5}
|
<>
|
||||||
size2={1}
|
<InfiniteGridHelper
|
||||||
distance={20}
|
size1={1 / 5}
|
||||||
color={new Color('#ffffff')}
|
size2={1}
|
||||||
/>
|
distance={20}
|
||||||
<InfiniteGridHelper
|
color={new Color('#ffffff')}
|
||||||
position={[0, 1e-4, 0]}
|
/>
|
||||||
size1={0}
|
<InfiniteGridHelper
|
||||||
size2={100}
|
position={[0, 1e-4, 0]}
|
||||||
distance={25}
|
size1={0}
|
||||||
color={new Color('red')}
|
size2={100}
|
||||||
/>
|
distance={25}
|
||||||
|
color={new Color('red')}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
<directionalLight
|
<directionalLight
|
||||||
position={[1, 1, -1]}
|
position={[1, 1, -1]}
|
||||||
|
|||||||
@@ -3,9 +3,6 @@
|
|||||||
import {
|
import {
|
||||||
ArrowLeftIcon,
|
ArrowLeftIcon,
|
||||||
CaretRightIcon,
|
CaretRightIcon,
|
||||||
EnterFullScreenIcon,
|
|
||||||
ExitFullScreenIcon,
|
|
||||||
GearIcon,
|
|
||||||
MagnifyingGlassIcon,
|
MagnifyingGlassIcon,
|
||||||
} from '@radix-ui/react-icons';
|
} from '@radix-ui/react-icons';
|
||||||
import {
|
import {
|
||||||
@@ -15,20 +12,17 @@ import {
|
|||||||
Dialog,
|
Dialog,
|
||||||
Flex,
|
Flex,
|
||||||
Heading,
|
Heading,
|
||||||
Slider,
|
|
||||||
Tabs,
|
Tabs,
|
||||||
Text,
|
Text,
|
||||||
TextField,
|
TextField,
|
||||||
Theme,
|
Theme,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
} from '@radix-ui/themes';
|
} from '@radix-ui/themes';
|
||||||
import { Html } from '@react-three/drei';
|
|
||||||
import { Canvas } from '@react-three/fiber';
|
|
||||||
import { go } from 'fuzzysort';
|
import { go } from 'fuzzysort';
|
||||||
import { debounce } from 'lodash';
|
import { debounce } from 'lodash';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { Suspense, use, useEffect, useRef, useState } from 'react';
|
import { use, useRef, useState } from 'react';
|
||||||
import { Group, Vector3 } from 'three';
|
import { Vector3 } from 'three';
|
||||||
import { Flag } from '../../../../components/Flag';
|
import { Flag } from '../../../../components/Flag';
|
||||||
import { ModuleButtons } from '../../../../components/ModuleButton';
|
import { ModuleButtons } from '../../../../components/ModuleButton';
|
||||||
import PageWrapper from '../../../../components/PageWrapper';
|
import PageWrapper from '../../../../components/PageWrapper';
|
||||||
@@ -38,13 +32,8 @@ import {
|
|||||||
tankDefinitions,
|
tankDefinitions,
|
||||||
tankNamesDiacritics,
|
tankNamesDiacritics,
|
||||||
} from '../../../../core/blitzkrieg/tankDefinitions';
|
} from '../../../../core/blitzkrieg/tankDefinitions';
|
||||||
import { theme } from '../../../../stitches.config';
|
import { useTankopedia } from '../../../../stores/tankopedia';
|
||||||
import mutateTankopedia from '../../../../stores/tankopedia';
|
import { TankDisplay } from './components/Model/TankDisplay';
|
||||||
import { Loader } from '../../components/Loader';
|
|
||||||
import { Controls } from './components/Control';
|
|
||||||
import { RotationInputs } from './components/RotationInputs';
|
|
||||||
import { SceneProps } from './components/SceneProps';
|
|
||||||
import { TankModel } from './components/TankModel';
|
|
||||||
|
|
||||||
const X_AXIS = new Vector3(1, 0, 0);
|
const X_AXIS = new Vector3(1, 0, 0);
|
||||||
|
|
||||||
@@ -56,9 +45,7 @@ export default function Page({ params }: { params: { id: string } }) {
|
|||||||
const [turret, setTurret] = useState(tank.turrets.at(-1)!);
|
const [turret, setTurret] = useState(tank.turrets.at(-1)!);
|
||||||
const [gun, setGun] = useState(turret.guns.at(-1)!);
|
const [gun, setGun] = useState(turret.guns.at(-1)!);
|
||||||
const [crew, setCrew] = useState(100);
|
const [crew, setCrew] = useState(100);
|
||||||
const [mode, setMode] = useState('model');
|
|
||||||
const versusTankSearchInput = useRef<HTMLInputElement>(null);
|
const versusTankSearchInput = useRef<HTMLInputElement>(null);
|
||||||
const hullContainer = useRef<Group>(null);
|
|
||||||
const [versusTankSearchResults, setVersusTankSearchResults] = useState<
|
const [versusTankSearchResults, setVersusTankSearchResults] = useState<
|
||||||
number[]
|
number[]
|
||||||
>([]);
|
>([]);
|
||||||
@@ -66,35 +53,7 @@ export default function Page({ params }: { params: { id: string } }) {
|
|||||||
const [versusTurret, setVersusTurret] = useState(versusTank.turrets.at(-1)!);
|
const [versusTurret, setVersusTurret] = useState(versusTank.turrets.at(-1)!);
|
||||||
const [versusGun, setVersusGun] = useState(versusTurret.guns.at(-1)!);
|
const [versusGun, setVersusGun] = useState(versusTurret.guns.at(-1)!);
|
||||||
const [versusTankTab, setVersusTankTab] = useState('search');
|
const [versusTankTab, setVersusTankTab] = useState('search');
|
||||||
const canvas = useRef<HTMLCanvasElement>(null);
|
const mode = useTankopedia((state) => state.mode);
|
||||||
const canvasWrapper = useRef<HTMLDivElement>(null);
|
|
||||||
const [isFullScreen, setIsFullScreen] = useState(false);
|
|
||||||
|
|
||||||
function handlePointerDown() {
|
|
||||||
window.addEventListener('pointermove', handlePointerMove);
|
|
||||||
window.addEventListener('pointerup', handlePointerUp);
|
|
||||||
}
|
|
||||||
function handlePointerMove(event: PointerEvent) {
|
|
||||||
event.preventDefault();
|
|
||||||
}
|
|
||||||
function handlePointerUp(event: PointerEvent) {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
window.removeEventListener('pointermove', handlePointerMove);
|
|
||||||
window.removeEventListener('pointerup', handlePointerUp);
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
function handleFullScreenChange() {
|
|
||||||
setIsFullScreen(document.fullscreenElement !== null);
|
|
||||||
}
|
|
||||||
|
|
||||||
document.addEventListener('fullscreenchange', handleFullScreenChange);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
document.removeEventListener('fullscreenchange', handleFullScreenChange);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageWrapper color="purple">
|
<PageWrapper color="purple">
|
||||||
@@ -117,153 +76,7 @@ export default function Page({ params }: { params: { id: string } }) {
|
|||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<Theme radius={isFullScreen ? 'none' : undefined}>
|
<TankDisplay gunId={gun.id} tankId={tank.id} turretId={turret.id} />
|
||||||
<Card
|
|
||||||
style={{
|
|
||||||
position: 'relative',
|
|
||||||
border: isFullScreen ? 'none' : 'unset',
|
|
||||||
}}
|
|
||||||
ref={canvasWrapper}
|
|
||||||
>
|
|
||||||
<Theme radius="full" style={{ height: '100%' }}>
|
|
||||||
<Flex
|
|
||||||
style={{
|
|
||||||
height: isFullScreen ? '100%' : '75vh',
|
|
||||||
maxHeight: isFullScreen ? 'unset' : 576,
|
|
||||||
}}
|
|
||||||
direction="column"
|
|
||||||
gap="2"
|
|
||||||
>
|
|
||||||
<Tabs.Root value={mode} onValueChange={setMode}>
|
|
||||||
<Tabs.List>
|
|
||||||
<Tabs.Trigger value="model">Model</Tabs.Trigger>
|
|
||||||
<Tabs.Trigger value="armor">Armor</Tabs.Trigger>
|
|
||||||
</Tabs.List>
|
|
||||||
</Tabs.Root>
|
|
||||||
|
|
||||||
<div style={{ height: '100%' }}>
|
|
||||||
<Canvas
|
|
||||||
shadows
|
|
||||||
ref={canvas}
|
|
||||||
camera={{ fov: 20 }}
|
|
||||||
onPointerDown={handlePointerDown}
|
|
||||||
>
|
|
||||||
<Controls />
|
|
||||||
<SceneProps />
|
|
||||||
|
|
||||||
<Suspense
|
|
||||||
fallback={
|
|
||||||
<Html center position={[0, 1.5, 0]}>
|
|
||||||
<Loader
|
|
||||||
naked
|
|
||||||
color={theme.colors.textHighContrast_purple}
|
|
||||||
/>
|
|
||||||
</Html>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<TankModel
|
|
||||||
gunId={gun.id}
|
|
||||||
tankId={tank.id}
|
|
||||||
turretId={turret.id}
|
|
||||||
ref={hullContainer}
|
|
||||||
/>
|
|
||||||
</Suspense>
|
|
||||||
</Canvas>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Flex
|
|
||||||
gap="4"
|
|
||||||
style={{
|
|
||||||
position: 'absolute',
|
|
||||||
top: 18,
|
|
||||||
right: 18,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
onClick={() => {
|
|
||||||
mutateTankopedia((draft) => {
|
|
||||||
draft.showSettings = true;
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<GearIcon />
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
onClick={() => {
|
|
||||||
if (isFullScreen) {
|
|
||||||
document.exitFullscreen();
|
|
||||||
} else {
|
|
||||||
canvasWrapper.current?.requestFullscreen();
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{isFullScreen ? (
|
|
||||||
<ExitFullScreenIcon />
|
|
||||||
) : (
|
|
||||||
<EnterFullScreenIcon />
|
|
||||||
)}
|
|
||||||
</Button>
|
|
||||||
</Flex>
|
|
||||||
|
|
||||||
<RotationInputs
|
|
||||||
gunId={gun.id}
|
|
||||||
tankId={tank.id}
|
|
||||||
turretId={turret.id}
|
|
||||||
/>
|
|
||||||
</Flex>
|
|
||||||
</Theme>
|
|
||||||
</Card>
|
|
||||||
</Theme>
|
|
||||||
|
|
||||||
<Theme radius="small">
|
|
||||||
<Flex gap="4">
|
|
||||||
<Flex gap="1">
|
|
||||||
{tank.turrets.map((thisTurret) => {
|
|
||||||
return (
|
|
||||||
<Tooltip content={thisTurret.name} key={thisTurret.id}>
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
setTurret(thisTurret);
|
|
||||||
setGun(thisTurret.guns.at(-1)!);
|
|
||||||
}}
|
|
||||||
variant={turret.id === thisTurret.id ? 'solid' : 'soft'}
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
src={asset('icons/modules/turret.webp')}
|
|
||||||
width={32}
|
|
||||||
height={32}
|
|
||||||
/>
|
|
||||||
{TIER_ROMAN_NUMERALS[thisTurret.tier]}
|
|
||||||
</Button>
|
|
||||||
</Tooltip>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Flex>
|
|
||||||
|
|
||||||
<Flex gap="1">
|
|
||||||
{turret.guns.map((thisGun) => {
|
|
||||||
return (
|
|
||||||
<Tooltip content={thisGun.name} key={thisGun.id}>
|
|
||||||
<Button
|
|
||||||
onClick={() => setGun(thisGun)}
|
|
||||||
variant={gun.id === thisGun.id ? 'solid' : 'soft'}
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
src={asset('icons/modules/gun.webp')}
|
|
||||||
width={32}
|
|
||||||
height={32}
|
|
||||||
/>
|
|
||||||
{TIER_ROMAN_NUMERALS[thisGun.tier]}
|
|
||||||
</Button>
|
|
||||||
</Tooltip>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</Theme>
|
|
||||||
|
|
||||||
{mode === 'armor' && (
|
{mode === 'armor' && (
|
||||||
<Card>
|
<Card>
|
||||||
@@ -464,7 +277,7 @@ export default function Page({ params }: { params: { id: string } }) {
|
|||||||
|
|
||||||
<Flex gap="2" align="center">
|
<Flex gap="2" align="center">
|
||||||
<Checkbox defaultChecked />
|
<Checkbox defaultChecked />
|
||||||
<Text>Calibrated shells</Text>
|
<Text>Calibrated</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<Flex gap="1">
|
<Flex gap="1">
|
||||||
@@ -493,44 +306,53 @@ export default function Page({ params }: { params: { id: string } }) {
|
|||||||
</Flex>
|
</Flex>
|
||||||
</Card>
|
</Card>
|
||||||
)}
|
)}
|
||||||
</Flex>
|
|
||||||
|
|
||||||
<Flex gap="6" direction="column">
|
<Theme radius="small">
|
||||||
<Heading>Build</Heading>
|
<Flex gap="4">
|
||||||
|
<Flex gap="1">
|
||||||
|
{tank.turrets.map((thisTurret) => {
|
||||||
|
return (
|
||||||
|
<Tooltip content={thisTurret.name} key={thisTurret.id}>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
setTurret(thisTurret);
|
||||||
|
setGun(thisTurret.guns.at(-1)!);
|
||||||
|
}}
|
||||||
|
variant={turret.id === thisTurret.id ? 'solid' : 'soft'}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={asset('icons/modules/turret.webp')}
|
||||||
|
width={32}
|
||||||
|
height={32}
|
||||||
|
/>
|
||||||
|
{TIER_ROMAN_NUMERALS[thisTurret.tier]}
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Flex>
|
||||||
|
|
||||||
<Flex gap="2" direction="column">
|
<Flex gap="1">
|
||||||
<Heading size="5" weight="regular">
|
{turret.guns.map((thisGun) => {
|
||||||
Modules
|
return (
|
||||||
</Heading>
|
<Tooltip content={thisGun.name} key={thisGun.id}>
|
||||||
</Flex>
|
<Button
|
||||||
|
onClick={() => setGun(thisGun)}
|
||||||
<Flex gap="2" direction="column">
|
variant={gun.id === thisGun.id ? 'solid' : 'soft'}
|
||||||
<Heading size="5" weight="regular">
|
>
|
||||||
Crew
|
<img
|
||||||
</Heading>
|
src={asset('icons/modules/gun.webp')}
|
||||||
|
width={32}
|
||||||
<Flex gap="4" align="center">
|
height={32}
|
||||||
<Text>{crew}%</Text>
|
/>
|
||||||
|
{TIER_ROMAN_NUMERALS[thisGun.tier]}
|
||||||
<Slider
|
</Button>
|
||||||
style={{ flex: 1 }}
|
</Tooltip>
|
||||||
value={[crew]}
|
);
|
||||||
min={50}
|
})}
|
||||||
max={100}
|
</Flex>
|
||||||
onValueChange={([crew]) => setCrew(crew)}
|
|
||||||
/>
|
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Theme>
|
||||||
</Flex>
|
|
||||||
|
|
||||||
<Flex gap="2" direction="column">
|
|
||||||
<Heading size="5">Shells</Heading>
|
|
||||||
|
|
||||||
{gun.shells.map((shell) => (
|
|
||||||
<Text key={shell.id}>
|
|
||||||
{shell.name} ({shell.type}): {shell.damage.armor}
|
|
||||||
</Text>
|
|
||||||
))}
|
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
</PageWrapper>
|
</PageWrapper>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { Tier } from '../core/blitzkrieg/tankDefinitions';
|
|||||||
export type TankopediaSortBy = 'tier' | 'name';
|
export type TankopediaSortBy = 'tier' | 'name';
|
||||||
export type TankopediaSortDirection = 'ascending' | 'descending';
|
export type TankopediaSortDirection = 'ascending' | 'descending';
|
||||||
export type TankopediaTestTankDisplay = 'include' | 'exclude' | 'only';
|
export type TankopediaTestTankDisplay = 'include' | 'exclude' | 'only';
|
||||||
|
export type TankopediaMode = 'model' | 'armor';
|
||||||
|
|
||||||
type Tankopedia = {
|
type Tankopedia = {
|
||||||
sort: {
|
sort: {
|
||||||
@@ -24,8 +25,9 @@ type Tankopedia = {
|
|||||||
turretYaw: number;
|
turretYaw: number;
|
||||||
gunPitch: number;
|
gunPitch: number;
|
||||||
controlsEnabled: boolean;
|
controlsEnabled: boolean;
|
||||||
|
showGrid: boolean;
|
||||||
};
|
};
|
||||||
showSettings: boolean;
|
mode: TankopediaMode;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useTankopedia = create<Tankopedia>()(() => ({
|
export const useTankopedia = create<Tankopedia>()(() => ({
|
||||||
@@ -45,8 +47,9 @@ export const useTankopedia = create<Tankopedia>()(() => ({
|
|||||||
hullYaw: 0,
|
hullYaw: 0,
|
||||||
turretYaw: 0,
|
turretYaw: 0,
|
||||||
controlsEnabled: true,
|
controlsEnabled: true,
|
||||||
|
showGrid: true,
|
||||||
},
|
},
|
||||||
showSettings: false,
|
mode: 'model',
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export default function mutateTankopedia(recipe: (draft: Tankopedia) => void) {
|
export default function mutateTankopedia(recipe: (draft: Tankopedia) => void) {
|
||||||
|
|||||||
Reference in new issue
Block a user