(2) some implementation

This commit is contained in:
tresabhi committed 2026-08-01 13:53:00 -05:00
1 parent b0611d7d72
commit f7180f0e1a
3 files changed
+258 -227

No files matched your search

+256 -189
View File
@@ -1,9 +1,11 @@
import { import {
AbstractVFS, AbstractVFS,
Armor, Armor,
AssaultRanges,
BlitzCrewType, BlitzCrewType,
BlitzModuleType, BlitzModuleType,
BlitzStrings, BlitzStrings,
BlitzTankClass,
CamouflageDefinitions, CamouflageDefinitions,
CamouflagesXml, CamouflagesXml,
CamouflagesYaml, CamouflagesYaml,
@@ -14,7 +16,9 @@ import {
Crew, Crew,
CrewType, CrewType,
EngineDefinitionsList, EngineDefinitionsList,
Equalizer,
EquipmentDefinitions, EquipmentDefinitions,
GunDefinition,
GunDefinitionsList, GunDefinitionsList,
I18nString, I18nString,
MapDefinitions, MapDefinitions,
@@ -27,14 +31,18 @@ import {
ProvisionsCommon, ProvisionsCommon,
ResearchCost, ResearchCost,
ShellDefinitionsList, ShellDefinitionsList,
ShellKind,
ShellType,
SkillDefinitions, SkillDefinitions,
sluggify, sluggify,
SquadBattleTypeStylesYaml, SquadBattleTypeStylesYaml,
TankClass,
TankDefinitions, TankDefinitions,
TankmenAvatar, TankmenAvatar,
TankParameters, TankParameters,
TankPrice, TankPrice,
TankPriceType, TankPriceType,
TankType,
toUniqueId, toUniqueId,
TurretDefinitionsList, TurretDefinitionsList,
Unlock, Unlock,
@@ -45,6 +53,7 @@ import {
} from "@blitzkit/core"; } from "@blitzkit/core";
import { SUPPORTED_LOCALE_BLITZ_MAP } from "@blitzkit/i18n"; import { SUPPORTED_LOCALE_BLITZ_MAP } from "@blitzkit/i18n";
import locales from "@blitzkit/i18n/locales.json"; import locales from "@blitzkit/i18n/locales.json";
import { parse as parsePath } from "path";
import { parse as parseYaml } from "yaml"; import { parse as parseYaml } from "yaml";
import { BlitzKitAPI } from "./base"; import { BlitzKitAPI } from "./base";
@@ -84,6 +93,18 @@ export class ServerBlitzKitAPI extends BlitzKitAPI {
loader: CrewType.CREW_TYPE_LOADER, loader: CrewType.CREW_TYPE_LOADER,
radioman: CrewType.CREW_TYPE_RADIOMAN, radioman: CrewType.CREW_TYPE_RADIOMAN,
}; };
private blitzTankClassToBlitzkit: Record<BlitzTankClass, TankClass> = {
lightTank: TankClass.TANK_CLASS_LIGHT,
"AT-SPG": TankClass.TANK_CLASS_TANK_DESTROYER,
heavyTank: TankClass.TANK_CLASS_HEAVY,
mediumTank: TankClass.TANK_CLASS_MEDIUM,
};
private blitzShellKindToBlitzkit: Record<ShellKind, ShellType> = {
ARMOR_PIERCING: ShellType.SHELL_TYPE_AP,
ARMOR_PIERCING_CR: ShellType.SHELL_TYPE_APCR,
HIGH_EXPLOSIVE: ShellType.SHELL_TYPE_HE,
HOLLOW_CHARGE: ShellType.SHELL_TYPE_HEAT,
};
private stringsI18n: Record<string, Record<string, string>> = {}; private stringsI18n: Record<string, Record<string, string>> = {};
private nationsDir?: string[]; private nationsDir?: string[];
@@ -235,6 +256,24 @@ export class ServerBlitzKitAPI extends BlitzKitAPI {
} }
} }
private parseResearchCost(raw: number | string) {
if (typeof raw === "number") {
return {
research_cost_type: { $case: "xp", value: raw },
} satisfies ResearchCost;
} else {
return {
research_cost_type: {
$case: "seasonal_tokens",
value: {
season: Number(/prx_season_(\d+):\d+/.exec(raw)![1]),
tokens: Number(/prx_season_\d+:(\d+)/.exec(raw)![1]),
},
},
} satisfies ResearchCost;
}
}
async tankDefinitions() { async tankDefinitions() {
const tankDefinitions = TankDefinitions.create(); const tankDefinitions = TankDefinitions.create();
@@ -515,7 +554,9 @@ export class ServerBlitzKitAPI extends BlitzKitAPI {
} }
} }
const camouflages = Object.entries(camouflagesXml.root.camouflages) const camouflages = Object.entries(
this.camouflagesXml!.root.camouflages,
)
.filter(([, camo]) => { .filter(([, camo]) => {
if (!camo.vehicleFilter.include) return false; if (!camo.vehicleFilter.include) return false;
if (camo.unlockCostCategory !== "legendary-skins-gold") if (camo.unlockCostCategory !== "legendary-skins-gold")
@@ -535,7 +576,7 @@ export class ServerBlitzKitAPI extends BlitzKitAPI {
}) })
.map(([, camo]) => camo.id); .map(([, camo]) => camo.id);
let equalizerEntry = tierEqualizer.find( let equalizerEntry = this.tierEqualizer!.find(
(line) => line[0] === `${nation}:${tankKey}`, (line) => line[0] === `${nation}:${tankKey}`,
); );
@@ -566,8 +607,8 @@ export class ServerBlitzKitAPI extends BlitzKitAPI {
max_provisions: tankDefinition.root.provisionSlots, max_provisions: tankDefinition.root.provisionSlots,
name: name:
(tank.shortUserString (tank.shortUserString
? getString(tank.shortUserString) ? this.getString(tank.shortUserString)
: undefined) ?? getString(tank.userString), : undefined) ?? this.getString(tank.userString),
slug, slug,
nation, nation,
type: tankTags.includes("collectible") type: tankTags.includes("collectible")
@@ -576,7 +617,7 @@ export class ServerBlitzKitAPI extends BlitzKitAPI {
? TankType.TANK_TYPE_PREMIUM ? TankType.TANK_TYPE_PREMIUM
: TankType.TANK_TYPE_RESEARCHABLE, : TankType.TANK_TYPE_RESEARCHABLE,
tier: tank.level, tier: tank.level,
class: blitzTankClassToBlitzkit[tankTags[0] as BlitzTankClass], class: this.blitzTankClassToBlitzkit[tankTags[0] as BlitzTankClass],
testing: tankTags.includes("testTank"), testing: tankTags.includes("testTank"),
deprecated: tankTags.includes("deprecated"), deprecated: tankTags.includes("deprecated"),
price: tankPrice, price: tankPrice,
@@ -601,7 +642,8 @@ export class ServerBlitzKitAPI extends BlitzKitAPI {
); );
} }
tankDefinitions.tanks[tankId].roles[id] = combatRoles[role].id; tankDefinitions.tanks[tankId].roles[id] =
this.combatRoles![role].id;
}); });
} }
@@ -616,7 +658,7 @@ export class ServerBlitzKitAPI extends BlitzKitAPI {
tankDefinitions.tanks[tankId].tracks.push({ tankDefinitions.tanks[tankId].tracks.push({
id: trackId, id: trackId,
weight: track.weight, weight: track.weight,
name: getString(track.userString), name: this.getString(track.userString),
traverse_speed: track.rotationSpeed, traverse_speed: track.rotationSpeed,
dispersion_move: track.shotDispersionFactors.vehicleMovement, dispersion_move: track.shotDispersionFactors.vehicleMovement,
dispersion_traverse: track.shotDispersionFactors.vehicleRotation, dispersion_traverse: track.shotDispersionFactors.vehicleRotation,
@@ -624,7 +666,10 @@ export class ServerBlitzKitAPI extends BlitzKitAPI {
resistance_medium: terrainResistances[1], resistance_medium: terrainResistances[1],
resistance_soft: terrainResistances[2], resistance_soft: terrainResistances[2],
tier: track.level, tier: track.level,
unlocks: resolveUnlocks(track.unlocks), unlocks: resolveUnlocks(
this.blitzModuleTypeToBlitzkit,
track.unlocks,
),
}); });
}); });
@@ -636,17 +681,21 @@ export class ServerBlitzKitAPI extends BlitzKitAPI {
totalUnlocks.push(engine.unlocks); totalUnlocks.push(engine.unlocks);
tankDefinitions.tanks[tankId].engines.push({ tankDefinitions.tanks[tankId].engines.push({
id: engineId, id: engineId,
name: getString(engineListEntry.userString), name: this.getString(engineListEntry.userString),
fire_chance: engineListEntry.fireStartingChance, fire_chance: engineListEntry.fireStartingChance,
tier: engineListEntry.level, tier: engineListEntry.level,
weight: engineListEntry.weight, weight: engineListEntry.weight,
power: engineListEntry.power, power: engineListEntry.power,
unlocks: resolveUnlocks(engine.unlocks), unlocks: resolveUnlocks(
this.blitzModuleTypeToBlitzkit,
engine.unlocks,
),
}); });
}); });
Object.keys(tankDefinition.root.turrets0).forEach( let turretIndex = 0;
(turretKey, turretIndex) => { for (const turretKey in tankDefinition.root.turrets0) {
{
const turret = tankDefinition.root.turrets0[turretKey]; const turret = tankDefinition.root.turrets0[turretKey];
const turretId = toUniqueId(nation, turretList.root.ids[turretKey]); const turretId = toUniqueId(nation, turretList.root.ids[turretKey]);
@@ -665,7 +714,7 @@ export class ServerBlitzKitAPI extends BlitzKitAPI {
const armorId = parseInt(armorIdString); const armorId = parseInt(armorIdString);
const armorRaw = turret.armor[name]; const armorRaw = turret.armor[name];
assignArmor(armorRaw, armorId, turretArmor); this.assignArmor(armorRaw, armorId, turretArmor);
}); });
turret.userString; turret.userString;
@@ -673,205 +722,223 @@ export class ServerBlitzKitAPI extends BlitzKitAPI {
tankDefinitions.tanks[tankId].turrets.push({ tankDefinitions.tanks[tankId].turrets.push({
id: turretId, id: turretId,
traverse_speed: turret.rotationSpeed, traverse_speed: turret.rotationSpeed,
name: getString(turret.userString), name: this.getString(turret.userString),
tier: turret.level, tier: turret.level,
guns: [], guns: [],
health: turret.maxHealth, health: turret.maxHealth,
view_range: turret.circularVisionRadius, view_range: turret.circularVisionRadius,
weight: turret.weight, weight: turret.weight,
unlocks: resolveUnlocks(turret.unlocks), unlocks: resolveUnlocks(
this.blitzModuleTypeToBlitzkit,
turret.unlocks,
),
}); });
Object.keys(turret.guns).forEach((gunKey, gunIndex) => { let gunIndex = 0;
const gun = turret.guns[gunKey]; for (const gunKey in turret.guns) {
const gunId = toUniqueId(nation, gunList.root.ids[gunKey]); {
const gunListEntry = gunList.root.shared[gunKey]; const gun = turret.guns[gunKey];
const pitchLimitsRaw = const gunId = toUniqueId(nation, gunList.root.ids[gunKey]);
gun.pitchLimits ?? gunListEntry.pitchLimits; const gunListEntry = gunList.root.shared[gunKey];
const gunPitch = ( const pitchLimitsRaw =
typeof pitchLimitsRaw === "string" gun.pitchLimits ?? gunListEntry.pitchLimits;
? pitchLimitsRaw const gunPitch = (
: pitchLimitsRaw.at(-1)! typeof pitchLimitsRaw === "string"
) ? pitchLimitsRaw
.split(" ") : pitchLimitsRaw.at(-1)!
.map(Number) as [number, number]; )
const gunModel = Number(
parsePath(gun.models.undamaged).name.split("_")[1],
);
const gunName = getString(gunListEntry.userString);
const gunType =
"clip" in gun
? gun.pumpGunMode
? "autoReloader"
: "autoLoader"
: "regular";
// const gunReload = gun.reloadTime;
// const shellReloads =
const gunClipCount = gunType === "regular" ? 1 : gun.clip!.count;
const gunArmor: Armor = { thickness: {}, spaced: [] };
const shotDispersionFactors =
gun.shotDispersionFactors ?? gunListEntry.shotDispersionFactors;
const maskName = `gun_${gunModel.toString().padStart(2, "0")}`;
const maskEnabled =
tankParameters.maskSlice?.[maskName]?.enabled ?? false;
let assault_ranges: AssaultRanges | undefined;
if (gun.extras?.trayShell) {
const types = gun.extras.trayShell.kinds
.split(" ") .split(" ")
.map((string) => { .map(Number) as [number, number];
const trimmed = string.trim(); const gunModel = Number(
parsePath(gun.models.undamaged).name.split("_")[1],
);
const gunName = this.getString(gunListEntry.userString);
const gunType =
"clip" in gun
? gun.pumpGunMode
? "autoReloader"
: "autoLoader"
: "regular";
// const gunReload = gun.reloadTime;
// const shellReloads =
const gunClipCount =
gunType === "regular" ? 1 : gun.clip!.count;
const gunArmor: Armor = { thickness: {}, spaced: [] };
const shotDispersionFactors =
gun.shotDispersionFactors ??
gunListEntry.shotDispersionFactors;
const maskName = `gun_${gunModel.toString().padStart(2, "0")}`;
const maskEnabled =
tankParameters.maskSlice?.[maskName]?.enabled ?? false;
let assault_ranges: AssaultRanges | undefined;
if (trimmed in blitzShellKindToBlitzkit) { if (gun.extras?.trayShell) {
return blitzShellKindToBlitzkit[trimmed as ShellKind]; const types = gun.extras.trayShell.kinds
} .split(" ")
.map((string) => {
const trimmed = string.trim();
throw new SyntaxError(`Invalid shell kind: ${trimmed}`); if (trimmed in this.blitzShellKindToBlitzkit) {
}); return this.blitzShellKindToBlitzkit[
const sectorNames = Object.keys(gun.extras.trayShell.sectors); trimmed as ShellKind
];
}
if (sectorNames.length !== 1 || sectorNames[0] !== "sector") { throw new SyntaxError(`Invalid shell kind: ${trimmed}`);
throw new SyntaxError("Invalid tray shell sector"); });
const sectorNames = Object.keys(gun.extras.trayShell.sectors);
if (sectorNames.length !== 1 || sectorNames[0] !== "sector") {
throw new SyntaxError("Invalid tray shell sector");
}
const sector = gun.extras.trayShell.sectors.sector;
assault_ranges = {
types,
ranges: sector.map((value) => ({
factor: value.factor,
distance: value.distance,
})),
};
} }
const sector = gun.extras.trayShell.sectors.sector; totalUnlocks.push(gun.unlocks);
Object.keys(gun.armor)
.filter((name) => name.startsWith("armor_"))
.forEach((name) => {
const armorIdString = name.match(/armor_(\d+)/)?.[1];
if (armorIdString === undefined) {
throw new SyntaxError(`Invalid armor id: ${name}`);
}
const armorId = parseInt(armorIdString);
const armorRaw = gun.armor[name];
assault_ranges = { this.assignArmor(armorRaw, armorId, gunArmor);
types, });
ranges: sector.map((value) => ({
factor: value.factor,
distance: value.distance,
})),
};
}
if (maskEnabled) { tankDefinitions.tanks[tankId].turrets[turretIndex].guns.push({
const maskRaw = tankParameters.maskSlice![maskName]!; id: gunId,
mask = maskRaw.planePosition[1]; weight: gunListEntry.weight,
} else { rotation_speed: gunListEntry.rotationSpeed,
mask = undefined; name: gunName,
} tier: gunListEntry.level,
shells: [],
totalUnlocks.push(gun.unlocks); camouflage_loss:
Object.keys(gun.armor) typeof gun.invisibilityFactorAtShot === "number"
.filter((name) => name.startsWith("armor_")) ? gun.invisibilityFactorAtShot
.forEach((name) => { : gun.invisibilityFactorAtShot.at(-1)!,
const armorIdString = name.match(/armor_(\d+)/)?.[1]; aim_time: gun.aimingTime ?? gunListEntry.aimingTime,
if (armorIdString === undefined) { dispersion_base:
throw new SyntaxError(`Invalid armor id: ${name}`); gun.shotDispersionRadius ??
} gunListEntry.shotDispersionRadius,
const armorId = parseInt(armorIdString); dispersion_damaged: shotDispersionFactors.whileGunDamaged,
const armorRaw = gun.armor[name]; dispersion_shot: shotDispersionFactors.afterShot,
dispersion_traverse: shotDispersionFactors.turretRotation,
assignArmor(armorRaw, armorId, gunArmor); unlocks: resolveUnlocks(
}); this.blitzModuleTypeToBlitzkit,
gun.unlocks,
tankDefinitions.tanks[tankId].turrets[turretIndex].guns.push({ ),
id: gunId, shell_capacity: gun.maxAmmo ?? gunListEntry.maxAmmo,
weight: gunListEntry.weight, assault_ranges,
rotation_speed: gunListEntry.rotationSpeed, burst:
name: gunName, gun.burst && gun.burst.count > 1
tier: gunListEntry.level,
shells: [],
camouflage_loss:
typeof gun.invisibilityFactorAtShot === "number"
? gun.invisibilityFactorAtShot
: gun.invisibilityFactorAtShot.at(-1)!,
aim_time: gun.aimingTime ?? gunListEntry.aimingTime,
dispersion_base:
gun.shotDispersionRadius ?? gunListEntry.shotDispersionRadius,
dispersion_damaged: shotDispersionFactors.whileGunDamaged,
dispersion_shot: shotDispersionFactors.afterShot,
dispersion_traverse: shotDispersionFactors.turretRotation,
unlocks: resolveUnlocks(gun.unlocks),
shell_capacity: gun.maxAmmo ?? gunListEntry.maxAmmo,
assault_ranges,
burst:
gun.burst && gun.burst.count > 1
? {
count: gun.burst.count,
interval: 60 / gun.burst.rate,
}
: undefined,
gun_type:
gunType === "regular"
? {
$case: "regular",
value: {
reload: gun.reloadTime,
},
}
: gunType === "autoReloader"
? { ? {
$case: "auto_reloader", count: gun.burst.count,
interval: 60 / gun.burst.rate,
}
: undefined,
gun_type:
gunType === "regular"
? {
$case: "regular",
value: { value: {
intra_clip: 60 / gun.clip!.rate, reload: gun.reloadTime,
shell_count: gunClipCount,
shell_reloads: gun
.pumpGunReloadTimes!.split(" ")
.map(Number),
}, },
} }
: { : gunType === "autoReloader"
$case: "auto_loader", ? {
value: { $case: "auto_reloader",
intra_clip: 60 / gun.clip!.rate, value: {
clip_reload: gun.reloadTime, intra_clip: 60 / gun.clip!.rate,
shell_count: gunClipCount, shell_count: gunClipCount,
shell_reloads: gun
.pumpGunReloadTimes!.split(" ")
.map(Number),
},
}
: {
$case: "auto_loader",
value: {
intra_clip: 60 / gun.clip!.rate,
clip_reload: gun.reloadTime,
shell_count: gunClipCount,
},
}, },
}, } satisfies GunDefinition);
} satisfies GunDefinition);
Object.keys(gunListEntry.shots).forEach((shellKey) => { for (const shellKey in gunListEntry.shots) {
const gunShellEntry = gunListEntry.shots[shellKey]; {
const shell = shellList.root[shellKey]; const gunShellEntry = gunListEntry.shots[shellKey];
const shellId = toUniqueId(nation, shell.id); const shell = shellList.root[shellKey];
const shellName = getString(shell.userString); const shellId = toUniqueId(nation, shell.id);
const penetrationRaw = gunShellEntry.piercingPower const shellName = this.getString(shell.userString);
.split(" ") const penetrationRaw = gunShellEntry.piercingPower
.filter((penetrationString) => penetrationString !== "") .split(" ")
.map(Number); .filter((penetrationString) => penetrationString !== "")
.map(Number);
tankDefinitions.tanks[tankId].turrets[turretIndex].guns[ tankDefinitions.tanks[tankId].turrets[turretIndex].guns[
gunIndex gunIndex
].shells.push({ ].shells.push({
id: shellId, id: shellId,
name: shellName, name: shellName,
velocity: gunShellEntry.speed, velocity: gunShellEntry.speed,
armor_damage: shell.damage.armor, armor_damage: shell.damage.armor,
module_damage: shell.damage.devices, module_damage: shell.damage.devices,
caliber: shell.caliber, caliber: shell.caliber,
normalization: shell.normalizationAngle, normalization: shell.normalizationAngle,
ricochet: shell.ricochetAngle, ricochet: shell.ricochetAngle,
type: blitzShellKindToBlitzkit[shell.kind], type: this.blitzShellKindToBlitzkit[shell.kind],
explosion_radius: explosion_radius:
shell.kind === "HIGH_EXPLOSIVE" shell.kind === "HIGH_EXPLOSIVE"
? (shell.explosionRadius ?? 0) ? (shell.explosionRadius ?? 0)
: undefined, : undefined,
icon: shell.icon, icon: shell.icon,
penetration: { penetration: {
near: penetrationRaw[0], near: penetrationRaw[0],
far: penetrationRaw[1], far: penetrationRaw[1],
}, },
range: gunShellEntry.maxDistance, range: gunShellEntry.maxDistance,
}); });
}); }
}); }
}, }
);
totalUnlocks.forEach((unlocks) => { gunIndex++;
}
}
turretIndex++;
}
for (const unlocks of totalUnlocks) {
if (unlocks === undefined) return; if (unlocks === undefined) return;
Object.entries(unlocks).forEach(([key, value]) => { for (const key in unlocks) {
(Array.isArray(value) ? value : [value]).forEach((vehicle) => { const value = unlocks[key as keyof BlitzModuleType];
for (const vehicle of Array.isArray(value) ? value : [value]) {
switch (key as keyof BlitzModuleType) { switch (key as keyof BlitzModuleType) {
case "vehicle": { case "vehicle": {
const tankListEntry = tankList.root[vehicle["#text"]]; const tankListEntry = tankList.root[vehicle["#text"]];
const currentTank = tankDefinitions.tanks[tankId]; const currentTank = tankDefinitions.tanks[tankId];
const successorId = toUniqueId(nation, tankListEntry.id); const successorId = toUniqueId(nation, tankListEntry.id);
tankXps.set(successorId, parseResearchCost(vehicle.cost)); tankXps.set(
successorId,
this.parseResearchCost(vehicle.cost),
);
if (currentTank.successors === undefined) { if (currentTank.successors === undefined) {
currentTank.successors = []; currentTank.successors = [];
@@ -885,7 +952,7 @@ export class ServerBlitzKitAPI extends BlitzKitAPI {
case "gun": { case "gun": {
gunXps.set( gunXps.set(
toUniqueId(nation, gunList.root.ids[vehicle["#text"]]), toUniqueId(nation, gunList.root.ids[vehicle["#text"]]),
parseResearchCost(vehicle.cost), this.parseResearchCost(vehicle.cost),
); );
break; break;
} }
@@ -893,7 +960,7 @@ export class ServerBlitzKitAPI extends BlitzKitAPI {
case "turret": { case "turret": {
turretXps.set( turretXps.set(
toUniqueId(nation, turretList.root.ids[vehicle["#text"]]), toUniqueId(nation, turretList.root.ids[vehicle["#text"]]),
parseResearchCost(vehicle.cost), this.parseResearchCost(vehicle.cost),
); );
break; break;
} }
@@ -901,7 +968,7 @@ export class ServerBlitzKitAPI extends BlitzKitAPI {
case "engine": { case "engine": {
engineXps.set( engineXps.set(
toUniqueId(nation, enginesList.root.ids[vehicle["#text"]]), toUniqueId(nation, enginesList.root.ids[vehicle["#text"]]),
parseResearchCost(vehicle.cost), this.parseResearchCost(vehicle.cost),
); );
break; break;
} }
@@ -909,14 +976,14 @@ export class ServerBlitzKitAPI extends BlitzKitAPI {
case "chassis": { case "chassis": {
trackXps.set( trackXps.set(
toUniqueId(nation, chassisList.root.ids[vehicle["#text"]]), toUniqueId(nation, chassisList.root.ids[vehicle["#text"]]),
parseResearchCost(vehicle.cost), this.parseResearchCost(vehicle.cost),
); );
break; break;
} }
} }
}); }
}); }
}); }
Object.values(tankDefinitions.tanks[tankId].turrets).forEach( Object.values(tankDefinitions.tanks[tankId].turrets).forEach(
(turret) => { (turret) => {
@@ -974,7 +1041,7 @@ export class ServerBlitzKitAPI extends BlitzKitAPI {
async modelDefinitions() { async modelDefinitions() {
const modelDefinitions = ModelDefinitions.create(); const modelDefinitions = ModelDefinitions.create();
for (const nation of nationsDir) { for (const nation of this.nationsDir!) {
const tankList = await vfs.xml<{ root: VehicleDefinitionList }>( const tankList = await vfs.xml<{ root: VehicleDefinitionList }>(
`Data/XML/item_defs/vehicles/${nation}/list.xml`, `Data/XML/item_defs/vehicles/${nation}/list.xml`,
); );
@@ -15,7 +15,7 @@ export type ShellDefinitionsList = Record<
icons: Record<string, string>; icons: Record<string, string>;
}; };
type ShellKind = export type ShellKind =
| "ARMOR_PIERCING" | "ARMOR_PIERCING"
| "ARMOR_PIERCING_CR" | "ARMOR_PIERCING_CR"
| "HIGH_EXPLOSIVE" | "HIGH_EXPLOSIVE"
@@ -4,40 +4,18 @@ import {
Consumable, Consumable,
ConsumableDefinitions, ConsumableDefinitions,
ConsumableTankCategoryFilterCategory, ConsumableTankCategoryFilterCategory,
CrewType,
EquipmentDefinitions, EquipmentDefinitions,
EquipmentSlot, EquipmentSlot,
MapDefinitions, MapDefinitions,
ModelDefinitions, ModelDefinitions,
Provision, Provision,
ProvisionDefinitions, ProvisionDefinitions,
ResearchCost,
ShellType,
SkillDefinitions, SkillDefinitions,
TankClass,
TankDefinitions, TankDefinitions,
Vector3, Vector3,
} from "@blitzkit/core"; } from "@blitzkit/core";
import type { Vector3Tuple } from "three"; import type { Vector3Tuple } from "three";
function parseResearchCost(raw: number | string) {
if (typeof raw === "number") {
return {
research_cost_type: { $case: "xp", value: raw },
} satisfies ResearchCost;
} else {
return {
research_cost_type: {
$case: "seasonal_tokens",
value: {
season: Number(/prx_season_(\d+):\d+/.exec(raw)![1]),
tokens: Number(/prx_season_\d+:(\d+)/.exec(raw)![1]),
},
},
} satisfies ResearchCost;
}
}
type BlitzTankFilterDefinitionCategory = "clip"; type BlitzTankFilterDefinitionCategory = "clip";
const blitzTankFilterDefinitionCategoryToBlitzkit: Record< const blitzTankFilterDefinitionCategoryToBlitzkit: Record<
BlitzTankFilterDefinitionCategory, BlitzTankFilterDefinitionCategory,
@@ -48,18 +26,11 @@ const blitzTankFilterDefinitionCategoryToBlitzkit: Record<
function vector3TupleToBlitzkit(tuple: Vector3Tuple) { function vector3TupleToBlitzkit(tuple: Vector3Tuple) {
return { x: tuple[0], y: tuple[1], z: tuple[2] } satisfies Vector3; return { x: tuple[0], y: tuple[1], z: tuple[2] } satisfies Vector3;
} }
const blitzTankClassToBlitzkit: Record<BlitzTankClass, TankClass> = {
lightTank: TankClass.TANK_CLASS_LIGHT,
"AT-SPG": TankClass.TANK_CLASS_TANK_DESTROYER,
heavyTank: TankClass.TANK_CLASS_HEAVY,
mediumTank: TankClass.TANK_CLASS_MEDIUM,
};
export interface BlitzStrings { export interface BlitzStrings {
[key: string]: string; [key: string]: string;
} }
export interface ConsumablesCommon { export interface ConsumablesCommon {
[key: string]: { [key: string]: {
id: number; id: number;
@@ -116,13 +87,6 @@ type CombatRolesYaml = Record<
} }
>; >;
const blitzShellKindToBlitzkit: Record<ShellKind, ShellType> = {
ARMOR_PIERCING: ShellType.SHELL_TYPE_AP,
ARMOR_PIERCING_CR: ShellType.SHELL_TYPE_APCR,
HIGH_EXPLOSIVE: ShellType.SHELL_TYPE_HE,
HOLLOW_CHARGE: ShellType.SHELL_TYPE_HEAT,
};
export async function definitions() { export async function definitions() {
Object.values(tankDefinitions.tanks).forEach((tank) => { Object.values(tankDefinitions.tanks).forEach((tank) => {
tank.research_cost = tankXps.get(tank.id); tank.research_cost = tankXps.get(tank.id);