feat: integrated tank definitions

This commit is contained in:
TrèsAbhi committed 2023-12-14 22:19:01 -06:00
1 parent adb0bb2e69
commit 2aefe3f9e3
26 files changed
+475 -273

No files matched your search

+121 -66
View File
@@ -1,5 +1,5 @@
import { config } from 'dotenv'; import { config } from 'dotenv';
import { existsSync } from 'fs'; import { existsSync, writeFileSync } from 'fs';
import { mkdir, readdir, rm } from 'fs/promises'; import { mkdir, readdir, rm } from 'fs/promises';
import { argv } from 'process'; import { argv } from 'process';
import sharp from 'sharp'; import sharp from 'sharp';
@@ -15,16 +15,14 @@ import { toUniqueId } from '../src/core/blitz/toUniqueId';
import commitMultipleFiles, { import commitMultipleFiles, {
FileChange, FileChange,
} from '../src/core/blitzkrieg/commitMultipleFiles'; } from '../src/core/blitzkrieg/commitMultipleFiles';
import { BlitzkriegGunDefinitions } from '../src/core/blitzkrieg/definitions/guns';
import { import {
BlitzkriegShellDefinitions, GunDefinition,
ShellDefinition,
ShellType, ShellType,
} from '../src/core/blitzkrieg/definitions/shells'; TankDefinitions,
import {
BlitzkriegTankDefinitions,
Tier, Tier,
} from '../src/core/blitzkrieg/definitions/tanks'; TurretDefinition,
import { BlitzkriegTurretDefinitions } from '../src/core/blitzkrieg/definitions/turrets'; } from '../src/core/blitzkrieg/tankDefinitions';
config(); config();
@@ -98,8 +96,12 @@ interface VehicleDefinitions {
yawLimits: string | string[]; yawLimits: string | string[];
guns: { guns: {
[key: string]: { [key: string]: {
reloadTime: number;
maxAmmo: number; maxAmmo: number;
pitchLimits?: string | string[]; pitchLimits?: string | string[];
pumpGunMode?: boolean;
pumpGunReloadTimes?: string;
clip?: { count: number; rate: number };
}; };
}; };
}; };
@@ -141,6 +143,7 @@ const DOI = {
flags: 'Gfx/Lobby/flags', flags: 'Gfx/Lobby/flags',
'3d': '3d', '3d': '3d',
bigShellIcons: 'Gfx/Shared/tank-supply/ammunition/big', bigShellIcons: 'Gfx/Shared/tank-supply/ammunition/big',
moduleIcons: 'Gfx/UI/ModulesTechTree',
}; };
const allTargets = argv.includes('--all-targets'); const allTargets = argv.includes('--all-targets');
@@ -154,10 +157,7 @@ if (!targets && !allTargets) throw new Error('No target(s) specified');
if (allTargets || targets?.includes('definitions')) { if (allTargets || targets?.includes('definitions')) {
console.log('Building tank definitions...'); console.log('Building tank definitions...');
const tankDefinitions: BlitzkriegTankDefinitions = {}; const definitions: TankDefinitions = {};
const turretDefinitions: BlitzkriegTurretDefinitions = {};
const gunDefinitions: BlitzkriegGunDefinitions = {};
const shellDefinitions: BlitzkriegShellDefinitions = {};
const nations = await readdir(`${DATA}/${DOI.vehicleDefinitions}`).then( const nations = await readdir(`${DATA}/${DOI.vehicleDefinitions}`).then(
(nations) => nations.filter((nation) => nation !== 'common'), (nations) => nations.filter((nation) => nation !== 'common'),
); );
@@ -203,15 +203,41 @@ if (allTargets || targets?.includes('definitions')) {
const gun = gunList.root.shared[gunKey]; const gun = gunList.root.shared[gunKey];
const pitchLimitsRaw = const pitchLimitsRaw =
turretGunEntry.pitchLimits ?? gun.pitchLimits; turretGunEntry.pitchLimits ?? gun.pitchLimits;
const gunPitch = (
typeof pitchLimitsRaw === 'string'
? pitchLimitsRaw
: pitchLimitsRaw.at(-1)!
)
.split(' ')
.map(Number) as [number, number];
const gunName =
strings[gun.userString] ?? gunKey.replaceAll('_', ' ');
const gunType =
'clip' in turretGunEntry
? turretGunEntry.pumpGunMode
? 'auto_reloader'
: 'auto_loader'
: 'regular';
const gunReload =
gunType === 'auto_reloader'
? turretGunEntry.pumpGunReloadTimes!.split(' ').map(Number)
: turretGunEntry.reloadTime;
const gunClipCount =
gunType === 'regular' ? undefined : turretGunEntry.clip!.count;
const gunInterClip =
gunType === 'regular'
? undefined
: 60 / turretGunEntry.clip!.rate;
const gunShells = Object.keys(gun.shots).map((shellKey) => { const gunShells = Object.keys(gun.shots).map((shellKey) => {
const turretShellEntry = gun.shots[shellKey]; const turretShellEntry = gun.shots[shellKey];
const shell = shellList.root[shellKey]; const shell = shellList.root[shellKey];
const shellId = toUniqueId(nation, shell.id); const shellId = toUniqueId(nation, shell.id);
const shellName =
strings[shell.userString] ?? shellKey.replaceAll('_', ' ');
shellDefinitions[shellId] = { return {
id: shellId, id: shellId,
name: name: shellName,
strings[shell.userString] ?? shellKey.replaceAll('_', ' '),
speed: turretShellEntry.speed, speed: turretShellEntry.speed,
damage: { damage: {
armor: shell.damage.armor, armor: shell.damage.armor,
@@ -222,28 +248,23 @@ if (allTargets || targets?.includes('definitions')) {
ricochet: shell.ricochetAngle, ricochet: shell.ricochetAngle,
type: blitzShellKindToBLitzkrieg[shell.kind], type: blitzShellKindToBLitzkrieg[shell.kind],
icon: shell.icon, icon: shell.icon,
}; } satisfies ShellDefinition;
return shellId;
}); });
gunDefinitions[gunId] = { return {
id: gunId, id: gunId,
pitch: (typeof pitchLimitsRaw === 'string' pitch: gunPitch,
? pitchLimitsRaw name: gunName,
: pitchLimitsRaw.at(-1)!
)
.split(' ')
.map(Number) as [number, number],
name: strings[gun.userString] ?? gunKey.replaceAll('_', ' '),
tier: gun.level as Tier, tier: gun.level as Tier,
shells: gunShells, shells: gunShells,
}; type: gunType,
reload: gunReload,
return gunId; count: gunClipCount,
interClip: gunInterClip,
} as GunDefinition;
}); });
turretDefinitions[turretId] = { return {
id: turretId, id: turretId,
name: name:
strings[turret.userString] ?? strings[turret.userString] ??
@@ -258,13 +279,11 @@ if (allTargets || targets?.includes('definitions')) {
.split(' ') .split(' ')
.map(Number) as [number, number], .map(Number) as [number, number],
guns: turretGuns, guns: turretGuns,
}; } satisfies TurretDefinition;
return turretId;
}, },
); );
tankDefinitions[tankId] = { definitions[tankId] = {
id: tankId, id: tankId,
name: name:
(tank.shortUserString (tank.shortUserString
@@ -288,36 +307,37 @@ if (allTargets || targets?.includes('definitions')) {
); );
console.log('Committing tank definitions...'); console.log('Committing tank definitions...');
await commitMultipleFiles( writeFileSync('test.json', JSON.stringify(definitions, null, 2));
'tresabhi', // await commitMultipleFiles(
'blitzkrieg-assets', // 'tresabhi',
'main', // 'blitzkrieg-assets',
'definitions', // 'main',
[ // 'definitions',
{ // [
content: JSON.stringify(tankDefinitions), // {
encoding: 'utf-8', // content: JSON.stringify(tankDefinitions),
path: 'definitions/tanks.json', // encoding: 'utf-8',
}, // path: 'definitions/tanks.json',
{ // },
content: JSON.stringify(turretDefinitions), // {
encoding: 'utf-8', // content: JSON.stringify(turretDefinitions),
path: 'definitions/turrets.json', // encoding: 'utf-8',
}, // path: 'definitions/turrets.json',
{ // },
content: JSON.stringify(gunDefinitions), // {
encoding: 'utf-8', // content: JSON.stringify(gunDefinitions),
path: 'definitions/guns.json', // encoding: 'utf-8',
}, // path: 'definitions/guns.json',
{ // },
content: JSON.stringify(shellDefinitions), // {
encoding: 'utf-8', // content: JSON.stringify(shellDefinitions),
path: 'definitions/shells.json', // encoding: 'utf-8',
}, // path: 'definitions/shells.json',
], // },
// ],
true, // true,
); // );
} }
if ( if (
@@ -356,14 +376,14 @@ if (
changes.push({ changes.push({
content: await readBase64DVPL(big), content: await readBase64DVPL(big),
encoding: 'base64', encoding: 'base64',
path: `icons/big/${id}.webp`, path: `icons/tank/big/${id}.webp`,
}); });
} }
if (allTargets || targets?.includes('smallTankIcons')) { if (allTargets || targets?.includes('smallTankIcons')) {
changes.push({ changes.push({
content: await readBase64DVPL(small), content: await readBase64DVPL(small),
encoding: 'base64', encoding: 'base64',
path: `icons/small/${id}.webp`, path: `icons/tank/small/${id}.webp`,
}); });
} }
} }
@@ -527,7 +547,7 @@ if (allTargets || targets?.includes('shellIcons')) {
.toBuffer() .toBuffer()
).toString('base64'), ).toString('base64'),
encoding: 'base64', encoding: 'base64',
path: `shells/${name}.webp`, path: `icons/shells/${name}.webp`,
} satisfies FileChange; } satisfies FileChange;
}), }),
); );
@@ -542,3 +562,38 @@ if (allTargets || targets?.includes('shellIcons')) {
true, true,
); );
} }
if (allTargets || targets?.includes('moduleIcons')) {
console.log('Building module icons...');
const changes = await Promise.all(
(await readdir(`${DATA}/${DOI.moduleIcons}`))
.filter(
(file) =>
!file.endsWith('@2x.packed.webp.dvpl') && file.startsWith('vehicle'),
)
.map(async (file) => {
const nameRaw = file.match(/vehicle(.+)\.packed\.webp\.dvpl/)![1];
const name = nameRaw[0].toLowerCase() + nameRaw.slice(1);
const content = await readBase64DVPL(
`${DATA}/${DOI.moduleIcons}/${file}`,
);
return {
content,
path: `icons/modules/${name}.webp`,
encoding: 'base64',
} satisfies FileChange;
}),
);
console.log('Committing module icons...');
commitMultipleFiles(
'tresabhi',
'blitzkrieg-assets',
'main',
'module icons',
changes,
true,
);
}
+3 -3
View File
@@ -7,9 +7,9 @@ import { TreeTypeEnum } from '../../../components/Tanks';
import { WARGAMING_APPLICATION_ID } from '../../../constants/wargamingApplicationID'; import { WARGAMING_APPLICATION_ID } from '../../../constants/wargamingApplicationID';
import fetchBlitz from '../../../core/blitz/fetchBlitz'; import fetchBlitz from '../../../core/blitz/fetchBlitz';
import { import {
BlitzkriegTankDefinition, TankDefinition,
tankDefinitions, tankDefinitions,
} from '../../../core/blitzkrieg/definitions/tanks'; } from '../../../core/blitzkrieg/tankDefinitions';
import { tankAverages } from '../../../core/blitzstars/tankAverages'; import { tankAverages } from '../../../core/blitzstars/tankAverages';
import calculateWN8 from '../../../core/statistics/calculateWN8'; import calculateWN8 from '../../../core/statistics/calculateWN8';
import { deltaTankStats } from '../../../core/statistics/deltaTankStats'; import { deltaTankStats } from '../../../core/statistics/deltaTankStats';
@@ -30,7 +30,7 @@ export default function SessionPage() {
list: { list: {
stats: IndividualTankStats; stats: IndividualTankStats;
tankDefinitions?: BlitzkriegTankDefinition; tankDefinitions?: TankDefinition;
career: IndividualTankStats; career: IndividualTankStats;
currentWN8?: number; currentWN8?: number;
careerWN8?: number; careerWN8?: number;
+216 -95
View File
@@ -12,7 +12,6 @@ import {
Dialog, Dialog,
Flex, Flex,
Heading, Heading,
Separator,
Slider, Slider,
Tabs, Tabs,
Text, Text,
@@ -27,52 +26,64 @@ import { debounce } from 'lodash';
import Link from 'next/link'; import Link from 'next/link';
import { use, useEffect, useRef, useState } from 'react'; import { use, useEffect, useRef, useState } from 'react';
import { Flag } from '../../../../components/Flag'; import { Flag } from '../../../../components/Flag';
import { ModuleButtons } from '../../../../components/ModuleButton';
import PageWrapper from '../../../../components/PageWrapper'; import PageWrapper from '../../../../components/PageWrapper';
import { asset } from '../../../../core/blitzkrieg/asset'; import { asset } from '../../../../core/blitzkrieg/asset';
import { gunDefinitions } from '../../../../core/blitzkrieg/definitions/guns'; import { gunDefinitions } from '../../../../core/blitzkrieg/definitions/guns';
import { shellDefinitions } from '../../../../core/blitzkrieg/definitions/shells'; import { shellDefinitions } from '../../../../core/blitzkrieg/definitions/shells';
import { turretDefinitions } from '../../../../core/blitzkrieg/definitions/turrets';
import { import {
TIER_ROMAN_NUMERALS, TIER_ROMAN_NUMERALS,
tankDefinitions, tankDefinitions,
tankNamesDiacritics, tankNamesDiacritics,
tanksDefinitionsArray, } from '../../../../core/blitzkrieg/tankDefinitions';
} from '../../../../core/blitzkrieg/definitions/tanks';
import { turretDefinitions } from '../../../../core/blitzkrieg/definitions/turrets';
import * as styles from '../page.css'; import * as styles from '../page.css';
type Mode = 'model' | 'armor';
export default function Page({ params }: { params: { id: string } }) { export default function Page({ params }: { params: { id: string } }) {
const id = parseInt(params.id); const id = parseInt(params.id);
const awaitedTankDefinitions = use(tankDefinitions); const awaitedTankDefinitions = use(tankDefinitions);
const awaitedTanksDefinitionsArray = use(tanksDefinitionsArray);
const awaitedTurretDefinitions = use(turretDefinitions); const awaitedTurretDefinitions = use(turretDefinitions);
const awaitedGunDefinitions = use(gunDefinitions); const awaitedGunDefinitions = use(gunDefinitions);
const awaitedShellDefinitions = use(shellDefinitions); const awaitedShellDefinitions = use(shellDefinitions);
const awaitedTankNamesDiacritics = use(tankNamesDiacritics); const awaitedTankNamesDiacritics = use(tankNamesDiacritics);
const tank = awaitedTankDefinitions[id]; const tank = awaitedTankDefinitions[id];
const [turret, setTurret] = useState( const [turret, setTurret] = useState(
awaitedTurretDefinitions[tank.turrets[0]], awaitedTurretDefinitions[tank.turrets.at(-1)!],
); );
const [gun, setGun] = useState(awaitedGunDefinitions[turret.guns[0]]); const [gun, setGun] = useState(awaitedGunDefinitions[turret.guns.at(-1)!]);
const [crew, setCrew] = useState(100); const [crew, setCrew] = useState(100);
const [versusTankId, setVersusTankId] = useState( const [mode, setMode] = useState('model');
awaitedTanksDefinitionsArray[0].id,
);
const [mode, setMode] = useState<Mode>('model');
const versusTankSearchInput = useRef<HTMLInputElement>(null); const versusTankSearchInput = useRef<HTMLInputElement>(null);
const [versusTankSearchResults, setVersusTankSearchResults] = useState< const [versusTankSearchResults, setVersusTankSearchResults] = useState<
number[] number[]
>([]); >([]);
const [versusTankTurretId, setVersusTankTurretId] = useState( const [versusTank, setVersusTank] = useState(tank);
awaitedTurretDefinitions[tank.turrets[0]].id, const [versusTurret, setVersusTurret] = useState(
awaitedTurretDefinitions[versusTank.turrets.at(-1)!],
); );
const versusTank = awaitedTankDefinitions[versusTankId]; const [versusGun, setVersusGun] = useState(
awaitedGunDefinitions[versusTurret.guns.at(-1)!],
);
const [versusTankTab, setVersusTankTab] = useState('search');
useEffect(() => setGun(awaitedGunDefinitions[turret.guns[0]]), [turret]); useEffect(() => {
if (!turret.guns.includes(gun.id)) {
setGun(awaitedGunDefinitions[turret.guns.at(-1)!]);
}
}, [turret]);
useEffect(() => {
if (!versusTank.turrets.includes(versusTurret.id)) {
setVersusTurret(awaitedTurretDefinitions[versusTank.turrets.at(-1)!]);
}
}, [versusTank]);
useEffect(() => {
if (!versusTurret.guns.includes(versusGun.id)) {
setVersusGun(awaitedGunDefinitions[versusTurret.guns.at(-1)!]);
}
}, [versusTurret]);
return ( return (
<PageWrapper> <PageWrapper color="purple">
<Flex gap="8" direction="column"> <Flex gap="8" direction="column">
<Flex gap="4" direction="column"> <Flex gap="4" direction="column">
<Flex justify="between" align="center"> <Flex justify="between" align="center">
@@ -94,10 +105,7 @@ export default function Page({ params }: { params: { id: string } }) {
<Card style={{ position: 'relative' }}> <Card style={{ position: 'relative' }}>
<Flex direction="column" gap="2"> <Flex direction="column" gap="2">
<Tabs.Root <Tabs.Root value={mode} onValueChange={setMode}>
value={mode}
onValueChange={(value) => setMode(value as Mode)}
>
<Tabs.List> <Tabs.List>
<Tabs.Trigger value="model">Model</Tabs.Trigger> <Tabs.Trigger value="model">Model</Tabs.Trigger>
<Tabs.Trigger value="armor">Armor</Tabs.Trigger> <Tabs.Trigger value="armor">Armor</Tabs.Trigger>
@@ -136,86 +144,191 @@ export default function Page({ params }: { params: { id: string } }) {
<Dialog.Root> <Dialog.Root>
<Dialog.Trigger> <Dialog.Trigger>
<Button variant="ghost"> <Button variant="ghost">
{awaitedTankDefinitions[versusTankId].name} {versusTank.name}
<CaretRightIcon /> <CaretRightIcon />
</Button> </Button>
</Dialog.Trigger> </Dialog.Trigger>
<Dialog.Content> <Dialog.Content>
<Flex direction="column" gap="6"> <Tabs.Root
<Flex direction="column" gap="2"> value={versusTankTab}
<Heading size="4">Search tank</Heading> onValueChange={setVersusTankTab}
>
<Flex gap="4" direction="column">
<Tabs.List>
<Tabs.Trigger value="search">Search</Tabs.Trigger>
<Tabs.Trigger value="configure">
Configure
</Tabs.Trigger>
</Tabs.List>
<TextField.Root> <Tabs.Content value="search">
<TextField.Slot> <Flex
<MagnifyingGlassIcon /> direction="column"
</TextField.Slot> gap="4"
<TextField.Input style={{ flex: 1 }}
ref={versusTankSearchInput} justify="center"
placeholder="Search tank..." >
onChange={debounce(() => { <TextField.Root>
setVersusTankSearchResults( <TextField.Slot>
go( <MagnifyingGlassIcon />
versusTankSearchInput.current!.value, </TextField.Slot>
awaitedTankNamesDiacritics, <TextField.Input
{ key: 'combined', limit: 8 }, ref={versusTankSearchInput}
).map((item) => item.obj.id), placeholder="Search tank..."
); onChange={debounce(() => {
}, 500)} setVersusTankSearchResults(
/> go(
</TextField.Root> versusTankSearchInput.current!.value,
</Flex> awaitedTankNamesDiacritics,
{ key: 'combined', limit: 8 },
).map((item) => item.obj.id),
);
}, 500)}
/>
</TextField.Root>
{(versusTankSearchResults.length > 0 || <Flex direction="column" gap="2">
versusTankSearchInput.current?.value) && ( {(versusTankSearchResults.length > 0 ||
<Flex direction="column" gap="2"> versusTankSearchInput.current?.value) && (
{versusTankSearchResults.map((id) => ( <Flex direction="column" gap="2">
<Button {versusTankSearchResults.map((id) => (
key={id} <Button
variant="ghost" key={id}
onClick={() => { variant="ghost"
setVersusTankId(id); onClick={() => {
setVersusTankSearchResults([]); setVersusTank(
versusTankSearchInput.current!.value = ''; awaitedTankDefinitions[id],
}} );
setVersusTankSearchResults([]);
versusTankSearchInput.current!.value =
'';
setVersusTankTab('configure');
}}
>
{awaitedTankDefinitions[id].name}
</Button>
))}
{versusTankSearchResults.length === 0 &&
(versusTankSearchInput.current
? versusTankSearchInput.current.value
.length > 0
: false) && (
<Button disabled variant="ghost">
No search results
</Button>
)}
</Flex>
)}
</Flex>
</Flex>
</Tabs.Content>
<Tabs.Content value="configure">
<Flex direction="column" gap="4">
<Flex
direction="column"
gap="2"
style={{ flex: 1 }}
> >
{awaitedTankDefinitions[id].name} <Heading size="4">Configuration</Heading>
</Button>
))}
{versusTankSearchResults.length === 0 && <Flex gap="2" wrap="wrap">
(versusTankSearchInput.current <Flex>
? versusTankSearchInput.current.value.length > 0 {versusTank.turrets.map((id, index) => (
: false) && ( <Tooltip
<Button disabled variant="ghost"> content={
No search results awaitedTurretDefinitions[id].name
</Button> }
)} >
</Flex> <ModuleButtons
)} key={id}
onClick={() =>
setVersusTurret(
awaitedTurretDefinitions[id],
)
}
selected={versusTurret.id === id}
tier={
awaitedTurretDefinitions[id].tier
}
type="turret"
style={{
margin: -0.5,
borderTopLeftRadius:
index === 0 ? undefined : 0,
borderBottomLeftRadius:
index === 0 ? undefined : 0,
borderTopRightRadius:
index ===
versusTank.turrets.length - 1
? undefined
: 0,
borderBottomRightRadius:
index ===
versusTank.turrets.length - 1
? undefined
: 0,
}}
/>
</Tooltip>
))}
</Flex>
<Flex>
{versusTurret.guns.map((id, index) => (
<Tooltip
content={awaitedGunDefinitions[id].name}
>
<ModuleButtons
key={id}
onClick={() =>
setVersusGun(
awaitedGunDefinitions[id],
)
}
selected={versusGun.id === id}
tier={awaitedGunDefinitions[id].tier}
type="gun"
style={{
margin: -0.5,
borderTopLeftRadius:
index === 0 ? undefined : 0,
borderBottomLeftRadius:
index === 0 ? undefined : 0,
borderTopRightRadius:
index ===
versusTurret.guns.length - 1
? undefined
: 0,
borderBottomRightRadius:
index ===
versusTurret.guns.length - 1
? undefined
: 0,
}}
/>
</Tooltip>
))}
</Flex>
</Flex>
</Flex>
<Separator style={{ width: '100%' }} /> <Flex
direction="column"
<Flex direction="column" gap="2"> gap="2"
<Heading size="4"> style={{ flex: 1 }}
{awaitedTankDefinitions[versusTankId].name}
</Heading>
<Flex>
{versusTank.turrets.map((id) => (
<Button
key={id}
variant="ghost"
onClick={() => setVersusTankTurretId(id)}
radius="small"
color="gray"
> >
{awaitedTurretDefinitions[id].name} <Heading size="4">Properties</Heading>
</Button>
))} <ul>
</Flex> <li>Penetration</li>
</ul>
</Flex>
</Flex>
</Tabs.Content>
</Flex> </Flex>
</Flex> </Tabs.Root>
</Dialog.Content> </Dialog.Content>
</Dialog.Root> </Dialog.Root>
</Flex> </Flex>
@@ -227,17 +340,25 @@ export default function Page({ params }: { params: { id: string } }) {
<Flex gap="1"> <Flex gap="1">
<Button variant="solid" radius="small"> <Button variant="solid" radius="small">
<img src={asset('shells/ap.webp')} width={24} height={24} />
</Button>
<Button variant="soft" radius="small" color="gray">
<img <img
src={asset('shells/hc_premium.webp')} src={asset('icons/shells/ap.webp')}
width={24} width={24}
height={24} height={24}
/> />
</Button> </Button>
<Button variant="soft" radius="small" color="gray"> <Button variant="soft" radius="small" color="gray">
<img src={asset('shells/he.webp')} width={24} height={24} /> <img
src={asset('icons/shells/hc_premium.webp')}
width={24}
height={24}
/>
</Button>
<Button variant="soft" radius="small" color="gray">
<img
src={asset('icons/shells/he.webp')}
width={24}
height={24}
/>
</Button> </Button>
</Flex> </Flex>
</Flex> </Flex>
@@ -15,7 +15,7 @@ import {
NATIONS, NATIONS,
TIERS, TIERS,
TIER_ROMAN_NUMERALS, TIER_ROMAN_NUMERALS,
} from '../../../../../core/blitzkrieg/definitions/tanks'; } from '../../../../../core/blitzkrieg/tankDefinitions';
import mutateTankopedia, { import mutateTankopedia, {
TankopediaTestTankDisplay, TankopediaTestTankDisplay,
useTankopedia, useTankopedia,
@@ -1,12 +1,12 @@
import { CaretLeftIcon, CaretRightIcon } from '@radix-ui/react-icons'; import { CaretLeftIcon, CaretRightIcon } from '@radix-ui/react-icons';
import { Button, Flex, TextField } from '@radix-ui/themes'; import { Button, Flex, TextField } from '@radix-ui/themes';
import { useEffect, useRef } from 'react'; import { useEffect, useRef } from 'react';
import { BlitzkriegTankDefinition } from '../../../../core/blitzkrieg/definitions/tanks'; import { TankDefinition } from '../../../../core/blitzkrieg/tankDefinitions';
interface PageTurnerProps { interface PageTurnerProps {
page: number; page: number;
setPage: (page: number) => void; setPage: (page: number) => void;
searchedList: BlitzkriegTankDefinition[]; searchedList: TankDefinition[];
tanksPerPage: number; tanksPerPage: number;
} }
+3 -6
View File
@@ -20,13 +20,13 @@ import { Suspense, use, useEffect, useMemo, useRef, useState } from 'react';
import PageWrapper from '../../../components/PageWrapper'; import PageWrapper from '../../../components/PageWrapper';
import { asset } from '../../../core/blitzkrieg/asset'; import { asset } from '../../../core/blitzkrieg/asset';
import { import {
BlitzkriegTankDefinition,
TANK_ICONS, TANK_ICONS,
TANK_ICONS_COLLECTOR, TANK_ICONS_COLLECTOR,
TANK_ICONS_PREMIUM, TANK_ICONS_PREMIUM,
TIER_ROMAN_NUMERALS, TIER_ROMAN_NUMERALS,
TankDefinition,
tanksDefinitionsArray, tanksDefinitionsArray,
} from '../../../core/blitzkrieg/definitions/tanks'; } from '../../../core/blitzkrieg/tankDefinitions';
import { tankIcon } from '../../../core/blitzkrieg/tankIcon'; import { tankIcon } from '../../../core/blitzkrieg/tankIcon';
import { theme } from '../../../stitches.config'; import { theme } from '../../../stitches.config';
import mutateTankopedia, { import mutateTankopedia, {
@@ -117,10 +117,7 @@ export default function Page() {
} else { } else {
setSearchedList( setSearchedList(
go(event.target.value, searchableTanks, { go(event.target.value, searchableTanks, {
keys: [ keys: ['name', 'id'] satisfies (keyof TankDefinition)[],
'name',
'id',
] satisfies (keyof BlitzkriegTankDefinition)[],
}).map(({ obj }) => obj), }).map(({ obj }) => obj),
); );
} }
+1 -1
View File
@@ -8,8 +8,8 @@ import { AllStats, getAccountInfo } from '../core/blitz/getAccountInfo';
import { getClanAccountInfo } from '../core/blitz/getClanAccountInfo'; import { getClanAccountInfo } from '../core/blitz/getClanAccountInfo';
import getTankStats from '../core/blitz/getTankStats'; import getTankStats from '../core/blitz/getTankStats';
import getTreeType from '../core/blitz/getTreeType'; import getTreeType from '../core/blitz/getTreeType';
import { tankDefinitions } from '../core/blitzkrieg/definitions/tanks';
import { filtersToDescription } from '../core/blitzkrieg/filtersToDescription'; import { filtersToDescription } from '../core/blitzkrieg/filtersToDescription';
import { tankDefinitions } from '../core/blitzkrieg/tankDefinitions';
import { getBlitzStarsLinkButton } from '../core/blitzstars/getBlitzStarsLinkButton'; import { getBlitzStarsLinkButton } from '../core/blitzstars/getBlitzStarsLinkButton';
import getStatsInPeriod from '../core/blitzstars/getStatsInPeriod'; import getStatsInPeriod from '../core/blitzstars/getStatsInPeriod';
import { tankAverages } from '../core/blitzstars/tankAverages'; import { tankAverages } from '../core/blitzstars/tankAverages';
+1 -1
View File
@@ -1,8 +1,8 @@
import { SlashCommandBuilder } from 'discord.js'; import { SlashCommandBuilder } from 'discord.js';
import { getAccountInfo } from '../core/blitz/getAccountInfo'; import { getAccountInfo } from '../core/blitz/getAccountInfo';
import { tankDefinitions } from '../core/blitzkrieg/definitions/tanks';
import getPeriodNow from '../core/blitzkrieg/getPeriodNow'; import getPeriodNow from '../core/blitzkrieg/getPeriodNow';
import getTimeDaysAgo from '../core/blitzkrieg/getTimeDaysAgo'; import getTimeDaysAgo from '../core/blitzkrieg/getTimeDaysAgo';
import { tankDefinitions } from '../core/blitzkrieg/tankDefinitions';
import getStatsInPeriod from '../core/blitzstars/getStatsInPeriod'; import getStatsInPeriod from '../core/blitzstars/getStatsInPeriod';
import { tankAverages } from '../core/blitzstars/tankAverages'; import { tankAverages } from '../core/blitzstars/tankAverages';
import addUsernameChoices from '../core/discord/addUsernameChoices'; import addUsernameChoices from '../core/discord/addUsernameChoices';
+4 -4
View File
@@ -8,13 +8,13 @@ import { getAccountInfo } from '../core/blitz/getAccountInfo';
import { getClanAccountInfo } from '../core/blitz/getClanAccountInfo'; import { getClanAccountInfo } from '../core/blitz/getClanAccountInfo';
import getTankStats from '../core/blitz/getTankStats'; import getTankStats from '../core/blitz/getTankStats';
import getTreeType from '../core/blitz/getTreeType'; import getTreeType from '../core/blitz/getTreeType';
import { emblemIdToURL } from '../core/blitzkrieg/emblemIdToURL';
import { import {
BlitzkriegTankDefinition,
TIER_ROMAN_NUMERALS, TIER_ROMAN_NUMERALS,
TankDefinition,
Tier, Tier,
tankDefinitions, tankDefinitions,
} from '../core/blitzkrieg/definitions/tanks'; } from '../core/blitzkrieg/tankDefinitions';
import { emblemIdToURL } from '../core/blitzkrieg/emblemIdToURL';
import { tankIcon } from '../core/blitzkrieg/tankIcon'; import { tankIcon } from '../core/blitzkrieg/tankIcon';
import addTierChoices from '../core/discord/addTierChoices'; import addTierChoices from '../core/discord/addTierChoices';
import addUsernameChoices from '../core/discord/addUsernameChoices'; import addUsernameChoices from '../core/discord/addUsernameChoices';
@@ -47,7 +47,7 @@ export const ownedTanksCommand: CommandRegistry = {
) )
).filter((tank) => tank.tankDefinitions?.tier === tier); ).filter((tank) => tank.tankDefinitions?.tier === tier);
const clanAccountInfo = await getClanAccountInfo(server, id, ['clan']); const clanAccountInfo = await getClanAccountInfo(server, id, ['clan']);
const groupedTanks: Record<string, BlitzkriegTankDefinition[]> = {}; const groupedTanks: Record<string, TankDefinition[]> = {};
const nations: string[] = []; const nations: string[] = [];
filteredTanks.forEach((tank) => { filteredTanks.forEach((tank) => {
+1 -1
View File
@@ -1,7 +1,7 @@
import { SlashCommandBuilder } from 'discord.js'; import { SlashCommandBuilder } from 'discord.js';
import { go } from 'fuzzysort'; import { go } from 'fuzzysort';
import markdownEscape from 'markdown-escape'; import markdownEscape from 'markdown-escape';
import { tankNamesDiacritics } from '../core/blitzkrieg/definitions/tanks'; import { tankNamesDiacritics } from '../core/blitzkrieg/tankDefinitions';
import addTankChoices from '../core/discord/addTankChoices'; import addTankChoices from '../core/discord/addTankChoices';
import embedInfo from '../core/discord/embedInfo'; import embedInfo from '../core/discord/embedInfo';
import { CommandRegistry } from '../events/interactionCreate'; import { CommandRegistry } from '../events/interactionCreate';
+60
View File
@@ -0,0 +1,60 @@
import { Button, Text } from '@radix-ui/themes';
import { ComponentProps } from 'react';
import { asset } from '../core/blitzkrieg/asset';
import { TIER_ROMAN_NUMERALS, Tier } from '../core/blitzkrieg/tankDefinitions';
interface ModuleButtonProps
extends Omit<ComponentProps<typeof Button>, 'type'> {
type: string;
tier: Tier;
selected?: boolean;
}
export function ModuleButtons({
type,
tier,
selected,
style,
...props
}: ModuleButtonProps) {
return (
<Button
radius="medium"
color={selected ? undefined : 'gray'}
variant={selected ? 'solid' : 'soft'}
style={{
padding: 0,
width: 48,
height: 40,
position: 'relative',
...style,
}}
{...props}
>
<img
src={asset(`icons/modules/${type}.webp`)}
style={{
width: 32,
height: 32,
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(calc(-50% + 2px), calc(-50% + 2px))',
}}
/>
<Text
size="1"
weight="bold"
style={{
zIndex: 1,
position: 'absolute',
top: '50%',
right: 8,
textAlign: 'right',
}}
>
{TIER_ROMAN_NUMERALS[tier]}
</Text>
</Button>
);
}
+1 -1
View File
@@ -2,7 +2,7 @@ import {
TANK_ICONS, TANK_ICONS,
TANK_ICONS_COLLECTOR, TANK_ICONS_COLLECTOR,
TANK_ICONS_PREMIUM, TANK_ICONS_PREMIUM,
} from '../../../core/blitzkrieg/definitions/tanks'; } from '../../../core/blitzkrieg/tankDefinitions';
import { theme } from '../../../stitches.config'; import { theme } from '../../../stitches.config';
export enum TreeTypeEnum { export enum TreeTypeEnum {
+1 -1
View File
@@ -1,4 +1,4 @@
import { Tier } from '../core/blitzkrieg/definitions/tanks'; import { Tier } from '../core/blitzkrieg/tankDefinitions';
import { theme } from '../stitches.config'; import { theme } from '../stitches.config';
export type TierWeightsRecord = Partial<Record<Tier, number>>; export type TierWeightsRecord = Partial<Record<Tier, number>>;
+1 -1
View File
@@ -1,5 +1,5 @@
import { TreeTypeEnum } from '../../components/Tanks'; import { TreeTypeEnum } from '../../components/Tanks';
import { tankDefinitions } from '../blitzkrieg/definitions/tanks'; import { tankDefinitions } from '../blitzkrieg/tankDefinitions';
export default async function getTreeType(id: number) { export default async function getTreeType(id: number) {
const entry = (await tankDefinitions)[id]; const entry = (await tankDefinitions)[id];
+1 -1
View File
@@ -2,7 +2,7 @@ import { go } from 'fuzzysort';
import { import {
tankDefinitions, tankDefinitions,
tanksDefinitionsArray, tanksDefinitionsArray,
} from '../blitzkrieg/definitions/tanks'; } from '../blitzkrieg/tankDefinitions';
import { UserError } from '../blitzkrieg/userError'; import { UserError } from '../blitzkrieg/userError';
export default async function resolveTankId(tank: string | number) { export default async function resolveTankId(tank: string | number) {
-18
View File
@@ -1,18 +0,0 @@
import { asset } from '../asset';
import { Tier } from './tanks';
export interface BlitzkriegGunDefinition {
id: number;
name: string;
tier: Tier;
pitch: [number, number];
shells: number[];
}
export type BlitzkriegGunDefinitions = Record<number, BlitzkriegGunDefinition>;
export const gunDefinitions = fetch(asset('definitions/guns.json'), {
cache: 'no-cache',
}).then(
async (response) => response.json() as Promise<BlitzkriegGunDefinitions>,
);
-26
View File
@@ -1,26 +0,0 @@
import { asset } from '../asset';
export type ShellType = 'ap' | 'ap_cr' | 'hc' | 'he';
export interface BlitzkriegShellDefinition {
id: number;
name: string;
speed: number;
damage: { armor: number; devices: number };
caliber: number;
normalization: number;
ricochet: number;
type: ShellType;
icon: string;
}
export type BlitzkriegShellDefinitions = Record<
number,
BlitzkriegShellDefinition
>;
export const shellDefinitions = fetch(asset('definitions/shells.json'), {
cache: 'no-cache',
}).then(
async (response) => response.json() as Promise<BlitzkriegShellDefinitions>,
);
@@ -1,21 +0,0 @@
import { asset } from '../asset';
import { Tier } from './tanks';
export interface BlitzkriegTurretDefinition {
id: number;
name: string;
tier: Tier;
yaw: [number, number];
guns: number[];
}
export type BlitzkriegTurretDefinitions = Record<
number,
BlitzkriegTurretDefinition
>;
export const turretDefinitions = fetch(asset('definitions/turrets.json'), {
cache: 'no-cache',
}).then(
async (response) => response.json() as Promise<BlitzkriegTurretDefinitions>,
);
+1 -5
View File
@@ -1,11 +1,7 @@
import { TREE_TYPE_NAMES } from '../../components/Tanks'; import { TREE_TYPE_NAMES } from '../../components/Tanks';
import { encyclopediaInfo } from '../blitz/encyclopediaInfo'; import { encyclopediaInfo } from '../blitz/encyclopediaInfo';
import { StatFilters } from '../statistics/filterStats'; import { StatFilters } from '../statistics/filterStats';
import { import { TIER_ROMAN_NUMERALS, Tier, tankDefinitions } from './tankDefinitions';
TIER_ROMAN_NUMERALS,
Tier,
tankDefinitions,
} from './definitions/tanks';
export async function filtersToDescription({ export async function filtersToDescription({
nation, nation,
@@ -1,8 +1,51 @@
import { deburr } from 'lodash'; import { deburr } from 'lodash';
import { TankType, TreeTypeString } from '../../../components/Tanks'; import { TankType, TreeTypeString } from '../../components/Tanks';
import { asset } from '../asset'; import { asset } from './asset';
export interface BlitzkriegTankDefinition { export type ShellType = 'ap' | 'ap_cr' | 'hc' | 'he';
export interface ShellDefinition {
id: number;
name: string;
speed: number;
damage: { armor: number; devices: number };
caliber: number;
normalization: number;
ricochet: number;
type: ShellType;
icon: string;
}
export type GunDefinition = {
id: number;
name: string;
tier: Tier;
pitch: [number, number];
shells: ShellDefinition[];
} & (
| {
type: 'regular';
reload: number;
}
| {
type: 'auto_loader';
reload: number;
interClip: number;
count: number;
}
| {
type: 'auto_reloader';
reload: number[];
interClip: number;
count: number;
}
);
export interface TurretDefinition {
id: number;
name: string;
tier: Tier;
yaw: [number, number];
guns: GunDefinition[];
}
export interface TankDefinition {
id: number; id: number;
nation: string; nation: string;
name: string; name: string;
@@ -10,23 +53,18 @@ export interface BlitzkriegTankDefinition {
tier: Tier; tier: Tier;
type: TankType; type: TankType;
testing?: boolean; testing?: boolean;
turrets: number[]; turrets: TurretDefinition[];
} }
export type BlitzkriegTankDefinitions = Record< export type TankDefinitions = Record<number, TankDefinition>;
number,
BlitzkriegTankDefinition
>;
export const tankDefinitions = fetch(asset('definitions/tanks.json'), { export const tankDefinitions = fetch(asset('definitions/tanks.json'), {
cache: 'no-cache', cache: 'no-cache',
}).then( }).then(async (response) => response.json() as Promise<TankDefinitions>);
async (response) => response.json() as Promise<BlitzkriegTankDefinitions>,
);
const entries = new Promise<BlitzkriegTankDefinition[]>(async (resolve) => { const entries = new Promise<TankDefinition[]>(async (resolve) => {
resolve(Object.entries(await tankDefinitions).map(([, entry]) => entry)); resolve(Object.entries(await tankDefinitions).map(([, entry]) => entry));
}); });
export const tanksDefinitionsArray = new Promise<BlitzkriegTankDefinition[]>( export const tanksDefinitionsArray = new Promise<TankDefinition[]>(
async (resolve) => { async (resolve) => {
resolve((await entries).map((entry) => entry)); resolve((await entries).map((entry) => entry));
}, },
+1 -1
View File
@@ -1,5 +1,5 @@
import { asset } from './asset'; import { asset } from './asset';
export function tankIcon(id: number, size: 'big' | 'small' = 'big') { export function tankIcon(id: number, size: 'big' | 'small' = 'big') {
return asset(`icons/${size}/${id}.webp`); return asset(`icons/tanks/${size}/${id}.webp`);
} }
+1 -1
View File
@@ -1,6 +1,6 @@
import { AllStats } from '../blitz/getAccountInfo'; import { AllStats } from '../blitz/getAccountInfo';
import { context } from '../blitzkrieg/context'; import { context } from '../blitzkrieg/context';
import { tankDefinitions } from '../blitzkrieg/definitions/tanks'; import { tankDefinitions } from '../blitzkrieg/tankDefinitions';
export interface SpecialStats { export interface SpecialStats {
winrate: number; winrate: number;
+1 -1
View File
@@ -4,7 +4,7 @@ import {
CacheType, CacheType,
} from 'discord.js'; } from 'discord.js';
import { go } from 'fuzzysort'; import { go } from 'fuzzysort';
import { tankNamesDiacritics } from '../blitzkrieg/definitions/tanks'; import { tankNamesDiacritics } from '../blitzkrieg/tankDefinitions';
import { import {
DISCORD_CHOICES_MAX_NAME_SIZE, DISCORD_CHOICES_MAX_NAME_SIZE,
OVERFLOW_SUFFIX, OVERFLOW_SUFFIX,
+1 -1
View File
@@ -1,5 +1,5 @@
import { TreeTypeString } from '../../components/Tanks'; import { TreeTypeString } from '../../components/Tanks';
import { tankDefinitions } from '../blitzkrieg/definitions/tanks'; import { tankDefinitions } from '../blitzkrieg/tankDefinitions';
import { DiffedTankStats } from '../blitzstars/getStatsInPeriod'; import { DiffedTankStats } from '../blitzstars/getStatsInPeriod';
import { tankAverages } from '../blitzstars/tankAverages'; import { tankAverages } from '../blitzstars/tankAverages';
import calculateWN8 from './calculateWN8'; import calculateWN8 from './calculateWN8';
+1 -1
View File
@@ -1,6 +1,6 @@
import { TierWeightsRecord } from '../../components/TierWeights'; import { TierWeightsRecord } from '../../components/TierWeights';
import { AllStats } from '../blitz/getAccountInfo'; import { AllStats } from '../blitz/getAccountInfo';
import { tankDefinitions } from '../blitzkrieg/definitions/tanks'; import { tankDefinitions } from '../blitzkrieg/tankDefinitions';
export async function getTierWeights( export async function getTierWeights(
stats: Record<number, AllStats>, stats: Record<number, AllStats>,
+1 -1
View File
@@ -1,7 +1,7 @@
import { produce } from 'immer'; import { produce } from 'immer';
import { create } from 'zustand'; import { create } from 'zustand';
import { TankType, TreeTypeString } from '../components/Tanks'; import { TankType, TreeTypeString } from '../components/Tanks';
import { Tier } from '../core/blitzkrieg/definitions/tanks'; 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';