From b9388cc544e96a23e349ad2cab239db74bc9f3fb Mon Sep 17 00:00:00 2001 From: tresabhi <43380238+tresabhi@users.noreply.github.com> Date: Mon, 19 Oct 2026 19:08:25 -0500 Subject: [PATCH] add basic xi compatibility --- .../core/src/blitzkit/api/server/0_base.ts | 21 ++++++++++++++++++ .../blitzkit/api/server/1_tankDefinitions.ts | 22 ++++++++++++++++--- .../api/server/2_camouflageDefinitions.ts | 2 ++ .../api/server/6_consumableDefinitions.ts | 8 ++----- .../api/server/7_provisionDefinitions.ts | 10 +++------ packages/core/src/types/gunDefinitionsList.ts | 12 +++++----- .../core/src/types/shellDefinitionsList.ts | 3 +++ .../src/core/blitzkit/tankToDuelMember.ts | 17 +++++++++++--- .../[...locale]/tanks/[slug]/index.astro | 5 ++--- 9 files changed, 73 insertions(+), 27 deletions(-) diff --git a/packages/core/src/blitzkit/api/server/0_base.ts b/packages/core/src/blitzkit/api/server/0_base.ts index 2efff35ec..fd522a898 100644 --- a/packages/core/src/blitzkit/api/server/0_base.ts +++ b/packages/core/src/blitzkit/api/server/0_base.ts @@ -246,6 +246,27 @@ export abstract class ServerBlitzKitAPI0 extends BlitzKitAPI { return this; } + protected getTankFilterIds( + name: string, + definitionKey: string, + filter: "include" | "exclude", + ) { + const keys = name.trim().split(/\s+/).filter(Boolean); + + return keys.flatMap((key) => { + const id = this.tankStringIdMap[key]; + + if (id === undefined) { + console.warn( + `Skipping unavailable tank "${key}" in ${filter} vehicle filter for "${definitionKey}"`, + ); + return []; + } + + return [id]; + }); + } + protected getString(name: string) { const collection: Record = { [locales.default]: this.stringsI18n[locales.default][name], diff --git a/packages/core/src/blitzkit/api/server/1_tankDefinitions.ts b/packages/core/src/blitzkit/api/server/1_tankDefinitions.ts index 9e211001b..432672ee1 100644 --- a/packages/core/src/blitzkit/api/server/1_tankDefinitions.ts +++ b/packages/core/src/blitzkit/api/server/1_tankDefinitions.ts @@ -595,9 +595,25 @@ export abstract class ServerBlitzKitAPI1 extends ServerBlitzKitAPI0 { for (const shellKey in gunListEntry.shots) { const gunShellEntry = gunListEntry.shots[shellKey]; const shell = shellList.root[shellKey]; + + // Newer client data stores shot properties on the shell rather + // than repeating them under every gun's entry. + const speed = + typeof gunShellEntry === "string" + ? shell.speed + : gunShellEntry.speed; + const maxDistance = + typeof gunShellEntry === "string" + ? shell.maxDistance + : gunShellEntry.maxDistance; + const piercingPower = + typeof gunShellEntry === "string" + ? shell.piercingPower + : gunShellEntry.piercingPower; + const shellId = toUniqueId(nation, shell.id); const shellName = this.getString(shell.userString); - const penetrationRaw = gunShellEntry.piercingPower + const penetrationRaw = piercingPower .split(" ") .filter((penetrationString) => penetrationString !== "") .map(Number); @@ -607,7 +623,7 @@ export abstract class ServerBlitzKitAPI1 extends ServerBlitzKitAPI0 { ].shells.push({ id: shellId, name: shellName, - velocity: gunShellEntry.speed, + velocity: speed, armor_damage: shell.damage.armor, module_damage: shell.damage.devices, caliber: shell.caliber, @@ -623,7 +639,7 @@ export abstract class ServerBlitzKitAPI1 extends ServerBlitzKitAPI0 { near: penetrationRaw[0], far: penetrationRaw[1], }, - range: gunShellEntry.maxDistance, + range: maxDistance, }); } diff --git a/packages/core/src/blitzkit/api/server/2_camouflageDefinitions.ts b/packages/core/src/blitzkit/api/server/2_camouflageDefinitions.ts index 30ce31d6a..5c107bb9a 100644 --- a/packages/core/src/blitzkit/api/server/2_camouflageDefinitions.ts +++ b/packages/core/src/blitzkit/api/server/2_camouflageDefinitions.ts @@ -10,6 +10,8 @@ export abstract class ServerBlitzKitAPI2 extends ServerBlitzKitAPI1 { for (const camoKey in this.camouflagesXml!.root.camouflages) { const camo = this.camouflagesXml!.root.camouflages[camoKey]; + if (!(camoKey in this.camouflagesYaml!)) continue; + const yamlEntry = this.camouflagesYaml![camoKey]; const fullName = yamlEntry.userString ? this.getString(yamlEntry.userString) diff --git a/packages/core/src/blitzkit/api/server/6_consumableDefinitions.ts b/packages/core/src/blitzkit/api/server/6_consumableDefinitions.ts index f4b333f61..d12b7612d 100644 --- a/packages/core/src/blitzkit/api/server/6_consumableDefinitions.ts +++ b/packages/core/src/blitzkit/api/server/6_consumableDefinitions.ts @@ -44,9 +44,7 @@ export abstract class ServerBlitzKitAPI6 extends ServerBlitzKitAPI5 { filter_type: { $case: "ids", value: { - ids: includeRaw.name.split(/ +/).map((key) => { - return this.tankStringIdMap[key]; - }), + ids: this.getTankFilterIds(includeRaw.name, key, "include"), }, }, }); @@ -72,9 +70,7 @@ export abstract class ServerBlitzKitAPI6 extends ServerBlitzKitAPI5 { filter_type: { $case: "ids", value: { - ids: excludeRaw.name.split(/ +/).map((key) => { - return this.tankStringIdMap[key]; - }), + ids: this.getTankFilterIds(excludeRaw.name, key, "exclude"), }, }, }); diff --git a/packages/core/src/blitzkit/api/server/7_provisionDefinitions.ts b/packages/core/src/blitzkit/api/server/7_provisionDefinitions.ts index 32db2bcc1..d7baf6135 100644 --- a/packages/core/src/blitzkit/api/server/7_provisionDefinitions.ts +++ b/packages/core/src/blitzkit/api/server/7_provisionDefinitions.ts @@ -11,7 +11,7 @@ export abstract class ServerBlitzKitAPI7 extends ServerBlitzKitAPI6 { async provisionDefinitions() { const provisionDefinitions = ProvisionDefinitions.create(); - Object.entries(this.provisionsCommon).forEach(([, provision]) => { + Object.entries(this.provisionsCommon).forEach(([key, provision]) => { const entry: Provision = { id: provision.id, exclude: [], @@ -42,9 +42,7 @@ export abstract class ServerBlitzKitAPI7 extends ServerBlitzKitAPI6 { filter_type: { $case: "ids", value: { - ids: includeRaw.name.split(/ +/).map((key) => { - return this.tankStringIdMap[key]; - }), + ids: this.getTankFilterIds(includeRaw.name, key, "include"), }, }, }); @@ -70,9 +68,7 @@ export abstract class ServerBlitzKitAPI7 extends ServerBlitzKitAPI6 { filter_type: { $case: "ids", value: { - ids: excludeRaw.name - .split(/ +/) - .map((key) => this.tankStringIdMap[key]), + ids: this.getTankFilterIds(excludeRaw.name, key, "exclude"), }, }, }); diff --git a/packages/core/src/types/gunDefinitionsList.ts b/packages/core/src/types/gunDefinitionsList.ts index 005238d10..51b2c939d 100644 --- a/packages/core/src/types/gunDefinitionsList.ts +++ b/packages/core/src/types/gunDefinitionsList.ts @@ -20,11 +20,13 @@ export interface GunDefinitionsList { burst?: { count: number; rate: number }; clip?: { count: number; rate: number }; shots: { - [key: string]: { - speed: number; - piercingPower: string; - maxDistance: number; - }; + [key: string]: + | "" + | { + speed: number; + piercingPower: string; + maxDistance: number; + }; }; }; }; diff --git a/packages/core/src/types/shellDefinitionsList.ts b/packages/core/src/types/shellDefinitionsList.ts index d9e5f3e6e..e8ec812c1 100644 --- a/packages/core/src/types/shellDefinitionsList.ts +++ b/packages/core/src/types/shellDefinitionsList.ts @@ -10,6 +10,9 @@ export type ShellDefinitionsList = Record< normalizationAngle?: number; ricochetAngle?: number; explosionRadius?: number; + speed: number; + maxDistance?: number; + piercingPower: string; } > & { icons: Record; diff --git a/packages/website/src/core/blitzkit/tankToDuelMember.ts b/packages/website/src/core/blitzkit/tankToDuelMember.ts index 34f0aec50..b1045042e 100644 --- a/packages/website/src/core/blitzkit/tankToDuelMember.ts +++ b/packages/website/src/core/blitzkit/tankToDuelMember.ts @@ -7,12 +7,23 @@ import { import type { DuelMember } from "../../stores/duel"; import { genericDefaultEquipmentMatrix } from "../../stores/duel/constants"; +export function defaultTankWeapon(tank: TankDefinition) { + for (const turret of [...tank.turrets].reverse()) { + for (const gun of [...turret.guns].reverse()) { + const shell = gun.shells[0]; + + if (shell) return { turret, gun, shell }; + } + } + + throw new Error(`Tank ${tank.id} has no gun with shells`); +} + export function tankToDuelMember( tank: TankDefinition, provisionDefinitions: ProvisionDefinitions, ) { - const turret = tank.turrets.at(-1)!; - const gun = turret.guns.at(-1)!; + const { turret, gun, shell } = defaultTankWeapon(tank); return { camouflage: true, @@ -22,7 +33,7 @@ export function tankToDuelMember( provisions: createDefaultProvisions(tank, gun, provisionDefinitions), engine: tank.engines.at(-1)!, gun, - shell: tank.turrets.at(-1)!.guns.at(-1)!.shells[0], + shell, tank, track: tank.tracks.at(-1)!, turret, diff --git a/packages/website/src/pages/[...chunk_default]/[...locale]/tanks/[slug]/index.astro b/packages/website/src/pages/[...chunk_default]/[...locale]/tanks/[slug]/index.astro index 67727b72b..dcfde0699 100644 --- a/packages/website/src/pages/[...chunk_default]/[...locale]/tanks/[slug]/index.astro +++ b/packages/website/src/pages/[...chunk_default]/[...locale]/tanks/[slug]/index.astro @@ -13,6 +13,7 @@ import { load } from "cheerio"; import { api } from "../../../../../core/blitzkit/api"; import { mixStaticPaths } from "../../../../../core/blitzkit/mixStaticPaths"; import { tankCharacteristics } from "../../../../../core/blitzkit/tankCharacteristics"; +import { defaultTankWeapon } from "../../../../../core/blitzkit/tankToDuelMember"; import { getStrings } from "../../../../../core/i18n/getStrings"; import BaseLayout from "../../../../../layouts/BaseLayout.astro"; import { genericDefaultEquipmentMatrix } from "../../../../../stores/duel/constants"; @@ -53,9 +54,7 @@ const title = literals(strings.website.tools.tankopedia.tank_seo_title, { const url = import.meta.env.SITE + Astro.url.pathname; const icon = alias("api", `/tanks/${id}/icons/big.webp`); -const turret = tank.turrets.at(-1)!; -const gun = turret.guns.at(-1)!; -const shell = gun.shells[0]; +const { turret, gun, shell } = defaultTankWeapon(tank); const characteristics = tankCharacteristics( { tank,