mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-25 03:29:51 +01:00
Compare commits
8
Commits
b52c1fc2d9
...
84ecaec5d3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
84ecaec5d3 | ||
|
|
01a5f1bd47 | ||
|
|
de0433ca02 | ||
|
|
cf76ae7136 | ||
|
|
ea21636c4e | ||
|
|
dafa834d28 | ||
|
|
f1a1c78588 | ||
|
|
14d16e5daf |
No files matched your search
@@ -7,6 +7,8 @@ on:
|
||||
schedule:
|
||||
- cron: 0 */6 * * *
|
||||
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
+2
-1
@@ -6,7 +6,8 @@
|
||||
"scripts": {
|
||||
"dev": "cd packages/website && bun run dev",
|
||||
"build": "cd packages/website && bun run build",
|
||||
"discover-ids": "cd packages/scripts && bun run discover-ids"
|
||||
"discover-ids": "cd packages/scripts && bun run discover-ids",
|
||||
"build-protos": "cd packages/scripts && bun run build-protos"
|
||||
},
|
||||
"type": "module",
|
||||
"workspaces": [
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
{
|
||||
"name": "@blitzkit/scripts",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"discover-ids": "bun src/discoverIds/index.ts",
|
||||
"build-protos": "bun src/protos.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@blitzkit/closed": "workspace:*",
|
||||
"@blitzkit/core": "workspace:*",
|
||||
@@ -34,9 +39,5 @@
|
||||
"unzipit": "^1.4.3",
|
||||
"yaml": "^2.8.1"
|
||||
},
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"discover-ids": "bun src/discoverIds/index.ts"
|
||||
},
|
||||
"type": "module"
|
||||
}
|
||||
@@ -1,229 +0,0 @@
|
||||
import {
|
||||
AverageDefinitions,
|
||||
AverageDefinitionsAllStats,
|
||||
AverageDefinitionsEntry,
|
||||
type IndividualTankStats,
|
||||
REGIONS,
|
||||
type Region,
|
||||
Samples,
|
||||
averageDefinitionsAllStatsKeys,
|
||||
emptySamples,
|
||||
getAccountInfo,
|
||||
getTankStats,
|
||||
idToRegion,
|
||||
} from "@blitzkit/core";
|
||||
import { chunk, times } from "lodash-es";
|
||||
import { AssetUploader } from "./core/github/assetUploader";
|
||||
import { fetchPreDiscoveredIds } from "./core/github/fetchPreDiscoveredIds";
|
||||
|
||||
interface DataPoint {
|
||||
x: number;
|
||||
y: number;
|
||||
w: number;
|
||||
}
|
||||
|
||||
const MINUTE = 60 * 1000;
|
||||
const HOUR = 60 * MINUTE;
|
||||
const DAY = 24 * HOUR;
|
||||
// const MAX_ACTIVITY_TIME = 120 * DAY;
|
||||
// const MIN_BATTLES = 5000;
|
||||
const RUN_TIME = 5 * HOUR + 55 * MINUTE;
|
||||
const THREADS = 10;
|
||||
const PLAYER_IDS_PER_CALL = 100;
|
||||
|
||||
const startTime = Date.now();
|
||||
const preDiscoveredIds = await fetchPreDiscoveredIds();
|
||||
const playerIds: Record<Region, number[][]> = {
|
||||
asia: chunk(
|
||||
preDiscoveredIds.filter((id) => idToRegion(id) === "asia"),
|
||||
PLAYER_IDS_PER_CALL,
|
||||
),
|
||||
com: chunk(
|
||||
preDiscoveredIds.filter((id) => idToRegion(id) === "com"),
|
||||
PLAYER_IDS_PER_CALL,
|
||||
),
|
||||
eu: chunk(
|
||||
preDiscoveredIds.filter((id) => idToRegion(id) === "eu"),
|
||||
PLAYER_IDS_PER_CALL,
|
||||
),
|
||||
};
|
||||
let regionIndex = 0;
|
||||
const samples: Samples = { ...emptySamples };
|
||||
const availableRegions = [...REGIONS];
|
||||
const tankIds: number[] = [];
|
||||
const tanksSorted: Record<number, IndividualTankStats[]> = {};
|
||||
let postWorkRequested = false;
|
||||
|
||||
times(THREADS, async () => {
|
||||
while (availableRegions.length > 0 && startTime + RUN_TIME > Date.now()) {
|
||||
const region = availableRegions[regionIndex];
|
||||
const regionIds = playerIds[region];
|
||||
const [ids] = regionIds.splice(
|
||||
Math.floor(Math.random() * regionIds.length),
|
||||
1,
|
||||
);
|
||||
const accountInfo = await getAccountInfo(region, ids, undefined, {
|
||||
fields: "last_battle_time,statistics.all.battles",
|
||||
});
|
||||
|
||||
const filteredIds = ids.filter((_, index) => {
|
||||
const info = accountInfo[index];
|
||||
|
||||
if (info === null) return false;
|
||||
|
||||
const timeSinceLastActivity = Date.now() - info.last_battle_time * 1000;
|
||||
|
||||
samples.total++;
|
||||
if (timeSinceLastActivity <= 120 * DAY) samples.d_120++;
|
||||
if (timeSinceLastActivity <= 90 * DAY) samples.d_90++;
|
||||
if (timeSinceLastActivity <= 60 * DAY) samples.d_60++;
|
||||
if (timeSinceLastActivity <= 30 * DAY) samples.d_30++;
|
||||
if (timeSinceLastActivity <= 7 * DAY) samples.d_7++;
|
||||
if (timeSinceLastActivity <= 1 * DAY) samples.d_1++;
|
||||
|
||||
return true;
|
||||
|
||||
// return (
|
||||
// timeSinceLastActivity <= MAX_ACTIVITY_TIME &&
|
||||
// info.statistics.all.battles > MIN_BATTLES
|
||||
// );
|
||||
});
|
||||
|
||||
const players = await Promise.all(
|
||||
filteredIds.map((id) =>
|
||||
getTankStats(region, id, {
|
||||
fields: "last_battle_time,battle_life_time,all,tank_id",
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
players.forEach((tanks) => {
|
||||
tanks?.forEach((tank) => {
|
||||
if (tank.all.battles === 0) return;
|
||||
|
||||
if (!tankIds.includes(tank.tank_id)) {
|
||||
console.log(
|
||||
`Found new tank: ${tank.tank_id} (${tankIds.length + 1})`,
|
||||
);
|
||||
tankIds.push(tank.tank_id);
|
||||
tanksSorted[tank.tank_id] = [];
|
||||
}
|
||||
|
||||
tanksSorted[tank.tank_id].push(tank);
|
||||
});
|
||||
});
|
||||
|
||||
if (regionIds.length === 0 && availableRegions.includes(region)) {
|
||||
availableRegions.splice(availableRegions.indexOf(region), 1);
|
||||
}
|
||||
regionIndex = (regionIndex + 1) % availableRegions.length;
|
||||
}
|
||||
|
||||
if (!postWorkRequested) {
|
||||
postWorkRequested = true;
|
||||
postWork();
|
||||
}
|
||||
});
|
||||
|
||||
async function postWork() {
|
||||
console.log(
|
||||
`Generating statistics based on ${samples.d_120.toLocaleString()} players (${samples.total.toLocaleString()} checked in total) and ${
|
||||
tankIds.length
|
||||
} tanks...`,
|
||||
);
|
||||
|
||||
using uploader = new AssetUploader("averages");
|
||||
const averages: Record<number, AverageDefinitionsEntry> = {};
|
||||
|
||||
tankIds.forEach((id) => {
|
||||
const tanksSortedEntry = tanksSorted[id];
|
||||
// const tanks = tanksSortedEntry.filter(
|
||||
// (tank) => Date.now() - tank.last_battle_time * 1000 <= MAX_ACTIVITY_TIME,
|
||||
// );
|
||||
const tanks = tanksSortedEntry;
|
||||
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: {},
|
||||
samples: { ...emptySamples },
|
||||
};
|
||||
|
||||
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++;
|
||||
});
|
||||
|
||||
const dataWY = tanks.map((tank) => ({
|
||||
w: tank.all.battles,
|
||||
y: tank.all.wins / tank.all.battles,
|
||||
}));
|
||||
|
||||
averageDefinitionsAllStatsKeys.forEach((key) => {
|
||||
const data: DataPoint[] = dataWY.map(({ w, y }, index) => ({
|
||||
w,
|
||||
x:
|
||||
(key === "battle_life_time"
|
||||
? tanks[index].battle_life_time
|
||||
: tanks[index].all[key]) / w,
|
||||
y,
|
||||
}));
|
||||
|
||||
if (data.length === 0) return;
|
||||
|
||||
function sum(slicer: (data: DataPoint) => number) {
|
||||
return data.reduce(
|
||||
(accumulator, data) => accumulator + slicer(data),
|
||||
0,
|
||||
);
|
||||
}
|
||||
|
||||
const sum_w = sum(({ w }) => w);
|
||||
const sum_wx = sum(({ w, x }) => w * x);
|
||||
const sum_wy = sum(({ w, y }) => w * y);
|
||||
const x_bar = sum_wx / sum_w;
|
||||
const y_bar = sum_wy / sum_w;
|
||||
// undo normalization for mu to get a simple average
|
||||
const mu = sum(({ w, x }) => w * x) / samples.d_120;
|
||||
|
||||
const sigma_numerator = sum(({ w, x }) => w * (x - x_bar) ** 2);
|
||||
const sigma = Math.sqrt(sigma_numerator / sum_w);
|
||||
|
||||
const r_numerator = sum(({ w, x, y }) => w * (x - x_bar) * (y - y_bar));
|
||||
const r_denominator_x = sum(({ w, x }) => w * (x - x_bar) ** 2);
|
||||
const r_denominator_y = sum(({ w, y }) => w * (y - y_bar) ** 2);
|
||||
const r = r_numerator / Math.sqrt(r_denominator_x * r_denominator_y);
|
||||
|
||||
entry.mu[key] = mu;
|
||||
entry.sigma[key] = sigma;
|
||||
entry.r[key] = isNaN(r) ? 0 : r;
|
||||
});
|
||||
|
||||
averages[id] = entry as AverageDefinitionsEntry;
|
||||
});
|
||||
|
||||
const time = Date.now();
|
||||
const averageDefinitions = {
|
||||
averages,
|
||||
samples,
|
||||
time,
|
||||
} satisfies AverageDefinitions;
|
||||
|
||||
await uploader.add({
|
||||
path: "averages.pb",
|
||||
content: AverageDefinitions.encode(averageDefinitions).finish(),
|
||||
});
|
||||
|
||||
await uploader.flush();
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
import { readdir, readFile, writeFile } from "fs/promises";
|
||||
|
||||
const SOURCES = "../../packages/closed/protos";
|
||||
const TARGET = "../../packages/closed/src/unreal/catalogItemAccessor.ts";
|
||||
|
||||
const componentPattern = /^message (\w+)Component {/gm;
|
||||
|
||||
const files = await readdir(SOURCES).then((files) =>
|
||||
files.filter(
|
||||
(file) =>
|
||||
file.startsWith("blitz_static_") && file.endsWith("_component.proto")
|
||||
)
|
||||
);
|
||||
|
||||
const imports: { name: string; file: string }[] = [];
|
||||
|
||||
for (const file of files) {
|
||||
const content = await readFile(`${SOURCES}/${file}`).then((buffer) =>
|
||||
buffer.toString()
|
||||
);
|
||||
const components = content.matchAll(componentPattern);
|
||||
|
||||
for (const match of components) {
|
||||
const name = match[1];
|
||||
const duplicate = imports.find((_import) => _import.name === name);
|
||||
|
||||
if (duplicate) {
|
||||
console.warn(
|
||||
`Duplicate component ${name} across ${duplicate.file}.proto and ${file}`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
const importFile = file.replace(".proto", "");
|
||||
|
||||
imports.push({ name, file: importFile });
|
||||
}
|
||||
}
|
||||
|
||||
let content = "";
|
||||
|
||||
for (const _import of imports) {
|
||||
content += `import { ${_import.name}Component } from "@protos/${_import.file}";\n`;
|
||||
}
|
||||
|
||||
content +=
|
||||
'import { BaseCatalogItemAccessor } from "./baseCatalogItemAccessor";\n\n';
|
||||
content +=
|
||||
"export class CatalogItemAccessor extends BaseCatalogItemAccessor {\n";
|
||||
|
||||
for (const _import of imports) {
|
||||
const strippedName = _import.name.replace("BlitzStatic", "");
|
||||
const defaultName = strippedName[0].toLowerCase() + strippedName.slice(1);
|
||||
|
||||
content += ` ${strippedName}(name = "${defaultName}Component") {\n`;
|
||||
content += ` return this.component(name, ${_import.name}Component);\n`;
|
||||
content += " }\n";
|
||||
}
|
||||
|
||||
content += "}\n";
|
||||
|
||||
await writeFile(TARGET, content);
|
||||
@@ -1,40 +0,0 @@
|
||||
import { readFile, writeFile } from "fs/promises";
|
||||
|
||||
const IMPORT = "@protos/auto_legacy_proxy_client";
|
||||
const SOURCE = "../../packages/closed/protos/auto_legacy_proxy_client.proto";
|
||||
const TARGET = "../../packages/closed/src/unreal/methodPopulatedProxyClient.ts";
|
||||
|
||||
const requestPattern = /^message (\w+)Request {/gm;
|
||||
const source = await readFile(SOURCE).then((buffer) => buffer.toString());
|
||||
const requests = source.matchAll(requestPattern);
|
||||
|
||||
const imports: string[] = [];
|
||||
const names: string[] = [];
|
||||
|
||||
for (const match of requests) {
|
||||
const name = match[1];
|
||||
|
||||
imports.push(`${name}Request`, `${name}Response`);
|
||||
names.push(name);
|
||||
}
|
||||
|
||||
let content = "";
|
||||
|
||||
content += "import {\n";
|
||||
content += imports.map((i) => ` ${i},`).join("\n");
|
||||
content += `\n} from "${IMPORT}";\n`;
|
||||
|
||||
content += 'import { BaseProxyClient } from "./baseProxyClient";\n\n';
|
||||
|
||||
content +=
|
||||
"export abstract class MethodPopulatedProxyClient extends BaseProxyClient {\n";
|
||||
content += names
|
||||
.map((name) => {
|
||||
return ` ${name}(data: ${name}Request) {
|
||||
return this.request("${name}Request", ${name}Request, data, ${name}Response);
|
||||
}\n`;
|
||||
})
|
||||
.join("");
|
||||
content += "}\n";
|
||||
|
||||
await writeFile(TARGET, content);
|
||||
@@ -0,0 +1,58 @@
|
||||
import { $ } from "bun";
|
||||
import { mkdir, rm } from "node:fs/promises";
|
||||
|
||||
export class GitObjectStorage {
|
||||
constructor(
|
||||
private repo: string,
|
||||
private base: string,
|
||||
private token?: string,
|
||||
) {}
|
||||
|
||||
async init() {
|
||||
await rm(this.base, { recursive: true, force: true });
|
||||
|
||||
console.log(`Cloning ${this.repo} to ${this.base}`);
|
||||
|
||||
const url = this.token
|
||||
? `https://x-access-token:${this.token}@github.com/${this.repo}.git`
|
||||
: `https://github.com/${this.repo}.git`;
|
||||
|
||||
await $`git clone --depth 1 ${url} ${this.base}`;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
async commit() {
|
||||
const shell = $.cwd(this.base);
|
||||
|
||||
await shell`git add .`.quiet();
|
||||
await shell`git commit --amend --reset-author -m "ids update ${new Date().toISOString()}"`.quiet();
|
||||
await shell`git push --force-with-lease`;
|
||||
}
|
||||
|
||||
async mkdir(path: string) {
|
||||
await mkdir(`${this.base}/${path}`, { recursive: true });
|
||||
}
|
||||
|
||||
async rm(path: string) {
|
||||
await rm(`${this.base}/${path}`, { force: true });
|
||||
}
|
||||
|
||||
async write(path: string, bytes: Uint8Array | string) {
|
||||
await Bun.write(`${this.base}/${path}`, bytes);
|
||||
}
|
||||
|
||||
file(path: string) {
|
||||
return Bun.file(`${this.base}/${path}`);
|
||||
}
|
||||
|
||||
async bytes(path: string) {
|
||||
const file = this.file(path);
|
||||
return await file.bytes();
|
||||
}
|
||||
|
||||
async json<Type>(path: string) {
|
||||
const file = this.file(path);
|
||||
return (await file.json()) as Type;
|
||||
}
|
||||
}
|
||||
File renamed without changes.
@@ -0,0 +1,9 @@
|
||||
export const WORKING_DIR = `../../temp/ids`;
|
||||
export const REPO = "blitzkit/ids";
|
||||
|
||||
export const ACCOMMODATED_ID_COUNT = 10_000_000_000; // 10B ids
|
||||
export const MAX_BYTES_PER_CHUNK = 100 * 1_000_000; // 100MB
|
||||
|
||||
export const MAX_IDS_PER_CALL = 100;
|
||||
export const API_RATE = 10; // 10Hz
|
||||
export const MAX_DRY_STREAK = 10_000;
|
||||
@@ -1,73 +1,40 @@
|
||||
import { $ } from "bun";
|
||||
import { range } from "lodash-es";
|
||||
import { compress, decompress } from "lz4js";
|
||||
import { mkdir, rm } from "node:fs/promises";
|
||||
import { exit } from "node:process";
|
||||
import { IdArray } from "./idArray";
|
||||
import { GitObjectStorage } from "../core/blitzkit/gitObjectStorage";
|
||||
import { IdArray } from "../core/blitzkit/idArray";
|
||||
import {
|
||||
ACCOMMODATED_ID_COUNT,
|
||||
API_RATE,
|
||||
MAX_BYTES_PER_CHUNK,
|
||||
MAX_DRY_STREAK,
|
||||
MAX_IDS_PER_CALL,
|
||||
REPO,
|
||||
WORKING_DIR,
|
||||
} from "./constants";
|
||||
import { ManifestV1, QuickInfo, RegionDescriptor } from "./types";
|
||||
|
||||
interface ManifestV1 {
|
||||
version: 1;
|
||||
last_verified: number;
|
||||
chunks: number;
|
||||
total_count: number;
|
||||
}
|
||||
|
||||
interface RegionDescriptor {
|
||||
domain: string;
|
||||
seed: number;
|
||||
max: number;
|
||||
dry_streak: number;
|
||||
}
|
||||
|
||||
export interface QuickInfo {
|
||||
status: string;
|
||||
data: Record<string, {} | null>;
|
||||
}
|
||||
|
||||
const TEMP = "../../temp";
|
||||
const WORKING_DIR = `${TEMP}/ids`;
|
||||
const CHUNKS_DIR = `${WORKING_DIR}/chunks`;
|
||||
const REPO = "blitzkit/ids";
|
||||
|
||||
const ACCOMMODATED_ID_COUNT = 10_000_000_000; // 10B ids
|
||||
const MAX_BYTES_PER_CHUNK = 100 * 1_000_000; // 100MB
|
||||
|
||||
const MAX_IDS_PER_CALL = 100;
|
||||
const API_RATE = 10; // 10Hz
|
||||
const MAX_DRY_STREAK = 10_000;
|
||||
const storage = await new GitObjectStorage(
|
||||
REPO,
|
||||
WORKING_DIR,
|
||||
import.meta.env.GH_TOKEN,
|
||||
).init();
|
||||
|
||||
const t0 = performance.now();
|
||||
const maxT = 5.5 * 60 * 60 * 1000; // 5hr 30min
|
||||
const maxT = (5 * 60 + 55) * 60 * 1000; // 5hr 55min
|
||||
const t1 = t0 + maxT;
|
||||
|
||||
await mkdir(TEMP, { recursive: true });
|
||||
await rm(WORKING_DIR, { recursive: true, force: true });
|
||||
|
||||
console.log(`Cloning ${REPO} to ${WORKING_DIR}`);
|
||||
|
||||
const token = process.env.GH_TOKEN;
|
||||
const repoUrl = token
|
||||
? `https://x-access-token:${token}@github.com/${REPO}.git`
|
||||
: `https://github.com/${REPO}.git`;
|
||||
|
||||
await $`git clone --depth 1 ${repoUrl} ${WORKING_DIR}`;
|
||||
await mkdir(CHUNKS_DIR, { recursive: true });
|
||||
await storage.mkdir("chunks");
|
||||
|
||||
console.log(`Discovering pre-existing chunks...`);
|
||||
|
||||
const discoveredChunks: (IdArray | null)[] = [];
|
||||
|
||||
const manifest: ManifestV1 = await Bun.file(
|
||||
`${WORKING_DIR}/manifest.json`,
|
||||
).json();
|
||||
const manifest = await storage.json<ManifestV1>("manifest.json");
|
||||
|
||||
for (let i = 0; i < manifest.chunks; i++) {
|
||||
const path = `${CHUNKS_DIR}/chunk-${i}.dat.lz4`;
|
||||
const file = Bun.file(path);
|
||||
|
||||
const bytes = await file.bytes();
|
||||
const bytes = await storage.bytes(`chunks/chunk-${i}.dat.lz4`);
|
||||
const uncompressed = decompress(bytes);
|
||||
|
||||
const ids = IdArray.fromBytes(uncompressed);
|
||||
|
||||
discoveredChunks.push(ids);
|
||||
@@ -146,33 +113,25 @@ async function save() {
|
||||
|
||||
console.log("Saving chunks...");
|
||||
|
||||
await rm(`${CHUNKS_DIR}/*`, { force: true });
|
||||
await storage.rm("chunks/*");
|
||||
|
||||
let totalFound = 0;
|
||||
|
||||
for (let i = 0; i < chunks.length; i++) {
|
||||
const path = `${CHUNKS_DIR}/chunk-${i}.dat.lz4`;
|
||||
const chunk = chunks[i];
|
||||
const bytes = chunk.toBytes();
|
||||
const compressed = compress(bytes);
|
||||
|
||||
totalFound += chunk.size();
|
||||
|
||||
await Bun.write(path, compressed);
|
||||
await storage.write(`chunks/chunk-${i}.dat.lz4`, compressed);
|
||||
}
|
||||
|
||||
manifest.total_count = totalFound;
|
||||
|
||||
await Bun.write(
|
||||
`${WORKING_DIR}/manifest.json`,
|
||||
JSON.stringify(manifest, null, 2),
|
||||
);
|
||||
await storage.write("manifest.json", JSON.stringify(manifest, null, 2));
|
||||
|
||||
const git = $.cwd(WORKING_DIR);
|
||||
|
||||
await git`git add .`.quiet();
|
||||
await git`git commit --amend --reset-author -m "ids update ${new Date().toISOString()}"`.quiet();
|
||||
await git`git push --force-with-lease`.quiet();
|
||||
await storage.commit();
|
||||
|
||||
console.log(
|
||||
`Committed ${totalFound.toLocaleString()} total ids across ${manifest.chunks} chunks`,
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
export interface ManifestV1 {
|
||||
version: 1;
|
||||
last_verified: number;
|
||||
chunks: number;
|
||||
total_count: number;
|
||||
}
|
||||
|
||||
export interface RegionDescriptor {
|
||||
domain: string;
|
||||
seed: number;
|
||||
max: number;
|
||||
dry_streak: number;
|
||||
}
|
||||
|
||||
export interface QuickInfo {
|
||||
status: string;
|
||||
data: Record<string, {} | null>;
|
||||
}
|
||||
@@ -1,319 +0,0 @@
|
||||
import {
|
||||
availableProvisions,
|
||||
createDefaultProvisions,
|
||||
createDefaultSkills,
|
||||
fetchConsumableDefinitions,
|
||||
fetchEquipmentDefinitions,
|
||||
fetchModelDefinitions,
|
||||
fetchProvisionDefinitions,
|
||||
fetchSkillDefinitions,
|
||||
fetchTankDefinitions,
|
||||
TankDefinition,
|
||||
tankIcon,
|
||||
} from "@blitzkit/core";
|
||||
import { checkConsumableProvisionInclusivity } from "@blitzkit/core/src/blitzkit/checkConsumableProvisionInclusivity";
|
||||
import strings from "@blitzkit/i18n/strings/en.json";
|
||||
import { writeFile } from "fs/promises";
|
||||
import { isEqual } from "lodash-es";
|
||||
import { tankCharacteristics } from "../../website/src/core/blitzkit/tankCharacteristics";
|
||||
import { genericDefaultEquipmentMatrix } from "../../website/src/stores/duel/constants";
|
||||
|
||||
const domain = "https://opentest.blitzkit.app";
|
||||
|
||||
import.meta.env.PUBLIC_ASSET_BASE = "https://blitzkit.github.io/assets";
|
||||
const currentTankDefinitions = await fetchTankDefinitions();
|
||||
const currentConsumableDefinitions = await fetchConsumableDefinitions();
|
||||
const currentProvisionDefinitions = await fetchProvisionDefinitions();
|
||||
const currentEquipmentDefinitions = await fetchEquipmentDefinitions();
|
||||
const currentSkillDefinitions = await fetchSkillDefinitions();
|
||||
const currentModelDefinitions = await fetchModelDefinitions();
|
||||
|
||||
import.meta.env.PUBLIC_ASSET_BASE = "https://blitzkit.github.io/assets-preview";
|
||||
const previewTankDefinitions = await fetchTankDefinitions();
|
||||
const previewConsumableDefinitions = await fetchConsumableDefinitions();
|
||||
const previewProvisionDefinitions = await fetchProvisionDefinitions();
|
||||
const previewEquipmentDefinitions = await fetchEquipmentDefinitions();
|
||||
const previewSkillDefinitions = await fetchSkillDefinitions();
|
||||
const previewModelDefinitions = await fetchModelDefinitions();
|
||||
|
||||
let notes = "# BlitzKit OpenTest Patch Notes\n\n";
|
||||
|
||||
const newTanks: TankDefinition[] = [];
|
||||
|
||||
for (const id in previewTankDefinitions.tanks) {
|
||||
if (!(id in currentTankDefinitions.tanks)) {
|
||||
newTanks.push(previewTankDefinitions.tanks[id]);
|
||||
}
|
||||
}
|
||||
|
||||
newTanks.sort((a, b) => b.tier - a.tier);
|
||||
|
||||
import.meta.env.PUBLIC_ASSET_BASE = "https://blitzkit.github.io/assets-preview";
|
||||
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 += `})\n\n`;
|
||||
}
|
||||
|
||||
notes += "\n";
|
||||
}
|
||||
|
||||
const changedTanks: [TankDefinition, TankDefinition][] = [];
|
||||
|
||||
for (const stringId in previewTankDefinitions.tanks) {
|
||||
const id = Number(stringId);
|
||||
|
||||
if (newTanks.some((tank) => tank.id === id)) continue;
|
||||
|
||||
const current = currentTankDefinitions.tanks[id];
|
||||
const preview = previewTankDefinitions.tanks[id];
|
||||
|
||||
if (isEqual(current, preview)) continue;
|
||||
|
||||
changedTanks.push([current, preview]);
|
||||
}
|
||||
|
||||
changedTanks.sort((a, b) => b[1].tier - a[1].tier);
|
||||
|
||||
import.meta.env.PUBLIC_ASSET_BASE = "https://blitzkit.github.io/assets";
|
||||
if (changedTanks.length > 0) {
|
||||
notes += "## Changed Tanks\n\n";
|
||||
|
||||
for (const [current, preview] of changedTanks) {
|
||||
const currentTurret = current.turrets.at(-1)!;
|
||||
const previewTurret = preview.turrets.at(-1)!;
|
||||
const currentGun = currentTurret.guns.at(-1)!;
|
||||
const previewGun = previewTurret.guns.at(-1)!;
|
||||
const currentShell = currentGun.shells[0];
|
||||
const previewShell = previewGun.shells[0];
|
||||
const currentEngine = current.engines.at(-1)!;
|
||||
const previewEngine = preview.engines.at(-1)!;
|
||||
const currentTracks = current.tracks.at(-1)!;
|
||||
const previewTracks = preview.tracks.at(-1)!;
|
||||
const currentTankModel = currentModelDefinitions.models[current.id];
|
||||
const previewTankModel = previewModelDefinitions.models[preview.id];
|
||||
|
||||
const currentConsumables = Object.values(
|
||||
currentConsumableDefinitions.consumables,
|
||||
).filter((consumable) =>
|
||||
checkConsumableProvisionInclusivity(consumable, current, currentGun),
|
||||
);
|
||||
const previewConsumables = Object.values(
|
||||
previewConsumableDefinitions.consumables,
|
||||
).filter((consumable) =>
|
||||
checkConsumableProvisionInclusivity(consumable, preview, previewGun),
|
||||
);
|
||||
const addedConsumables = previewConsumables.filter(
|
||||
(previewConsumable) =>
|
||||
!currentConsumables.some(
|
||||
(currentConsumable) => currentConsumable.id === previewConsumable.id,
|
||||
),
|
||||
);
|
||||
const removedConsumables = currentConsumables.filter(
|
||||
(currentConsumable) =>
|
||||
!previewConsumables.some(
|
||||
(previewConsumable) => previewConsumable.id === currentConsumable.id,
|
||||
),
|
||||
);
|
||||
|
||||
const currentProvisions = availableProvisions(
|
||||
current,
|
||||
currentGun,
|
||||
currentProvisionDefinitions,
|
||||
);
|
||||
const previewProvisions = availableProvisions(
|
||||
preview,
|
||||
previewGun,
|
||||
previewProvisionDefinitions,
|
||||
);
|
||||
const addedProvisions = previewProvisions.filter(
|
||||
(previewProvision) =>
|
||||
!currentProvisions.some(
|
||||
(currentProvision) => currentProvision.id === previewProvision.id,
|
||||
),
|
||||
);
|
||||
const removedProvisions = currentProvisions.filter(
|
||||
(currentProvision) =>
|
||||
!previewProvisions.some(
|
||||
(previewProvision) => previewProvision.id === currentProvision.id,
|
||||
),
|
||||
);
|
||||
|
||||
const currentEquipment = currentEquipmentDefinitions.presets[
|
||||
current.equipment_preset
|
||||
].slots
|
||||
.map(({ left, right }) => [left, right])
|
||||
.flat();
|
||||
const previewEquipment = previewEquipmentDefinitions.presets[
|
||||
preview.equipment_preset
|
||||
].slots
|
||||
.map(({ left, right }) => [left, right])
|
||||
.flat();
|
||||
const addedEquipment = previewEquipment
|
||||
.filter(
|
||||
(previewEquipment) =>
|
||||
!currentEquipment.some(
|
||||
(currentEquipment) => currentEquipment === previewEquipment,
|
||||
),
|
||||
)
|
||||
.map((id) => previewEquipmentDefinitions.equipments[id]);
|
||||
const removedEquipment = currentEquipment
|
||||
.filter(
|
||||
(currentEquipment) =>
|
||||
!previewEquipment.some(
|
||||
(previewEquipment) => previewEquipment === currentEquipment,
|
||||
),
|
||||
)
|
||||
.map((id) => currentEquipmentDefinitions.equipments[id]);
|
||||
|
||||
const equalize = false;
|
||||
const currentCharacteristics = tankCharacteristics(
|
||||
{
|
||||
tank: current,
|
||||
gun: currentGun,
|
||||
shell: currentShell,
|
||||
applyDynamicArmor: false,
|
||||
applyReactiveArmor: false,
|
||||
applySpallLiner: false,
|
||||
assaultDistance: 0,
|
||||
crewSkills: createDefaultSkills(currentSkillDefinitions),
|
||||
camouflage: true,
|
||||
consumables: [],
|
||||
engine: currentEngine,
|
||||
provisions: createDefaultProvisions(
|
||||
current,
|
||||
currentGun,
|
||||
currentProvisionDefinitions,
|
||||
),
|
||||
equipmentMatrix: genericDefaultEquipmentMatrix,
|
||||
stockEngine: current.engines[0],
|
||||
stockGun: current.turrets[0].guns[0],
|
||||
stockTrack: current.tracks[0],
|
||||
stockTurret: current.turrets[0],
|
||||
track: currentTracks,
|
||||
turret: currentTurret,
|
||||
equalize,
|
||||
},
|
||||
{
|
||||
equipmentDefinitions: currentEquipmentDefinitions,
|
||||
provisionDefinitions: currentProvisionDefinitions,
|
||||
tankModelDefinition: currentTankModel,
|
||||
},
|
||||
);
|
||||
const previewCharacteristics = tankCharacteristics(
|
||||
{
|
||||
tank: preview,
|
||||
gun: previewGun,
|
||||
shell: previewShell,
|
||||
applyDynamicArmor: false,
|
||||
applyReactiveArmor: false,
|
||||
applySpallLiner: false,
|
||||
assaultDistance: 0,
|
||||
crewSkills: createDefaultSkills(previewSkillDefinitions),
|
||||
camouflage: true,
|
||||
consumables: [],
|
||||
engine: previewEngine,
|
||||
provisions: createDefaultProvisions(
|
||||
preview,
|
||||
previewGun,
|
||||
previewProvisionDefinitions,
|
||||
),
|
||||
equipmentMatrix: genericDefaultEquipmentMatrix,
|
||||
stockEngine: preview.engines[0],
|
||||
stockGun: preview.turrets[0].guns[0],
|
||||
stockTrack: preview.tracks[0],
|
||||
stockTurret: preview.turrets[0],
|
||||
track: previewTracks,
|
||||
turret: previewTurret,
|
||||
equalize,
|
||||
},
|
||||
{
|
||||
equipmentDefinitions: previewEquipmentDefinitions,
|
||||
provisionDefinitions: previewProvisionDefinitions,
|
||||
tankModelDefinition: previewTankModel,
|
||||
},
|
||||
);
|
||||
const changedCharacteristics: string[] = [];
|
||||
|
||||
for (const key in currentCharacteristics) {
|
||||
const characteristicKey = key as keyof typeof currentCharacteristics;
|
||||
if (
|
||||
currentCharacteristics[characteristicKey] !==
|
||||
previewCharacteristics[characteristicKey]
|
||||
) {
|
||||
changedCharacteristics.push(characteristicKey);
|
||||
}
|
||||
}
|
||||
|
||||
notes += `### [${preview.name!.locales.en}](${domain}/tanks/${preview.slug})\n`;
|
||||
notes += `})\n\n`;
|
||||
|
||||
if (addedConsumables.length + removedConsumables.length > 0) {
|
||||
notes += `Consumables: `;
|
||||
notes += [
|
||||
...addedConsumables.map(
|
||||
(consumable) => `🟢 ${consumable.name!.locales.en}`,
|
||||
),
|
||||
...removedConsumables.map(
|
||||
(consumable) => `🔴 ${consumable.name!.locales.en}`,
|
||||
),
|
||||
].join(", ");
|
||||
notes += `\n`;
|
||||
}
|
||||
|
||||
if (addedProvisions.length + removedProvisions.length > 0) {
|
||||
notes += `Provisions: `;
|
||||
notes += [
|
||||
...addedProvisions.map(
|
||||
(provision) => `🟢 ${provision.name!.locales.en}`,
|
||||
),
|
||||
...removedProvisions.map(
|
||||
(provision) => `🔴 ${provision.name!.locales.en}`,
|
||||
),
|
||||
].join(", ");
|
||||
notes += `\n`;
|
||||
}
|
||||
|
||||
if (addedEquipment.length + removedEquipment.length > 0) {
|
||||
notes += `-Equipment: `;
|
||||
notes += [
|
||||
...addedEquipment.map(
|
||||
(equipment) => `🟢 ${equipment.name!.locales.en}`,
|
||||
),
|
||||
...removedEquipment.map(
|
||||
(equipment) => `🔴 ${equipment.name!.locales.en}`,
|
||||
),
|
||||
].join(", ");
|
||||
notes += `\n`;
|
||||
}
|
||||
|
||||
if (changedCharacteristics.length > 0) {
|
||||
notes += `Characteristics:\n`;
|
||||
|
||||
for (const characteristicUntyped of changedCharacteristics) {
|
||||
const characteristic =
|
||||
characteristicUntyped as keyof typeof currentCharacteristics;
|
||||
const name =
|
||||
strings.website.tools.tankopedia.characteristics.values[
|
||||
characteristic
|
||||
];
|
||||
const before = currentCharacteristics[characteristic];
|
||||
const after = previewCharacteristics[characteristic];
|
||||
|
||||
notes += `- ${
|
||||
typeof before === "number" && typeof after === "number"
|
||||
? after > before
|
||||
? "⬆️"
|
||||
: "🔻"
|
||||
: "✏️"
|
||||
} ${name}: \`${
|
||||
typeof before === "number" ? before.toFixed(2) : before
|
||||
}\` → \`${typeof after === "number" ? after.toFixed(2) : after}\`\n`;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await writeFile("../../temp/patch-notes.md", notes);
|
||||
@@ -1,50 +0,0 @@
|
||||
import { type TranslationTree } from "@blitzkit/i18n";
|
||||
import locales from "@blitzkit/i18n/locales.json";
|
||||
import { writeFile } from "fs/promises";
|
||||
|
||||
const ROOT = "@blitzkit/i18n/strings";
|
||||
const WORKING_ROOT = "../../packages/i18n/strings";
|
||||
|
||||
const defaultStrings = require(`${ROOT}/${locales.default}.json`).default;
|
||||
const nonDefaultLocales = locales.supported.filter(
|
||||
({ locale }) => locale !== locales.default
|
||||
);
|
||||
|
||||
function prune(defaultStrings: TranslationTree, strings: TranslationTree) {
|
||||
for (const key in strings) {
|
||||
if (key in defaultStrings) {
|
||||
if (typeof defaultStrings[key] === typeof strings[key]) {
|
||||
if (typeof strings[key] === "string") {
|
||||
if (
|
||||
// are equivalent regardless of %s flavor
|
||||
(defaultStrings[key] as string).replaceAll(/%s\d/g, "%s") ===
|
||||
(strings[key] as string).replaceAll(/%s\d/g, "%s") ||
|
||||
// string is empty
|
||||
strings[key] === "" ||
|
||||
// string has old flavor of %s
|
||||
strings[key].match(/%s([^\d]|$)/g) !== null
|
||||
) {
|
||||
delete strings[key];
|
||||
}
|
||||
} else {
|
||||
prune(defaultStrings[key] as TranslationTree, strings[key]);
|
||||
}
|
||||
} else {
|
||||
delete strings[key];
|
||||
}
|
||||
} else {
|
||||
delete strings[key];
|
||||
}
|
||||
}
|
||||
|
||||
return strings;
|
||||
}
|
||||
|
||||
for (const locale of nonDefaultLocales) {
|
||||
console.log(`Pruning ${locale}...`);
|
||||
|
||||
const strings = require(`${ROOT}/${locale}.json`).default;
|
||||
const pruned = prune(defaultStrings, strings);
|
||||
|
||||
writeFile(`${WORKING_ROOT}/${locale}.json`, JSON.stringify(pruned, null, 2));
|
||||
}
|
||||
@@ -1,619 +0,0 @@
|
||||
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
|
||||
|
@@ -11,6 +11,11 @@ export const getStaticPaths = mixStaticPaths(_getStaticPaths, async () => {
|
||||
.dir(`Data/Gfx/Shared/tank-supply/ammunition/big`)
|
||||
.then((files) => files.filter((file) => file.endsWith("_l.txt")));
|
||||
|
||||
console.log(
|
||||
"Shell files raw",
|
||||
await vfs.dir(`Data/Gfx/Shared/tank-supply/ammunition/big`),
|
||||
);
|
||||
|
||||
console.log("Shell file paths", files);
|
||||
|
||||
for (const file of files) {
|
||||
|
||||
Reference in new issue
Block a user