mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-21 10:47:04 +02:00
feat: module ui
This commit is contained in:
1 parent
dabeab8612
commit
dcc3ab6e4e
7 files changed
+191
-120
No files matched your search
Vendored
+1
@@ -4,6 +4,7 @@
|
||||
"Abhi",
|
||||
"accountinfo",
|
||||
"alphatest",
|
||||
"apcr",
|
||||
"bepaint",
|
||||
"BINORMAL",
|
||||
"blitzstars",
|
||||
|
||||
+96
-29
@@ -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 (
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>,
|
||||
);
|
||||
@@ -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>,
|
||||
);
|
||||
Reference in new issue
Block a user