Merge branch 'main' into ue

This commit is contained in:
TrèsAbhi committed 2026-04-04 20:25:51 -05:00
commit c0376182f8
164 files changed
+5145 -4079

No files matched your search

+23 -20
View File
@@ -1,5 +1,6 @@
import {
AverageDefinitions,
AverageDefinitionsAllStats,
AverageDefinitionsEntry,
type AverageDefinitionsManifest,
type IndividualTankStats,
@@ -30,9 +31,6 @@ type OptionalSecondLevel<T> = {
};
};
type AverageDefinitionsEntrySubPartial =
OptionalSecondLevel<AverageDefinitionsEntry>;
const MINUTE = 60 * 1000;
const HOUR = 60 * MINUTE;
const DAY = 24 * HOUR;
@@ -47,15 +45,15 @@ const preDiscoveredIds = await fetchPreDiscoveredIds();
const playerIds: Record<Region, number[][]> = {
asia: chunk(
preDiscoveredIds.filter((id) => idToRegion(id) === "asia"),
PLAYER_IDS_PER_CALL
PLAYER_IDS_PER_CALL,
),
com: chunk(
preDiscoveredIds.filter((id) => idToRegion(id) === "com"),
PLAYER_IDS_PER_CALL
PLAYER_IDS_PER_CALL,
),
eu: chunk(
preDiscoveredIds.filter((id) => idToRegion(id) === "eu"),
PLAYER_IDS_PER_CALL
PLAYER_IDS_PER_CALL,
),
};
let regionIndex = 0;
@@ -71,7 +69,7 @@ times(THREADS, async () => {
const regionIds = playerIds[region];
const [ids] = regionIds.splice(
Math.floor(Math.random() * regionIds.length),
1
1,
);
const accountInfo = await getAccountInfo(region, ids, undefined, {
fields: "last_battle_time,statistics.all.battles",
@@ -104,8 +102,8 @@ times(THREADS, async () => {
filteredIds.map((id) =>
getTankStats(region, id, {
fields: "last_battle_time,battle_life_time,all,tank_id",
})
)
}),
),
);
players.forEach((tanks) => {
@@ -114,7 +112,7 @@ times(THREADS, async () => {
if (!tankIds.includes(tank.tank_id)) {
console.log(
`Found new tank: ${tank.tank_id} (${tankIds.length + 1})`
`Found new tank: ${tank.tank_id} (${tankIds.length + 1})`,
);
tankIds.push(tank.tank_id);
tanksSorted[tank.tank_id] = [];
@@ -140,7 +138,7 @@ async function postWork() {
console.log(
`Generating statistics based on ${samples.d_120.toLocaleString()} players (${samples.total.toLocaleString()} checked in total) and ${
tankIds.length
} tanks...`
} tanks...`,
);
using uploader = new AssetUploader("averages");
@@ -152,7 +150,12 @@ async function postWork() {
// (tank) => Date.now() - tank.last_battle_time * 1000 <= MAX_ACTIVITY_TIME,
// );
const tanks = tanksSortedEntry;
const entry: AverageDefinitionsEntrySubPartial = {
const entry: {
mu: Partial<Record<keyof AverageDefinitionsAllStats, number>>;
sigma: Partial<Record<keyof AverageDefinitionsAllStats, number>>;
r: Partial<Record<keyof AverageDefinitionsAllStats, number>>;
samples: Samples;
} = {
mu: {},
sigma: {},
r: {},
@@ -162,13 +165,13 @@ async function postWork() {
tanks.forEach((tank) => {
const timeSinceLastActivity = Date.now() - tank.last_battle_time * 1000;
entry.samples.total++;
if (timeSinceLastActivity <= 120 * DAY) entry.samples.d_120++;
if (timeSinceLastActivity <= 90 * DAY) entry.samples.d_90++;
if (timeSinceLastActivity <= 60 * DAY) entry.samples.d_60++;
if (timeSinceLastActivity <= 30 * DAY) entry.samples.d_30++;
if (timeSinceLastActivity <= 7 * DAY) entry.samples.d_7++;
if (timeSinceLastActivity <= 1 * DAY) entry.samples.d_1++;
entry.samples!.total++;
if (timeSinceLastActivity <= 120 * DAY) entry.samples!.d_120++;
if (timeSinceLastActivity <= 90 * DAY) entry.samples!.d_90++;
if (timeSinceLastActivity <= 60 * DAY) entry.samples!.d_60++;
if (timeSinceLastActivity <= 30 * DAY) entry.samples!.d_30++;
if (timeSinceLastActivity <= 7 * DAY) entry.samples!.d_7++;
if (timeSinceLastActivity <= 1 * DAY) entry.samples!.d_1++;
});
const dataWY = tanks.map((tank) => ({
@@ -191,7 +194,7 @@ async function postWork() {
function sum(slicer: (data: DataPoint) => number) {
return data.reduce(
(accumulator, data) => accumulator + slicer(data),
0
0,
);
}
@@ -6,13 +6,15 @@ export async function boosterIcons() {
console.log("Building booster icons...");
const uploader = new AssetUploader("booster icons");
const boosterFiles = vfs
const boosterFiles = await vfs
.dir("Data/Gfx/Shared/boosters")
.filter(
(file) => !file.includes("@2x.txt") && !file.startsWith("texture0")
.then((files) =>
files.filter(
(file) => !file.includes("@2x.txt") && !file.startsWith("texture0"),
),
);
const image = sharp(
await vfs.file(`Data/Gfx/Shared/boosters/texture0.packed.webp`)
await vfs.file(`Data/Gfx/Shared/boosters/texture0.packed.webp`),
);
for (const file of boosterFiles) {
@@ -6,7 +6,7 @@ export async function camouflageIcons() {
using uploader = new AssetUploader("camo icons");
const content = await vfs.file(
`Data/Gfx/UI/Hangar/IconCamouflage.packed.webp`
`Data/Gfx/UI/Hangar/IconCamouflage.packed.webp`,
);
await uploader.add({ content, path: "icons/camo.webp" });
@@ -1,10 +1,12 @@
import { assertSecret } from "@blitzkit/core";
import { AbstractVFS } from "../core/vfs/abstract";
import { LocalVFS } from "../core/vfs/local";
import { SteamVFS } from "../core/vfs/steam";
import { ZipVFS } from "../core/vfs/zip";
const steamPattern = /steam:(\d+)\/(\d+)/;
const zipPattern = /zip:(.+)/;
const localePattern = /local:"?(.+)"?/;
const source = Bun.argv
.find((arg) => arg.startsWith("--source="))
@@ -21,11 +23,14 @@ if (!source) {
assertSecret(import.meta.env.STEAM_USERNAME),
assertSecret(import.meta.env.STEAM_PASSWORD),
app,
depot
depot,
).init();
} else if (zipPattern.test(source)) {
const [, url] = source.match(zipPattern)!;
resolved = await new ZipVFS(url).init();
} else if (localePattern.test(source)) {
const [, path] = source.match(localePattern)!;
resolved = await new LocalVFS(path).init();
} else {
throw new Error("No valid source specified");
}
@@ -18,26 +18,20 @@ export async function consumableProvisionIcons() {
using uploader = new AssetUploader("consumable and provision icons");
const styles = [
"UI/Styles/Lobby/Inventory/InventoryNormalStyles.yaml",
"UI/Styles/Lobby/Inventory/Event/InventoryBigStyles.yaml",
"UI/Styles/Lobby/Inventory/Event/InventoryNormalStyles.yaml",
"UI/Styles/Lobby/Inventory/InventoryNormalStyles.yaml",
"UI/Screens3/Lobby/Inventory/Equipment/EquipmentItemImage.style.yaml",
"UI/Screens/Battle/Styles/BattleEquipmentStyles.yaml",
];
const styleSheets = await Promise.all(
styles.map(async (path) =>
Object.values(
(
await vfs.yaml<Mappings>(`Data/${path}`)
).StyleSheets
)
)
Object.values((await vfs.yaml<Mappings>(`Data/${path}`)).StyleSheets),
),
).then((array) => array.flat());
for (const match of (
await vfs.text(
`Data/XML/item_defs/vehicles/common/consumables/list.xml`
)
await vfs.text(`Data/XML/item_defs/vehicles/common/consumables/list.xml`)
).matchAll(listItemsPattern)) {
const consumablesCommon = await vfs.xml<{
root: ConsumablesCommon;
@@ -46,13 +40,13 @@ export async function consumableProvisionIcons() {
for (const consumable of Object.values(consumablesCommon.root)) {
const styleSheet = styleSheets.find((styleSheet) =>
styleSheet.selectors.some((selector) =>
selector.includes(`${consumable.icon} `)
)
selector.includes(`${consumable.icon} `),
),
);
if (!styleSheet) {
console.warn(
`No style sheet found for consumable ${consumable.icon}; skipping...`
`No style sheet found for consumable ${consumable.icon}; skipping...`,
);
continue;
}
@@ -62,10 +56,8 @@ export async function consumableProvisionIcons() {
.replace(".psd", "")
.replace(".txt", "");
if (vfs.resolve(`Data/${configPath}.packed.webp`)) {
const image = sharp(
await vfs.file(`Data/${configPath}.packed.webp`)
);
if (await vfs.resolve(`Data/${configPath}.packed.webp`)) {
const image = sharp(await vfs.file(`Data/${configPath}.packed.webp`));
const content = await image.trim({ threshold: 100 }).toBuffer();
await uploader.add({
@@ -78,8 +70,8 @@ export async function consumableProvisionIcons() {
`${`Data/${configPath}`
.split("/")
.slice(0, -1)
.join("/")}/texture0.packed.webp`
)
.join("/")}/texture0.packed.webp`,
),
);
const sizes = (await vfs.text(`Data/${configPath}.txt`))
.split("\n")[4]
@@ -104,9 +96,7 @@ export async function consumableProvisionIcons() {
}
for (const match of (
await vfs.text(
`Data/XML/item_defs/vehicles/common/provisions/list.xml`
)
await vfs.text(`Data/XML/item_defs/vehicles/common/provisions/list.xml`)
).matchAll(listItemsPattern)) {
const provisionsCommon = await vfs.xml<{
root: ProvisionsCommon;
@@ -115,13 +105,13 @@ export async function consumableProvisionIcons() {
for (const provision of Object.values(provisionsCommon.root)) {
const styleSheet = styleSheets.find((styleSheet) =>
styleSheet.selectors.some((selector) =>
selector.includes(`${provision.icon} `)
)
selector.includes(`${provision.icon} `),
),
);
if (!styleSheet) {
console.warn(
`No style sheet found for provision ${provision.icon}; skipping...`
`No style sheet found for provision ${provision.icon}; skipping...`,
);
continue;
}
@@ -131,10 +121,8 @@ export async function consumableProvisionIcons() {
.replace(".psd", "")
.replace(".txt", "");
if (vfs.resolve(`Data/${configPath}.packed.webp`)) {
const image = sharp(
await vfs.file(`Data/${configPath}.packed.webp`)
);
if (await vfs.resolve(`Data/${configPath}.packed.webp`)) {
const image = sharp(await vfs.file(`Data/${configPath}.packed.webp`));
const content = await image.trim({ threshold: 100 }).toBuffer();
await uploader.add({
@@ -147,8 +135,8 @@ export async function consumableProvisionIcons() {
`${`Data/${configPath}`
.split("/")
.slice(0, -1)
.join("/")}/texture0.packed.webp`
)
.join("/")}/texture0.packed.webp`,
),
);
const sizes = (await vfs.text(`Data/${configPath}.txt`))
.split("\n")[4]
@@ -20,7 +20,7 @@ export async function currencies() {
for (const [file, name] of ICONS) {
const content = await sharp(
await vfs.file(`Data/Gfx/Lobby/currency/${file}`)
await vfs.file(`Data/Gfx/Lobby/currency/${file}`),
)
.trim()
.toBuffer();
@@ -36,14 +36,14 @@ export async function currencies() {
.then((glossary) =>
Object.values(glossary)
.entries()
.filter(([key]) => /^prx_season_\d+$/.test(`${key}`))
.filter(([key]) => /^prx_season_\d+$/.test(`${key}`)),
);
for (const [key, item] of glossary) {
if (item.image_url === null) throw new Error(`No image_url for ${key}`);
const imageRaw = await fetch(item.image_url).then((response) =>
response.arrayBuffer()
response.arrayBuffer(),
);
const content = await sharp(imageRaw)
.trim({
+155 -122
View File
@@ -8,6 +8,7 @@ import {
ConsumableTankCategoryFilterCategory,
Crew,
CrewType,
Equalizer,
EquipmentDefinitions,
EquipmentSlot,
GameDefinitions,
@@ -32,6 +33,7 @@ import {
} from "@blitzkit/core";
import { SUPPORTED_LOCALE_BLITZ_MAP } from "@blitzkit/i18n";
import locales from "@blitzkit/i18n/locales.json";
import { readFile } from "fs/promises";
import { deburr } from "lodash-es";
import { parse as parsePath } from "path";
import type { Vector3Tuple } from "three";
@@ -81,16 +83,16 @@ const blitzTankFilterDefinitionCategoryToBlitzkit: Record<
BlitzTankFilterDefinitionCategory,
ConsumableTankCategoryFilterCategory
> = {
clip: ConsumableTankCategoryFilterCategory.CLIP,
clip: ConsumableTankCategoryFilterCategory.CONSUMABLE_TANK_CATEGORY_FILTER_CATEGORY_CLIP,
};
function vector3TupleToBlitzkit(tuple: Vector3Tuple) {
return { x: tuple[0], y: tuple[1], z: tuple[2] } satisfies Vector3;
}
const blitzTankClassToBlitzkit: Record<BlitzTankClass, TankClass> = {
lightTank: TankClass.LIGHT,
"AT-SPG": TankClass.TANK_DESTROYER,
heavyTank: TankClass.HEAVY,
mediumTank: TankClass.MEDIUM,
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 {
[key: string]: string;
@@ -135,7 +137,7 @@ export interface ChassisDefinitionsList {
}
type VehicleDefinitionArmor = Record<
string,
number | { vehicleDamageFactor: 0; "#text": number }
number | { vehicleDamageFactor: 0; "#text": number } | number[]
>;
interface UnlocksInner {
cost: number | string;
@@ -149,18 +151,18 @@ type BlitzModuleType = {
type UnlocksListing = BlitzModuleType | undefined;
type BlitzCrewType = "commander" | "radioman" | "gunner" | "driver" | "loader";
const blitzCrewTypeToBlitzkit: Record<BlitzCrewType, CrewType> = {
commander: CrewType.COMMANDER,
driver: CrewType.DRIVER,
gunner: CrewType.GUNNER,
loader: CrewType.LOADER,
radioman: CrewType.RADIOMAN,
commander: CrewType.CREW_TYPE_COMMANDER,
driver: CrewType.CREW_TYPE_DRIVER,
gunner: CrewType.CREW_TYPE_GUNNER,
loader: CrewType.CREW_TYPE_LOADER,
radioman: CrewType.CREW_TYPE_RADIOMAN,
};
const blitzkitCrewTypeToBlitz: Record<CrewType, BlitzCrewType> = {
[CrewType.COMMANDER]: "commander",
[CrewType.DRIVER]: "driver",
[CrewType.GUNNER]: "gunner",
[CrewType.LOADER]: "loader",
[CrewType.RADIOMAN]: "radioman",
[CrewType.CREW_TYPE_COMMANDER]: "commander",
[CrewType.CREW_TYPE_DRIVER]: "driver",
[CrewType.CREW_TYPE_GUNNER]: "gunner",
[CrewType.CREW_TYPE_LOADER]: "loader",
[CrewType.CREW_TYPE_RADIOMAN]: "radioman",
};
interface VehicleDefinitions {
invisibility: {
@@ -298,7 +300,7 @@ interface VehicleDefinitions {
type: "default";
enabled: false;
factorsModifiers: "";
}
},
];
};
};
@@ -517,20 +519,36 @@ type CombatRolesYaml = Record<
>;
const blitzShellKindToBlitzkit: Record<ShellKind, ShellType> = {
ARMOR_PIERCING: ShellType.AP,
ARMOR_PIERCING_CR: ShellType.APCR,
HIGH_EXPLOSIVE: ShellType.HE,
HOLLOW_CHARGE: ShellType.HEAT,
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 const botPattern = /^.+((tutorial_bot(\d+)?)|(TU))$/;
const blitzModuleTypeToBlitzkit: Record<keyof BlitzModuleType, ModuleType> = {
chassis: ModuleType.TRACKS,
engine: ModuleType.ENGINE,
gun: ModuleType.GUN,
turret: ModuleType.TURRET,
vehicle: ModuleType.VEHICLE,
chassis: ModuleType.MODULE_TYPE_TRACKS,
engine: ModuleType.MODULE_TYPE_ENGINE,
gun: ModuleType.MODULE_TYPE_GUN,
turret: ModuleType.MODULE_TYPE_TURRET,
vehicle: ModuleType.MODULE_TYPE_VEHICLE,
};
function assignArmor(
raw: VehicleDefinitionArmor[string],
id: number,
armor: Armor,
) {
if (typeof raw === "number") {
armor.thickness[id] = raw;
} else if (Array.isArray(raw)) {
armor.thickness[id] = raw.at(-1)!;
} else {
if (!armor.spaced) armor.spaced = [];
armor.thickness[id] = raw["#text"];
armor.spaced.push(id);
}
}
export async function definitions() {
console.log("Building definitions...");
@@ -555,18 +573,18 @@ export async function definitions() {
const provisionDefinitions: ProvisionDefinitions = { provisions: {} };
const skillDefinitions: SkillDefinitions = {
classes: {
[TankClass.LIGHT]: { skills: [] },
[TankClass.MEDIUM]: { skills: [] },
[TankClass.HEAVY]: { skills: [] },
[TankClass.TANK_DESTROYER]: { skills: [] },
[TankClass.TANK_CLASS_LIGHT]: { skills: [] },
[TankClass.TANK_CLASS_MEDIUM]: { skills: [] },
[TankClass.TANK_CLASS_HEAVY]: { skills: [] },
[TankClass.TANK_CLASS_TANK_DESTROYER]: { skills: [] },
},
};
const nations = vfs
const nations = await vfs
.dir("Data/XML/item_defs/vehicles")
.filter((nation) => nation !== "common");
.then((files) => files.filter((nation) => nation !== "common"));
const tankStringIdMap: Record<string, number> = {};
const optionalDevices = await vfs.xml<{ root: OptionalDevices }>(
"Data/XML/item_defs/vehicles/common/optional_devices.xml"
"Data/XML/item_defs/vehicles/common/optional_devices.xml",
);
const stringsI18n: Record<string, Record<string, string>> = {};
@@ -574,12 +592,12 @@ export async function definitions() {
locales.supported.map(async ({ locale }) => {
const blitzLocale = SUPPORTED_LOCALE_BLITZ_MAP[locale];
const cache = await fetch(
`https://stufficons.wgcdn.co/localizations/${blitzLocale}.yaml`
`https://stufficons.wgcdn.co/localizations/${blitzLocale}.yaml`,
)
.then((response) => response.text())
.then((string) => parseYaml(string) as BlitzStrings);
const preInstalled = await vfs.yaml<BlitzStrings>(
`Data/Strings/${blitzLocale}.yaml`
`Data/Strings/${blitzLocale}.yaml`,
);
stringsI18n[locale] = {
@@ -588,7 +606,7 @@ export async function definitions() {
};
console.log(`Loaded strings for ${locale}`);
})
}),
);
function getString(name: string) {
@@ -627,9 +645,9 @@ export async function definitions() {
consumables,
(
await vfs.xml<{ root: ConsumablesCommon }>(
`Data/XML/item_defs/vehicles/common/consumables/${match[1]}.xml`
`Data/XML/item_defs/vehicles/common/consumables/${match[1]}.xml`,
)
).root
).root,
);
}
@@ -642,39 +660,57 @@ export async function definitions() {
provisions,
(
await vfs.xml<{ root: ConsumablesCommon }>(
`Data/XML/item_defs/vehicles/common/provisions/${match[1]}.xml`
`Data/XML/item_defs/vehicles/common/provisions/${match[1]}.xml`,
)
).root
).root,
);
}
const avatar = await vfs.xml<{ root: Avatar }>(
`Data/XML/item_defs/tankmen/avatar.xml`
`Data/XML/item_defs/tankmen/avatar.xml`,
);
const maps = await vfs.yaml<Maps>(`Data/maps.yaml`);
const tankXps = new Map<number, ResearchCost>();
const camouflagesXml = await vfs.xml<{ root: CamouflagesXML }>(
`Data/XML/item_defs/vehicles/common/camouflages.xml`
`Data/XML/item_defs/vehicles/common/camouflages.xml`,
);
const camouflagesYaml = await vfs.yaml<CamouflagesYaml>(
`Data/camouflages.yaml`
`Data/camouflages.yaml`,
);
const camouflagesXmlEntries = Object.entries(camouflagesXml.root.camouflages);
const squadBattleTypeStyles = await vfs.yaml<SquadBattleTypeStyles>(
`Data/UI/Screens3/Lobby/Hangar/Squad/SquadBattleType.yaml`
`Data/UI/Screens3/Lobby/Hangar/Squad/SquadBattleType.yaml`,
);
const gameTypeSelectorStyles = await vfs.yaml<SquadBattleTypeStyles>(
`Data/UI/Screens/Lobby/Hangar/GameTypeSelector.yaml`
`Data/UI/Screens/Lobby/Hangar/GameTypeSelector.yaml`,
);
const gameModeNativeNames: Record<string, number> = {};
const combatRoles = await vfs.yaml<CombatRolesYaml>(
`Data/XML/item_defs/vehicles/common/combat_roles.yaml`
`Data/XML/item_defs/vehicles/common/combat_roles.yaml`,
);
const consumableNativeNames: Record<string, number> = {};
const provisionNativeNames: Record<string, number> = {};
let equalizerDefinitions: string[][];
if (
await vfs.resolve("Data/XML/item_defs/vehicles/common/tier_equializer.csv")
) {
equalizerDefinitions = await vfs.csv(
`Data/XML/item_defs/vehicles/common/tier_equializer.csv`,
{ delimiter: ";" },
);
} else {
const stubEqualizerDefinitions = await readFile("stub/equalizer.csv").then(
(buffer) => buffer.toString(),
);
equalizerDefinitions = await vfs._csv(stubEqualizerDefinitions, {
delimiter: ";",
});
}
for (const match of squadBattleTypeStyles.Prototypes[0].components.UIDataLocalBindingsComponent.data[1][2].matchAll(
/"(\d+)" -> "(battleType\/([a-zA-Z]+))"/g
/"(\d+)" -> "(battleType\/([a-zA-Z]+))"/g,
)) {
const id = Number(match[1]);
const name = getString(match[2]);
@@ -686,7 +722,7 @@ export async function definitions() {
}
for (const match of gameTypeSelectorStyles.Prototypes[0].components.UIDataLocalBindingsComponent.data[1][2].matchAll(
/eGameMode\.([a-zA-Z]+) -> "~res:\/Gfx\/UI\/Hangar\/GameTypes\/battle-type_([^"]+)"/g
/eGameMode\.([a-zA-Z]+) -> "~res:\/Gfx\/UI\/Hangar\/GameTypes\/battle-type_([^"]+)"/g,
)) {
Object.entries(gameModeNativeNames).forEach(([key, value]) => {
if (key.toLowerCase() === match[2].toLowerCase()) {
@@ -720,7 +756,7 @@ export async function definitions() {
for (const nation of nations) {
const tankList = await vfs.xml<{ root: VehicleDefinitionList }>(
`Data/XML/item_defs/vehicles/${nation}/list.xml`
`Data/XML/item_defs/vehicles/${nation}/list.xml`,
);
for (const tankKey in tankList.root) {
@@ -761,7 +797,7 @@ export async function definitions() {
console.warn(
`Multiple tanks share slug ${slug}: ${requesters
.map(({ key }) => key)
.join(", ")}`
.join(", ")}`,
);
if (requesters.length !== 2) {
@@ -791,7 +827,7 @@ export async function definitions() {
if (canonical === undefined) {
throw new Error(
"Two tutorial bots share the same slug? The world is truly broken."
"Two tutorial bots share the same slug? The world is truly broken.",
);
}
@@ -804,7 +840,7 @@ export async function definitions() {
for (const nation of nations) {
const tankList = await vfs.xml<{ root: VehicleDefinitionList }>(
`Data/XML/item_defs/vehicles/${nation}/list.xml`
`Data/XML/item_defs/vehicles/${nation}/list.xml`,
);
const turretList = await vfs.xml<{
root: TurretDefinitionsList;
@@ -857,7 +893,7 @@ export async function definitions() {
: Number(item.cost.split(":")[1]),
},
} satisfies Unlock;
})
}),
)
.flat();
}
@@ -872,10 +908,10 @@ export async function definitions() {
const tank = tankList.root[tankKey];
let tankPrice: TankPrice;
const tankDefinition = await vfs.xml<{ root: VehicleDefinitions }>(
`Data/XML/item_defs/vehicles/${nation}/${tankKey}.xml`
`Data/XML/item_defs/vehicles/${nation}/${tankKey}.xml`,
);
const tankParameters = await vfs.yaml<TankParameters>(
`Data/3d/Tanks/Parameters/${nation}/${tankKey}.yaml`
`Data/3d/Tanks/Parameters/${nation}/${tankKey}.yaml`,
);
const turretOrigin = tankDefinition.root.hull.turretPositions.turret
.split(" ")
@@ -890,23 +926,23 @@ export async function definitions() {
if (slug === undefined) {
throw new Error(
`Could not find slug for ${nation}/${tankKey} (${tankId})`
`Could not find slug for ${nation}/${tankKey} (${tankId})`,
);
}
if (tank.sellPrice) {
tankPrice = {
type: TankPriceType.GOLD,
type: TankPriceType.TANK_PRICE_TYPE_GOLD,
value: tank.sellPrice["#text"] * 2,
};
} else if (typeof tank.price === "number") {
tankPrice = {
type: TankPriceType.CREDITS,
type: TankPriceType.TANK_PRICE_TYPE_CREDITS,
value: tank.price,
};
} else {
tankPrice = {
type: TankPriceType.CREDITS,
type: TankPriceType.TANK_PRICE_TYPE_CREDITS,
value: tank.price["#text"] * 400,
};
}
@@ -923,13 +959,7 @@ export async function definitions() {
const armorId = parseInt(armorIdString);
const armorRaw = tankDefinition.root.hull.armor[name];
if (typeof armorRaw === "number") {
hullArmor.thickness[armorId] = armorRaw;
} else {
if (!hullArmor.spaced) hullArmor.spaced = [];
hullArmor.thickness[armorId] = armorRaw["#text"];
hullArmor.spaced.push(armorId);
}
assignArmor(armorRaw, armorId, hullArmor);
});
const crew: Crew[] = [];
const fixedCamouflage = tankTags.includes("eventCamouflage_user");
@@ -938,7 +968,7 @@ export async function definitions() {
Object.entries(tankDefinition.root.crew).forEach(([crewKey, value]) => {
let entry: Crew;
const index = crew.findIndex(
({ type }) => blitzkitCrewTypeToBlitz[type] === crewKey
({ type }) => blitzkitCrewTypeToBlitz[type] === crewKey,
);
if (index === -1) {
if (crewKey === "#text") return;
@@ -988,10 +1018,23 @@ export async function definitions() {
})
.map(([, camo]) => camo.id);
let equalizerEntry = equalizerDefinitions.find(
(line) => line[0] === `${nation}:${tankKey}`,
);
let equalizer: Equalizer | undefined;
if (equalizerEntry) {
const [health, penetration, module_health, damage, armor] =
equalizerEntry?.slice(1).map(Number);
equalizer = { health, penetration, module_health, damage, armor };
}
tankDefinitions.tanks[tankId] = {
ancestors: [],
successors: [],
id: tankId,
dev_name: tankKey,
roles: {},
camouflages: camouflages,
fixed_camouflage: fixedCamouflage,
@@ -1011,10 +1054,10 @@ export async function definitions() {
slug,
nation,
type: tankTags.includes("collectible")
? TankType.COLLECTOR
? TankType.TANK_TYPE_COLLECTOR
: (typeof tank.price === "number" ? false : "gold" in tank.price)
? TankType.PREMIUM
: TankType.RESEARCHABLE,
? TankType.TANK_TYPE_PREMIUM
: TankType.TANK_TYPE_RESEARCHABLE,
tier: tank.level,
class: blitzTankClassToBlitzkit[tankTags[0] as BlitzTankClass],
testing: tankTags.includes("testTank"),
@@ -1022,21 +1065,22 @@ export async function definitions() {
price: tankPrice,
camouflage_still: tankDefinition.root.invisibility.still,
camouflage_moving: tankDefinition.root.invisibility.moving,
camouflage_onFire: tankDefinition.root.invisibility.firePenalty,
camouflage_on_fire: tankDefinition.root.invisibility.firePenalty,
turrets: [],
engines: [],
tracks: [],
equalizer,
};
if (tank.combatRole) {
Object.entries(tank.combatRole).forEach(([gameMode, role]) => {
const id = Object.entries(gameModeNativeNames).find(
([key]) => key.toLowerCase() === gameMode.toLowerCase()
([key]) => key.toLowerCase() === gameMode.toLowerCase(),
)?.[1];
if (id === undefined) {
throw new Error(
`Unknown game mode in tank ${tankKey}: ${gameMode}`
`Unknown game mode in tank ${tankKey}: ${gameMode}`,
);
}
@@ -1118,7 +1162,7 @@ export async function definitions() {
(turretKey, turretIndex) => {
const turret = tankDefinition.root.turrets0[turretKey];
const turretModel = Number(
parsePath(turret.models.undamaged).name.split("_")[1]
parsePath(turret.models.undamaged).name.split("_")[1],
);
const turretId = toUniqueId(nation, turretList.root.ids[turretKey]);
const turretYaw = (
@@ -1150,13 +1194,7 @@ export async function definitions() {
const armorId = parseInt(armorIdString);
const armorRaw = turret.armor[name];
if (typeof armorRaw === "number") {
turretArmor.thickness[armorId] = armorRaw;
} else {
if (!turretArmor.spaced) turretArmor.spaced = [];
turretArmor.thickness[armorId] = armorRaw["#text"];
turretArmor.spaced.push(armorId);
}
assignArmor(armorRaw, armorId, turretArmor);
});
turret.userString;
@@ -1186,12 +1224,12 @@ export async function definitions() {
min: vector3TupleToBlitzkit(
tankParameters.collision[
parsePath(turret.hitTester.collisionModel).name.toLowerCase()
].bbox.min
].bbox.min,
),
max: vector3TupleToBlitzkit(
tankParameters.collision[
parsePath(turret.hitTester.collisionModel).name.toLowerCase()
].bbox.max
].bbox.max,
),
},
};
@@ -1209,7 +1247,7 @@ export async function definitions() {
.split(" ")
.map(Number) as [number, number];
const gunModel = Number(
parsePath(gun.models.undamaged).name.split("_")[1]
parsePath(gun.models.undamaged).name.split("_")[1],
);
const gunName = getString(gunListEntry.userString);
const gunType =
@@ -1287,13 +1325,8 @@ export async function definitions() {
}
const armorId = parseInt(armorIdString);
const armorRaw = gun.armor[name];
if (typeof armorRaw === "number") {
gunArmor.thickness[armorId] = armorRaw;
} else {
if (!gunArmor.spaced) gunArmor.spaced = [];
gunArmor.thickness[armorId] = armorRaw["#text"];
gunArmor.spaced.push(armorId);
}
assignArmor(armorRaw, armorId, gunArmor);
});
tankDefinitions.tanks[tankId].turrets[turretIndex].guns.push({
@@ -1332,24 +1365,24 @@ export async function definitions() {
},
}
: gunType === "autoReloader"
? {
$case: "auto_reloader",
value: {
intra_clip: 60 / gun.clip!.rate,
shell_count: gunClipCount,
shell_reloads: gun
.pumpGunReloadTimes!.split(" ")
.map(Number),
? {
$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,
},
},
}
: {
$case: "auto_loader",
value: {
intra_clip: 60 / gun.clip!.rate,
clip_reload: gun.reloadTime,
shell_count: gunClipCount,
},
},
} satisfies GunDefinition);
modelDefinitions.models[tankId].turrets[turretId].guns[gunId] = {
@@ -1360,8 +1393,8 @@ export async function definitions() {
gun.armor.gun === undefined
? 0
: typeof gun.armor.gun === "number"
? gun.armor.gun
: gun.armor.gun["#text"],
? gun.armor.gun
: gun.armor.gun["#text"],
pitch: {
min: gunPitch[0],
max: gunPitch[1],
@@ -1408,7 +1441,7 @@ export async function definitions() {
type: blitzShellKindToBlitzkit[shell.kind],
explosion_radius:
shell.kind === "HIGH_EXPLOSIVE"
? shell.explosionRadius ?? 0
? (shell.explosionRadius ?? 0)
: undefined,
icon: shell.icon,
penetration: {
@@ -1419,7 +1452,7 @@ export async function definitions() {
});
});
});
}
},
);
totalUnlocks.forEach((unlocks) => {
@@ -1447,7 +1480,7 @@ export async function definitions() {
case "gun": {
gunXps.set(
toUniqueId(nation, gunList.root.ids[vehicle["#text"]]),
parseResearchCost(vehicle.cost)
parseResearchCost(vehicle.cost),
);
break;
}
@@ -1455,7 +1488,7 @@ export async function definitions() {
case "turret": {
turretXps.set(
toUniqueId(nation, turretList.root.ids[vehicle["#text"]]),
parseResearchCost(vehicle.cost)
parseResearchCost(vehicle.cost),
);
break;
}
@@ -1463,7 +1496,7 @@ export async function definitions() {
case "engine": {
engineXps.set(
toUniqueId(nation, enginesList.root.ids[vehicle["#text"]]),
parseResearchCost(vehicle.cost)
parseResearchCost(vehicle.cost),
);
break;
}
@@ -1471,7 +1504,7 @@ export async function definitions() {
case "chassis": {
trackXps.set(
toUniqueId(nation, chassisList.root.ids[vehicle["#text"]]),
parseResearchCost(vehicle.cost)
parseResearchCost(vehicle.cost),
);
break;
}
@@ -1518,7 +1551,7 @@ export async function definitions() {
name: getString(optionalDeviceEntry.userString),
description: getString(optionalDeviceEntry.description),
};
}
},
);
Object.entries(optionalDeviceSlots.root.presets).forEach(
@@ -1537,7 +1570,7 @@ export async function definitions() {
})
.flat(),
};
}
},
);
Object.entries(consumables).forEach(([key, consumable]) => {
@@ -1620,7 +1653,7 @@ export async function definitions() {
(item) =>
blitzTankFilterDefinitionCategoryToBlitzkit[
item as BlitzTankFilterDefinitionCategory
]
],
),
},
},
@@ -1718,7 +1751,7 @@ export async function definitions() {
(item) =>
blitzTankFilterDefinitionCategoryToBlitzkit[
item as BlitzTankFilterDefinitionCategory
]
],
),
},
},
@@ -1749,11 +1782,11 @@ export async function definitions() {
value.default_abilities.forEach((ability) => {
if (ability in consumableNativeNames) {
gameDefinitions.roles[value.id].consumables.push(
consumableNativeNames[ability]
consumableNativeNames[ability],
);
} else if (ability in provisionNativeNames) {
gameDefinitions.roles[value.id].provisions.push(
provisionNativeNames[ability]
provisionNativeNames[ability],
);
} else throw new Error(`Unknown ability ${ability}`);
});
@@ -17,15 +17,15 @@ export async function equipmentIcons() {
using uploader = new AssetUploader("equipment icons");
const optionalDevices = await vfs.xml<{ root: OptionalDevices }>(
`Data/XML/item_defs/vehicles/common/optional_devices.xml`
`Data/XML/item_defs/vehicles/common/optional_devices.xml`,
);
const mappings = await vfs.yaml<Mappings>(
`Data/UI/Screens3/Lobby/Inventory/OptionalDevices/OptionalDevicesItemImage.style.yaml`
`Data/UI/Screens3/Lobby/Inventory/OptionalDevices/OptionalDevicesItemImage.style.yaml`,
);
const image = sharp(
await vfs.file(
`Data/Gfx/UI/InventoryIcons/Big/OptionalDevices/texture0.packed.webp`
)
`Data/Gfx/UI/InventoryIcons/Big/OptionalDevices/texture0.packed.webp`,
),
);
for (const optionalDeviceKey in optionalDevices.root) {
@@ -35,8 +35,8 @@ export async function equipmentIcons() {
const mapping = mappings.StyleSheets.find((mapping) =>
mapping.selectors.includes(
`.optional_device_item.${optionalDevice.icon} #Img`
)
`.optional_device_item.${optionalDevice.icon} #Img`,
),
);
if (!mapping) {
@@ -49,9 +49,7 @@ export async function equipmentIcons() {
if (configPath.startsWith("Gfx/Lobby")) {
const configPathWebp = configPath.replace(".txt", "");
const image = sharp(
await vfs.file(`Data/${configPathWebp}.packed.webp`)
);
const image = sharp(await vfs.file(`Data/${configPathWebp}.packed.webp`));
const content = await image.trim().toBuffer();
await uploader.add({
+24 -14
View File
@@ -6,20 +6,32 @@ export async function flags() {
console.log("Building flags...");
using uploader = new AssetUploader("flags");
const circleFiles = vfs.dir(`Data/Gfx/Lobby/flags`).filter(
(flag) =>
flag.startsWith("flag_profile-stat_") &&
!flag.endsWith("@2x.packed.webp")
const circleFiles = await vfs
.dir(`Data/Gfx/Lobby/flags`)
.then((files) =>
files.filter(
(flag) =>
flag.startsWith("flag_profile-stat_") &&
!flag.endsWith("@2x.packed.webp"),
),
);
const scratchedFiles = vfs.dir(`Data/Gfx/Lobby/flags`).filter(
const scratchedFiles = await vfs
.dir(`Data/Gfx/Lobby/flags`)
.then((files) =>
files.filter(
(flag) =>
flag.startsWith("flag_tutor-tank_") &&
!flag.endsWith("@2x.packed.webp")
)
const fadedFiles = vfs.dir(`Data/Gfx/Lobby/flags`).filter(
(flag) =>
flag.startsWith("flag_filter_") && flag.endsWith("@2x.packed.webp")
)
!flag.endsWith("@2x.packed.webp"),
),
);
const fadedFiles = await vfs
.dir(`Data/Gfx/Lobby/flags`)
.then((files) =>
files.filter(
(flag) =>
flag.startsWith("flag_filter_") && flag.endsWith("@2x.packed.webp"),
),
);
for (const flag of circleFiles) {
const image = sharp(await vfs.file(`Data/Gfx/Lobby/flags/${flag}`));
@@ -43,9 +55,7 @@ export async function flags() {
}
for (const flag of fadedFiles) {
const content = await sharp(
await vfs.file(`Data/Gfx/Lobby/flags/${flag}`)
)
const content = await sharp(await vfs.file(`Data/Gfx/Lobby/flags/${flag}`))
.trim({
threshold: 100,
background: { r: 0, g: 0, b: 0, alpha: 0 },
@@ -8,35 +8,33 @@ export async function gameModeBanners() {
using uploader = new AssetUploader("game mode banners");
const gameTypeSelectorStyles = await vfs.yaml<SquadBattleTypeStyles>(
`Data/UI/Screens/Lobby/Hangar/GameTypeSelector.yaml`
`Data/UI/Screens/Lobby/Hangar/GameTypeSelector.yaml`,
);
const squadBattleTypeStyles = await vfs.yaml<SquadBattleTypeStyles>(
`Data/UI/Screens3/Lobby/Hangar/Squad/SquadBattleType.yaml`
`Data/UI/Screens3/Lobby/Hangar/Squad/SquadBattleType.yaml`,
);
const bannerMatches: { name: string; path: string }[] = [];
for (const match of gameTypeSelectorStyles.Prototypes[0].components.UIDataLocalBindingsComponent.data[1][2].matchAll(
/eGameMode\.([a-zA-Z]+) -> "~res:([^"]+)"/g
/eGameMode\.([a-zA-Z]+) -> "~res:([^"]+)"/g,
)) {
bannerMatches.push({ name: match[1], path: match[2] });
}
for (const match of squadBattleTypeStyles.Prototypes[0].components.UIDataLocalBindingsComponent.data[1][2].matchAll(
/"(\d+)" -> "battleType\/([a-zA-Z]+)"/g
/"(\d+)" -> "battleType\/([a-zA-Z]+)"/g,
)) {
const id = Number(match[1]);
const name = match[2];
let path = bannerMatches.find(
(bannerMatch) => bannerMatch.name.toLowerCase() === name.toLowerCase()
(bannerMatch) => bannerMatch.name.toLowerCase() === name.toLowerCase(),
)?.path;
if (path === undefined) {
path = `/Gfx/UI/Hangar/GameTypes/battle-type_${name.toLowerCase()}`;
}
const content = await sharp(
await vfs.file(`Data${path}.packed.webp`)
)
const content = await sharp(await vfs.file(`Data${path}.packed.webp`))
.trim({ background: { r: 0, g: 0, b: 0, alpha: 0 } })
.toBuffer();
+6 -6
View File
@@ -15,7 +15,7 @@ export async function glossary() {
const glossary = await fetchGlossary(supported.blitz ?? supported.locale);
console.log(
`Found ${Object.keys(glossary).length} things for ${supported.locale}`
`Found ${Object.keys(glossary).length} things for ${supported.locale}`,
);
for (const key in glossary) {
@@ -30,7 +30,7 @@ export async function glossary() {
}
if (key in avatars) {
avatars[key].avatar.name.locales[supported.locale] =
avatars[key].avatar.name!.locales[supported.locale] =
glossaryEntry.title;
} else {
const extension = extname(glossaryEntry.image_url);
@@ -45,7 +45,7 @@ export async function glossary() {
};
}
}
})
}),
);
const gallery: Gallery = { avatars: [] };
@@ -53,12 +53,12 @@ export async function glossary() {
const bar = new ProgressBar(
`Fetching avatars of ${totalCount} things :bar`,
totalCount
totalCount,
);
await Promise.all(
Object.entries(avatars).map(async ([key, { url, avatar }]) => {
if (!(locales.default in avatars[key].avatar.name.locales)) {
if (!(locales.default in avatars[key].avatar.name!.locales)) {
console.warn(`Avatar ${key} has no ${locales.default} name`);
bar.tick();
@@ -77,7 +77,7 @@ export async function glossary() {
});
bar.tick();
})
}),
);
await uploader.add({
@@ -8,11 +8,13 @@ export async function migration() {
using uploader = new AssetUploader("migration map");
const map: Record<string, number> = {};
const nations = vfs.dir(`Data/XML/item_defs/vehicles`).filter((nation) => nation !== "common");
const nations = await vfs
.dir(`Data/XML/item_defs/vehicles`)
.then((files) => files.filter((nation) => nation !== "common"));
for (const nation of nations) {
const tankList = await vfs.xml<{ root: VehicleDefinitionList }>(
`Data/XML/item_defs/vehicles/${nation}/list.xml`
`Data/XML/item_defs/vehicles/${nation}/list.xml`,
);
for (const tankKey in tankList.root) {
@@ -6,15 +6,20 @@ export async function moduleIcons() {
console.log("Building module icons...");
using uploader = new AssetUploader("module icons");
const files = vfs.dir(`Data/Gfx/UI/ModulesTechTree`).filter(
(file) => !file.endsWith("@2x.packed.webp") && file.startsWith("vehicle")
)
const files = await vfs
.dir(`Data/Gfx/UI/ModulesTechTree`)
.then((files) =>
files.filter(
(file) =>
!file.endsWith("@2x.packed.webp") && file.startsWith("vehicle"),
),
);
for (const file of files) {
const nameRaw = file.match(/vehicle(.+)\.packed\.webp/)![1];
const name = nameRaw[0].toLowerCase() + nameRaw.slice(1);
const content = await sharp(
await vfs.file(`Data/Gfx/UI/ModulesTechTree/${file}`)
await vfs.file(`Data/Gfx/UI/ModulesTechTree/${file}`),
)
.trim()
.toBuffer();
@@ -17,7 +17,7 @@ export async function popularTanks() {
const analytics = google.analyticsdata({ version: "v1beta", auth });
const report = await analytics.properties.runReport({
property: `properties/${assertSecret(
import.meta.env.PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID
import.meta.env.PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID,
)}`,
requestBody: {
dateRanges: [{ startDate: "7daysAgo", endDate: "today" }],
@@ -47,13 +47,13 @@ export async function popularTanks() {
row.dimensionValues[0].value &&
row.dimensionValues[0].value !== "/tanks/" &&
row.metricValues &&
row.metricValues[0].value
row.metricValues[0].value,
)
.map((row) => ({
id: Number(
row.dimensionValues![0].value!.match(
/\/tools\/tankopedia\/(\d+)\/?/
)?.[1]
/\/tools\/tankopedia\/(\d+)\/?/,
)?.[1],
),
views: Number(row.metricValues![0].value!),
}))
@@ -8,19 +8,17 @@ export async function shellIcons() {
using uploader = new AssetUploader("shell icons");
const image = sharp(
await vfs.file(
`Data/Gfx/Shared/tank-supply/ammunition/big/texture0.packed.webp`
)
`Data/Gfx/Shared/tank-supply/ammunition/big/texture0.packed.webp`,
),
);
const files = vfs.dir(
`Data/Gfx/Shared/tank-supply/ammunition/big`
).filter((file) => file.endsWith("_l.txt"))
const files = await vfs
.dir(`Data/Gfx/Shared/tank-supply/ammunition/big`)
.then((files) => files.filter((file) => file.endsWith("_l.txt")));
for (const file of files) {
const name = file.match(/(.+)_l\.txt/)![1];
const sizes = (
await vfs.text(
`Data/Gfx/Shared/tank-supply/ammunition/big/${file}`
)
await vfs.text(`Data/Gfx/Shared/tank-supply/ammunition/big/${file}`)
)
.split("\n")[4]
.split(" ")
@@ -26,7 +26,7 @@ export async function skillIcons() {
console.log("Building skill icons...");
const avatar = await vfs.xml<{ root: Avatar }>(
`Data/XML/item_defs/tankmen/avatar.xml`
`Data/XML/item_defs/tankmen/avatar.xml`,
);
const uploader = new AssetUploader("skill icons");
@@ -10,11 +10,13 @@ export async function tankArmors() {
using uploader = new AssetUploader("tank armors");
const nodeIO = new NodeIO();
const nations = vfs.dir(`Data/XML/item_defs/vehicles`).filter((nation) => nation !== "common");
const nations = await vfs
.dir(`Data/XML/item_defs/vehicles`)
.then((files) => files.filter((nation) => nation !== "common"));
for (const nation of nations) {
const tanks = await vfs.xml<{ root: VehicleDefinitionList }>(
`Data/XML/item_defs/vehicles/${nation}/list.xml`
`Data/XML/item_defs/vehicles/${nation}/list.xml`,
);
for (const tankKey in tanks.root) {
@@ -41,11 +41,13 @@ export async function tankIcons() {
console.log("Building tank icons...");
using uploader = new AssetUploader("tank icons");
const nations = vfs.dir(`Data/XML/item_defs/vehicles`).filter((nation) => nation !== "common")
const nations = await vfs
.dir(`Data/XML/item_defs/vehicles`)
.then((files) => files.filter((nation) => nation !== "common"));
for (const nation of nations) {
const tanks = await vfs.xml<{ root: VehicleDefinitionList }>(
`Data/XML/item_defs/vehicles/${nation}/list.xml`
`Data/XML/item_defs/vehicles/${nation}/list.xml`,
);
for (const tankKey in tanks.root) {
@@ -57,7 +59,7 @@ export async function tankIcons() {
const id = (nationVehicleId << 8) + (NATION_IDS[nation] << 4) + 1;
const parameters = await vfs.yaml<TankParameters>(
`Data/3d/Tanks/Parameters/${nation}/${tankKey}.yaml`
`Data/3d/Tanks/Parameters/${nation}/${tankKey}.yaml`,
);
const smallPath = `Data/${parameters.resourcesPath.smallIconPath
.replace(/~res:\//, "")
@@ -12,9 +12,9 @@ export async function tankModels() {
using uploader = new AssetUploader("tank models");
const nodeIO = new NodeIO().registerExtensions(ALL_EXTENSIONS);
const nations = vfs
const nations = await vfs
.dir(`Data/XML/item_defs/vehicles`)
.filter((nation) => nation !== "common");
.then((files) => files.filter((nation) => nation !== "common"));
for (const nationIndex in nations) {
const nation = nations[nationIndex];
@@ -41,4 +41,4 @@ export async function tankModels() {
}
await uploader.flush();
}
}
@@ -170,6 +170,7 @@ export async function extractModel(vfs: AbstractVFS, path: string) {
break;
case "TransformComponent": {
component["tc.localTranslation"] = [0, 0, 0]; // Game when is loading model resets position to [0, 0, 0], so any set value is ignored.
node.setTranslation(component["tc.localTranslation"]);
node.setRotation(component["tc.localRotation"]);
node.setScale(component["tc.localScale"]);
@@ -3,7 +3,7 @@ import { vfs } from "../../../buildAssets/constants";
export async function readTexture(path: string) {
const ddsTexturePath = path.replace(".tex", ".dx11.dds");
const isDds = vfs.resolve(ddsTexturePath);
const isDds = await vfs.resolve(ddsTexturePath);
const resolvedTexturePath = isDds
? ddsTexturePath
: ddsTexturePath.replace(".dds", ".pvr");
+4 -4
View File
@@ -1,5 +1,5 @@
import { crc32 } from 'crc';
import { compressBlock } from 'lz4js';
import { crc32 } from "crc";
import { compressBlock } from "lz4js";
/**
* Thanks Maddoxkkm! Modernized heavily.
@@ -14,7 +14,7 @@ export function writeDVPL(buffer: Buffer) {
destination,
0,
source.length,
0,
[0],
);
let output = Buffer.from(destination);
@@ -58,7 +58,7 @@ function toDVPLFooter(
result.writeUInt32LE(compressedSize, 4);
result.writeUInt32LE(crc32, 8);
result.writeUInt32LE(type, 12);
result.write('DVPL', 16, 4, 'utf8');
result.write("DVPL", 16, 4, "utf8");
return result;
}
@@ -43,7 +43,7 @@ export class AssetUploader {
constructor(public message: string) {
[this.owner, this.repo] = assertSecret(
import.meta.env.PUBLIC_ASSET_REPO
import.meta.env.PUBLIC_ASSET_REPO,
).split("/");
this.branch = assertSecret(import.meta.env.PUBLIC_ASSET_BRANCH);
}
@@ -52,7 +52,7 @@ export class AssetUploader {
const blobPath = `${this.owner}/${this.repo}/${this.branch}/${change.path}`;
const response = await fetch(
`https://raw.githubusercontent.com/${blobPath}`,
{ headers: { "Accept-Encoding": "identity" } }
{ headers: { "Accept-Encoding": "identity" } },
);
let diff: { change: number; status: DiffStatus };
@@ -71,7 +71,7 @@ export class AssetUploader {
} else if (response.status === 200) {
if (
this.heuristicFormats.some((format) =>
change.path.endsWith(`.${format}`)
change.path.endsWith(`.${format}`),
)
) {
/**
@@ -94,7 +94,7 @@ export class AssetUploader {
}
} else {
throw new Error(
`Unexpected status code ${response.status} for ${change.path}`
`Unexpected status code ${response.status} for ${change.path}`,
);
}
@@ -104,7 +104,7 @@ export class AssetUploader {
console.log(
`${diff.status === DiffStatus.New ? "🟢" : "🟡"} (${
diff.change > 0 ? "+" : ""
}${diff.change.toLocaleString()}B) ${change.path}`
}${diff.change.toLocaleString()}B) ${change.path}`,
);
// flush BEFORE going over limit
@@ -160,8 +160,8 @@ export class AssetUploader {
await new Promise((resolve) =>
setTimeout(
resolve,
Math.max(0, this.time_between_blobs + (start - end))
)
Math.max(0, this.time_between_blobs + (start - end)),
),
);
blobs.push({ sha, path: change.path, mode: "100644", type: "blob" });
@@ -170,11 +170,11 @@ export class AssetUploader {
break;
} catch {
console.warn(
` failed ${change.path}; retrying in ${this.time_per_blob}ms...`
` failed ${change.path}; retrying in ${this.time_per_blob}ms...`,
);
await new Promise((resolve) =>
setTimeout(resolve, this.time_per_blob)
setTimeout(resolve, this.time_per_blob),
);
}
}
@@ -209,7 +209,7 @@ export class AssetUploader {
[Symbol.dispose]() {
if (this.changes.length > 0) {
throw new Error(
`${this.changes.length} uncommitted changes. Did you forget to flush?`
`${this.changes.length} uncommitted changes. Did you forget to flush?`,
);
}
}
+21 -26
View File
@@ -1,7 +1,8 @@
import { readDVPL } from "@blitzkit/core";
import { Options, parse as parseCSV } from "csv-parse/sync";
import { XMLParser } from "fast-xml-parser";
import { normalize } from "path/posix";
import { parse } from "yaml";
import { parse as parseYaml } from "yaml";
export abstract class AbstractVFS {
textDecoder = new TextDecoder();
@@ -11,24 +12,22 @@ export abstract class AbstractVFS {
abstract dispose(): void;
abstract has(path: string): boolean;
abstract has(path: string): Promise<boolean>;
abstract raw(path: string): Promise<Uint8Array>;
abstract paths(): string[];
resolve(path: string) {
async resolve(path: string) {
const normalized = normalize(path);
if (this.has(normalized)) return normalized;
if (await this.has(normalized)) return normalized;
const dvplPath = `${normalized}.dvpl`;
if (this.has(dvplPath)) return dvplPath;
if (await this.has(dvplPath)) return dvplPath;
return null;
}
assert(path: string) {
const resolved = this.resolve(path);
async assert(path: string) {
const resolved = await this.resolve(path);
if (resolved === null) throw new Error(`File not found: ${path}`);
@@ -36,7 +35,7 @@ export abstract class AbstractVFS {
}
async file(file: string) {
const resolved = this.assert(file);
const resolved = await this.assert(file);
const raw = await this.raw(resolved);
let buffer = raw;
@@ -47,21 +46,7 @@ export abstract class AbstractVFS {
return buffer;
}
dir(path: string) {
const parentSegments = path.split("/").length;
const children = new Set<string>();
for (const child of this.paths()) {
if (!child.startsWith(path) || child === path) continue;
const childSegments = child.split("/");
const nextSegment = childSegments[parentSegments];
children.add(nextSegment);
}
return Array.from(children);
}
abstract dir(path: string): Promise<string[]>;
async text(path: string) {
const file = await this.file(path);
@@ -70,7 +55,7 @@ export abstract class AbstractVFS {
async yaml<Type>(path: string) {
const file = await this.text(path);
return parse(file) as Type;
return parseYaml(file) as Type;
}
async xml<Type>(path: string) {
@@ -78,6 +63,16 @@ export abstract class AbstractVFS {
return this.xmlParser.parse(file) as Type;
}
async _csv(text: string, options: Options = {}) {
const parsed = parseCSV(text, options);
return parsed;
}
async csv(path: string, options: Options = {}) {
const text = await this.text(path);
return await this._csv(text, options);
}
[Symbol.dispose]() {
this.dispose();
}
+31
View File
@@ -0,0 +1,31 @@
import { readdir } from "node:fs/promises";
import { AbstractVFS } from "./abstract";
export class LocalVFS extends AbstractVFS {
constructor(private base: string) {
super();
}
async init() {
return this;
}
async has(path: string) {
const file = Bun.file(`${this.base}/${path}`);
return await file.exists();
}
async raw(path: string) {
const file = Bun.file(`${this.base}/${path}`);
const buffer = await file.arrayBuffer();
const array = new Uint8Array(buffer);
return array;
}
async dir(path: string) {
return await readdir(`${this.base}/${path}`);
}
dispose() {}
}
+18 -5
View File
@@ -38,7 +38,7 @@ export class SteamVFS extends AbstractVFS {
private username: string,
private password: string,
private app: number,
private depot: number
private depot: number,
) {
super();
}
@@ -57,13 +57,14 @@ export class SteamVFS extends AbstractVFS {
this.steam.getManifest(
this.app,
this.depot,
// @ts-expect-error
productInfo.apps[this.app].appinfo.depots[this.depot].manifests.public
.gid,
"public",
// @ts-expect-error
(_, response) => {
resolve(response);
}
},
);
});
@@ -75,7 +76,7 @@ export class SteamVFS extends AbstractVFS {
return this;
}
has(path: string) {
async has(path: string) {
return this.manifest.has(path);
}
@@ -88,8 +89,20 @@ export class SteamVFS extends AbstractVFS {
return new Uint8Array(downloaded.file);
}
paths() {
return Array.from(this.manifest.keys());
async dir(path: string) {
const parentSegments = path.split("/").length;
const children = new Set<string>();
for (const child of this.manifest.keys()) {
if (!child.startsWith(path) || child === path) continue;
const childSegments = child.split("/");
const nextSegment = childSegments[parentSegments];
children.add(nextSegment);
}
return Array.from(children);
}
dispose() {
+19 -4
View File
@@ -5,7 +5,10 @@ import { AbstractVFS } from "./abstract";
export class ZipVFS extends AbstractVFS {
entries = new Map<string, ZipEntry>();
constructor(private url: string, private root = "app/") {
constructor(
private url: string,
private root = "app/",
) {
super();
}
@@ -21,7 +24,7 @@ export class ZipVFS extends AbstractVFS {
return this;
}
has(path: string) {
async has(path: string) {
return this.entries.has(path);
}
@@ -30,8 +33,20 @@ export class ZipVFS extends AbstractVFS {
return new Uint8Array(arrayBuffer);
}
paths() {
return Array.from(this.entries.keys());
async dir(path: string) {
const parentSegments = path.split("/").length;
const children = new Set<string>();
for (const child of this.entries.keys()) {
if (!child.startsWith(path) || child === path) continue;
const childSegments = child.split("/");
const nextSegment = childSegments[parentSegments];
children.add(nextSegment);
}
return Array.from(children);
}
dispose() {}
+35 -30
View File
@@ -53,7 +53,7 @@ if (newTanks.length > 0) {
notes += "## New Tanks\n\n";
for (const tank of newTanks) {
notes += `### [${tank.name.locales.en}](${domain}/tanks/${tank.slug})\n`;
notes += `### [${tank.name!.locales.en}](${domain}/tanks/${tank.slug})\n`;
notes += `![](${tankIcon(tank.id)})\n\n`;
}
@@ -96,49 +96,49 @@ if (changedTanks.length > 0) {
const previewTankModel = previewModelDefinitions.models[preview.id];
const currentConsumables = Object.values(
currentConsumableDefinitions.consumables
currentConsumableDefinitions.consumables,
).filter((consumable) =>
checkConsumableProvisionInclusivity(consumable, current, currentGun)
checkConsumableProvisionInclusivity(consumable, current, currentGun),
);
const previewConsumables = Object.values(
previewConsumableDefinitions.consumables
previewConsumableDefinitions.consumables,
).filter((consumable) =>
checkConsumableProvisionInclusivity(consumable, preview, previewGun)
checkConsumableProvisionInclusivity(consumable, preview, previewGun),
);
const addedConsumables = previewConsumables.filter(
(previewConsumable) =>
!currentConsumables.some(
(currentConsumable) => currentConsumable.id === previewConsumable.id
)
(currentConsumable) => currentConsumable.id === previewConsumable.id,
),
);
const removedConsumables = currentConsumables.filter(
(currentConsumable) =>
!previewConsumables.some(
(previewConsumable) => previewConsumable.id === currentConsumable.id
)
(previewConsumable) => previewConsumable.id === currentConsumable.id,
),
);
const currentProvisions = availableProvisions(
current,
currentGun,
currentProvisionDefinitions
currentProvisionDefinitions,
);
const previewProvisions = availableProvisions(
preview,
previewGun,
previewProvisionDefinitions
previewProvisionDefinitions,
);
const addedProvisions = previewProvisions.filter(
(previewProvision) =>
!currentProvisions.some(
(currentProvision) => currentProvision.id === previewProvision.id
)
(currentProvision) => currentProvision.id === previewProvision.id,
),
);
const removedProvisions = currentProvisions.filter(
(currentProvision) =>
!previewProvisions.some(
(previewProvision) => previewProvision.id === currentProvision.id
)
(previewProvision) => previewProvision.id === currentProvision.id,
),
);
const currentEquipment = currentEquipmentDefinitions.presets[
@@ -155,19 +155,20 @@ if (changedTanks.length > 0) {
.filter(
(previewEquipment) =>
!currentEquipment.some(
(currentEquipment) => currentEquipment === previewEquipment
)
(currentEquipment) => currentEquipment === previewEquipment,
),
)
.map((id) => previewEquipmentDefinitions.equipments[id]);
const removedEquipment = currentEquipment
.filter(
(currentEquipment) =>
!previewEquipment.some(
(previewEquipment) => previewEquipment === currentEquipment
)
(previewEquipment) => previewEquipment === currentEquipment,
),
)
.map((id) => currentEquipmentDefinitions.equipments[id]);
const equalize = false;
const currentCharacteristics = tankCharacteristics(
{
tank: current,
@@ -184,7 +185,7 @@ if (changedTanks.length > 0) {
provisions: createDefaultProvisions(
current,
currentGun,
currentProvisionDefinitions
currentProvisionDefinitions,
),
equipmentMatrix: genericDefaultEquipmentMatrix,
stockEngine: current.engines[0],
@@ -193,12 +194,13 @@ if (changedTanks.length > 0) {
stockTurret: current.turrets[0],
track: currentTracks,
turret: currentTurret,
equalize,
},
{
equipmentDefinitions: currentEquipmentDefinitions,
provisionDefinitions: currentProvisionDefinitions,
tankModelDefinition: currentTankModel,
}
},
);
const previewCharacteristics = tankCharacteristics(
{
@@ -216,7 +218,7 @@ if (changedTanks.length > 0) {
provisions: createDefaultProvisions(
preview,
previewGun,
previewProvisionDefinitions
previewProvisionDefinitions,
),
equipmentMatrix: genericDefaultEquipmentMatrix,
stockEngine: preview.engines[0],
@@ -225,12 +227,13 @@ if (changedTanks.length > 0) {
stockTurret: preview.turrets[0],
track: previewTracks,
turret: previewTurret,
equalize,
},
{
equipmentDefinitions: previewEquipmentDefinitions,
provisionDefinitions: previewProvisionDefinitions,
tankModelDefinition: previewTankModel,
}
},
);
const changedCharacteristics: string[] = [];
@@ -244,17 +247,17 @@ if (changedTanks.length > 0) {
}
}
notes += `### [${preview.name.locales.en}](${domain}/tanks/${preview.slug})\n`;
notes += `### [${preview.name!.locales.en}](${domain}/tanks/${preview.slug})\n`;
notes += `![](${tankIcon(preview.id)})\n\n`;
if (addedConsumables.length + removedConsumables.length > 0) {
notes += `Consumables: `;
notes += [
...addedConsumables.map(
(consumable) => `🟢 ${consumable.name.locales.en}`
(consumable) => `🟢 ${consumable.name!.locales.en}`,
),
...removedConsumables.map(
(consumable) => `🔴 ${consumable.name.locales.en}`
(consumable) => `🔴 ${consumable.name!.locales.en}`,
),
].join(", ");
notes += `\n`;
@@ -264,10 +267,10 @@ if (changedTanks.length > 0) {
notes += `Provisions: `;
notes += [
...addedProvisions.map(
(provision) => `🟢 ${provision.name.locales.en}`
(provision) => `🟢 ${provision.name!.locales.en}`,
),
...removedProvisions.map(
(provision) => `🔴 ${provision.name.locales.en}`
(provision) => `🔴 ${provision.name!.locales.en}`,
),
].join(", ");
notes += `\n`;
@@ -276,9 +279,11 @@ if (changedTanks.length > 0) {
if (addedEquipment.length + removedEquipment.length > 0) {
notes += `-Equipment: `;
notes += [
...addedEquipment.map((equipment) => `🟢 ${equipment.name.locales.en}`),
...addedEquipment.map(
(equipment) => `🟢 ${equipment.name!.locales.en}`,
),
...removedEquipment.map(
(equipment) => `🔴 ${equipment.name.locales.en}`
(equipment) => `🔴 ${equipment.name!.locales.en}`,
),
].join(", ");
notes += `\n`;
+2 -2
View File
@@ -26,8 +26,8 @@ for (const location of TS_PROTO_EXECUTABLE_LOCATIONS) {
if (!tsProtoExecutableLocation) {
throw new Error(
`Could not find ts-proto executable. Checked locations: ${TS_PROTO_EXECUTABLE_LOCATIONS.join(
", "
)}`
", ",
)}`,
);
}
+10 -5
View File
@@ -5,6 +5,7 @@ import {
Video,
youtubers,
} from "@blitzkit/core";
import locales from "@blitzkit/i18n/locales.json";
import { google } from "googleapis";
import { cloneDeep, uniqBy } from "lodash-es";
import { AssetUploader } from "./core/github/assetUploader";
@@ -42,9 +43,11 @@ for (const tank of tanksSanitized) {
}
try {
const q = `World of Tanks Blitz ${tank.name!.locales[locales.default]}`;
const results = await youtube.search.list({
part: ["snippet"],
q: `World of Tanks Blitz ${tank.name}`,
q,
});
if (!results.data.items || results.data.items.length === 0) continue;
@@ -54,9 +57,9 @@ for (const tank of tanksSanitized) {
(item) =>
item.snippet?.channelId &&
item.id?.videoId &&
youtubers.some(({ id }) => id === item.snippet?.channelId)
youtubers.some(({ id }) => id === item.snippet?.channelId),
),
(item) => item.snippet!.channelId
(item) => item.snippet!.channelId,
);
reviews.reviews[tank.id] = {
@@ -66,11 +69,13 @@ for (const tank of tanksSanitized) {
({
id: item.id!.videoId!,
author: item.snippet!.channelId!,
} satisfies Video)
}) satisfies Video,
),
};
console.log(`${++done} done; found ${items.length} for ${tank.name.locales.en}`);
console.log(
`${++done} done; found ${items.length} for ${tank.name!.locales.en}`,
);
await new Promise((resolve) => setTimeout(resolve, 1000 / 5));
} catch (error) {
+619
View File
@@ -0,0 +1,619 @@
name;maxHealthFactor;piercingPowerFactor;moduleHealthFactor;damageFactor;armorFactor
china:Ch01_Type59;1.3;1.33;1.5;1.29;1.35
china:Ch02_Type62;1.5;1.6;1.5;1.27;1.61
china:Ch03_WZ-111;1.4;1.17;1.4;1.3;1.35
china:Ch04_T34_1;1.5;1.54;1.4;1.3;1.61
china:Ch05_T34_2;1.3;1.33;1.5;1.29;1.34
china:Ch07_Vickers_MkE_Type_BT26;5.5;5.07;3.3;7.05;8.8
china:Ch08_Type97_Chi_Ha;3.7;3.71;2.2;3.13;5.56
china:Ch09_M5;2.9;2.91;1.9;2.54;3.47
china:Ch10_IS2;1.6;1.44;1.4;1.33;1.61
china:Ch11_110;1.4;1.17;1.4;1.29;1.35
china:Ch12_111_1_2_3;1.2;1.02;1.4;1.17;1.14
china:Ch14_T34_3;1.3;1.37;1.5;1.13;1.17
china:Ch15_59_16;1.9;1.77;1.6;1.57;2.24
china:Ch15_59_16_BP;1.9;1.7;1.6;1.56;2.24
china:Ch16_WZ_131;1.6;1.52;1.5;1.28;1.61
china:Ch17_WZ131_1_WZ132;1.4;1.22;1.5;1.29;1.34
china:Ch18_WZ-120;1.1;1.09;1.4;1.11;1.17
china:Ch20_Type58;1.9;1.88;1.5;1.57;1.86
china:Ch21_T34;2.7;2.1;1.7;1.97;2.12
china:Ch23_112;1.4;1.17;1.4;1.31;1.35
china:Ch24_Type64;1.9;1.86;1.6;1.57;2.35
china:Ch26_59_Patton;1.3;1.33;1.5;1.1;1.16
china:Ch28_WZ_132A;1.1;1.04;1.5;1.11;1.24
china:Ch34_WZ-131G_FT;2.1;1.57;1.4;1.51;1.73
china:Ch34_WZ-131G_FT_BP;2.1;1.57;1.4;1.51;2.02
china:Ch35_T-34-2G_FT;1.8;1.48;1.4;1.25;1.61
china:Ch36_WZ-111-1G_FT;1.6;1.16;1.4;1.25;1.35
china:Ch37_WZ-111G_FT;1.2;1.05;1.4;1.15;1.13
china:Ch39_WZ120_1G_FT;1.6;1.16;1.4;1.24;1.35
china:Ch40_WZ120G_FT;1.1;1.05;1.4;1.08;1.13
china:Ch42_M41D;1.7;1.49;1.5;1.24;1.61
china:Ch43_WZ-122_2;1.3;1.21;1.5;1.31;1.35
china:Ch45_WZ_114;1.2;1.02;1.4;1.19;1.13
china:Ch47_BZ_176;1.2;1.03;1.4;1.17;1.13
china:Ch49_BZ_68;1.2;1.02;1.4;1.2;1.16
china:Ch50_BZ_166;1.4;1.17;1.4;1.3;1.35
china:Ch51_BZ_58;1.6;1.46;1.4;1.29;1.62
china:Ch56_BZ_74_1;1.2;1.02;1.4;1.16;1.14
china:Ch59_BZ58_2;1.2;1.02;1.4;1.19;1.13
china:Ch60_Type59_spc;1.3;1.37;1.5;1.32;1.35
china:Ch_LT_vz38;4.3;4.29;2.7;3.99;4.88
china:Ch_WZ-112v2;1.4;1.17;1.4;1.31;1.35
china:Type_58BP;1.9;1.88;1.5;1.57;2.04
european:Cz02_T50;1.1;1.03;1.4;1.12;1.19
european:Cz05_T34-100;1.5;1.45;1.4;1.29;1.61
european:Cz07_TVP_46;1.3;1.12;1.5;1.29;1.35
european:Cz08_T-25;1.9;1.83;1.5;1.56;2.02
european:Cz08_T-25BP;1.9;1.83;1.5;1.55;2.02
european:Cz08_T-25BPS;1.9;1.83;1.5;1.57;2.02
european:Cz13_T-27;1.1;1.07;1.4;1.12;1.19
european:Cz14_Skoda_T-56;1.4;1.17;1.4;1.33;1.35
european:Cz15_Skoda_T-45;1.7;1.44;1.4;1.27;1.61
european:Cz16_TNH_T_vz51;1.2;1.02;1.4;1.17;1.15
european:Cz18_TNH_105_1000;1.4;1.16;1.4;1.28;1.35
european:Cz19_Vz_44_1;1.7;1.43;1.4;1.3;1.61
european:Cz20_ShPTK_TVP_100;1.5;1.16;1.4;1.24;1.34
european:Cz29_Skoda_P-JS;2.2;1.67;1.4;1.56;1.78
european:Cz29_Skoda_P-JS_BP;2.2;1.67;1.4;1.57;1.79
european:F01_Vickers_MkF;5.5;5.07;3.3;6.16;8.8
european:It09_P43_ter;1.5;1.54;1.4;1.3;1.61
european:It10_P43_BD;1.9;1.74;1.5;1.57;1.82
european:It10_P43_bis;1.9;1.74;1.5;1.57;1.82
european:It10_P43_bis_BP;1.9;1.7;1.5;1.58;1.82
european:It116_Carro_d_assalto_P88;1.6;1.33;1.4;1.28;1.61
european:It117_ProgettoCC55mod54;1.3;1.13;1.4;1.29;1.35
european:It119_ProgettoC50mod66;1.1;1.02;1.4;1.18;1.13
european:It12_Prototipo_Standard_B;1.1;1.04;1.4;1.14;1.17
european:It13_Progetto_M35_mod_46;1.3;1.33;1.5;1.27;1.35
european:It14_P44_Pantera;1.3;1.21;1.5;1.26;1.35
european:It15_Semovente_M41;3.2;1.98;1.5;1.97;2.13
european:It16_Bassoto;2;1.59;1.4;1.53;1.8
european:It17_Semovente_mod_56;1.7;1.4;1.4;1.28;1.62
european:It18_Semovente_mod_64;1.4;1.2;1.4;1.24;1.35
european:It19_Controcarro_1MK2_Ciclope;1.1;1.05;1.4;1.19;1.13
european:It22_Progetto_C45_mod_71;1.3;1.14;1.4;1.23;1.35
european:It30_CC_mod_64_Prem;1.4;1.17;1.4;1.25;1.35
european:Pl03_PzV_Pudel;1.9;1.57;1.5;1.56;1.92
european:Pl05_50TP_Tyszkiewicza;1.2;1.02;1.4;1.18;1.13
european:Pl06_10TP;4.3;4.29;2.7;4.28;7.66
european:Pl07_14TP;3.6;3.57;2.1;3.12;5.56
european:Pl08_50TP_prot;1.2;1.02;1.4;1.17;1.14
european:Pl10_40TP_Habicha;2.1;1.64;1.4;1.57;1.83
european:Pl119_CS_52_LIS;1.5;1.56;1.4;1.31;1.61
european:Pl11_45TP_Habicha;1.6;1.44;1.4;1.3;1.61
european:Pl13_53TP_Markowskiego;1.4;1.14;1.4;1.32;1.35
european:Pl17_DS_PZlnz;2.6;2.28;1.7;1.95;2.12
european:Pl18_BUGI;1.9;1.57;1.5;1.58;1.94
european:Pl20_CS_44;1.5;1.54;1.4;1.3;1.61
european:Pl22_CS_59;1.1;1.04;1.4;1.1;1.21
european:Pl23_CS_53;1.3;1.31;1.5;1.29;1.35
european:Pl24_56TP;1.4;1.15;1.4;1.29;1.35
european:Pl27_SDP_Wz_66T;1.5;1.17;1.4;1.27;1.35
european:Pl28_SDP_Wz_60;1.1;1.05;1.4;1.11;1.12
european:Pl30_SDP_Wz58T;1.4;1.16;1.4;1.26;1.35
european:Pl31_SDP_Wz_57D;1.7;1.43;1.4;1.28;1.62
european:Pl32_SDP_Wz_44;2;1.57;1.4;1.52;1.76
european:Pl33_SDP_Wz_40;3.2;2.13;1.5;1.96;2.13
european:S02_StrvM42;2.7;2.22;1.7;1.95;2.11
european:S04_Lago-I;3.1;2.69;1.9;2.54;2.32
european:S07_Strv74BP;1.9;1.67;1.5;1.55;1.9
european:S07_Strv_74;1.9;1.67;1.5;1.55;1.9
european:S13_Leo;1.5;1.58;1.4;1.3;1.61
european:S13_Leo_NY;1.5;1.58;1.4;1.3;1.61
european:S17_EMIL_1952E2;1.2;1.02;1.4;1.16;1.12
european:S18_EMIL_1951E1;1.3;1.16;1.4;1.29;1.35
european:S23_Strv_81;1.3;1.14;1.5;1.26;1.35
european:S25_EMIL_51;1.3;1.12;1.4;1.29;1.35
european:S26_Lancen_C;1.3;1.22;1.5;1.31;1.35
european:S32_Bofors_Tornvagn;1.1;1.02;1.4;1.17;1.12
france:AMX38;4.4;4.29;2.7;4.15;5.95
france:AMX40;3;2.88;1.9;2.52;3.53
france:AMX50_Foch;1.2;1.05;1.4;1.11;1.14
france:AMX_12t;1.9;1.64;1.6;1.6;2.02
france:AMX_13_75;1.7;1.61;1.5;1.25;1.61
france:AMX_13_90;1.4;1.37;1.5;1.29;1.34
france:AMX_50_100;1.4;1.12;1.4;1.26;1.35
france:AMX_50_120;1.2;1.02;1.4;1.12;1.17
france:AMX_AC_Mle1946;1.7;1.45;1.4;1.24;1.61
france:AMX_AC_Mle1948;1.5;1.16;1.4;1.24;1.35
france:AMX_M4_1945;1.6;1.47;1.4;1.28;1.61
france:ARL_44;2.1;1.57;1.4;1.53;1.82
france:ARL_44BP;2.1;1.53;1.4;1.58;1.81
france:ARL_V39;2;1.59;1.4;1.52;1.82
france:Achilles_FR;2.1;1.6;1.4;1.52;1.83
france:B1;4.1;2.8;1.6;2.54;3.64
france:BDR_G1B;3.2;1.91;1.5;1.95;2.13
france:Bat-Chat-25CL;1.1;1.05;1.5;1.1;1.22
france:D1;4.3;4.62;2.7;3.8;6.52
france:D2;3.7;3.64;2.2;3;5.52
france:ELC_AMX;2.8;1.97;2.3;2.31;2.09
france:F113_PzV_FR;1.9;1.65;1.5;1.56;1.89
france:F114_Projet_4_1;1.1;1.03;1.4;1.09;1.22
france:F115_Lorraine_50t;1.2;1.02;1.4;1.13;1.16
france:F116_Bat_Chat_Bourrasque;1.3;1.28;1.5;1.31;1.34
france:F117_Alt_Proto_AMX_30;1.3;1.15;1.5;1.29;1.35
france:F11_Renault_G1R;3.2;2;1.5;1.96;2.12
france:F120_Project_Louis;1.1;1.04;1.4;1.12;1.14
france:F123_ERAC_105_Proto;1.3;1.22;1.5;1.3;1.35
france:F126_AMX_13_M24;1.5;1.58;1.4;1.32;1.62
france:F127_ELC_AMX_901_Proto;1.4;1.28;1.5;1.28;1.34
france:F140_Sentinelle;1.4;1.15;1.4;1.28;1.35
france:F19_Lorraine40t;1.3;1.17;1.5;1.3;1.34
france:F19_Lorraine40t_Com;1.3;1.17;1.5;1.29;1.34
france:F34_ARL_V39_BP;2.1;1.59;1.4;1.51;1.82
france:F68_AMX_Chasseur_de_char_46;1.3;1.17;1.5;1.25;1.33
france:F69_AMX_13D;1.4;1.16;1.5;1.28;1.34
france:F69_AMX_13_57GF;1.6;1.49;1.5;1.26;1.61
france:F71_AMX_30_prototype;1.1;1.06;1.4;1.12;1.19
france:F73_M4A1_Revalorise;1.3;1.23;1.5;1.27;1.34
france:F74_AMX_M4_1949;1.4;1.12;1.4;1.28;1.35
france:F75_Char_de_25t;1.1;1.02;1.5;1.11;1.22
france:F84_Somua_SM;1.4;1.12;1.4;1.14;1.14
france:F89_Canon_dassaut_105;1.6;1.15;1.4;1.26;1.35
france:FCM_36Pak40;4.2;2.95;1.9;3.18;5.52
france:FCM_50t;1.3;1.17;1.5;1.25;1.35
france:F_AMX-12BP;1.9;1.64;1.6;1.6;2.02
france:RenaultFT_AC;5.5;4.37;2.2;4.49;8.51
france:RenaultR35;5.5;5.28;3.3;6.76;7.8
france:RenaultUE57;4.2;2.79;1.9;2.68;5.95
france:S_35CA;3.2;2.07;1.5;1.96;2.11
france:Somua_Sau_40;3.7;2.75;1.8;2.54;3.62
germany:B-1bis_captured;4.1;2.96;1.6;2.55;3.89
germany:DW_II;3.7;2.52;1.6;2.52;3.53
germany:DickerMax;2.1;1.57;1.4;1.53;2.02
germany:E-25;1.8;1.46;1.4;1.24;1.61
germany:E-50;1.1;1.09;1.4;1.1;1.15
germany:E-75;1.2;1.02;1.4;1.17;1.14
germany:Ferdinand;1.4;1.16;1.4;1.25;1.35
germany:G100_Gtraktor_Krupp;4.9;3.31;1.9;3.49;5.75
germany:G103_RU_251;1.4;1.3;1.5;1.25;1.33
germany:G106_PzKpfwPanther_AusfF;1.3;1.21;1.5;1.26;1.35
germany:G109_Steyr_WT;1.8;1.37;1.4;1.27;1.6
germany:G110_Mauschen;1.2;1.02;1.4;1.18;1.12
germany:G112_JagdPanzer_105;1.2;1.05;1.4;1.04;1.18
germany:G113_SP_I_C;1.7;1.49;1.5;1.28;1.58
germany:G114_Rheinmetall_Scorpion;1.6;1.16;1.4;1.24;1.33
germany:G115_VK10001P;1.4;1.15;1.4;1.33;1.35
germany:G118_VK4503;1.7;1.3;1.4;1.27;1.61
germany:G119_Panzer58_SH;1.3;1.22;1.5;1.28;1.35
germany:G119_Pz58_Mutz;1.3;1.17;1.5;1.26;1.35
germany:G120_M41_90;1.4;1.29;1.5;1.26;1.34
germany:G126_HWK_12;1.1;1.05;1.5;1.08;1.29
germany:G136_Tiger_131;2.1;1.68;1.4;1.55;1.85
germany:G138_VK16801P;1.5;1.15;1.4;1.16;1.13
germany:G140_HWK_30;1.4;1.27;1.5;1.27;1.34
germany:G142_M48RPz;1.3;1.18;1.5;1.27;1.35
germany:G147_mPanzer_64;1.7;1.49;1.5;1.29;1.61
germany:G148_Elephant;1.4;1.16;1.4;1.21;1.35
germany:G154_Kpz_07_RH;1.3;1.15;1.5;1.26;1.35
germany:G162_Project_Kpz_07P_E;1.1;1.02;1.4;1.16;1.14
germany:G164_Kpz_Pr_68_P;1.3;1.22;1.5;1.3;1.34
germany:G165_Erich_Koncept;1.1;1.06;1.4;1.14;1.19
germany:G166_LKpz_70_K;1.1;1.02;1.5;1.11;1.32
germany:G167_Tiger_Maus;1.2;1.03;1.4;1.11;1.13
germany:G168_KJpz_T_III;1.2;1.05;1.4;1.05;1.29
germany:G190_VK_1602_Quby;1.4;1.36;1.5;1.25;1.34
germany:G20_Marder_II;4.2;3.01;1.9;3.18;5.54
germany:G85_Auf_Panther;1.5;1.49;1.5;1.25;1.61
germany:G_E-75RT;1.4;1.14;1.4;1.3;1.35
germany:G_E-75TS;1.2;1.03;1.4;1.13;1.13
germany:G_VK3502_BP;2.1;1.68;1.4;1.57;1.79
germany:H39_captured;4.3;5.58;2.7;3.99;6.04
germany:Hetzer;3.7;2.6;1.8;2.54;3.47
germany:Indien_Panzer;1.3;1.17;1.5;1.25;1.35
germany:JagdPanther;1.8;1.37;1.4;1.25;1.62
germany:JagdPantherII;1.6;1.16;1.4;1.23;1.35
germany:JagdPzIV;2.1;1.58;1.4;1.49;1.8
germany:JagdPzIV_AusfBP;2;1.58;1.4;1.49;1.8
germany:JagdTiger;1.1;1.05;1.4;1.04;1.12
germany:JagdTiger_SdKfz_185;1.4;1.17;1.4;1.21;1.35
germany:JagdTiger_SdKfz_185_Snowstorm;1.4;1.33;1.4;1.21;1.35
germany:Krupp-38D;2.6;2.22;1.7;1.95;2.12
germany:Lowe;1.4;1.11;1.4;1.29;1.35
germany:Nashorn;2;1.51;1.4;1.51;2.24
germany:Nashorn_BP;2;1.51;1.4;1.5;2.24
germany:Panther_II;1.3;1.21;1.5;1.26;1.35
germany:Panther_M10;1.5;1.54;1.4;1.26;1.61
germany:PanzerJager_I;5.5;4.33;2.2;4.49;8.8
germany:Pro_Ag_A;1.1;1.02;1.4;1.1;1.21
germany:Pz35t;4.4;4.9;2.7;3.94;6.05
germany:Pz38_NA;2.9;2.78;1.9;2.55;3.65
germany:Pz38t;3.6;3.88;2.1;3.05;5.58
germany:PzII;5.5;5.28;3.3;6.67;6.81
germany:PzIII;3.7;3.69;2.2;3.26;5.49
germany:PzIII_A;3.7;3.09;2.1;2.98;5.58
germany:PzIII_IV;2.7;2.22;1.7;1.95;2.12
germany:PzII_J;3.6;5.5;2.1;2.83;5.46
germany:PzII_Luchs;2.9;2.94;1.9;2.55;2.2
germany:PzIV;2.7;2.22;1.7;1.95;2.13
germany:PzIV_Hydro;2.5;2.22;1.7;1.95;2.11
germany:PzIV_schmalturm;1.9;1.65;1.5;1.53;1.87
germany:PzV;1.5;1.33;1.4;1.26;1.61
germany:PzVI;1.7;1.3;1.4;1.28;1.62
germany:PzVIB_Tiger_II;1.4;1.14;1.4;1.27;1.35
germany:PzVI_AusfBP;2.1;1.68;1.4;1.56;1.79
germany:PzVI_Tiger_P;1.7;1.3;1.4;1.28;1.62
germany:PzV_PzIV;2.6;2.22;1.7;1.95;2.12
germany:Pz_II_AusfG;3.8;2.9;2.1;2.85;5.55
germany:Pz_IV_AusfA;3.6;3.2;2.2;4.05;5.74
germany:Pz_IV_AusfBT;2.7;2.53;1.7;1.96;2.12
germany:Pz_IV_AusfD;3.1;2.43;1.9;2.54;3.09
germany:Pz_Sfl_IVc;3;1.61;1.5;1.98;2.08
germany:RhB_Waffentrager;1.6;1.26;1.4;1.28;1.33
germany:S01_Frankentank;1.7;1.33;1.4;1.37;1.62
germany:S35_captured;3.8;2.85;2.1;2.93;5.52
germany:StuGIII;3.2;1.93;1.5;1.97;2.12
germany:Sturer_Emil;1.8;1.31;1.4;1.28;1.61
germany:T-15;3.8;4.72;2.1;2.82;5.55
germany:T-25;2.5;2.13;1.7;1.95;2.11
germany:VK1602;2.8;2.9;2.3;1.96;2.11
germany:VK2801;1.9;1.53;1.6;1.58;1.95
germany:VK2801_BP;1.9;1.53;1.6;1.59;1.95
germany:VK3001H;3.2;1.75;1.5;1.96;2.13
germany:VK3001P;1.9;1.61;1.5;1.57;1.81
germany:VK3002DB;1.5;1.58;1.4;1.28;1.61
germany:VK3002DB_BP;1.9;1.65;1.5;1.56;1.87
germany:VK3002DB_V1;1.9;1.65;1.5;1.56;1.84
germany:VK3002M;1.9;1.65;1.5;1.57;1.91
germany:VK3601H;2.1;1.64;1.4;1.57;1.78
germany:VK4502A;1.3;1.17;1.4;1.29;1.35
germany:VK4502P;1.2;1.02;1.4;1.18;1.13
germany:Waffentrager_IV;1.2;1.06;1.4;1.12;1.19
germany:Waffentrager_IV_CL;1.2;1.05;1.4;1.06;1.19
japan:Chi_Ha;3.6;3.8;2.2;3.2;5.54
japan:Chi_He;3;2.72;1.9;2.55;3.24
japan:Chi_Ni;4.4;4.62;2.5;4.09;6.52
japan:Chi_Nu;2.6;2.02;1.7;1.94;2.12
japan:Chi_Nu_Kai;2.6;2.02;1.7;1.95;2.11
japan:Chi_Ri;1.5;1.58;1.4;1.25;1.61
japan:Chi_To;1.9;1.61;1.5;1.55;1.9
japan:Firefly_GuP;1.9;1.49;1.5;1.55;2.01
japan:G09_Hetzer_GuP;3.7;2.6;1.8;2.54;3.47
japan:Ha_Go;5.7;5.36;3.3;5.99;8.2
japan:IS-2_GuP;1.6;1.44;1.4;1.33;1.61
japan:J21_Ho_Ri;1.2;1.05;1.4;1.11;1.13
japan:J22_Ho_Ri_type96;1.5;1.16;1.4;1.25;1.35
japan:J23_Chi_To_SPG;1.8;1.37;1.4;1.25;1.62
japan:J24_Type_57;1.4;1.16;1.4;1.33;1.35
japan:J26_Type_68;1.2;1.02;1.4;1.13;1.13
japan:J27_Chi_Se;1.4;1.15;1.4;1.27;1.35
japan:J28_Ju_To;1.7;1.41;1.4;1.32;1.62
japan:J29_Ju_Nu;2.1;1.73;1.4;1.57;1.8
japan:J29_Ju_Nu_BP;2.1;1.73;1.4;1.57;1.8
japan:J30_Mitsu_108;3.2;2.37;1.5;1.97;2.13
japan:J31_Type_5_Ka_Ri;1.5;1.16;1.4;1.26;1.35
japan:J36_Type_63_HT;1.4;1.15;1.4;1.29;1.35
japan:J38_Type_95_Ji_Ro;1.5;1.16;1.4;1.22;1.35
japan:Ke_Ho;2.9;2.71;1.9;2.55;1
japan:Ke_Ni;3.8;4.07;2.1;2.67;5.71
japan:Ke_Ni_B;4.5;4.85;2.1;2.14;5.71
japan:PzVI_GuP;2.1;1.64;1.4;1.54;1.81
japan:Pz_IV_AusfH;2.6;2.22;1.7;1.95;2.12
japan:STA_1;1.3;1.15;1.5;1.28;1.34
japan:Type_61;1.1;1.02;1.4;1.1;1.24
other:AMX_dracula;1.5;1.58;1.4;1.3;1.61
other:Akawara;1.3;1.28;1.5;1.28;1.35
other:Oth06_Sega_Lupus;1.7;1.44;1.4;1.34;1.62
other:Oth08_WH_Vindicator;1.8;1.74;1.4;1.24;1.61
other:Oth09_WH_Predator;1.7;1.47;1.4;1.25;1.62
other:Oth11_T-55A;1.1;1.1;1.4;1.09;1.16
other:Oth12_Sega_Hafen;1.6;1.66;1.4;1.27;1.61
other:Oth14_MdM_AshKnight;1.7;1.47;1.4;1.27;1.62
other:Oth15_RustCrab;2.6;2.22;1.7;1.96;2.12
other:Oth16_KV_TG;1.6;1.76;1.4;1.27;1.61
other:Oth17_BDT-5A;2.6;2.22;1.7;1.95;2.12
other:Oth18_BDT-5B;1.9;1.85;1.5;1.55;1.94
other:Oth19_BDT-5C;1.5;1.66;1.4;1.25;1.61
other:Oth20_M4FL10;1.5;1.67;1.4;1.27;1.61
other:Oth22_Sega_Vulcan;1.7;1.41;1.4;1.33;1.62
other:Oth23_Werewolf;1.6;1.44;1.4;1.33;1.61
other:Oth24_StuH_42HW;3.2;2.6;1.5;1.97;2.13
other:Oth25_HWT20_FalconFire;1.7;1.47;1.4;1.25;1.62
other:Oth26_StrvM42HW;2.7;2.22;1.7;1.96;2.11
other:Oth27_M24_AMX;1.9;1.64;1.6;1.6;2.02
other:Oth28_Sturmfeur_HW;1.7;1.41;1.4;1.31;1.61
other:Oth29_A15HW;2.8;1.83;2.3;1.96;2.12
other:Oth30_T54MS;1.3;1.2;1.5;1.3;1.35
other:Oth31_T150BP;2.1;1.71;1.4;1.56;1.79
other:Oth33_Blizzard_BP;2;1.68;1.6;1.56;2.21
other:Oth34_PanzerMummy;1.7;1.39;1.4;1.3;1.61
other:Oth35_PanzerMejai;3.2;2.27;1.5;1.95;2.13
other:Oth36_Indien_Panzer_S1;1.5;1.56;1.4;1.28;1.61
other:Oth37_T26_E4_SuperPershing_S1;1.3;1.25;1.5;1.3;1.35
other:Oth38_50TP_Tyszkiewicza_S1;1.1;1.02;1.4;1.17;1.14
other:Oth39_T20_S2;1.5;1.62;1.4;1.28;1.61
other:Oth40_TVP_46_S2;1.3;1.15;1.5;1.28;1.35
other:Oth41_Centurion_Mk3_S2;1.1;1.05;1.4;1.11;1.18
other:Oth42_ForestWitch_HW23;1.7;1.42;1.4;1.32;1.61
other:Oth44_Charioteer_T;1.6;1.17;1.4;1.27;1.35
other:Oth45_Sandstriker;1.4;1.16;1.4;1.33;1.35
other:Oth46_Charioteer_S3;1.8;1.37;1.4;1.27;1.61
other:Oth47_AMX_13_90_S3;1.4;1.3;1.5;1.28;1.34
other:Oth48_EMIL_1951E1_S3;1.1;1.02;1.4;1.15;1.14
other:Oth49_TM_04_NT;1.5;1.63;1.4;1.34;1.61
other:Oth50_STT;1.6;1.55;1.5;1.28;1.61
other:Oth51_FD_24;1.3;1.18;1.5;1.28;1.35
other:Oth52_Toxique;1.3;1.32;1.5;1.31;1.35
other:Oth53_Rammer;1.6;1.46;1.4;1.31;1.61
other:Oth61_Pz_VM_Set;4.1;2.58;1.6;2.54;3.71
other:Oth62_Pz_VlD_Set;3.2;1.97;1.5;1.96;2.13
other:Oth63_Pz_VllF_Set;2.1;1.64;1.4;1.57;1.84
other:Oth64_Pz_VlllK_Set;1.6;1.44;1.4;1.31;1.61
other:Oth65_PzVIllM_Tigeom;2.1;1.67;1.4;1.59;1.86
other:Oth66_E100ANN_S4;1.7;1.47;1.4;1.33;1.62
other:Oth67_FV215b_183ANN_S4;1.5;1.16;1.4;1.24;1.35
other:Oth68_KV2ANN_S4;1.2;1.03;1.4;1.23;1.14
other:Oth69_Souleater;1.5;1.17;1.4;1.26;1.35
other:Oth70_Lightbringer;1.6;1.47;1.4;1.29;1.61
other:Oth71_Titan_Strife;1.3;1.16;1.4;1.28;1.35
other:Oth72_Hazard_I;1.5;1.57;1.4;1.3;1.61
other:Oth73_Nemini;1.4;1.17;1.4;1.31;1.35
other:Oth74_Argonaut;1.1;1.05;1.4;1.06;1.18
other:Oth75_Braniaq_DM5;1.1;1.06;1.4;1.14;1.18
other:Oth76_Destiny;1.2;1.02;1.4;1.17;1.15
other:Oth77_Prowler;1.4;1.16;1.4;1.3;1.35
other:Oth78_AC_17_P_Sterope;1.2;1.02;1.4;1.13;1.12
other:Oth80_AC_16_Celeno;1.2;1.02;1.4;1.13;1.12
other:Oth81_AC_15_Taygete;1.4;1.24;1.4;1.33;1.35
other:Oth82_AC_14_Pleione;1.4;1.15;1.4;1.29;1.35
other:Oth83_AC_13_Merope;1.7;1.4;1.4;1.32;1.61
other:Oth84_FVE_Defender;1.1;1.12;1.4;1.16;1.15
other:Oth85_Ghoul;1.3;1.36;1.5;1.28;1.35
other:Oth86_Survivalist;1.1;1.06;1.4;1.1;1.12
other:Oth92_JagdPantherII_Titan;1.4;1.17;1.4;1.24;1.35
other:Oth93_Convoyer;1.4;1.16;1.4;1.24;1.35
other:Sega_Edelweiss;1.5;1.62;1.4;1.28;1.61
other:Sega_Nameless;1.7;1.41;1.4;1.29;1.62
other:Strv_74A2;1.9;1.8;1.5;1.54;1.9
other:Van_Helsing;1.8;1.59;1.4;1.25;1.61
other:WZ-135G_FT;1.8;1.5;1.4;1.3;1.61
uk:GB03_Cruiser_Mk_I;4.4;4.9;2.7;3.96;9.12
uk:GB04_Valentine;2.9;1.85;1.9;2.53;3.37
uk:GB05_Vickers_Medium_Mk_II;6.5;3.84;2.2;3.8;12.37
uk:GB06_Vickers_Medium_Mk_III;4.9;3.9;1.9;2.88;5.74
uk:GB07_Matilda;4.1;2.83;1.6;2.56;3.64
uk:GB08_Churchill_I;3.3;1.8;1.5;1.96;2.13
uk:GB09_Churchill_VII;2.1;1.7;1.4;1.54;1.76
uk:GB09_Churchill_VII_BP;2.1;1.67;1.4;1.54;1.79
uk:GB101_FV1066_Senlac;1.4;1.16;1.5;1.24;1.33
uk:GB106_Cobra;1.1;1.05;1.4;1.14;1.16
uk:GB108_A46;1.5;1.56;1.5;1.25;1.6
uk:GB109_GSOR_1008;1.6;1.16;1.4;1.25;1.34
uk:GB109_GSOR_1008_Fearless;1.6;1.18;1.4;1.24;1.34
uk:GB10_Black_Prince;1.7;1.47;1.4;1.23;1.62
uk:GB110_FV4201_Chieftain_Prototype;1.1;1.02;1.4;1.13;1.15
uk:GB111_Charlemagne;1.4;1.13;1.4;1.31;1.35
uk:GB112_Caliban;1.4;1.16;1.4;1.45;1.35
uk:GB114_Vickers_MBT_Mk3;1.3;1.28;1.5;1.12;1.14
uk:GB116_Harry_Hopkins;2.5;1.97;2.3;1.96;2.12
uk:GB11_Caernarvon;1.3;1.13;1.4;1.23;1.35
uk:GB11_Caernarvon_D;1.4;1.16;1.4;1.3;1.35
uk:GB128_Nemesis;1.3;1.29;1.5;1.28;1.35
uk:GB12_Conqueror;1.1;1.02;1.4;1.1;1.13
uk:GB19_Sherman_Firefly;1.9;1.49;1.5;1.55;2.01
uk:GB20_Crusader;2.7;1.83;2.3;1.96;2.11
uk:GB21_A27BP;1.9;1.7;1.5;1.55;1.94
uk:GB21_Cromwell;1.9;1.7;1.5;1.55;1.94
uk:GB22_Comet;1.5;1.58;1.4;1.25;1.61
uk:GB23_Centurion;1.3;1.12;1.5;1.25;1.35
uk:GB24_Centurion_Mk3;1.1;1.02;1.4;1.14;1.15
uk:GB32_Tortoise;1.1;1.05;1.4;1.05;1.11
uk:GB35_Sentinel_AC_IV;1.9;1.7;1.5;1.56;1.94
uk:GB39_Universal_CarrierQF2;5.5;4.89;2.2;3.99;11.07
uk:GB40_Gun_Carrier_BP;2;1.56;1.4;1.52;1.77
uk:GB40_Gun_Carrier_Churchill;2.1;1.48;1.4;1.51;1.79
uk:GB41_Challenger;1.8;1.35;1.4;1.26;1.61
uk:GB42_Valentine_AT;4.3;2.68;1.9;2.43;5.48
uk:GB44_Archer_Custom;3.2;1.76;1.5;1.98;2.08
uk:GB51_Excelsior;3.2;2.27;1.5;1.97;2.13
uk:GB52_FV201_A45;1.7;1.47;1.4;1.24;1.61
uk:GB57_Alecto;3.7;2.93;1.8;2.54;1
uk:GB58_Cruiser_Mk_III;4.4;4.9;2.5;4.26;6.52
uk:GB59_Cruiser_Mk_IV;3.7;3.82;2.2;3.05;5.52
uk:GB60_Covenanter;3;3;1.9;2.55;2.68
uk:GB63_TOG_II;1.9;1.53;1.4;1.54;1.8
uk:GB63_TOG_II_BP;1.9;1.53;1.4;1.57;1.79
uk:GB68_Matilda_Black_Prince;2.5;1.96;1.7;1.98;2.12
uk:GB69_Cruiser_Mk_II;5.9;5.88;3.3;5.32;8.2
uk:GB71_AT_15A;1.7;1.58;1.4;1.22;1.62
uk:GB72_AT15;1.4;1.19;1.4;1.19;1.35
uk:GB73_AT2;3.2;2.45;1.5;1.97;2.13
uk:GB74_AT8;2;1.6;1.4;1.51;1.76
uk:GB75_AT7;1.7;1.33;1.4;1.23;1.62
uk:GB76_Mk_VIC;4.3;5.58;2.7;4.01;9.12
uk:GB80_Charioteer;1.6;1.16;1.4;1.27;1.34
uk:GB81_FV4004;1.2;1.05;1.4;1.11;1.2
uk:GB85_Cromwell_Berlin;1.9;1.7;1.5;1.55;1.94
uk:GB87_Chieftain_T95_turret;1.4;1.17;1.4;1.25;1.35
uk:GB93_Caernarvon_AX;1.3;1.13;1.4;1.25;1.35
uk:GB94_Centurion_Mk5-1_RAAC;1.3;1.12;1.5;1.28;1.35
uk:GB97_Chimera;1.3;1.15;1.5;1.32;1.35
uk:GB99_Turtle_Mk1;1.4;1.18;1.4;1.22;1.35
uk:GB_Centurion_MkDF;1.3;1.12;1.5;1.29;1.35
uk:GB_Churchill_MkVI;2.1;1.73;1.4;1.54;1.77
uk:GB_Churchill_MkVIII;2.1;1.64;1.4;1.56;1.79
uk:GB_FV301;1.4;1.07;1.5;1.25;1.35
uk:GB_Sherman_V;2.6;2.28;1.7;1.96;2.11
uk:GB_Vickers_Cruiser;1.1;1;1.5;1.11;1.2
uk:Sentinel_AC1;3;2.88;1.9;2.55;3.47
usa:A05_M4_105BD;2.7;2.4;1.7;1.96;2.12
usa:A100_T49;1.4;1.53;1.5;1.29;1.34
usa:A102_T28_concept;1.7;1.47;1.4;1.27;1.62
usa:A104_M4A3E8A;1.9;1.88;1.5;1.54;1.84
usa:A111_T25_Pilot;1.3;1.27;1.5;1.26;1.35
usa:A115_Chrysler_K;1.4;1.16;1.4;1.3;1.35
usa:A117_T26E5;1.4;1.15;1.4;1.27;1.35
usa:A121_T26E3;1.5;1.54;1.4;1.31;1.61
usa:A122_TS-5;1.4;1.15;1.4;1.23;1.35
usa:A124_T54E2;1.4;1.15;1.4;1.29;1.35
usa:A125_AEP_1;1.1;1.02;1.4;1.15;1.14
usa:A130_Super_Hellcat;1.7;1.51;1.4;1.24;1.6
usa:A132_T77;1.3;1.15;1.4;1.29;1.35
usa:A136_T42;1.5;1.54;1.4;1.29;1.61
usa:A140_ASTRON_REX_105;1.3;1.31;1.5;1.31;1.35
usa:A141_M-4-Y;1.4;1.16;1.4;1.27;1.35
usa:A142_Pawlack_Tank;2.1;1.68;1.4;1.58;1.85
usa:A146_TL_7_120;1.1;1.02;1.4;1.14;1.14
usa:A150_MBT_B;1.2;1.02;1.4;1.21;1.13
usa:A151_TS_60;1.3;1.14;1.4;1.06;1.15
usa:A175_ATAC;1.1;1.02;1.5;1.11;1.32
usa:A68_T28D;1.4;1.2;1.4;1.28;1.35
usa:A77_T6M;3;2.72;1.9;2.52;3.24
usa:A94_T37;1.9;1.73;1.6;1.55;2.21
usa:A99_T92E1;1.1;1.1;1.5;1.2;1.2
usa:M-3-Y;1.4;1.14;1.4;1.28;1.35
usa:M-3-Y-Midas;1.4;1.14;1.4;1.28;1.35
usa:M-5-Y;1.1;1.02;1.4;1.2;1.14
usa:M-7-Y;1.7;1.32;1.4;1.32;1.61
usa:M103;1.1;1.02;1.4;1.14;1.13
usa:M10_Wolverine;3.2;2.18;1.5;1.96;2.13
usa:M18E2BP;1.9;1.65;1.5;1.57;2.02
usa:M18_Hellcat;2.1;1.58;1.4;1.51;2.99
usa:M22_Locust;3.8;4.23;2.1;3;5.59
usa:M24_Chaffee;1.9;1.73;1.6;1.55;2.36
usa:M24_Chaffee_BP;1.9;1.73;1.6;1.55;2.36
usa:M2_lt;5.5;5.28;3.3;6.76;7.39
usa:M2_med;3.6;4.29;2.2;2.93;5.54
usa:M36_BP;2.1;1.58;1.4;1.53;1.85
usa:M36_Slagger;2.1;1.58;1.4;1.54;1.85
usa:M3_Grant;3;2.38;1.9;2.55;3.13
usa:M3_Stuart;4.4;4.29;2.7;4.23;5.89
usa:M41_Bulldog;1.6;1.49;1.5;1.25;1.61
usa:M46_CL;1.1;1.04;1.4;1.08;1.17
usa:M46_Patton;1.1;1.08;1.4;1.09;1.17
usa:M4A2E4;2.6;2.28;1.7;1.96;2.13
usa:M4A3E8_BP;1.9;1.88;1.5;1.55;1.84
usa:M4A3E8_Sherman;1.9;1.88;1.5;1.55;1.84
usa:M4_Sherman;2.7;1.98;1.7;1.95;2.12
usa:M5_Stuart;3.7;3.48;2.1;2.94;5.57
usa:M6;2.1;1.6;1.4;1.58;1.8
usa:M6A2E1;1.6;1.32;1.4;1.33;1.62
usa:M6A2E1_exp;1.4;1.07;1.4;1.33;1.35
usa:M6E2BP;2.1;1.6;1.4;1.57;1.77
usa:M6E2V2_BP;2.1;1.64;1.4;1.53;1.78
usa:M7_med;3.1;2.38;1.9;2.54;2.98
usa:M8A1;4;2.73;1.8;2.54;2.87
usa:Pershing;1.3;1.33;1.5;1.25;1.35
usa:Ram-II;2.5;2.22;1.7;1.97;2.12
usa:Sherman_Jumbo;1.9;1.88;1.5;1.56;1.8
usa:T14;3.2;2.35;1.5;1.97;2.13
usa:T18;5.5;4.48;2.2;4.43;5.65
usa:T1_E6;4.4;5;2.7;4.12;8.54
usa:T1_hvy;3.3;2;1.5;1.96;2.13
usa:T20;1.5;1.54;1.4;1.28;1.61
usa:T21;1.9;1.81;1.6;1.57;2.27
usa:T21_BP;1.9;1.81;1.6;1.56;2.24
usa:T23E3;1.5;1.54;1.4;1.25;1.61
usa:T25_2;1.7;1.4;1.4;1.23;1.61
usa:T25_AT;1.7;1.44;1.4;1.26;1.61
usa:T26_E4_SuperPershing;1.3;1.2;1.5;1.29;1.35
usa:T28;1.4;1.16;1.4;1.23;1.35
usa:T28_Prototype;1.4;1.16;1.4;1.24;1.35
usa:T29;1.7;1.32;1.4;1.31;1.61
usa:T2_lt;4.3;5.45;2.7;3.9;8.26
usa:T2_med;4.6;5;2.5;4.23;7.11
usa:T30;1.1;1.05;1.4;1.12;1.17
usa:T32;1.4;1.16;1.4;1.29;1.35
usa:T34_hvy;1.3;1.07;1.4;1.3;1.35
usa:T34_hvy_Freedom;1.4;1.07;1.4;1.31;1.35
usa:T40;4;2.38;1.8;2.54;3.13
usa:T49;3.2;2.18;1.5;1.97;2.08
usa:T49_Fearless;1.4;1.53;1.5;1.28;1.34
usa:T54E1;1.1;1.06;1.4;1.1;1.16
usa:T69;1.3;1.33;1.5;1.27;1.35
usa:T71;1.6;1.49;1.5;1.27;1.6
usa:T7_Combat_Car;4.4;5.58;2.7;4.26;8.54
usa:T82;4.1;13.5;1.9;2.78;5.78
usa:T95;1.2;1.05;1.4;1.11;1.12
usa:T95E2;1.3;1.33;1.5;1.25;1.35
ussr:A-20;3.1;2.72;1.9;2.54;3
ussr:A-32;3;2.85;1.9;2.52;3.43
ussr:AT-1;5.5;3.99;2.2;3.99;8.51
ussr:BT-2;4.3;4.71;2.7;4.18;6.52
ussr:BT-7;3.6;2.98;2.1;3.06;5.51
ussr:BT-SV;3.8;2.86;2.1;3.16;5.58
ussr:Churchill_LL;3.2;2.27;1.5;1.97;2.13
ussr:GAZ-74b;4;2.48;1.8;2.53;3.05
ussr:IS;1.6;1.44;1.4;1.32;1.62
ussr:IS-2Sh;1.4;1.15;1.4;1.31;1.35
ussr:IS-3;1.4;1.16;1.4;1.3;1.35
ussr:IS8;1.2;1.02;1.4;1.12;1.15
ussr:ISU-152;1.6;1.13;1.4;1.38;1.35
ussr:K-91;1.1;1.02;1.4;1.1;1.12
ussr:KV-13;1.5;1.67;1.4;1.26;1.61
ussr:KV-1s;2.3;1.6;1.4;1.6;1.82
ussr:KV-1s-BP;2.3;1.5;1.4;1.64;1.83
ussr:KV-220;3.2;2.27;1.5;1.96;2.13
ussr:KV-3;1.7;1.44;1.4;1.33;1.62
ussr:KV-5;1.5;1.15;1.4;1.29;1.35
ussr:KV1;3.2;2.11;1.5;1.95;2.13
ussr:KV2;2.4;2.25;1.4;1.52;1.85
ussr:KV4;1.4;1.13;1.4;1.3;1.35
ussr:LTP;3.8;2.88;2.1;3.16;5.56
ussr:M3_Stuart_LL;3.7;5.04;2.1;2.86;5.51
ussr:MS-1obr21;4.3;4.71;2.7;4.18;6.98
ussr:MT25;2;1.79;1.6;1.56;2.06
ussr:Matilda_II_LL;2.5;2.22;1.7;1.96;2.12
ussr:Object252;1.4;1.14;1.4;1.33;1.35
ussr:Object252_Fearless;1.4;1.41;1.4;1.33;1.35
ussr:Object_704;1.1;1.05;1.4;1.1;1.13
ussr:R105_BT_7A;3.8;6.27;2.1;3.86;5.64
ussr:R107_LTB;1.6;1.56;1.5;1.28;1.61
ussr:R109_T54S;1.4;1.34;1.5;1.29;1.34
ussr:R111_ISU130;1.5;1.16;1.4;1.23;1.35
ussr:R112_T54_45;1.3;1.37;1.5;1.31;1.35
ussr:R113_Object_730;1.4;1.15;1.4;1.31;1.35
ussr:R115_IS-3_auto;1.4;1.15;1.4;1.3;1.35
ussr:R116_ISU122C_Berlin;1.8;1.45;1.4;1.22;1.62
ussr:R127_T44_100_P;1.3;1.33;1.5;1.27;1.35
ussr:R131_LTG;1.6;1.61;1.5;1.28;1.61
ussr:R134_Object_252U;1.4;1.14;1.4;1.33;1.35
ussr:R140_M4A2_Loza;1.9;1.88;1.5;1.55;1.84
ussr:R146_STG;1.3;1.33;1.5;1.33;1.35
ussr:R154_T-34E-43;2.6;2.22;1.7;1.96;2.11
ussr:R158_LT_432;1.4;1.34;1.5;1.28;1.34
ussr:R159_SU_130PM;1.6;1.16;1.4;1.26;1.34
ussr:R160_object_244;2.1;1.71;1.4;1.55;1.82
ussr:R172_Object_752;1.1;1.02;1.4;1.13;1.13
ussr:R175_IS_2E;1.6;1.47;1.4;1.31;1.61
ussr:R180_Object_274A;1.3;1.31;1.5;1.3;1.35
ussr:R181_Object_452K;1.2;1.02;1.4;1.15;1.13
ussr:R187_Object_590;1.3;1.32;1.5;1.29;1.35
ussr:R192_K_2;1.4;1.12;1.4;1.33;1.35
ussr:R194_ST_62_var_2;1.3;1.31;1.5;1.28;1.35
ussr:R217_Object_Kust;1.5;1.16;1.4;1.27;1.35
ussr:R43_T-70_57;3.8;3.41;2.1;3.43;5.49
ussr:R63_ST_IBD;1.2;1.02;1.4;1.17;1.14
ussr:R71_IS_2B;1.6;1.44;1.4;1.33;1.61
ussr:R98_T44_85;1.5;1.64;1.4;1.26;1.61
ussr:R_Object_84;1.1;1.07;1.5;1.12;1.17
ussr:ST_I;1.2;1.02;1.4;1.16;1.13
ussr:SU-100;2.1;1.57;1.4;1.55;1.81
ussr:SU-101;1.5;1.15;1.4;1.26;1.35
ussr:SU-152;1.8;2;1.4;1.38;1.61
ussr:SU-76;4.2;3.6;1.9;3.18;5.56
ussr:SU-85;3.2;1.99;1.5;1.97;2.12
ussr:SU100M1;1.7;1.35;1.4;1.25;1.61
ussr:SU100Y;2.1;1.53;1.4;1.57;1.89
ussr:SU122_44;1.8;1.45;1.4;1.25;1.61
ussr:SU122_54;1.2;1.05;1.4;1.03;1.14
ussr:SU76I;4.1;3.31;1.9;3.18;5.47
ussr:SU_85I;3.1;2.3;1.5;1.98;2.12
ussr:T-127;3.8;2.76;2.1;3.16;5.52
ussr:T-26;5.5;5.07;3.3;6.16;8.2
ussr:T-28;3;2.85;1.9;2.54;3.09
ussr:T-34;2.7;2.01;1.7;1.96;2.12
ussr:T-34-85;1.9;1.71;1.5;1.58;1.87
ussr:T-34-85_Rudy;1.5;1.67;1.4;1.26;1.61
ussr:T-34-85_Victory;1.9;1.88;1.5;1.57;1.87
ussr:T-43;1.5;1.6;1.4;1.25;1.61
ussr:T-44;1.3;1.33;1.5;1.34;1.35
ussr:T-46;3.8;4.32;2.1;2.83;5.6
ussr:T-54;1.1;1.1;1.4;1.09;1.16
ussr:T150;2.1;1.55;1.4;1.59;1.82
ussr:T34_85BP;1.9;1.88;1.5;1.57;1.87
ussr:T34_85TUR;1.5;1.58;1.4;1.29;1.62
ussr:Tetrarch_LL;4.3;3.88;2.7;3.88;8.51
ussr:Valentine_LL;2.9;4.55;1.9;2.55;3.42
1 name maxHealthFactor piercingPowerFactor moduleHealthFactor damageFactor armorFactor
2 china:Ch01_Type59 1.3 1.33 1.5 1.29 1.35
3 china:Ch02_Type62 1.5 1.6 1.5 1.27 1.61
4 china:Ch03_WZ-111 1.4 1.17 1.4 1.3 1.35
5 china:Ch04_T34_1 1.5 1.54 1.4 1.3 1.61
6 china:Ch05_T34_2 1.3 1.33 1.5 1.29 1.34
7 china:Ch07_Vickers_MkE_Type_BT26 5.5 5.07 3.3 7.05 8.8
8 china:Ch08_Type97_Chi_Ha 3.7 3.71 2.2 3.13 5.56
9 china:Ch09_M5 2.9 2.91 1.9 2.54 3.47
10 china:Ch10_IS2 1.6 1.44 1.4 1.33 1.61
11 china:Ch11_110 1.4 1.17 1.4 1.29 1.35
12 china:Ch12_111_1_2_3 1.2 1.02 1.4 1.17 1.14
13 china:Ch14_T34_3 1.3 1.37 1.5 1.13 1.17
14 china:Ch15_59_16 1.9 1.77 1.6 1.57 2.24
15 china:Ch15_59_16_BP 1.9 1.7 1.6 1.56 2.24
16 china:Ch16_WZ_131 1.6 1.52 1.5 1.28 1.61
17 china:Ch17_WZ131_1_WZ132 1.4 1.22 1.5 1.29 1.34
18 china:Ch18_WZ-120 1.1 1.09 1.4 1.11 1.17
19 china:Ch20_Type58 1.9 1.88 1.5 1.57 1.86
20 china:Ch21_T34 2.7 2.1 1.7 1.97 2.12
21 china:Ch23_112 1.4 1.17 1.4 1.31 1.35
22 china:Ch24_Type64 1.9 1.86 1.6 1.57 2.35
23 china:Ch26_59_Patton 1.3 1.33 1.5 1.1 1.16
24 china:Ch28_WZ_132A 1.1 1.04 1.5 1.11 1.24
25 china:Ch34_WZ-131G_FT 2.1 1.57 1.4 1.51 1.73
26 china:Ch34_WZ-131G_FT_BP 2.1 1.57 1.4 1.51 2.02
27 china:Ch35_T-34-2G_FT 1.8 1.48 1.4 1.25 1.61
28 china:Ch36_WZ-111-1G_FT 1.6 1.16 1.4 1.25 1.35
29 china:Ch37_WZ-111G_FT 1.2 1.05 1.4 1.15 1.13
30 china:Ch39_WZ120_1G_FT 1.6 1.16 1.4 1.24 1.35
31 china:Ch40_WZ120G_FT 1.1 1.05 1.4 1.08 1.13
32 china:Ch42_M41D 1.7 1.49 1.5 1.24 1.61
33 china:Ch43_WZ-122_2 1.3 1.21 1.5 1.31 1.35
34 china:Ch45_WZ_114 1.2 1.02 1.4 1.19 1.13
35 china:Ch47_BZ_176 1.2 1.03 1.4 1.17 1.13
36 china:Ch49_BZ_68 1.2 1.02 1.4 1.2 1.16
37 china:Ch50_BZ_166 1.4 1.17 1.4 1.3 1.35
38 china:Ch51_BZ_58 1.6 1.46 1.4 1.29 1.62
39 china:Ch56_BZ_74_1 1.2 1.02 1.4 1.16 1.14
40 china:Ch59_BZ58_2 1.2 1.02 1.4 1.19 1.13
41 china:Ch60_Type59_spc 1.3 1.37 1.5 1.32 1.35
42 china:Ch_LT_vz38 4.3 4.29 2.7 3.99 4.88
43 china:Ch_WZ-112v2 1.4 1.17 1.4 1.31 1.35
44 china:Type_58BP 1.9 1.88 1.5 1.57 2.04
45 european:Cz02_T50 1.1 1.03 1.4 1.12 1.19
46 european:Cz05_T34-100 1.5 1.45 1.4 1.29 1.61
47 european:Cz07_TVP_46 1.3 1.12 1.5 1.29 1.35
48 european:Cz08_T-25 1.9 1.83 1.5 1.56 2.02
49 european:Cz08_T-25BP 1.9 1.83 1.5 1.55 2.02
50 european:Cz08_T-25BPS 1.9 1.83 1.5 1.57 2.02
51 european:Cz13_T-27 1.1 1.07 1.4 1.12 1.19
52 european:Cz14_Skoda_T-56 1.4 1.17 1.4 1.33 1.35
53 european:Cz15_Skoda_T-45 1.7 1.44 1.4 1.27 1.61
54 european:Cz16_TNH_T_vz51 1.2 1.02 1.4 1.17 1.15
55 european:Cz18_TNH_105_1000 1.4 1.16 1.4 1.28 1.35
56 european:Cz19_Vz_44_1 1.7 1.43 1.4 1.3 1.61
57 european:Cz20_ShPTK_TVP_100 1.5 1.16 1.4 1.24 1.34
58 european:Cz29_Skoda_P-JS 2.2 1.67 1.4 1.56 1.78
59 european:Cz29_Skoda_P-JS_BP 2.2 1.67 1.4 1.57 1.79
60 european:F01_Vickers_MkF 5.5 5.07 3.3 6.16 8.8
61 european:It09_P43_ter 1.5 1.54 1.4 1.3 1.61
62 european:It10_P43_BD 1.9 1.74 1.5 1.57 1.82
63 european:It10_P43_bis 1.9 1.74 1.5 1.57 1.82
64 european:It10_P43_bis_BP 1.9 1.7 1.5 1.58 1.82
65 european:It116_Carro_d_assalto_P88 1.6 1.33 1.4 1.28 1.61
66 european:It117_ProgettoCC55mod54 1.3 1.13 1.4 1.29 1.35
67 european:It119_ProgettoC50mod66 1.1 1.02 1.4 1.18 1.13
68 european:It12_Prototipo_Standard_B 1.1 1.04 1.4 1.14 1.17
69 european:It13_Progetto_M35_mod_46 1.3 1.33 1.5 1.27 1.35
70 european:It14_P44_Pantera 1.3 1.21 1.5 1.26 1.35
71 european:It15_Semovente_M41 3.2 1.98 1.5 1.97 2.13
72 european:It16_Bassoto 2 1.59 1.4 1.53 1.8
73 european:It17_Semovente_mod_56 1.7 1.4 1.4 1.28 1.62
74 european:It18_Semovente_mod_64 1.4 1.2 1.4 1.24 1.35
75 european:It19_Controcarro_1MK2_Ciclope 1.1 1.05 1.4 1.19 1.13
76 european:It22_Progetto_C45_mod_71 1.3 1.14 1.4 1.23 1.35
77 european:It30_CC_mod_64_Prem 1.4 1.17 1.4 1.25 1.35
78 european:Pl03_PzV_Pudel 1.9 1.57 1.5 1.56 1.92
79 european:Pl05_50TP_Tyszkiewicza 1.2 1.02 1.4 1.18 1.13
80 european:Pl06_10TP 4.3 4.29 2.7 4.28 7.66
81 european:Pl07_14TP 3.6 3.57 2.1 3.12 5.56
82 european:Pl08_50TP_prot 1.2 1.02 1.4 1.17 1.14
83 european:Pl10_40TP_Habicha 2.1 1.64 1.4 1.57 1.83
84 european:Pl119_CS_52_LIS 1.5 1.56 1.4 1.31 1.61
85 european:Pl11_45TP_Habicha 1.6 1.44 1.4 1.3 1.61
86 european:Pl13_53TP_Markowskiego 1.4 1.14 1.4 1.32 1.35
87 european:Pl17_DS_PZlnz 2.6 2.28 1.7 1.95 2.12
88 european:Pl18_BUGI 1.9 1.57 1.5 1.58 1.94
89 european:Pl20_CS_44 1.5 1.54 1.4 1.3 1.61
90 european:Pl22_CS_59 1.1 1.04 1.4 1.1 1.21
91 european:Pl23_CS_53 1.3 1.31 1.5 1.29 1.35
92 european:Pl24_56TP 1.4 1.15 1.4 1.29 1.35
93 european:Pl27_SDP_Wz_66T 1.5 1.17 1.4 1.27 1.35
94 european:Pl28_SDP_Wz_60 1.1 1.05 1.4 1.11 1.12
95 european:Pl30_SDP_Wz58T 1.4 1.16 1.4 1.26 1.35
96 european:Pl31_SDP_Wz_57D 1.7 1.43 1.4 1.28 1.62
97 european:Pl32_SDP_Wz_44 2 1.57 1.4 1.52 1.76
98 european:Pl33_SDP_Wz_40 3.2 2.13 1.5 1.96 2.13
99 european:S02_StrvM42 2.7 2.22 1.7 1.95 2.11
100 european:S04_Lago-I 3.1 2.69 1.9 2.54 2.32
101 european:S07_Strv74BP 1.9 1.67 1.5 1.55 1.9
102 european:S07_Strv_74 1.9 1.67 1.5 1.55 1.9
103 european:S13_Leo 1.5 1.58 1.4 1.3 1.61
104 european:S13_Leo_NY 1.5 1.58 1.4 1.3 1.61
105 european:S17_EMIL_1952E2 1.2 1.02 1.4 1.16 1.12
106 european:S18_EMIL_1951E1 1.3 1.16 1.4 1.29 1.35
107 european:S23_Strv_81 1.3 1.14 1.5 1.26 1.35
108 european:S25_EMIL_51 1.3 1.12 1.4 1.29 1.35
109 european:S26_Lancen_C 1.3 1.22 1.5 1.31 1.35
110 european:S32_Bofors_Tornvagn 1.1 1.02 1.4 1.17 1.12
111 france:AMX38 4.4 4.29 2.7 4.15 5.95
112 france:AMX40 3 2.88 1.9 2.52 3.53
113 france:AMX50_Foch 1.2 1.05 1.4 1.11 1.14
114 france:AMX_12t 1.9 1.64 1.6 1.6 2.02
115 france:AMX_13_75 1.7 1.61 1.5 1.25 1.61
116 france:AMX_13_90 1.4 1.37 1.5 1.29 1.34
117 france:AMX_50_100 1.4 1.12 1.4 1.26 1.35
118 france:AMX_50_120 1.2 1.02 1.4 1.12 1.17
119 france:AMX_AC_Mle1946 1.7 1.45 1.4 1.24 1.61
120 france:AMX_AC_Mle1948 1.5 1.16 1.4 1.24 1.35
121 france:AMX_M4_1945 1.6 1.47 1.4 1.28 1.61
122 france:ARL_44 2.1 1.57 1.4 1.53 1.82
123 france:ARL_44BP 2.1 1.53 1.4 1.58 1.81
124 france:ARL_V39 2 1.59 1.4 1.52 1.82
125 france:Achilles_FR 2.1 1.6 1.4 1.52 1.83
126 france:B1 4.1 2.8 1.6 2.54 3.64
127 france:BDR_G1B 3.2 1.91 1.5 1.95 2.13
128 france:Bat-Chat-25CL 1.1 1.05 1.5 1.1 1.22
129 france:D1 4.3 4.62 2.7 3.8 6.52
130 france:D2 3.7 3.64 2.2 3 5.52
131 france:ELC_AMX 2.8 1.97 2.3 2.31 2.09
132 france:F113_PzV_FR 1.9 1.65 1.5 1.56 1.89
133 france:F114_Projet_4_1 1.1 1.03 1.4 1.09 1.22
134 france:F115_Lorraine_50t 1.2 1.02 1.4 1.13 1.16
135 france:F116_Bat_Chat_Bourrasque 1.3 1.28 1.5 1.31 1.34
136 france:F117_Alt_Proto_AMX_30 1.3 1.15 1.5 1.29 1.35
137 france:F11_Renault_G1R 3.2 2 1.5 1.96 2.12
138 france:F120_Project_Louis 1.1 1.04 1.4 1.12 1.14
139 france:F123_ERAC_105_Proto 1.3 1.22 1.5 1.3 1.35
140 france:F126_AMX_13_M24 1.5 1.58 1.4 1.32 1.62
141 france:F127_ELC_AMX_901_Proto 1.4 1.28 1.5 1.28 1.34
142 france:F140_Sentinelle 1.4 1.15 1.4 1.28 1.35
143 france:F19_Lorraine40t 1.3 1.17 1.5 1.3 1.34
144 france:F19_Lorraine40t_Com 1.3 1.17 1.5 1.29 1.34
145 france:F34_ARL_V39_BP 2.1 1.59 1.4 1.51 1.82
146 france:F68_AMX_Chasseur_de_char_46 1.3 1.17 1.5 1.25 1.33
147 france:F69_AMX_13D 1.4 1.16 1.5 1.28 1.34
148 france:F69_AMX_13_57GF 1.6 1.49 1.5 1.26 1.61
149 france:F71_AMX_30_prototype 1.1 1.06 1.4 1.12 1.19
150 france:F73_M4A1_Revalorise 1.3 1.23 1.5 1.27 1.34
151 france:F74_AMX_M4_1949 1.4 1.12 1.4 1.28 1.35
152 france:F75_Char_de_25t 1.1 1.02 1.5 1.11 1.22
153 france:F84_Somua_SM 1.4 1.12 1.4 1.14 1.14
154 france:F89_Canon_dassaut_105 1.6 1.15 1.4 1.26 1.35
155 france:FCM_36Pak40 4.2 2.95 1.9 3.18 5.52
156 france:FCM_50t 1.3 1.17 1.5 1.25 1.35
157 france:F_AMX-12BP 1.9 1.64 1.6 1.6 2.02
158 france:RenaultFT_AC 5.5 4.37 2.2 4.49 8.51
159 france:RenaultR35 5.5 5.28 3.3 6.76 7.8
160 france:RenaultUE57 4.2 2.79 1.9 2.68 5.95
161 france:S_35CA 3.2 2.07 1.5 1.96 2.11
162 france:Somua_Sau_40 3.7 2.75 1.8 2.54 3.62
163 germany:B-1bis_captured 4.1 2.96 1.6 2.55 3.89
164 germany:DW_II 3.7 2.52 1.6 2.52 3.53
165 germany:DickerMax 2.1 1.57 1.4 1.53 2.02
166 germany:E-25 1.8 1.46 1.4 1.24 1.61
167 germany:E-50 1.1 1.09 1.4 1.1 1.15
168 germany:E-75 1.2 1.02 1.4 1.17 1.14
169 germany:Ferdinand 1.4 1.16 1.4 1.25 1.35
170 germany:G100_Gtraktor_Krupp 4.9 3.31 1.9 3.49 5.75
171 germany:G103_RU_251 1.4 1.3 1.5 1.25 1.33
172 germany:G106_PzKpfwPanther_AusfF 1.3 1.21 1.5 1.26 1.35
173 germany:G109_Steyr_WT 1.8 1.37 1.4 1.27 1.6
174 germany:G110_Mauschen 1.2 1.02 1.4 1.18 1.12
175 germany:G112_JagdPanzer_105 1.2 1.05 1.4 1.04 1.18
176 germany:G113_SP_I_C 1.7 1.49 1.5 1.28 1.58
177 germany:G114_Rheinmetall_Scorpion 1.6 1.16 1.4 1.24 1.33
178 germany:G115_VK10001P 1.4 1.15 1.4 1.33 1.35
179 germany:G118_VK4503 1.7 1.3 1.4 1.27 1.61
180 germany:G119_Panzer58_SH 1.3 1.22 1.5 1.28 1.35
181 germany:G119_Pz58_Mutz 1.3 1.17 1.5 1.26 1.35
182 germany:G120_M41_90 1.4 1.29 1.5 1.26 1.34
183 germany:G126_HWK_12 1.1 1.05 1.5 1.08 1.29
184 germany:G136_Tiger_131 2.1 1.68 1.4 1.55 1.85
185 germany:G138_VK16801P 1.5 1.15 1.4 1.16 1.13
186 germany:G140_HWK_30 1.4 1.27 1.5 1.27 1.34
187 germany:G142_M48RPz 1.3 1.18 1.5 1.27 1.35
188 germany:G147_mPanzer_64 1.7 1.49 1.5 1.29 1.61
189 germany:G148_Elephant 1.4 1.16 1.4 1.21 1.35
190 germany:G154_Kpz_07_RH 1.3 1.15 1.5 1.26 1.35
191 germany:G162_Project_Kpz_07P_E 1.1 1.02 1.4 1.16 1.14
192 germany:G164_Kpz_Pr_68_P 1.3 1.22 1.5 1.3 1.34
193 germany:G165_Erich_Koncept 1.1 1.06 1.4 1.14 1.19
194 germany:G166_LKpz_70_K 1.1 1.02 1.5 1.11 1.32
195 germany:G167_Tiger_Maus 1.2 1.03 1.4 1.11 1.13
196 germany:G168_KJpz_T_III 1.2 1.05 1.4 1.05 1.29
197 germany:G190_VK_1602_Quby 1.4 1.36 1.5 1.25 1.34
198 germany:G20_Marder_II 4.2 3.01 1.9 3.18 5.54
199 germany:G85_Auf_Panther 1.5 1.49 1.5 1.25 1.61
200 germany:G_E-75RT 1.4 1.14 1.4 1.3 1.35
201 germany:G_E-75TS 1.2 1.03 1.4 1.13 1.13
202 germany:G_VK3502_BP 2.1 1.68 1.4 1.57 1.79
203 germany:H39_captured 4.3 5.58 2.7 3.99 6.04
204 germany:Hetzer 3.7 2.6 1.8 2.54 3.47
205 germany:Indien_Panzer 1.3 1.17 1.5 1.25 1.35
206 germany:JagdPanther 1.8 1.37 1.4 1.25 1.62
207 germany:JagdPantherII 1.6 1.16 1.4 1.23 1.35
208 germany:JagdPzIV 2.1 1.58 1.4 1.49 1.8
209 germany:JagdPzIV_AusfBP 2 1.58 1.4 1.49 1.8
210 germany:JagdTiger 1.1 1.05 1.4 1.04 1.12
211 germany:JagdTiger_SdKfz_185 1.4 1.17 1.4 1.21 1.35
212 germany:JagdTiger_SdKfz_185_Snowstorm 1.4 1.33 1.4 1.21 1.35
213 germany:Krupp-38D 2.6 2.22 1.7 1.95 2.12
214 germany:Lowe 1.4 1.11 1.4 1.29 1.35
215 germany:Nashorn 2 1.51 1.4 1.51 2.24
216 germany:Nashorn_BP 2 1.51 1.4 1.5 2.24
217 germany:Panther_II 1.3 1.21 1.5 1.26 1.35
218 germany:Panther_M10 1.5 1.54 1.4 1.26 1.61
219 germany:PanzerJager_I 5.5 4.33 2.2 4.49 8.8
220 germany:Pro_Ag_A 1.1 1.02 1.4 1.1 1.21
221 germany:Pz35t 4.4 4.9 2.7 3.94 6.05
222 germany:Pz38_NA 2.9 2.78 1.9 2.55 3.65
223 germany:Pz38t 3.6 3.88 2.1 3.05 5.58
224 germany:PzII 5.5 5.28 3.3 6.67 6.81
225 germany:PzIII 3.7 3.69 2.2 3.26 5.49
226 germany:PzIII_A 3.7 3.09 2.1 2.98 5.58
227 germany:PzIII_IV 2.7 2.22 1.7 1.95 2.12
228 germany:PzII_J 3.6 5.5 2.1 2.83 5.46
229 germany:PzII_Luchs 2.9 2.94 1.9 2.55 2.2
230 germany:PzIV 2.7 2.22 1.7 1.95 2.13
231 germany:PzIV_Hydro 2.5 2.22 1.7 1.95 2.11
232 germany:PzIV_schmalturm 1.9 1.65 1.5 1.53 1.87
233 germany:PzV 1.5 1.33 1.4 1.26 1.61
234 germany:PzVI 1.7 1.3 1.4 1.28 1.62
235 germany:PzVIB_Tiger_II 1.4 1.14 1.4 1.27 1.35
236 germany:PzVI_AusfBP 2.1 1.68 1.4 1.56 1.79
237 germany:PzVI_Tiger_P 1.7 1.3 1.4 1.28 1.62
238 germany:PzV_PzIV 2.6 2.22 1.7 1.95 2.12
239 germany:Pz_II_AusfG 3.8 2.9 2.1 2.85 5.55
240 germany:Pz_IV_AusfA 3.6 3.2 2.2 4.05 5.74
241 germany:Pz_IV_AusfBT 2.7 2.53 1.7 1.96 2.12
242 germany:Pz_IV_AusfD 3.1 2.43 1.9 2.54 3.09
243 germany:Pz_Sfl_IVc 3 1.61 1.5 1.98 2.08
244 germany:RhB_Waffentrager 1.6 1.26 1.4 1.28 1.33
245 germany:S01_Frankentank 1.7 1.33 1.4 1.37 1.62
246 germany:S35_captured 3.8 2.85 2.1 2.93 5.52
247 germany:StuGIII 3.2 1.93 1.5 1.97 2.12
248 germany:Sturer_Emil 1.8 1.31 1.4 1.28 1.61
249 germany:T-15 3.8 4.72 2.1 2.82 5.55
250 germany:T-25 2.5 2.13 1.7 1.95 2.11
251 germany:VK1602 2.8 2.9 2.3 1.96 2.11
252 germany:VK2801 1.9 1.53 1.6 1.58 1.95
253 germany:VK2801_BP 1.9 1.53 1.6 1.59 1.95
254 germany:VK3001H 3.2 1.75 1.5 1.96 2.13
255 germany:VK3001P 1.9 1.61 1.5 1.57 1.81
256 germany:VK3002DB 1.5 1.58 1.4 1.28 1.61
257 germany:VK3002DB_BP 1.9 1.65 1.5 1.56 1.87
258 germany:VK3002DB_V1 1.9 1.65 1.5 1.56 1.84
259 germany:VK3002M 1.9 1.65 1.5 1.57 1.91
260 germany:VK3601H 2.1 1.64 1.4 1.57 1.78
261 germany:VK4502A 1.3 1.17 1.4 1.29 1.35
262 germany:VK4502P 1.2 1.02 1.4 1.18 1.13
263 germany:Waffentrager_IV 1.2 1.06 1.4 1.12 1.19
264 germany:Waffentrager_IV_CL 1.2 1.05 1.4 1.06 1.19
265 japan:Chi_Ha 3.6 3.8 2.2 3.2 5.54
266 japan:Chi_He 3 2.72 1.9 2.55 3.24
267 japan:Chi_Ni 4.4 4.62 2.5 4.09 6.52
268 japan:Chi_Nu 2.6 2.02 1.7 1.94 2.12
269 japan:Chi_Nu_Kai 2.6 2.02 1.7 1.95 2.11
270 japan:Chi_Ri 1.5 1.58 1.4 1.25 1.61
271 japan:Chi_To 1.9 1.61 1.5 1.55 1.9
272 japan:Firefly_GuP 1.9 1.49 1.5 1.55 2.01
273 japan:G09_Hetzer_GuP 3.7 2.6 1.8 2.54 3.47
274 japan:Ha_Go 5.7 5.36 3.3 5.99 8.2
275 japan:IS-2_GuP 1.6 1.44 1.4 1.33 1.61
276 japan:J21_Ho_Ri 1.2 1.05 1.4 1.11 1.13
277 japan:J22_Ho_Ri_type96 1.5 1.16 1.4 1.25 1.35
278 japan:J23_Chi_To_SPG 1.8 1.37 1.4 1.25 1.62
279 japan:J24_Type_57 1.4 1.16 1.4 1.33 1.35
280 japan:J26_Type_68 1.2 1.02 1.4 1.13 1.13
281 japan:J27_Chi_Se 1.4 1.15 1.4 1.27 1.35
282 japan:J28_Ju_To 1.7 1.41 1.4 1.32 1.62
283 japan:J29_Ju_Nu 2.1 1.73 1.4 1.57 1.8
284 japan:J29_Ju_Nu_BP 2.1 1.73 1.4 1.57 1.8
285 japan:J30_Mitsu_108 3.2 2.37 1.5 1.97 2.13
286 japan:J31_Type_5_Ka_Ri 1.5 1.16 1.4 1.26 1.35
287 japan:J36_Type_63_HT 1.4 1.15 1.4 1.29 1.35
288 japan:J38_Type_95_Ji_Ro 1.5 1.16 1.4 1.22 1.35
289 japan:Ke_Ho 2.9 2.71 1.9 2.55 1
290 japan:Ke_Ni 3.8 4.07 2.1 2.67 5.71
291 japan:Ke_Ni_B 4.5 4.85 2.1 2.14 5.71
292 japan:PzVI_GuP 2.1 1.64 1.4 1.54 1.81
293 japan:Pz_IV_AusfH 2.6 2.22 1.7 1.95 2.12
294 japan:STA_1 1.3 1.15 1.5 1.28 1.34
295 japan:Type_61 1.1 1.02 1.4 1.1 1.24
296 other:AMX_dracula 1.5 1.58 1.4 1.3 1.61
297 other:Akawara 1.3 1.28 1.5 1.28 1.35
298 other:Oth06_Sega_Lupus 1.7 1.44 1.4 1.34 1.62
299 other:Oth08_WH_Vindicator 1.8 1.74 1.4 1.24 1.61
300 other:Oth09_WH_Predator 1.7 1.47 1.4 1.25 1.62
301 other:Oth11_T-55A 1.1 1.1 1.4 1.09 1.16
302 other:Oth12_Sega_Hafen 1.6 1.66 1.4 1.27 1.61
303 other:Oth14_MdM_AshKnight 1.7 1.47 1.4 1.27 1.62
304 other:Oth15_RustCrab 2.6 2.22 1.7 1.96 2.12
305 other:Oth16_KV_TG 1.6 1.76 1.4 1.27 1.61
306 other:Oth17_BDT-5A 2.6 2.22 1.7 1.95 2.12
307 other:Oth18_BDT-5B 1.9 1.85 1.5 1.55 1.94
308 other:Oth19_BDT-5C 1.5 1.66 1.4 1.25 1.61
309 other:Oth20_M4FL10 1.5 1.67 1.4 1.27 1.61
310 other:Oth22_Sega_Vulcan 1.7 1.41 1.4 1.33 1.62
311 other:Oth23_Werewolf 1.6 1.44 1.4 1.33 1.61
312 other:Oth24_StuH_42HW 3.2 2.6 1.5 1.97 2.13
313 other:Oth25_HWT20_FalconFire 1.7 1.47 1.4 1.25 1.62
314 other:Oth26_StrvM42HW 2.7 2.22 1.7 1.96 2.11
315 other:Oth27_M24_AMX 1.9 1.64 1.6 1.6 2.02
316 other:Oth28_Sturmfeur_HW 1.7 1.41 1.4 1.31 1.61
317 other:Oth29_A15HW 2.8 1.83 2.3 1.96 2.12
318 other:Oth30_T54MS 1.3 1.2 1.5 1.3 1.35
319 other:Oth31_T150BP 2.1 1.71 1.4 1.56 1.79
320 other:Oth33_Blizzard_BP 2 1.68 1.6 1.56 2.21
321 other:Oth34_PanzerMummy 1.7 1.39 1.4 1.3 1.61
322 other:Oth35_PanzerMejai 3.2 2.27 1.5 1.95 2.13
323 other:Oth36_Indien_Panzer_S1 1.5 1.56 1.4 1.28 1.61
324 other:Oth37_T26_E4_SuperPershing_S1 1.3 1.25 1.5 1.3 1.35
325 other:Oth38_50TP_Tyszkiewicza_S1 1.1 1.02 1.4 1.17 1.14
326 other:Oth39_T20_S2 1.5 1.62 1.4 1.28 1.61
327 other:Oth40_TVP_46_S2 1.3 1.15 1.5 1.28 1.35
328 other:Oth41_Centurion_Mk3_S2 1.1 1.05 1.4 1.11 1.18
329 other:Oth42_ForestWitch_HW23 1.7 1.42 1.4 1.32 1.61
330 other:Oth44_Charioteer_T 1.6 1.17 1.4 1.27 1.35
331 other:Oth45_Sandstriker 1.4 1.16 1.4 1.33 1.35
332 other:Oth46_Charioteer_S3 1.8 1.37 1.4 1.27 1.61
333 other:Oth47_AMX_13_90_S3 1.4 1.3 1.5 1.28 1.34
334 other:Oth48_EMIL_1951E1_S3 1.1 1.02 1.4 1.15 1.14
335 other:Oth49_TM_04_NT 1.5 1.63 1.4 1.34 1.61
336 other:Oth50_STT 1.6 1.55 1.5 1.28 1.61
337 other:Oth51_FD_24 1.3 1.18 1.5 1.28 1.35
338 other:Oth52_Toxique 1.3 1.32 1.5 1.31 1.35
339 other:Oth53_Rammer 1.6 1.46 1.4 1.31 1.61
340 other:Oth61_Pz_VM_Set 4.1 2.58 1.6 2.54 3.71
341 other:Oth62_Pz_VlD_Set 3.2 1.97 1.5 1.96 2.13
342 other:Oth63_Pz_VllF_Set 2.1 1.64 1.4 1.57 1.84
343 other:Oth64_Pz_VlllK_Set 1.6 1.44 1.4 1.31 1.61
344 other:Oth65_PzVIllM_Tigeom 2.1 1.67 1.4 1.59 1.86
345 other:Oth66_E100ANN_S4 1.7 1.47 1.4 1.33 1.62
346 other:Oth67_FV215b_183ANN_S4 1.5 1.16 1.4 1.24 1.35
347 other:Oth68_KV2ANN_S4 1.2 1.03 1.4 1.23 1.14
348 other:Oth69_Souleater 1.5 1.17 1.4 1.26 1.35
349 other:Oth70_Lightbringer 1.6 1.47 1.4 1.29 1.61
350 other:Oth71_Titan_Strife 1.3 1.16 1.4 1.28 1.35
351 other:Oth72_Hazard_I 1.5 1.57 1.4 1.3 1.61
352 other:Oth73_Nemini 1.4 1.17 1.4 1.31 1.35
353 other:Oth74_Argonaut 1.1 1.05 1.4 1.06 1.18
354 other:Oth75_Braniaq_DM5 1.1 1.06 1.4 1.14 1.18
355 other:Oth76_Destiny 1.2 1.02 1.4 1.17 1.15
356 other:Oth77_Prowler 1.4 1.16 1.4 1.3 1.35
357 other:Oth78_AC_17_P_Sterope 1.2 1.02 1.4 1.13 1.12
358 other:Oth80_AC_16_Celeno 1.2 1.02 1.4 1.13 1.12
359 other:Oth81_AC_15_Taygete 1.4 1.24 1.4 1.33 1.35
360 other:Oth82_AC_14_Pleione 1.4 1.15 1.4 1.29 1.35
361 other:Oth83_AC_13_Merope 1.7 1.4 1.4 1.32 1.61
362 other:Oth84_FVE_Defender 1.1 1.12 1.4 1.16 1.15
363 other:Oth85_Ghoul 1.3 1.36 1.5 1.28 1.35
364 other:Oth86_Survivalist 1.1 1.06 1.4 1.1 1.12
365 other:Oth92_JagdPantherII_Titan 1.4 1.17 1.4 1.24 1.35
366 other:Oth93_Convoyer 1.4 1.16 1.4 1.24 1.35
367 other:Sega_Edelweiss 1.5 1.62 1.4 1.28 1.61
368 other:Sega_Nameless 1.7 1.41 1.4 1.29 1.62
369 other:Strv_74A2 1.9 1.8 1.5 1.54 1.9
370 other:Van_Helsing 1.8 1.59 1.4 1.25 1.61
371 other:WZ-135G_FT 1.8 1.5 1.4 1.3 1.61
372 uk:GB03_Cruiser_Mk_I 4.4 4.9 2.7 3.96 9.12
373 uk:GB04_Valentine 2.9 1.85 1.9 2.53 3.37
374 uk:GB05_Vickers_Medium_Mk_II 6.5 3.84 2.2 3.8 12.37
375 uk:GB06_Vickers_Medium_Mk_III 4.9 3.9 1.9 2.88 5.74
376 uk:GB07_Matilda 4.1 2.83 1.6 2.56 3.64
377 uk:GB08_Churchill_I 3.3 1.8 1.5 1.96 2.13
378 uk:GB09_Churchill_VII 2.1 1.7 1.4 1.54 1.76
379 uk:GB09_Churchill_VII_BP 2.1 1.67 1.4 1.54 1.79
380 uk:GB101_FV1066_Senlac 1.4 1.16 1.5 1.24 1.33
381 uk:GB106_Cobra 1.1 1.05 1.4 1.14 1.16
382 uk:GB108_A46 1.5 1.56 1.5 1.25 1.6
383 uk:GB109_GSOR_1008 1.6 1.16 1.4 1.25 1.34
384 uk:GB109_GSOR_1008_Fearless 1.6 1.18 1.4 1.24 1.34
385 uk:GB10_Black_Prince 1.7 1.47 1.4 1.23 1.62
386 uk:GB110_FV4201_Chieftain_Prototype 1.1 1.02 1.4 1.13 1.15
387 uk:GB111_Charlemagne 1.4 1.13 1.4 1.31 1.35
388 uk:GB112_Caliban 1.4 1.16 1.4 1.45 1.35
389 uk:GB114_Vickers_MBT_Mk3 1.3 1.28 1.5 1.12 1.14
390 uk:GB116_Harry_Hopkins 2.5 1.97 2.3 1.96 2.12
391 uk:GB11_Caernarvon 1.3 1.13 1.4 1.23 1.35
392 uk:GB11_Caernarvon_D 1.4 1.16 1.4 1.3 1.35
393 uk:GB128_Nemesis 1.3 1.29 1.5 1.28 1.35
394 uk:GB12_Conqueror 1.1 1.02 1.4 1.1 1.13
395 uk:GB19_Sherman_Firefly 1.9 1.49 1.5 1.55 2.01
396 uk:GB20_Crusader 2.7 1.83 2.3 1.96 2.11
397 uk:GB21_A27BP 1.9 1.7 1.5 1.55 1.94
398 uk:GB21_Cromwell 1.9 1.7 1.5 1.55 1.94
399 uk:GB22_Comet 1.5 1.58 1.4 1.25 1.61
400 uk:GB23_Centurion 1.3 1.12 1.5 1.25 1.35
401 uk:GB24_Centurion_Mk3 1.1 1.02 1.4 1.14 1.15
402 uk:GB32_Tortoise 1.1 1.05 1.4 1.05 1.11
403 uk:GB35_Sentinel_AC_IV 1.9 1.7 1.5 1.56 1.94
404 uk:GB39_Universal_CarrierQF2 5.5 4.89 2.2 3.99 11.07
405 uk:GB40_Gun_Carrier_BP 2 1.56 1.4 1.52 1.77
406 uk:GB40_Gun_Carrier_Churchill 2.1 1.48 1.4 1.51 1.79
407 uk:GB41_Challenger 1.8 1.35 1.4 1.26 1.61
408 uk:GB42_Valentine_AT 4.3 2.68 1.9 2.43 5.48
409 uk:GB44_Archer_Custom 3.2 1.76 1.5 1.98 2.08
410 uk:GB51_Excelsior 3.2 2.27 1.5 1.97 2.13
411 uk:GB52_FV201_A45 1.7 1.47 1.4 1.24 1.61
412 uk:GB57_Alecto 3.7 2.93 1.8 2.54 1
413 uk:GB58_Cruiser_Mk_III 4.4 4.9 2.5 4.26 6.52
414 uk:GB59_Cruiser_Mk_IV 3.7 3.82 2.2 3.05 5.52
415 uk:GB60_Covenanter 3 3 1.9 2.55 2.68
416 uk:GB63_TOG_II 1.9 1.53 1.4 1.54 1.8
417 uk:GB63_TOG_II_BP 1.9 1.53 1.4 1.57 1.79
418 uk:GB68_Matilda_Black_Prince 2.5 1.96 1.7 1.98 2.12
419 uk:GB69_Cruiser_Mk_II 5.9 5.88 3.3 5.32 8.2
420 uk:GB71_AT_15A 1.7 1.58 1.4 1.22 1.62
421 uk:GB72_AT15 1.4 1.19 1.4 1.19 1.35
422 uk:GB73_AT2 3.2 2.45 1.5 1.97 2.13
423 uk:GB74_AT8 2 1.6 1.4 1.51 1.76
424 uk:GB75_AT7 1.7 1.33 1.4 1.23 1.62
425 uk:GB76_Mk_VIC 4.3 5.58 2.7 4.01 9.12
426 uk:GB80_Charioteer 1.6 1.16 1.4 1.27 1.34
427 uk:GB81_FV4004 1.2 1.05 1.4 1.11 1.2
428 uk:GB85_Cromwell_Berlin 1.9 1.7 1.5 1.55 1.94
429 uk:GB87_Chieftain_T95_turret 1.4 1.17 1.4 1.25 1.35
430 uk:GB93_Caernarvon_AX 1.3 1.13 1.4 1.25 1.35
431 uk:GB94_Centurion_Mk5-1_RAAC 1.3 1.12 1.5 1.28 1.35
432 uk:GB97_Chimera 1.3 1.15 1.5 1.32 1.35
433 uk:GB99_Turtle_Mk1 1.4 1.18 1.4 1.22 1.35
434 uk:GB_Centurion_MkDF 1.3 1.12 1.5 1.29 1.35
435 uk:GB_Churchill_MkVI 2.1 1.73 1.4 1.54 1.77
436 uk:GB_Churchill_MkVIII 2.1 1.64 1.4 1.56 1.79
437 uk:GB_FV301 1.4 1.07 1.5 1.25 1.35
438 uk:GB_Sherman_V 2.6 2.28 1.7 1.96 2.11
439 uk:GB_Vickers_Cruiser 1.1 1 1.5 1.11 1.2
440 uk:Sentinel_AC1 3 2.88 1.9 2.55 3.47
441 usa:A05_M4_105BD 2.7 2.4 1.7 1.96 2.12
442 usa:A100_T49 1.4 1.53 1.5 1.29 1.34
443 usa:A102_T28_concept 1.7 1.47 1.4 1.27 1.62
444 usa:A104_M4A3E8A 1.9 1.88 1.5 1.54 1.84
445 usa:A111_T25_Pilot 1.3 1.27 1.5 1.26 1.35
446 usa:A115_Chrysler_K 1.4 1.16 1.4 1.3 1.35
447 usa:A117_T26E5 1.4 1.15 1.4 1.27 1.35
448 usa:A121_T26E3 1.5 1.54 1.4 1.31 1.61
449 usa:A122_TS-5 1.4 1.15 1.4 1.23 1.35
450 usa:A124_T54E2 1.4 1.15 1.4 1.29 1.35
451 usa:A125_AEP_1 1.1 1.02 1.4 1.15 1.14
452 usa:A130_Super_Hellcat 1.7 1.51 1.4 1.24 1.6
453 usa:A132_T77 1.3 1.15 1.4 1.29 1.35
454 usa:A136_T42 1.5 1.54 1.4 1.29 1.61
455 usa:A140_ASTRON_REX_105 1.3 1.31 1.5 1.31 1.35
456 usa:A141_M-4-Y 1.4 1.16 1.4 1.27 1.35
457 usa:A142_Pawlack_Tank 2.1 1.68 1.4 1.58 1.85
458 usa:A146_TL_7_120 1.1 1.02 1.4 1.14 1.14
459 usa:A150_MBT_B 1.2 1.02 1.4 1.21 1.13
460 usa:A151_TS_60 1.3 1.14 1.4 1.06 1.15
461 usa:A175_ATAC 1.1 1.02 1.5 1.11 1.32
462 usa:A68_T28D 1.4 1.2 1.4 1.28 1.35
463 usa:A77_T6M 3 2.72 1.9 2.52 3.24
464 usa:A94_T37 1.9 1.73 1.6 1.55 2.21
465 usa:A99_T92E1 1.1 1.1 1.5 1.2 1.2
466 usa:M-3-Y 1.4 1.14 1.4 1.28 1.35
467 usa:M-3-Y-Midas 1.4 1.14 1.4 1.28 1.35
468 usa:M-5-Y 1.1 1.02 1.4 1.2 1.14
469 usa:M-7-Y 1.7 1.32 1.4 1.32 1.61
470 usa:M103 1.1 1.02 1.4 1.14 1.13
471 usa:M10_Wolverine 3.2 2.18 1.5 1.96 2.13
472 usa:M18E2BP 1.9 1.65 1.5 1.57 2.02
473 usa:M18_Hellcat 2.1 1.58 1.4 1.51 2.99
474 usa:M22_Locust 3.8 4.23 2.1 3 5.59
475 usa:M24_Chaffee 1.9 1.73 1.6 1.55 2.36
476 usa:M24_Chaffee_BP 1.9 1.73 1.6 1.55 2.36
477 usa:M2_lt 5.5 5.28 3.3 6.76 7.39
478 usa:M2_med 3.6 4.29 2.2 2.93 5.54
479 usa:M36_BP 2.1 1.58 1.4 1.53 1.85
480 usa:M36_Slagger 2.1 1.58 1.4 1.54 1.85
481 usa:M3_Grant 3 2.38 1.9 2.55 3.13
482 usa:M3_Stuart 4.4 4.29 2.7 4.23 5.89
483 usa:M41_Bulldog 1.6 1.49 1.5 1.25 1.61
484 usa:M46_CL 1.1 1.04 1.4 1.08 1.17
485 usa:M46_Patton 1.1 1.08 1.4 1.09 1.17
486 usa:M4A2E4 2.6 2.28 1.7 1.96 2.13
487 usa:M4A3E8_BP 1.9 1.88 1.5 1.55 1.84
488 usa:M4A3E8_Sherman 1.9 1.88 1.5 1.55 1.84
489 usa:M4_Sherman 2.7 1.98 1.7 1.95 2.12
490 usa:M5_Stuart 3.7 3.48 2.1 2.94 5.57
491 usa:M6 2.1 1.6 1.4 1.58 1.8
492 usa:M6A2E1 1.6 1.32 1.4 1.33 1.62
493 usa:M6A2E1_exp 1.4 1.07 1.4 1.33 1.35
494 usa:M6E2BP 2.1 1.6 1.4 1.57 1.77
495 usa:M6E2V2_BP 2.1 1.64 1.4 1.53 1.78
496 usa:M7_med 3.1 2.38 1.9 2.54 2.98
497 usa:M8A1 4 2.73 1.8 2.54 2.87
498 usa:Pershing 1.3 1.33 1.5 1.25 1.35
499 usa:Ram-II 2.5 2.22 1.7 1.97 2.12
500 usa:Sherman_Jumbo 1.9 1.88 1.5 1.56 1.8
501 usa:T14 3.2 2.35 1.5 1.97 2.13
502 usa:T18 5.5 4.48 2.2 4.43 5.65
503 usa:T1_E6 4.4 5 2.7 4.12 8.54
504 usa:T1_hvy 3.3 2 1.5 1.96 2.13
505 usa:T20 1.5 1.54 1.4 1.28 1.61
506 usa:T21 1.9 1.81 1.6 1.57 2.27
507 usa:T21_BP 1.9 1.81 1.6 1.56 2.24
508 usa:T23E3 1.5 1.54 1.4 1.25 1.61
509 usa:T25_2 1.7 1.4 1.4 1.23 1.61
510 usa:T25_AT 1.7 1.44 1.4 1.26 1.61
511 usa:T26_E4_SuperPershing 1.3 1.2 1.5 1.29 1.35
512 usa:T28 1.4 1.16 1.4 1.23 1.35
513 usa:T28_Prototype 1.4 1.16 1.4 1.24 1.35
514 usa:T29 1.7 1.32 1.4 1.31 1.61
515 usa:T2_lt 4.3 5.45 2.7 3.9 8.26
516 usa:T2_med 4.6 5 2.5 4.23 7.11
517 usa:T30 1.1 1.05 1.4 1.12 1.17
518 usa:T32 1.4 1.16 1.4 1.29 1.35
519 usa:T34_hvy 1.3 1.07 1.4 1.3 1.35
520 usa:T34_hvy_Freedom 1.4 1.07 1.4 1.31 1.35
521 usa:T40 4 2.38 1.8 2.54 3.13
522 usa:T49 3.2 2.18 1.5 1.97 2.08
523 usa:T49_Fearless 1.4 1.53 1.5 1.28 1.34
524 usa:T54E1 1.1 1.06 1.4 1.1 1.16
525 usa:T69 1.3 1.33 1.5 1.27 1.35
526 usa:T71 1.6 1.49 1.5 1.27 1.6
527 usa:T7_Combat_Car 4.4 5.58 2.7 4.26 8.54
528 usa:T82 4.1 13.5 1.9 2.78 5.78
529 usa:T95 1.2 1.05 1.4 1.11 1.12
530 usa:T95E2 1.3 1.33 1.5 1.25 1.35
531 ussr:A-20 3.1 2.72 1.9 2.54 3
532 ussr:A-32 3 2.85 1.9 2.52 3.43
533 ussr:AT-1 5.5 3.99 2.2 3.99 8.51
534 ussr:BT-2 4.3 4.71 2.7 4.18 6.52
535 ussr:BT-7 3.6 2.98 2.1 3.06 5.51
536 ussr:BT-SV 3.8 2.86 2.1 3.16 5.58
537 ussr:Churchill_LL 3.2 2.27 1.5 1.97 2.13
538 ussr:GAZ-74b 4 2.48 1.8 2.53 3.05
539 ussr:IS 1.6 1.44 1.4 1.32 1.62
540 ussr:IS-2Sh 1.4 1.15 1.4 1.31 1.35
541 ussr:IS-3 1.4 1.16 1.4 1.3 1.35
542 ussr:IS8 1.2 1.02 1.4 1.12 1.15
543 ussr:ISU-152 1.6 1.13 1.4 1.38 1.35
544 ussr:K-91 1.1 1.02 1.4 1.1 1.12
545 ussr:KV-13 1.5 1.67 1.4 1.26 1.61
546 ussr:KV-1s 2.3 1.6 1.4 1.6 1.82
547 ussr:KV-1s-BP 2.3 1.5 1.4 1.64 1.83
548 ussr:KV-220 3.2 2.27 1.5 1.96 2.13
549 ussr:KV-3 1.7 1.44 1.4 1.33 1.62
550 ussr:KV-5 1.5 1.15 1.4 1.29 1.35
551 ussr:KV1 3.2 2.11 1.5 1.95 2.13
552 ussr:KV2 2.4 2.25 1.4 1.52 1.85
553 ussr:KV4 1.4 1.13 1.4 1.3 1.35
554 ussr:LTP 3.8 2.88 2.1 3.16 5.56
555 ussr:M3_Stuart_LL 3.7 5.04 2.1 2.86 5.51
556 ussr:MS-1obr21 4.3 4.71 2.7 4.18 6.98
557 ussr:MT25 2 1.79 1.6 1.56 2.06
558 ussr:Matilda_II_LL 2.5 2.22 1.7 1.96 2.12
559 ussr:Object252 1.4 1.14 1.4 1.33 1.35
560 ussr:Object252_Fearless 1.4 1.41 1.4 1.33 1.35
561 ussr:Object_704 1.1 1.05 1.4 1.1 1.13
562 ussr:R105_BT_7A 3.8 6.27 2.1 3.86 5.64
563 ussr:R107_LTB 1.6 1.56 1.5 1.28 1.61
564 ussr:R109_T54S 1.4 1.34 1.5 1.29 1.34
565 ussr:R111_ISU130 1.5 1.16 1.4 1.23 1.35
566 ussr:R112_T54_45 1.3 1.37 1.5 1.31 1.35
567 ussr:R113_Object_730 1.4 1.15 1.4 1.31 1.35
568 ussr:R115_IS-3_auto 1.4 1.15 1.4 1.3 1.35
569 ussr:R116_ISU122C_Berlin 1.8 1.45 1.4 1.22 1.62
570 ussr:R127_T44_100_P 1.3 1.33 1.5 1.27 1.35
571 ussr:R131_LTG 1.6 1.61 1.5 1.28 1.61
572 ussr:R134_Object_252U 1.4 1.14 1.4 1.33 1.35
573 ussr:R140_M4A2_Loza 1.9 1.88 1.5 1.55 1.84
574 ussr:R146_STG 1.3 1.33 1.5 1.33 1.35
575 ussr:R154_T-34E-43 2.6 2.22 1.7 1.96 2.11
576 ussr:R158_LT_432 1.4 1.34 1.5 1.28 1.34
577 ussr:R159_SU_130PM 1.6 1.16 1.4 1.26 1.34
578 ussr:R160_object_244 2.1 1.71 1.4 1.55 1.82
579 ussr:R172_Object_752 1.1 1.02 1.4 1.13 1.13
580 ussr:R175_IS_2E 1.6 1.47 1.4 1.31 1.61
581 ussr:R180_Object_274A 1.3 1.31 1.5 1.3 1.35
582 ussr:R181_Object_452K 1.2 1.02 1.4 1.15 1.13
583 ussr:R187_Object_590 1.3 1.32 1.5 1.29 1.35
584 ussr:R192_K_2 1.4 1.12 1.4 1.33 1.35
585 ussr:R194_ST_62_var_2 1.3 1.31 1.5 1.28 1.35
586 ussr:R217_Object_Kust 1.5 1.16 1.4 1.27 1.35
587 ussr:R43_T-70_57 3.8 3.41 2.1 3.43 5.49
588 ussr:R63_ST_IBD 1.2 1.02 1.4 1.17 1.14
589 ussr:R71_IS_2B 1.6 1.44 1.4 1.33 1.61
590 ussr:R98_T44_85 1.5 1.64 1.4 1.26 1.61
591 ussr:R_Object_84 1.1 1.07 1.5 1.12 1.17
592 ussr:ST_I 1.2 1.02 1.4 1.16 1.13
593 ussr:SU-100 2.1 1.57 1.4 1.55 1.81
594 ussr:SU-101 1.5 1.15 1.4 1.26 1.35
595 ussr:SU-152 1.8 2 1.4 1.38 1.61
596 ussr:SU-76 4.2 3.6 1.9 3.18 5.56
597 ussr:SU-85 3.2 1.99 1.5 1.97 2.12
598 ussr:SU100M1 1.7 1.35 1.4 1.25 1.61
599 ussr:SU100Y 2.1 1.53 1.4 1.57 1.89
600 ussr:SU122_44 1.8 1.45 1.4 1.25 1.61
601 ussr:SU122_54 1.2 1.05 1.4 1.03 1.14
602 ussr:SU76I 4.1 3.31 1.9 3.18 5.47
603 ussr:SU_85I 3.1 2.3 1.5 1.98 2.12
604 ussr:T-127 3.8 2.76 2.1 3.16 5.52
605 ussr:T-26 5.5 5.07 3.3 6.16 8.2
606 ussr:T-28 3 2.85 1.9 2.54 3.09
607 ussr:T-34 2.7 2.01 1.7 1.96 2.12
608 ussr:T-34-85 1.9 1.71 1.5 1.58 1.87
609 ussr:T-34-85_Rudy 1.5 1.67 1.4 1.26 1.61
610 ussr:T-34-85_Victory 1.9 1.88 1.5 1.57 1.87
611 ussr:T-43 1.5 1.6 1.4 1.25 1.61
612 ussr:T-44 1.3 1.33 1.5 1.34 1.35
613 ussr:T-46 3.8 4.32 2.1 2.83 5.6
614 ussr:T-54 1.1 1.1 1.4 1.09 1.16
615 ussr:T150 2.1 1.55 1.4 1.59 1.82
616 ussr:T34_85BP 1.9 1.88 1.5 1.57 1.87
617 ussr:T34_85TUR 1.5 1.58 1.4 1.29 1.62
618 ussr:Tetrarch_LL 4.3 3.88 2.7 3.88 8.51
619 ussr:Valentine_LL 2.9 4.55 1.9 2.55 3.42