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