From ed31f4087642b35baf24feaa891c203f563f846b Mon Sep 17 00:00:00 2001 From: tresabhi <43380238+tresabhi@users.noreply.github.com> Date: Sat, 1 Aug 2026 14:13:18 -0500 Subject: [PATCH] (5) some implementation --- packages/core/src/blitzkit/api/server.ts | 682 +++++++++++------- .../blitzTankFilterDefinitionCategory.ts | 1 + packages/core/src/types/index.ts | 1 + .../buildAssets/consumableProvisionIcons.ts | 2 +- .../scripts/src/buildAssets/definitions.ts | 335 --------- 5 files changed, 442 insertions(+), 579 deletions(-) create mode 100644 packages/core/src/types/blitzTankFilterDefinitionCategory.ts delete mode 100644 packages/scripts/src/buildAssets/definitions.ts diff --git a/packages/core/src/blitzkit/api/server.ts b/packages/core/src/blitzkit/api/server.ts index 074fe1fb4..ed2d7ad7a 100644 --- a/packages/core/src/blitzkit/api/server.ts +++ b/packages/core/src/blitzkit/api/server.ts @@ -6,13 +6,16 @@ import { BlitzModuleType, BlitzStrings, BlitzTankClass, + BlitzTankFilterDefinitionCategory, CamouflageDefinitions, CamouflagesXml, CamouflagesYaml, ChassisDefinitionsList, CombatRolesYaml, + Consumable, ConsumableDefinitions, ConsumablesCommon, + ConsumableTankCategoryFilterCategory, Crew, CrewType, EngineDefinitionsList, @@ -28,6 +31,7 @@ import { ModuleType, OptionalDevices, OptionalDeviceSlots, + Provision, ProvisionDefinitions, ProvisionsCommon, ResearchCost, @@ -108,6 +112,13 @@ export class ServerBlitzKitAPI extends BlitzKitAPI { HIGH_EXPLOSIVE: ShellType.SHELL_TYPE_HE, HOLLOW_CHARGE: ShellType.SHELL_TYPE_HEAT, }; + private blitzTankFilterDefinitionCategoryToBlitzkit: Record< + BlitzTankFilterDefinitionCategory, + ConsumableTankCategoryFilterCategory + > = { + clip: ConsumableTankCategoryFilterCategory.CONSUMABLE_TANK_CATEGORY_FILTER_CATEGORY_CLIP, + }; + private botPattern = /^.+((tutorial_bot(\d+)?)|(TU))$/; private stringsI18n: Record> = {}; @@ -165,6 +176,7 @@ export class ServerBlitzKitAPI extends BlitzKitAPI { `Data/XML/item_defs/vehicles/common/combat_roles.yaml`, ); this.tierEqualizer = await this.vfs.csv( + // spell:disable-next-line `Data/XML/item_defs/vehicles/common/tier_equializer.csv`, { delimiter: ";" }, ); @@ -490,40 +502,37 @@ export class ServerBlitzKitAPI extends BlitzKitAPI { for (const crewKey in tankDefinition.root.crew) { const value = tankDefinition.root.crew[crewKey as BlitzCrewType]; + let entry: Crew; + const index = crew.findIndex( + ({ type }) => this.blitzkitCrewTypeToBlitz[type] === crewKey, + ); + if (index === -1) { + if (crewKey === "#text") return; + entry = { + type: this.blitzCrewTypeToBlitzkit[crewKey as BlitzCrewType], + count: 0, + substitute: [], + }; + crew.push(entry); + } else { + entry = crew[index]; + } - { - let entry: Crew; - const index = crew.findIndex( - ({ type }) => this.blitzkitCrewTypeToBlitz[type] === crewKey, - ); - if (index === -1) { - if (crewKey === "#text") return; - entry = { - type: this.blitzCrewTypeToBlitzkit[crewKey as BlitzCrewType], - count: 0, - substitute: [], - }; - crew.push(entry); - } else { - entry = crew[index]; + if (typeof value === "string") { + entry.count++; + + if (value !== "") { + entry.substitute = value.split(/\n| /).map((member) => { + return this.blitzCrewTypeToBlitzkit[ + member.trim() as BlitzCrewType + ]; + }); } - - if (typeof value === "string") { - entry.count++; - - if (value !== "") { - entry.substitute = value.split(/\n| /).map((member) => { - return this.blitzCrewTypeToBlitzkit[ - member.trim() as BlitzCrewType - ]; - }); - } + } else { + if (entry.count === undefined) { + entry.count = value.length; } else { - if (entry.count === undefined) { - entry.count = value.length; - } else { - entry.count += value.length; - } + entry.count += value.length; } } } @@ -669,198 +678,169 @@ export class ServerBlitzKitAPI extends BlitzKitAPI { let turretIndex = 0; for (const turretKey in tankDefinition.root.turrets0) { - { - const turret = tankDefinition.root.turrets0[turretKey]; - const turretId = toUniqueId(nation, turretList.root.ids[turretKey]); + const turret = tankDefinition.root.turrets0[turretKey]; + const turretId = toUniqueId(nation, turretList.root.ids[turretKey]); - totalUnlocks.push(turret.unlocks); + totalUnlocks.push(turret.unlocks); - tankDefinitions.tanks[tankId].turrets.push({ - id: turretId, - traverse_speed: turret.rotationSpeed, - name: this.getString(turret.userString), - tier: turret.level, - guns: [], - health: turret.maxHealth, - view_range: turret.circularVisionRadius, - weight: turret.weight, - unlocks: resolveUnlocks( - this.blitzModuleTypeToBlitzkit, - turret.unlocks, - ), - }); + tankDefinitions.tanks[tankId].turrets.push({ + id: turretId, + traverse_speed: turret.rotationSpeed, + name: this.getString(turret.userString), + tier: turret.level, + guns: [], + health: turret.maxHealth, + view_range: turret.circularVisionRadius, + weight: turret.weight, + unlocks: resolveUnlocks( + this.blitzModuleTypeToBlitzkit, + turret.unlocks, + ), + }); - let gunIndex = 0; - for (const gunKey in turret.guns) { - { - const gun = turret.guns[gunKey]; - const gunId = toUniqueId(nation, gunList.root.ids[gunKey]); - const gunListEntry = gunList.root.shared[gunKey]; - const pitchLimitsRaw = - gun.pitchLimits ?? gunListEntry.pitchLimits; - const gunPitch = ( - typeof pitchLimitsRaw === "string" - ? pitchLimitsRaw - : pitchLimitsRaw.at(-1)! - ) - .split(" ") - .map(Number) as [number, number]; - 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; + let gunIndex = 0; + for (const gunKey in turret.guns) { + const gun = turret.guns[gunKey]; + const gunId = toUniqueId(nation, gunList.root.ids[gunKey]); + const gunListEntry = gunList.root.shared[gunKey]; + const gunName = this.getString(gunListEntry.userString); + const gunType = + "clip" in gun + ? gun.pumpGunMode + ? "autoReloader" + : "autoLoader" + : "regular"; + const gunClipCount = gunType === "regular" ? 1 : gun.clip!.count; + const shotDispersionFactors = + gun.shotDispersionFactors ?? gunListEntry.shotDispersionFactors; + let assault_ranges: AssaultRanges | undefined; - if (gun.extras?.trayShell) { - const types = gun.extras.trayShell.kinds - .split(" ") - .map((string) => { - const trimmed = string.trim(); + if (gun.extras?.trayShell) { + const types = gun.extras.trayShell.kinds + .split(" ") + .map((string) => { + const trimmed = string.trim(); - if (trimmed in this.blitzShellKindToBlitzkit) { - return this.blitzShellKindToBlitzkit[ - trimmed as ShellKind - ]; - } - - throw new SyntaxError(`Invalid shell kind: ${trimmed}`); - }); - const sectorNames = Object.keys(gun.extras.trayShell.sectors); - - if (sectorNames.length !== 1 || sectorNames[0] !== "sector") { - throw new SyntaxError("Invalid tray shell sector"); + if (trimmed in this.blitzShellKindToBlitzkit) { + return this.blitzShellKindToBlitzkit[trimmed as ShellKind]; } - const sector = gun.extras.trayShell.sectors.sector; + throw new SyntaxError(`Invalid shell kind: ${trimmed}`); + }); + const sectorNames = Object.keys(gun.extras.trayShell.sectors); - assault_ranges = { - types, - ranges: sector.map((value) => ({ - factor: value.factor, - distance: value.distance, - })), - }; - } - - totalUnlocks.push(gun.unlocks); - - tankDefinitions.tanks[tankId].turrets[turretIndex].guns.push({ - id: gunId, - weight: gunListEntry.weight, - rotation_speed: gunListEntry.rotationSpeed, - name: gunName, - 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( - this.blitzModuleTypeToBlitzkit, - 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", - value: { - intra_clip: 60 / gun.clip!.rate, - 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); - - for (const shellKey in gunListEntry.shots) { - { - const gunShellEntry = gunListEntry.shots[shellKey]; - const shell = shellList.root[shellKey]; - const shellId = toUniqueId(nation, shell.id); - const shellName = this.getString(shell.userString); - const penetrationRaw = gunShellEntry.piercingPower - .split(" ") - .filter((penetrationString) => penetrationString !== "") - .map(Number); - - tankDefinitions.tanks[tankId].turrets[turretIndex].guns[ - gunIndex - ].shells.push({ - id: shellId, - name: shellName, - velocity: gunShellEntry.speed, - armor_damage: shell.damage.armor, - module_damage: shell.damage.devices, - caliber: shell.caliber, - normalization: shell.normalizationAngle, - ricochet: shell.ricochetAngle, - type: this.blitzShellKindToBlitzkit[shell.kind], - explosion_radius: - shell.kind === "HIGH_EXPLOSIVE" - ? (shell.explosionRadius ?? 0) - : undefined, - icon: shell.icon, - penetration: { - near: penetrationRaw[0], - far: penetrationRaw[1], - }, - range: gunShellEntry.maxDistance, - }); - } - } + if (sectorNames.length !== 1 || sectorNames[0] !== "sector") { + throw new SyntaxError("Invalid tray shell sector"); } - gunIndex++; + const sector = gun.extras.trayShell.sectors.sector; + + assault_ranges = { + types, + ranges: sector.map((value) => ({ + factor: value.factor, + distance: value.distance, + })), + }; } + + totalUnlocks.push(gun.unlocks); + + tankDefinitions.tanks[tankId].turrets[turretIndex].guns.push({ + id: gunId, + weight: gunListEntry.weight, + rotation_speed: gunListEntry.rotationSpeed, + name: gunName, + 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( + this.blitzModuleTypeToBlitzkit, + 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", + value: { + intra_clip: 60 / gun.clip!.rate, + 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); + + for (const shellKey in gunListEntry.shots) { + const gunShellEntry = gunListEntry.shots[shellKey]; + const shell = shellList.root[shellKey]; + const shellId = toUniqueId(nation, shell.id); + const shellName = this.getString(shell.userString); + const penetrationRaw = gunShellEntry.piercingPower + .split(" ") + .filter((penetrationString) => penetrationString !== "") + .map(Number); + + tankDefinitions.tanks[tankId].turrets[turretIndex].guns[ + gunIndex + ].shells.push({ + id: shellId, + name: shellName, + velocity: gunShellEntry.speed, + armor_damage: shell.damage.armor, + module_damage: shell.damage.devices, + caliber: shell.caliber, + normalization: shell.normalizationAngle, + ricochet: shell.ricochetAngle, + type: this.blitzShellKindToBlitzkit[shell.kind], + explosion_radius: + shell.kind === "HIGH_EXPLOSIVE" + ? (shell.explosionRadius ?? 0) + : undefined, + icon: shell.icon, + penetration: { + near: penetrationRaw[0], + far: penetrationRaw[1], + }, + range: gunShellEntry.maxDistance, + }); + } + + gunIndex++; } turretIndex++; @@ -1076,40 +1056,37 @@ export class ServerBlitzKitAPI extends BlitzKitAPI { for (const crewKey in tankDefinition.root.crew) { const value = tankDefinition.root.crew[crewKey as BlitzCrewType]; + let entry: Crew; + const index = crew.findIndex( + ({ type }) => this.blitzkitCrewTypeToBlitz[type] === crewKey, + ); + if (index === -1) { + if (crewKey === "#text") return; + entry = { + type: this.blitzCrewTypeToBlitzkit[crewKey as BlitzCrewType], + count: 0, + substitute: [], + }; + crew.push(entry); + } else { + entry = crew[index]; + } - { - let entry: Crew; - const index = crew.findIndex( - ({ type }) => this.blitzkitCrewTypeToBlitz[type] === crewKey, - ); - if (index === -1) { - if (crewKey === "#text") return; - entry = { - type: this.blitzCrewTypeToBlitzkit[crewKey as BlitzCrewType], - count: 0, - substitute: [], - }; - crew.push(entry); - } else { - entry = crew[index]; + if (typeof value === "string") { + entry.count++; + + if (value !== "") { + entry.substitute = value.split(/\n| /).map((member) => { + return this.blitzCrewTypeToBlitzkit[ + member.trim() as BlitzCrewType + ]; + }); } - - if (typeof value === "string") { - entry.count++; - - if (value !== "") { - entry.substitute = value.split(/\n| /).map((member) => { - return this.blitzCrewTypeToBlitzkit[ - member.trim() as BlitzCrewType - ]; - }); - } + } else { + if (entry.count === undefined) { + entry.count = value.length; } else { - if (entry.count === undefined) { - entry.count = value.length; - } else { - entry.count += value.length; - } + entry.count += value.length; } } } @@ -1310,6 +1287,15 @@ export class ServerBlitzKitAPI extends BlitzKitAPI { async mapDefinitions() { const mapDefinitions = MapDefinitions.create(); + for (const key in this.mapsYaml!.maps) { + const map = this.mapsYaml!.maps[key]; + + mapDefinitions.maps[map.id] = { + id: map.id, + name: this.getString(`#maps:${key}:${map.localName}`), + }; + } + return mapDefinitions; } @@ -1352,18 +1338,228 @@ export class ServerBlitzKitAPI extends BlitzKitAPI { async consumableDefinitions() { const consumableDefinitions = ConsumableDefinitions.create(); + Object.entries(this.consumablesCommon).forEach(([key, consumable]) => { + const entry: Consumable = { + id: consumable.id, + game_mode_exclusive: "gameModeFilter" in consumable, + cooldown: consumable.script.cooldown, + duration: consumable.script.duration, + name: this.getString(consumable.userString), + exclude: [], + include: [], + }; + consumableDefinitions.consumables[consumable.id] = entry; + + const includeRaw = consumable.vehicleFilter?.include.vehicle; + const excludeRaw = consumable.vehicleFilter?.exclude?.vehicle; + + if (includeRaw) { + entry.include = []; + + if ("minLevel" in includeRaw) { + entry.include.push({ + filter_type: { + $case: "tiers", + value: { + min: includeRaw.minLevel, + max: includeRaw.maxLevel, + }, + }, + }); + } else if ("name" in includeRaw) { + entry.include.push({ + filter_type: { + $case: "ids", + value: { + ids: includeRaw.name.split(/ +/).map((key) => { + return this.tankStringIdMap[key]; + }), + }, + }, + }); + } else throw new SyntaxError("Unhandled include type"); + + if (consumable.vehicleFilter?.include.nations) { + entry.include.push({ + filter_type: { + $case: "nations", + value: { + nations: consumable.vehicleFilter.include.nations.split(" "), + }, + }, + }); + } + } + + if (excludeRaw) { + entry.exclude = []; + + if ("name" in excludeRaw) { + entry.exclude!.push({ + filter_type: { + $case: "ids", + value: { + ids: excludeRaw.name.split(/ +/).map((key) => { + return this.tankStringIdMap[key]; + }), + }, + }, + }); + } else if ("extendedTags" in excludeRaw) { + entry.exclude!.push({ + filter_type: { + $case: "categories", + value: { + categories: excludeRaw.extendedTags + .split(" ") + .map( + (item) => + this.blitzTankFilterDefinitionCategoryToBlitzkit[ + item as BlitzTankFilterDefinitionCategory + ], + ), + }, + }, + }); + } else throw new SyntaxError("Unhandled exclude type"); + + if (consumable.vehicleFilter?.exclude?.nations) { + entry.exclude!.push({ + filter_type: { + $case: "nations", + value: { + nations: consumable.vehicleFilter.exclude.nations.split(" "), + }, + }, + }); + } + } + }); + return consumableDefinitions; } async provisionDefinitions() { const provisionDefinitions = ProvisionDefinitions.create(); + Object.entries(this.provisionsCommon).forEach(([key, provision]) => { + const entry: Provision = { + id: provision.id, + exclude: [], + include: [], + game_mode_exclusive: "gameModeFilter" in provision, + name: this.getString(provision.userString), + }; + provisionDefinitions.provisions[provision.id] = entry; + + const includeRaw = provision.vehicleFilter?.include.vehicle; + const excludeRaw = provision.vehicleFilter?.exclude?.vehicle; + + if (includeRaw) { + entry.include = []; + + if ("minLevel" in includeRaw) { + entry.include.push({ + filter_type: { + $case: "tiers", + value: { + min: includeRaw.minLevel, + max: includeRaw.maxLevel, + }, + }, + }); + } else if ("name" in includeRaw) { + entry.include.push({ + filter_type: { + $case: "ids", + value: { + ids: includeRaw.name.split(/ +/).map((key) => { + return this.tankStringIdMap[key]; + }), + }, + }, + }); + } else throw new SyntaxError("Unhandled include type"); + + if (provision.vehicleFilter?.include.nations) { + entry.include.push({ + filter_type: { + $case: "nations", + value: { + nations: provision.vehicleFilter.include.nations.split(" "), + }, + }, + }); + } + } + + if (excludeRaw) { + entry.exclude = []; + + if ("name" in excludeRaw) { + entry.exclude!.push({ + filter_type: { + $case: "ids", + value: { + ids: excludeRaw.name + .split(/ +/) + .map((key) => this.tankStringIdMap[key]), + }, + }, + }); + } else if ("extendedTags" in excludeRaw) { + entry.exclude!.push({ + filter_type: { + $case: "categories", + value: { + categories: excludeRaw.extendedTags + .split(" ") + .map( + (item) => + this.blitzTankFilterDefinitionCategoryToBlitzkit[ + item as BlitzTankFilterDefinitionCategory + ], + ), + }, + }, + }); + } else throw new SyntaxError("Unhandled exclude type"); + + if (provision.vehicleFilter?.exclude?.nations) { + entry.exclude!.push({ + filter_type: { + $case: "nations", + value: { + nations: provision.vehicleFilter.exclude.nations.split(" "), + }, + }, + }); + } + } + + if (provision.script.bonusValues?.crewLevelIncrease !== undefined) { + provisionDefinitions.provisions[provision.id].crew = + provision.script.bonusValues?.crewLevelIncrease; + } + }); + return provisionDefinitions; } async skillDefinitions() { const skillDefinitions = SkillDefinitions.create(); + for (const tankClass in this.tankmenAvatar!.root.skillsByClasses) { + const skills = + this.tankmenAvatar!.root.skillsByClasses[ + Number(tankClass) as TankClass + ]; + + skillDefinitions.classes[ + this.blitzTankClassToBlitzkit[tankClass as BlitzTankClass] + ] = { skills: skills.split(" ") }; + } + return skillDefinitions; } } diff --git a/packages/core/src/types/blitzTankFilterDefinitionCategory.ts b/packages/core/src/types/blitzTankFilterDefinitionCategory.ts new file mode 100644 index 000000000..99620b0ec --- /dev/null +++ b/packages/core/src/types/blitzTankFilterDefinitionCategory.ts @@ -0,0 +1 @@ +export type BlitzTankFilterDefinitionCategory = "clip"; diff --git a/packages/core/src/types/index.ts b/packages/core/src/types/index.ts index 22bdfb454..a4038c044 100644 --- a/packages/core/src/types/index.ts +++ b/packages/core/src/types/index.ts @@ -1,6 +1,7 @@ export * from "./availableNationsYaml"; export * from "./blitzModuleType"; export * from "./blitzStrings"; +export * from "./blitzTankFilterDefinitionCategory"; export * from "./camouflagesXml"; export * from "./camouflagesYaml"; export * from "./chassisDefinitionsList"; diff --git a/packages/scripts/src/buildAssets/consumableProvisionIcons.ts b/packages/scripts/src/buildAssets/consumableProvisionIcons.ts index a9031bb24..b30fabdc9 100644 --- a/packages/scripts/src/buildAssets/consumableProvisionIcons.ts +++ b/packages/scripts/src/buildAssets/consumableProvisionIcons.ts @@ -1,6 +1,6 @@ import sharp from "sharp"; -import { AssetUploader } from "../core/github/assetUploader"; import { parsePackedSpriteRect } from "../core/blitz/parsePackedSpriteRect"; +import { AssetUploader } from "../core/github/assetUploader"; import { vfs } from "./constants"; import { ConsumablesCommon, ProvisionsCommon } from "./definitions"; diff --git a/packages/scripts/src/buildAssets/definitions.ts b/packages/scripts/src/buildAssets/definitions.ts deleted file mode 100644 index cf18760f4..000000000 --- a/packages/scripts/src/buildAssets/definitions.ts +++ /dev/null @@ -1,335 +0,0 @@ -import { - type BlitzTankClass, - CamouflageDefinitions, - Consumable, - ConsumableDefinitions, - ConsumableTankCategoryFilterCategory, - EquipmentDefinitions, - MapDefinitions, - ModelDefinitions, - Provision, - ProvisionDefinitions, - SkillDefinitions, - TankDefinitions, -} from "@blitzkit/core"; - -type BlitzTankFilterDefinitionCategory = "clip"; -const blitzTankFilterDefinitionCategoryToBlitzkit: Record< - BlitzTankFilterDefinitionCategory, - ConsumableTankCategoryFilterCategory -> = { - clip: ConsumableTankCategoryFilterCategory.CONSUMABLE_TANK_CATEGORY_FILTER_CATEGORY_CLIP, -}; - -export interface BlitzStrings { - [key: string]: string; -} - -export interface ConsumablesCommon { - [key: string]: { - id: number; - userString: string; - description: string; - icon: string; - category: string; - tags: string; - vehicleFilter?: { - include: { vehicle: ConsumablesVehicleFilter; nations?: string }; - exclude?: { vehicle: ConsumablesVehicleFilter; nations?: string }; - }; - script: { - "#text": string; - automatic?: boolean; - cooldown: number; - duration?: number; - shotEffect?: string; - bonusValues?: { [key: string]: number }; - } & Record; - }; -} -export interface ProvisionsCommon { - [key: string]: { - id: number; - userString: string; - description: string; - icon: string; - category: string; - tags: string; - vehicleFilter?: { - include: { vehicle: ConsumablesVehicleFilter; nations?: string }; - exclude?: { vehicle: ConsumablesVehicleFilter; nations?: string }; - }; - script: { - "#text": string; - automatic?: boolean; - shotEffect?: string; - bonusValues?: { [key: string]: number }; - } & Record; - }; -} - -type ConsumablesVehicleFilter = - | { minLevel: number; maxLevel: number } - | { name: string } - | { extendedTags: string }; - -type CombatRolesYaml = Record< - string, - { - id: number; - default_abilities: string[]; - } ->; - -export async function definitions() { - Object.entries(consumablesCommon).forEach(([key, consumable]) => { - const entry: Consumable = { - id: consumable.id, - game_mode_exclusive: "gameModeFilter" in consumable, - cooldown: consumable.script.cooldown, - duration: consumable.script.duration, - name: getString(consumable.userString), - exclude: [], - include: [], - }; - consumableDefinitions.consumables[consumable.id] = entry; - - const includeRaw = consumable.vehicleFilter?.include.vehicle; - const excludeRaw = consumable.vehicleFilter?.exclude?.vehicle; - - if (includeRaw) { - entry.include = []; - - if ("minLevel" in includeRaw) { - entry.include.push({ - filter_type: { - $case: "tiers", - value: { - min: includeRaw.minLevel, - max: includeRaw.maxLevel, - }, - }, - }); - } else if ("name" in includeRaw) { - entry.include.push({ - filter_type: { - $case: "ids", - value: { - ids: includeRaw.name.split(/ +/).map((key) => { - return tankStringIdMap[key]; - }), - }, - }, - }); - } else throw new SyntaxError("Unhandled include type"); - - if (consumable.vehicleFilter?.include.nations) { - entry.include.push({ - filter_type: { - $case: "nations", - value: { - nations: consumable.vehicleFilter.include.nations.split(" "), - }, - }, - }); - } - } - - if (excludeRaw) { - entry.exclude = []; - - if ("name" in excludeRaw) { - entry.exclude!.push({ - filter_type: { - $case: "ids", - value: { - ids: excludeRaw.name.split(/ +/).map((key) => { - return tankStringIdMap[key]; - }), - }, - }, - }); - } else if ("extendedTags" in excludeRaw) { - entry.exclude!.push({ - filter_type: { - $case: "categories", - value: { - categories: excludeRaw.extendedTags - .split(" ") - .map( - (item) => - blitzTankFilterDefinitionCategoryToBlitzkit[ - item as BlitzTankFilterDefinitionCategory - ], - ), - }, - }, - }); - } else throw new SyntaxError("Unhandled exclude type"); - - if (consumable.vehicleFilter?.exclude?.nations) { - entry.exclude!.push({ - filter_type: { - $case: "nations", - value: { - nations: consumable.vehicleFilter.exclude.nations.split(" "), - }, - }, - }); - } - } - }); - - Object.entries(provisionsCommon).forEach(([key, provision]) => { - const entry: Provision = { - id: provision.id, - exclude: [], - include: [], - game_mode_exclusive: "gameModeFilter" in provision, - name: getString(provision.userString), - }; - provisionDefinitions.provisions[provision.id] = entry; - - const includeRaw = provision.vehicleFilter?.include.vehicle; - const excludeRaw = provision.vehicleFilter?.exclude?.vehicle; - - if (includeRaw) { - entry.include = []; - - if ("minLevel" in includeRaw) { - entry.include.push({ - filter_type: { - $case: "tiers", - value: { - min: includeRaw.minLevel, - max: includeRaw.maxLevel, - }, - }, - }); - } else if ("name" in includeRaw) { - entry.include.push({ - filter_type: { - $case: "ids", - value: { - ids: includeRaw.name.split(/ +/).map((key) => { - return tankStringIdMap[key]; - }), - }, - }, - }); - } else throw new SyntaxError("Unhandled include type"); - - if (provision.vehicleFilter?.include.nations) { - entry.include.push({ - filter_type: { - $case: "nations", - value: { - nations: provision.vehicleFilter.include.nations.split(" "), - }, - }, - }); - } - } - - if (excludeRaw) { - entry.exclude = []; - - if ("name" in excludeRaw) { - entry.exclude!.push({ - filter_type: { - $case: "ids", - value: { - ids: excludeRaw.name - .split(/ +/) - .map((key) => tankStringIdMap[key]), - }, - }, - }); - } else if ("extendedTags" in excludeRaw) { - entry.exclude!.push({ - filter_type: { - $case: "categories", - value: { - categories: excludeRaw.extendedTags - .split(" ") - .map( - (item) => - blitzTankFilterDefinitionCategoryToBlitzkit[ - item as BlitzTankFilterDefinitionCategory - ], - ), - }, - }, - }); - } else throw new SyntaxError("Unhandled exclude type"); - - if (provision.vehicleFilter?.exclude?.nations) { - entry.exclude!.push({ - filter_type: { - $case: "nations", - value: { - nations: provision.vehicleFilter.exclude.nations.split(" "), - }, - }, - }); - } - } - - if (provision.script.bonusValues?.crewLevelIncrease !== undefined) { - provisionDefinitions.provisions[provision.id].crew = - provision.script.bonusValues?.crewLevelIncrease; - } - }); - - Object.entries(tankmenAvatar.root.skillsByClasses).forEach( - ([tankClass, skills]) => { - skillDefinitions.classes[ - blitzTankClassToBlitzkit[tankClass as BlitzTankClass] - ] = { - skills: skills.split(" "), - }; - }, - ); - - Object.entries(mapsYaml.maps).forEach(([key, map]) => { - mapDefinitions.maps[map.id] = { - id: map.id, - name: getString(`#maps:${key}:${map.localName}`), - }; - }); - - await uploader.add({ - content: TankDefinitions.encode(tankDefinitions).finish(), - path: "definitions/tanks.pb", - }); - await uploader.add({ - content: ModelDefinitions.encode(modelDefinitions).finish(), - path: "definitions/models.pb", - }); - await uploader.add({ - content: EquipmentDefinitions.encode(equipmentDefinitions).finish(), - path: "definitions/equipment.pb", - }); - await uploader.add({ - content: ConsumableDefinitions.encode(consumableDefinitions).finish(), - path: "definitions/consumables.pb", - }); - await uploader.add({ - content: ProvisionDefinitions.encode(provisionDefinitions).finish(), - path: "definitions/provisions.pb", - }); - await uploader.add({ - content: SkillDefinitions.encode(skillDefinitions).finish(), - path: "definitions/skills.pb", - }); - await uploader.add({ - content: MapDefinitions.encode(mapDefinitions).finish(), - path: "definitions/maps.pb", - }); - await uploader.add({ - content: CamouflageDefinitions.encode(camouflageDefinitions).finish(), - path: "definitions/camouflages.pb", - }); - - await uploader.flush(); -}