feat: module ui

This commit is contained in:
TrèsAbhi committed 2023-12-12 15:12:09 -06:00
1 parent dabeab8612
commit dcc3ab6e4e
7 files changed
+191 -120

No files matched your search

+1
View File
@@ -4,6 +4,7 @@
"Abhi",
"accountinfo",
"alphatest",
"apcr",
"bepaint",
"BINORMAL",
"blitzstars",
+96 -29
View File
@@ -13,6 +13,10 @@ import commitMultipleFiles, {
FileChange,
} from '../src/core/blitzkrieg/commitMultipleFiles';
import { BlitzkriegGunDefinitions } from '../src/core/blitzkrieg/definitions/guns';
import {
BlitzkriegShellDefinitions,
ShellType,
} from '../src/core/blitzkrieg/definitions/shells';
import {
BlitzkriegTankDefinitions,
Tier,
@@ -54,9 +58,35 @@ interface GunDefinitionsList {
pitchLimits: string;
burst?: { count: number; rate: number };
clip?: { count: number; rate: number };
shots: {
[key: string]: {
speed: number;
piercingPower: string;
};
};
};
};
}
type ShellKind =
| 'ARMOR_PIERCING'
| 'ARMOR_PIERCING_CR'
| 'HIGH_EXPLOSIVE'
| 'HOLLOW_CHARGE';
type ShellDefinitionsList = Record<
string,
{
id: number;
userString: string;
icon: string;
kind: ShellKind;
caliber: number;
damage: { armor: number; devices: number };
normalizationAngle: number;
ricochetAngle: number;
}
> & {
icons: Record<string, string>;
};
interface VehicleDefinitions {
turrets0: {
[key: string]: {
@@ -90,6 +120,12 @@ const blitzTankTypeToBlitzkrieg: Record<BlitzTankType, TankType> = {
mediumTank: 'medium',
heavyTank: 'heavy',
};
const blitzShellKindToBLitzkrieg: Record<ShellKind, ShellType> = {
ARMOR_PIERCING: 'ap',
ARMOR_PIERCING_CR: 'apcr',
HIGH_EXPLOSIVE: 'he',
HOLLOW_CHARGE: 'heat',
};
const DATA =
'C:/Program Files (x86)/Steam/steamapps/common/World of Tanks Blitz/Data';
const LANGUAGE = 'en';
@@ -117,6 +153,7 @@ if (allTargets || targets?.includes('definitions')) {
const tankDefinitions: BlitzkriegTankDefinitions = {};
const turretDefinitions: BlitzkriegTurretDefinitions = {};
const gunDefinitions: BlitzkriegGunDefinitions = {};
const shellDefinitions: BlitzkriegShellDefinitions = {};
const nations = await readdir(`${DATA}/${DOI.vehicleDefinitions}`).then(
(nations) => nations.filter((nation) => nation !== 'common'),
);
@@ -139,6 +176,11 @@ if (allTargets || targets?.includes('definitions')) {
}>(
`${DATA}/${DOI.vehicleDefinitions}/${nation}/components/guns.xml.dvpl`,
);
const shellList = await readXMLDVPL<{
root: ShellDefinitionsList;
}>(
`${DATA}/${DOI.vehicleDefinitions}/${nation}/components/shells.xml.dvpl`,
);
for (const tankKey in tankList.root) {
const tank = tankList.root[tankKey];
@@ -157,6 +199,28 @@ if (allTargets || targets?.includes('definitions')) {
const gun = gunList.root.shared[gunKey];
const pitchLimitsRaw =
turretGunEntry.pitchLimits ?? gun.pitchLimits;
const gunShells = Object.keys(gun.shots).map((shellKey) => {
const turretShellEntry = gun.shots[shellKey];
const shell = shellList.root[shellKey];
const shellId = toUniqueId(nation, shell.id);
shellDefinitions[shellId] = {
id: shellId,
name:
strings[shell.userString] ?? shellKey.replaceAll('_', ' '),
speed: turretShellEntry.speed,
damage: {
armor: shell.damage.armor,
devices: shell.damage.devices,
},
caliber: shell.caliber,
normalization: shell.normalizationAngle,
ricochet: shell.ricochetAngle,
type: blitzShellKindToBLitzkrieg[shell.kind],
};
return shellId;
});
gunDefinitions[gunId] = {
id: gunId,
@@ -168,6 +232,7 @@ if (allTargets || targets?.includes('definitions')) {
.map(Number) as [number, number],
name: strings[gun.userString] ?? gunKey.replaceAll('_', ' '),
tier: gun.level,
shells: gunShells,
};
return gunId;
@@ -217,35 +282,37 @@ if (allTargets || targets?.includes('definitions')) {
}),
);
// rmSync('dist/definitions', { force: true, recursive: true });
// mkdirSync('dist/definitions', { recursive: true });
// writeFile(
// 'dist/definitions/tanks.json',
// JSON.stringify(tankDefinitions, null, 2),
// );
// writeFile(
// 'dist/definitions/turrets.json',
// JSON.stringify(turretDefinitions, null, 2),
// );
// writeFile(
// 'dist/definitions/guns.json',
// JSON.stringify(gunDefinitions, null, 2),
// );
// console.log('Committing tank definitions...');
// await commitMultipleFiles(
// 'tresabhi',
// 'blitzkrieg-assets',
// 'main',
// 'tank definitions',
// [
// {
// content: JSON.stringify(tankDefinitions),
// encoding: 'utf-8',
// path: 'definitions/tanks.json',
// },
// ],
// true,
// );
console.log('Committing tank definitions...');
await commitMultipleFiles(
'tresabhi',
'blitzkrieg-assets',
'main',
'definitions',
[
{
content: JSON.stringify(tankDefinitions),
encoding: 'utf-8',
path: 'definitions/tanks.json',
},
{
content: JSON.stringify(turretDefinitions),
encoding: 'utf-8',
path: 'definitions/turrets.json',
},
{
content: JSON.stringify(gunDefinitions),
encoding: 'utf-8',
path: 'definitions/guns.json',
},
{
content: JSON.stringify(shellDefinitions),
encoding: 'utf-8',
path: 'definitions/shells.json',
},
],
true,
);
}
if (
+50 -82
View File
@@ -1,107 +1,75 @@
'use client';
import { Checkbox, Flex, Heading } from '@radix-ui/themes';
import { OrbitControls } from '@react-three/drei';
import { Canvas } from '@react-three/fiber';
import { use, useState } from 'react';
import {
BufferAttribute,
BufferGeometry,
Mesh,
MeshStandardMaterial,
Vector3Tuple,
} from 'three';
import model from '../../../../../test.json';
import { Button, Flex, Heading } from '@radix-ui/themes';
import { use, useEffect, useState } from 'react';
import { Flag } from '../../../../components/Flag';
import PageWrapper from '../../../../components/PageWrapper';
import { gunDefinitions } from '../../../../core/blitzkrieg/definitions/guns';
import { shellDefinitions } from '../../../../core/blitzkrieg/definitions/shells';
import { tankDefinitions } from '../../../../core/blitzkrieg/definitions/tanks';
import { turretDefinitions } from '../../../../core/blitzkrieg/definitions/turrets';
const SIZE = 0.05;
export default function Page({ params }: { params: { id: string } }) {
const id = parseInt(params.id);
const awaitedTankDefinitions = use(tankDefinitions);
const definition = awaitedTankDefinitions[id];
const [display, setDisplay] = useState<boolean[]>(model.map(() => true));
const awaitedTurretDefinitions = use(turretDefinitions);
const awaitedGunDefinitions = use(gunDefinitions);
const awaitedShellDefinitions = use(shellDefinitions);
const tank = awaitedTankDefinitions[id];
const [turret, setTurret] = useState(
awaitedTurretDefinitions[tank.turrets[0]],
);
const [gun, setGun] = useState(awaitedGunDefinitions[turret.guns[0]]);
const [shell, setShell] = useState(awaitedShellDefinitions[gun.shells[0]]);
useEffect(() => setGun(awaitedGunDefinitions[turret.guns[0]]), [turret]);
useEffect(() => setShell(awaitedShellDefinitions[gun.shells[0]]), [gun]);
return (
<PageWrapper>
<Flex gap="2" align="center">
<Flag nation={definition.nation} />
<Heading>{definition.name}</Heading>
<Flag nation={tank.nation} />
<Heading>{tank.name}</Heading>
</Flex>
<Canvas
style={{
width: '100%',
height: '60vh',
}}
>
<OrbitControls />
<Flex gap="2" align="center">
<Heading size="5">Turrets</Heading>
<ambientLight />
<directionalLight intensity={1} />
{tank.turrets.map((turretId) => (
<Button
onClick={() => setTurret(awaitedTurretDefinitions[turretId])}
variant={turret.id === turretId ? 'solid' : 'soft'}
>
{awaitedTurretDefinitions[turretId].name}
</Button>
))}
</Flex>
<group rotation={[-Math.PI / 2, 0, 0]}>
{(model as { vertices: Vector3Tuple[]; indices: number[] }[]).map(
({ vertices, indices }, index) => {
const geometry = new BufferGeometry();
const verticesArray = new Float32Array(vertices.flat());
const positionAttribute = new BufferAttribute(verticesArray, 3);
const indexAttribute = new BufferAttribute(
new Uint16Array(indices),
1,
);
const material = new MeshStandardMaterial({
color: 'white',
metalness: 0.75,
// roughness: 0.2,
});
<Flex gap="2" align="center">
<Heading size="5">Guns</Heading>
geometry
.setAttribute('position', positionAttribute)
.setIndex(indexAttribute)
.computeVertexNormals();
{turret.guns.map((gunId) => (
<Button
onClick={() => setGun(awaitedGunDefinitions[gunId])}
variant={gun.id === gunId ? 'solid' : 'soft'}
>
{awaitedTurretDefinitions[gunId]?.name ?? gunId}
</Button>
))}
</Flex>
return display[index] ? (
<mesh
key={index}
args={[geometry, material]}
onPointerOver={(event) => {
event.stopPropagation();
(
(event.object as Mesh).material as MeshStandardMaterial
).color.set('red');
}}
onPointerOut={(event) => {
event.stopPropagation();
(
(event.object as Mesh).material as MeshStandardMaterial
).color.set('white');
}}
onClick={(event) => {
event.stopPropagation();
const newDisplay = [...display];
newDisplay[index] = !newDisplay[index];
setDisplay(newDisplay);
}}
/>
) : null;
},
)}
</group>
</Canvas>
<Flex gap="2" align="center">
<Heading size="5">Shells</Heading>
<Flex wrap="wrap">
{model.map((group, index) => (
<Checkbox
checked={display[index]}
onCheckedChange={(value) => {
const newDisplay = [...display];
newDisplay[index] = value as boolean;
setDisplay(newDisplay);
}}
/>
{gun.shells.map((shellId) => (
<Button
onClick={() => setShell(awaitedShellDefinitions[shellId])}
variant={shell.id === shellId ? 'solid' : 'soft'}
>
{awaitedShellDefinitions[shellId].name}
</Button>
))}
</Flex>
</PageWrapper>
+2 -9
View File
@@ -74,11 +74,7 @@ export default function Page() {
diff = a.tier - b.tier;
}
if (tankopediaState.sort.by === 'name') {
diff = (
a.name_short ??
a.name ??
`Unknown tank ${a.id}`
).localeCompare(b.name_short ?? b.name ?? `Unknown tank ${b.id}`);
diff = a.name.localeCompare(b.name);
}
return tankopediaState.sort.direction === 'ascending' ? diff : -diff;
@@ -123,7 +119,6 @@ export default function Page() {
go(event.target.value, searchableTanks, {
keys: [
'name',
'name_short',
'id',
] satisfies (keyof BlitzkriegTankDefinition)[],
}).map(({ obj }) => obj),
@@ -307,9 +302,7 @@ export default function Page() {
overflow: 'hidden',
}}
>
{tank.name_short ??
tank.name ??
`Unknown tank ${tank.id}`}
{tank.name}
</Text>
</Flex>
+9
View File
@@ -1,8 +1,17 @@
import { asset } from '../asset';
export interface BlitzkriegGunDefinition {
id: number;
name: string;
tier: number;
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>,
);
+25
View File
@@ -0,0 +1,25 @@
import { asset } from '../asset';
export type ShellType = 'ap' | 'apcr' | 'he' | 'heat';
export interface BlitzkriegShellDefinition {
id: number;
name: string;
speed: number;
damage: { armor: number; devices: number };
caliber: number;
normalization: number;
ricochet: number;
type: ShellType;
}
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,3 +1,5 @@
import { asset } from '../asset';
export interface BlitzkriegTurretDefinition {
id: number;
name: string;
@@ -10,3 +12,9 @@ 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>,
);