@pietrovich/wot-utils 0.2.2 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -19
- package/dist/index.js +593 -204
- package/package.json +6 -3
- package/scripts/bake-color-dmg-fsr-vr-rld.sh +0 -81
- package/scripts/bake-pogs-clear.sh +0 -7
- package/scripts/bake-pogs-color-dmg-fsr-vr-rld.sh +0 -7
- package/scripts/baker-lib.sh +0 -165
- package/scripts/extract-atlas-assets.ps1 +0 -120
- package/scripts/extract-atlas-assets.sh +0 -116
- package/scripts/replace-suffixed-with-base.sh +0 -44
package/dist/index.js
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import "module";
|
|
5
5
|
import { config } from "dotenv";
|
|
6
|
-
import { resolve as
|
|
7
|
-
import { Command as
|
|
6
|
+
import { resolve as resolve6 } from "path";
|
|
7
|
+
import { Command as Command23 } from "commander";
|
|
8
8
|
|
|
9
9
|
// src/lib/WGData.ts
|
|
10
10
|
import { mkdirSync } from "fs";
|
|
@@ -382,10 +382,10 @@ function printVehiclesTable(vehicles) {
|
|
|
382
382
|
}
|
|
383
383
|
|
|
384
384
|
// src/commands/vehicle/list.ts
|
|
385
|
-
function listVehiclesCommand(
|
|
385
|
+
function listVehiclesCommand(app) {
|
|
386
386
|
return new Command("list").description("List vehicles from the WoT encyclopedia").option("--all", "show all vehicles (default: first 3)").option("--json", "output as JSON").action(async (options) => {
|
|
387
387
|
try {
|
|
388
|
-
const vehicles = await
|
|
388
|
+
const vehicles = await app.getVehicles();
|
|
389
389
|
const items = options.all ? vehicles : vehicles.slice(0, 3);
|
|
390
390
|
if (options.json) {
|
|
391
391
|
printJson(items);
|
|
@@ -405,10 +405,10 @@ function listVehiclesCommand(app2) {
|
|
|
405
405
|
|
|
406
406
|
// src/commands/vehicle/export.ts
|
|
407
407
|
import { Command as Command2 } from "commander";
|
|
408
|
-
function exportCommand(
|
|
408
|
+
function exportCommand(app) {
|
|
409
409
|
return new Command2("export").description("Export all vehicles to a JSON file").option("--output <path>", "output file path (default: wg-export-<timestamp>.json)").option("--no-cache", "bypass cache and fetch fresh data").action(async (options) => {
|
|
410
410
|
try {
|
|
411
|
-
await
|
|
411
|
+
await app.exportVehicles({ output: options.output, useCache: options.cache });
|
|
412
412
|
} catch (error) {
|
|
413
413
|
if (error instanceof WGApiError) {
|
|
414
414
|
console.error(`API error [${error.code}] ${error.field}: ${error.message}`);
|
|
@@ -422,20 +422,20 @@ function exportCommand(app2) {
|
|
|
422
422
|
|
|
423
423
|
// src/commands/cache/purge.ts
|
|
424
424
|
import { Command as Command3 } from "commander";
|
|
425
|
-
function cachePurgeCommand(
|
|
425
|
+
function cachePurgeCommand(app) {
|
|
426
426
|
return new Command3("purge").description("Delete all cached API responses").action(async () => {
|
|
427
|
-
await
|
|
427
|
+
await app.purgeCache();
|
|
428
428
|
console.error("Cache purged.");
|
|
429
429
|
});
|
|
430
430
|
}
|
|
431
431
|
|
|
432
432
|
// src/commands/vehicle/best-config.ts
|
|
433
433
|
import { Command as Command4 } from "commander";
|
|
434
|
-
function bestConfigCommand(
|
|
434
|
+
function bestConfigCommand(app) {
|
|
435
435
|
return new Command4("best-config").description("Infer best module configuration for a vehicle").argument("<query>", "tank_id (number), tag, or short_name").action(async (query) => {
|
|
436
436
|
try {
|
|
437
|
-
const result = await
|
|
438
|
-
console.log(
|
|
437
|
+
const result = await app.inferBestConfig(query);
|
|
438
|
+
console.log(app.configToProfileId(result));
|
|
439
439
|
} catch (error) {
|
|
440
440
|
if (error instanceof WGApiError) {
|
|
441
441
|
console.error(`API error [${error.code}] ${error.field}: ${error.message}`);
|
|
@@ -488,11 +488,11 @@ function printTable(rows) {
|
|
|
488
488
|
function extractProfile(data) {
|
|
489
489
|
return Object.values(data)[0];
|
|
490
490
|
}
|
|
491
|
-
function vehicleStatsCommand(
|
|
491
|
+
function vehicleStatsCommand(app) {
|
|
492
492
|
return new Command5("stats").description("Fetch stats for the best module configuration of a vehicle").argument("[query]", "tank_id (number), tag, or short_name").option("--all", "fetch stats for all vehicles and print as a table").option("-q, --quiet", "suppress progress output").option("--json", "output results as JSON array instead of a table (use with --all)").option("--raw", "print full JSON response (single vehicle only)").action(async (query, options) => {
|
|
493
493
|
try {
|
|
494
494
|
if (!query || options.all) {
|
|
495
|
-
const vehicles = await
|
|
495
|
+
const vehicles = await app.getVehicles();
|
|
496
496
|
const sorted = [...vehicles].sort((a, b) => a.short_name.localeCompare(b.short_name));
|
|
497
497
|
const targets = options.all ? sorted : sorted.slice(0, 10);
|
|
498
498
|
const rows = [];
|
|
@@ -505,7 +505,7 @@ function vehicleStatsCommand(app2) {
|
|
|
505
505
|
const results = await Promise.all(
|
|
506
506
|
batch.map(async (v) => {
|
|
507
507
|
try {
|
|
508
|
-
const data = await
|
|
508
|
+
const data = await app.getStatsForBestConfig(v.tank_id);
|
|
509
509
|
const profile = extractProfile(data);
|
|
510
510
|
return {
|
|
511
511
|
name: v.short_name,
|
|
@@ -532,7 +532,7 @@ function vehicleStatsCommand(app2) {
|
|
|
532
532
|
}
|
|
533
533
|
return;
|
|
534
534
|
}
|
|
535
|
-
const result = await
|
|
535
|
+
const result = await app.getStatsForBestConfig(query);
|
|
536
536
|
if (options.raw) {
|
|
537
537
|
console.log(JSON.stringify(result, null, 2));
|
|
538
538
|
return;
|
|
@@ -551,10 +551,10 @@ function vehicleStatsCommand(app2) {
|
|
|
551
551
|
|
|
552
552
|
// src/commands/vehicle/chars.ts
|
|
553
553
|
import { Command as Command6 } from "commander";
|
|
554
|
-
function charsCommand(
|
|
554
|
+
function charsCommand(app) {
|
|
555
555
|
return new Command6("chars").description("List unique characters found in vehicle short names").action(async () => {
|
|
556
556
|
try {
|
|
557
|
-
const { uniqueCharacters, maxLength, longestShortName, avgLength, medianLength, p80Length, p90Length } = await
|
|
557
|
+
const { uniqueCharacters, maxLength, longestShortName, avgLength, medianLength, p80Length, p90Length } = await app.getShortNameStats();
|
|
558
558
|
console.log("uniqueCharacters:", uniqueCharacters);
|
|
559
559
|
console.log("maxLength:", maxLength);
|
|
560
560
|
console.log("longestShortName:", longestShortName);
|
|
@@ -621,10 +621,10 @@ function save() {
|
|
|
621
621
|
}
|
|
622
622
|
|
|
623
623
|
// src/commands/vehicle/long-aliases.ts
|
|
624
|
-
function longAliasesCommand(
|
|
624
|
+
function longAliasesCommand(app) {
|
|
625
625
|
return new Command7("long-aliases").description("List vehicles whose shortened alias exceeds 10 characters").option("--update", "back-fill dictionary with aliases that are too long or unresolved").action(async (options) => {
|
|
626
626
|
try {
|
|
627
|
-
const vehicles = await
|
|
627
|
+
const vehicles = await app.getVehicles();
|
|
628
628
|
let dirty = false;
|
|
629
629
|
for (const vehicle2 of vehicles) {
|
|
630
630
|
const alias = lookupShortName(vehicle2);
|
|
@@ -1067,9 +1067,11 @@ var AtlasManager = class {
|
|
|
1067
1067
|
};
|
|
1068
1068
|
|
|
1069
1069
|
// src/commands/dds/decode.ts
|
|
1070
|
+
import { Command as Command13 } from "commander";
|
|
1071
|
+
|
|
1072
|
+
// src/lib/utils/dds.ts
|
|
1070
1073
|
import { readFile as readFile4, writeFile as writeFile6 } from "fs/promises";
|
|
1071
1074
|
import { extname as extname2, join as join4, dirname as dirname2, basename as basename3 } from "path";
|
|
1072
|
-
import { Command as Command13 } from "commander";
|
|
1073
1075
|
import { PNG as PNG3 } from "pngjs";
|
|
1074
1076
|
|
|
1075
1077
|
// src/lib/utex-mod/DDSUtils.ts
|
|
@@ -1636,22 +1638,26 @@ var DDSUtils = class _DDSUtils {
|
|
|
1636
1638
|
}
|
|
1637
1639
|
};
|
|
1638
1640
|
|
|
1641
|
+
// src/lib/utils/dds.ts
|
|
1642
|
+
async function convertToPngFile(file, pngFileName) {
|
|
1643
|
+
const buffer = await readFile4(file);
|
|
1644
|
+
const frames = new DDSUtils().decode(buffer.buffer);
|
|
1645
|
+
if (frames.length === 0) {
|
|
1646
|
+
throw new Error("No frames decoded from DDS file");
|
|
1647
|
+
}
|
|
1648
|
+
const frame = frames[0];
|
|
1649
|
+
const png = new PNG3({ width: frame.width, height: frame.height });
|
|
1650
|
+
png.data = Buffer.from(frame.image);
|
|
1651
|
+
const outName = pngFileName ?? basename3(file, extname2(file)) + ".png";
|
|
1652
|
+
const outPath = join4(dirname2(file), outName);
|
|
1653
|
+
await writeFile6(outPath, PNG3.sync.write(png));
|
|
1654
|
+
return outPath;
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1639
1657
|
// src/commands/dds/decode.ts
|
|
1640
1658
|
function ddsDecodeCommand() {
|
|
1641
1659
|
return new Command13("decode").description("Decode a DDS texture to a 32-bit RGBA PNG").argument("<file>", "path to the .dds file").action(async (file) => {
|
|
1642
|
-
const
|
|
1643
|
-
const frames = new DDSUtils().decode(buffer.buffer);
|
|
1644
|
-
if (frames.length === 0) {
|
|
1645
|
-
console.error("No frames decoded from DDS file");
|
|
1646
|
-
process.exit(1);
|
|
1647
|
-
}
|
|
1648
|
-
const frame = frames[0];
|
|
1649
|
-
const png = new PNG3({ width: frame.width, height: frame.height });
|
|
1650
|
-
png.data = Buffer.from(frame.image);
|
|
1651
|
-
const ext = extname2(file);
|
|
1652
|
-
const outName = basename3(file, ext) + ".png";
|
|
1653
|
-
const outPath = join4(dirname2(file), outName);
|
|
1654
|
-
await writeFile6(outPath, PNG3.sync.write(png));
|
|
1660
|
+
const outPath = await convertToPngFile(file);
|
|
1655
1661
|
console.log(`Decoded \u2192 ${outPath}`);
|
|
1656
1662
|
});
|
|
1657
1663
|
}
|
|
@@ -2221,35 +2227,6 @@ function nameText() {
|
|
|
2221
2227
|
};
|
|
2222
2228
|
}
|
|
2223
2229
|
|
|
2224
|
-
// src/lib/icons/pogs/PogsClear.ts
|
|
2225
|
-
var iconAligner = createAligner(PogsConstants, "bl.+", [18, 1]);
|
|
2226
|
-
var PogsClear = class {
|
|
2227
|
-
createBaker(app2) {
|
|
2228
|
-
return new ImageBaker(
|
|
2229
|
-
PogsConstants,
|
|
2230
|
-
[barAndShield(), vehicleIcon(app2, iconAligner), tierText(), nameText()]
|
|
2231
|
-
);
|
|
2232
|
-
}
|
|
2233
|
-
};
|
|
2234
|
-
|
|
2235
|
-
// src/lib/icons/pogs/PogsColor.ts
|
|
2236
|
-
var iconAligner2 = createAligner(PogsConstants, "bl.+", [18, 1]);
|
|
2237
|
-
var PogsColor = class {
|
|
2238
|
-
createBaker(app2) {
|
|
2239
|
-
return new ImageBaker(
|
|
2240
|
-
PogsConstants,
|
|
2241
|
-
[
|
|
2242
|
-
gradientBackground(),
|
|
2243
|
-
barAndShield(),
|
|
2244
|
-
vehicleIcon(app2, iconAligner2),
|
|
2245
|
-
tierText(),
|
|
2246
|
-
nameText()
|
|
2247
|
-
],
|
|
2248
|
-
(s) => s.removeAlpha()
|
|
2249
|
-
);
|
|
2250
|
-
}
|
|
2251
|
-
};
|
|
2252
|
-
|
|
2253
2230
|
// src/lib/icons/layers/pre-rendered-background.ts
|
|
2254
2231
|
import { readFile as readFile6 } from "fs/promises";
|
|
2255
2232
|
import { resolve as resolve4 } from "path";
|
|
@@ -2286,16 +2263,49 @@ function preRenderedBackground(version2, flavor = "") {
|
|
|
2286
2263
|
};
|
|
2287
2264
|
}
|
|
2288
2265
|
|
|
2266
|
+
// src/lib/icons/pogs/PogsBase.ts
|
|
2267
|
+
var PogsBase = class {
|
|
2268
|
+
version = 1;
|
|
2269
|
+
createBaker(data) {
|
|
2270
|
+
return new ImageBaker(
|
|
2271
|
+
PogsConstants,
|
|
2272
|
+
this.getBaseLayers(data)
|
|
2273
|
+
);
|
|
2274
|
+
}
|
|
2275
|
+
getBaseLayers(_data) {
|
|
2276
|
+
return [];
|
|
2277
|
+
}
|
|
2278
|
+
};
|
|
2279
|
+
|
|
2280
|
+
// src/lib/icons/pogs/PogsClearSimpleV1.ts
|
|
2281
|
+
var iconAligner = createAligner(PogsConstants, "bl.+", [18, 24]);
|
|
2282
|
+
var PogsClearSimpleV1 = class extends PogsBase {
|
|
2283
|
+
createBaker(data) {
|
|
2284
|
+
return new ImageBaker(
|
|
2285
|
+
PogsConstants,
|
|
2286
|
+
this.getBaseLayers(data)
|
|
2287
|
+
);
|
|
2288
|
+
}
|
|
2289
|
+
getBaseLayers(data) {
|
|
2290
|
+
return [
|
|
2291
|
+
preRenderedBackground(this.version, "clear"),
|
|
2292
|
+
vehicleIcon(data, iconAligner),
|
|
2293
|
+
tierText(),
|
|
2294
|
+
nameText()
|
|
2295
|
+
];
|
|
2296
|
+
}
|
|
2297
|
+
};
|
|
2298
|
+
|
|
2289
2299
|
// src/lib/icons/pogs/PogsClearV1.ts
|
|
2290
|
-
var
|
|
2300
|
+
var iconAligner2 = createAligner(PogsConstants, "bl.+", [18, "(bh - 1).+"]);
|
|
2291
2301
|
var PogsClearV1 = class {
|
|
2292
2302
|
version = 1;
|
|
2293
|
-
createBaker(
|
|
2303
|
+
createBaker(app) {
|
|
2294
2304
|
return new ImageBaker(
|
|
2295
2305
|
PogsConstants,
|
|
2296
2306
|
[
|
|
2297
2307
|
preRenderedBackground(this.version, "clear"),
|
|
2298
|
-
vehicleIcon(
|
|
2308
|
+
vehicleIcon(app, iconAligner2),
|
|
2299
2309
|
tierText(),
|
|
2300
2310
|
nameText()
|
|
2301
2311
|
]
|
|
@@ -2303,16 +2313,36 @@ var PogsClearV1 = class {
|
|
|
2303
2313
|
}
|
|
2304
2314
|
};
|
|
2305
2315
|
|
|
2306
|
-
// src/lib/icons/pogs/
|
|
2307
|
-
var
|
|
2316
|
+
// src/lib/icons/pogs/PogsClearSimpleV2.ts
|
|
2317
|
+
var PogsClearSimpleV2 = class extends PogsClearSimpleV1 {
|
|
2308
2318
|
version = 2;
|
|
2309
2319
|
};
|
|
2310
2320
|
|
|
2321
|
+
// src/lib/icons/pogs/PogsColorSimpleV1.ts
|
|
2322
|
+
var iconAligner3 = createAligner(PogsConstants, "bl.+", [18, 24]);
|
|
2323
|
+
var PogsColorSimpleV1 = class extends PogsBase {
|
|
2324
|
+
createBaker(data) {
|
|
2325
|
+
return new ImageBaker(
|
|
2326
|
+
PogsConstants,
|
|
2327
|
+
this.getBaseLayers(data),
|
|
2328
|
+
(s) => s.removeAlpha()
|
|
2329
|
+
);
|
|
2330
|
+
}
|
|
2331
|
+
getBaseLayers(data) {
|
|
2332
|
+
return [
|
|
2333
|
+
preRenderedBackground(this.version, ""),
|
|
2334
|
+
vehicleIcon(data, iconAligner3),
|
|
2335
|
+
tierText(),
|
|
2336
|
+
nameText()
|
|
2337
|
+
];
|
|
2338
|
+
}
|
|
2339
|
+
};
|
|
2340
|
+
|
|
2311
2341
|
// src/lib/icons/layers/text-view-range.ts
|
|
2312
2342
|
var defaultAligner3 = createAligner(PogsConstants, "br.+", [37, "bh - 1"]);
|
|
2313
|
-
function textViewRange(
|
|
2343
|
+
function textViewRange(app, aligner = defaultAligner3) {
|
|
2314
2344
|
return async (_box, _prev, vehicle2) => {
|
|
2315
|
-
const profiles = await
|
|
2345
|
+
const profiles = await app.getStatsForBestConfig(vehicle2);
|
|
2316
2346
|
const viewRange = profiles[vehicle2.tank_id]?.turret?.view_range;
|
|
2317
2347
|
if (viewRange === void 0) {
|
|
2318
2348
|
return null;
|
|
@@ -2331,9 +2361,9 @@ function textViewRange(app2, aligner = defaultAligner3) {
|
|
|
2331
2361
|
|
|
2332
2362
|
// src/lib/icons/layers/text-reload.ts
|
|
2333
2363
|
var defaultAligner4 = createAligner(PogsConstants, "br.+", [37, "bh - 8"]);
|
|
2334
|
-
function textReload(
|
|
2364
|
+
function textReload(app, aligner = defaultAligner4) {
|
|
2335
2365
|
return async (_box, _prev, vehicle2) => {
|
|
2336
|
-
const profiles = await
|
|
2366
|
+
const profiles = await app.getStatsForBestConfig(vehicle2);
|
|
2337
2367
|
const reloadTime = profiles[vehicle2.tank_id]?.gun?.reload_time;
|
|
2338
2368
|
if (reloadTime === void 0) {
|
|
2339
2369
|
return null;
|
|
@@ -2350,17 +2380,16 @@ function textReload(app2, aligner = defaultAligner4) {
|
|
|
2350
2380
|
};
|
|
2351
2381
|
}
|
|
2352
2382
|
|
|
2353
|
-
// src/lib/icons/layers/text-
|
|
2354
|
-
var defaultAligner5 = createAligner(PogsConstants, "
|
|
2355
|
-
function
|
|
2383
|
+
// src/lib/icons/layers/text-penetration.ts
|
|
2384
|
+
var defaultAligner5 = createAligner(PogsConstants, "rt", ["r", 2]);
|
|
2385
|
+
function textPenetration(app, aligner = defaultAligner5) {
|
|
2356
2386
|
return async (_box, _prev, vehicle2) => {
|
|
2357
|
-
const profiles = await
|
|
2358
|
-
const
|
|
2359
|
-
if (
|
|
2387
|
+
const profiles = await app.getStatsForBestConfig(vehicle2);
|
|
2388
|
+
const text = profiles[vehicle2.tank_id]?.ammo?.[0].penetration?.[1];
|
|
2389
|
+
if (text === void 0) {
|
|
2360
2390
|
return null;
|
|
2361
2391
|
}
|
|
2362
|
-
const
|
|
2363
|
-
const { data, width, height } = await renderWithShadow("pogs4px", text, Colors.white);
|
|
2392
|
+
const { data, width, height } = await renderWithShadow("pogs4px", text, Colors.beige);
|
|
2364
2393
|
const { left, top } = aligner.align({ width, height });
|
|
2365
2394
|
return {
|
|
2366
2395
|
input: data,
|
|
@@ -2372,20 +2401,17 @@ function textHullArmor(app2, aligner = defaultAligner5) {
|
|
|
2372
2401
|
};
|
|
2373
2402
|
}
|
|
2374
2403
|
|
|
2375
|
-
// src/lib/icons/layers/text-
|
|
2376
|
-
var defaultAligner6 = createAligner(PogsConstants, "
|
|
2377
|
-
function
|
|
2378
|
-
return async (_box,
|
|
2379
|
-
const profiles = await
|
|
2380
|
-
const
|
|
2381
|
-
if (
|
|
2404
|
+
// src/lib/icons/layers/text-damage.ts
|
|
2405
|
+
var defaultAligner6 = createAligner(PogsConstants, "rt", ["r", 9]);
|
|
2406
|
+
function textDamage(app, aligner = defaultAligner6) {
|
|
2407
|
+
return async (_box, _prev, vehicle2) => {
|
|
2408
|
+
const profiles = await app.getStatsForBestConfig(vehicle2);
|
|
2409
|
+
const text = profiles[vehicle2.tank_id]?.ammo?.[0].damage?.[1];
|
|
2410
|
+
if (text === void 0) {
|
|
2382
2411
|
return null;
|
|
2383
2412
|
}
|
|
2384
|
-
const
|
|
2385
|
-
const {
|
|
2386
|
-
const offsetX = -1 * prev.meta.width + width;
|
|
2387
|
-
const shifted = aligner.shift(offsetX, 0);
|
|
2388
|
-
const { left, top } = shifted.align({ width, height });
|
|
2413
|
+
const { data, width, height } = await renderWithShadow("pogs4px", text, Colors.beige);
|
|
2414
|
+
const { left, top } = aligner.align({ width, height });
|
|
2389
2415
|
return {
|
|
2390
2416
|
input: data,
|
|
2391
2417
|
raw: { width, height, channels: 4 },
|
|
@@ -2396,16 +2422,17 @@ function textTurretArmor(app2, aligner = defaultAligner6) {
|
|
|
2396
2422
|
};
|
|
2397
2423
|
}
|
|
2398
2424
|
|
|
2399
|
-
// src/lib/icons/layers/text-
|
|
2400
|
-
var defaultAligner7 = createAligner(PogsConstants, "
|
|
2401
|
-
function
|
|
2425
|
+
// src/lib/icons/layers/text-hull-armor.ts
|
|
2426
|
+
var defaultAligner7 = createAligner(PogsConstants, "br", ["r", "b - 1"]);
|
|
2427
|
+
function textHullArmor(app, aligner = defaultAligner7) {
|
|
2402
2428
|
return async (_box, _prev, vehicle2) => {
|
|
2403
|
-
const profiles = await
|
|
2404
|
-
const
|
|
2405
|
-
if (
|
|
2429
|
+
const profiles = await app.getStatsForBestConfig(vehicle2);
|
|
2430
|
+
const hull = profiles[vehicle2.tank_id]?.armor?.hull;
|
|
2431
|
+
if (hull === void 0) {
|
|
2406
2432
|
return null;
|
|
2407
2433
|
}
|
|
2408
|
-
const
|
|
2434
|
+
const text = `${hull.front}*${hull.sides}*${hull.rear}`;
|
|
2435
|
+
const { data, width, height } = await renderWithShadow("pogs4px", text, Colors.white);
|
|
2409
2436
|
const { left, top } = aligner.align({ width, height });
|
|
2410
2437
|
return {
|
|
2411
2438
|
input: data,
|
|
@@ -2417,17 +2444,20 @@ function textPenetration(app2, aligner = defaultAligner7) {
|
|
|
2417
2444
|
};
|
|
2418
2445
|
}
|
|
2419
2446
|
|
|
2420
|
-
// src/lib/icons/layers/text-
|
|
2421
|
-
var defaultAligner8 = createAligner(PogsConstants, "
|
|
2422
|
-
function
|
|
2423
|
-
return async (_box,
|
|
2424
|
-
const profiles = await
|
|
2425
|
-
const
|
|
2426
|
-
if (
|
|
2447
|
+
// src/lib/icons/layers/text-turret-armor.ts
|
|
2448
|
+
var defaultAligner8 = createAligner(PogsConstants, "br", ["r", "b - 8"]);
|
|
2449
|
+
function textTurretArmor(app, aligner = defaultAligner8) {
|
|
2450
|
+
return async (_box, prev, vehicle2) => {
|
|
2451
|
+
const profiles = await app.getStatsForBestConfig(vehicle2);
|
|
2452
|
+
const turret = profiles[vehicle2.tank_id]?.armor?.turret;
|
|
2453
|
+
if (!turret) {
|
|
2427
2454
|
return null;
|
|
2428
2455
|
}
|
|
2429
|
-
const
|
|
2430
|
-
const {
|
|
2456
|
+
const text = `${turret.front}*`;
|
|
2457
|
+
const { data, width, height } = await renderWithShadow("pogs4px", text, Colors.white);
|
|
2458
|
+
const offsetX = -1 * prev.meta.width + width;
|
|
2459
|
+
const shifted = aligner.shift(offsetX, 0);
|
|
2460
|
+
const { left, top } = shifted.align({ width, height });
|
|
2431
2461
|
return {
|
|
2432
2462
|
input: data,
|
|
2433
2463
|
raw: { width, height, channels: 4 },
|
|
@@ -2438,23 +2468,26 @@ function textDamage(app2, aligner = defaultAligner8) {
|
|
|
2438
2468
|
};
|
|
2439
2469
|
}
|
|
2440
2470
|
|
|
2471
|
+
// src/lib/icons/pogs/max-rld-fsr-vr.ts
|
|
2472
|
+
function getLayers(data) {
|
|
2473
|
+
return [
|
|
2474
|
+
textViewRange(data),
|
|
2475
|
+
textReload(data),
|
|
2476
|
+
textPenetration(data),
|
|
2477
|
+
textDamage(data),
|
|
2478
|
+
textHullArmor(data),
|
|
2479
|
+
textTurretArmor(data)
|
|
2480
|
+
];
|
|
2481
|
+
}
|
|
2482
|
+
|
|
2441
2483
|
// src/lib/icons/pogs/PogsColorV1.ts
|
|
2442
|
-
var PogsColorV1 = class {
|
|
2443
|
-
version = 1;
|
|
2484
|
+
var PogsColorV1 = class extends PogsColorSimpleV1 {
|
|
2444
2485
|
createBaker(data) {
|
|
2445
2486
|
return new ImageBaker(
|
|
2446
2487
|
PogsConstants,
|
|
2447
2488
|
[
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
textViewRange(data),
|
|
2451
|
-
textReload(data),
|
|
2452
|
-
textPenetration(data),
|
|
2453
|
-
textDamage(data),
|
|
2454
|
-
textHullArmor(data),
|
|
2455
|
-
textTurretArmor(data),
|
|
2456
|
-
tierText(),
|
|
2457
|
-
nameText()
|
|
2489
|
+
...this.getBaseLayers(data),
|
|
2490
|
+
...getLayers(data)
|
|
2458
2491
|
],
|
|
2459
2492
|
(s) => s.removeAlpha()
|
|
2460
2493
|
);
|
|
@@ -2466,6 +2499,24 @@ var PogsColorV2 = class extends PogsColorV1 {
|
|
|
2466
2499
|
version = 2;
|
|
2467
2500
|
};
|
|
2468
2501
|
|
|
2502
|
+
// src/lib/icons/pogs/PogsColorSimpleV2.ts
|
|
2503
|
+
var PogsColorSimpleV2 = class extends PogsColorSimpleV1 {
|
|
2504
|
+
version = 2;
|
|
2505
|
+
};
|
|
2506
|
+
|
|
2507
|
+
// src/lib/icons/pogs/PogsClearV2.ts
|
|
2508
|
+
var PogsClearV2 = class extends PogsClearSimpleV2 {
|
|
2509
|
+
createBaker(data) {
|
|
2510
|
+
return new ImageBaker(
|
|
2511
|
+
PogsConstants,
|
|
2512
|
+
[
|
|
2513
|
+
...this.getBaseLayers(data),
|
|
2514
|
+
...getLayers(data)
|
|
2515
|
+
]
|
|
2516
|
+
);
|
|
2517
|
+
}
|
|
2518
|
+
};
|
|
2519
|
+
|
|
2469
2520
|
// src/commands/icon/render.ts
|
|
2470
2521
|
var CONCURRENCY = 5;
|
|
2471
2522
|
async function renderOneToFile(vehicle2, baker, outDir) {
|
|
@@ -2473,8 +2524,8 @@ async function renderOneToFile(vehicle2, baker, outDir) {
|
|
|
2473
2524
|
const info = await (await baker.bake(vehicle2)).png().toFile(outPath);
|
|
2474
2525
|
console.log(`${outPath} \u2014 ${info.width}\xD7${info.height}px`);
|
|
2475
2526
|
}
|
|
2476
|
-
function iconRenderCommand(
|
|
2477
|
-
return new Command16("render").description("Render a vehicle icon with its short name label composited over a type background").argument("[query]", "tank_id (number), tag, or short_name").option("--all", "render all vehicles").option("--color", "use color variant (default)").option("--clear", "use clear variant (no color background)").option("--bg <version>", "use pre-rendered background at given version").option("--pre-rendered-bg <version>", "alias for --bg").option("--to <dir>", "output directory (default: current working directory)").option("--create", "create output directory if it does not exist").action(async (query, options) => {
|
|
2527
|
+
function iconRenderCommand(app) {
|
|
2528
|
+
return new Command16("render").description("Render a vehicle icon with its short name label composited over a type background").argument("[query]", "tank_id (number), tag, or short_name").option("--all", "render all vehicles").option("--color", "use color variant (default)").option("--clear", "use clear variant (no color background)").option("--simple", "use simple icon variant (no extra-data)").option("--bg <version>", "use pre-rendered background at given version").option("--pre-rendered-bg <version>", "alias for --bg").option("--to <dir>", "output directory (default: current working directory)").option("--create", "create output directory if it does not exist").action(async (query, options) => {
|
|
2478
2529
|
try {
|
|
2479
2530
|
if (!query && !options.all) {
|
|
2480
2531
|
console.error("Provide a query argument or use --all to render all vehicles.");
|
|
@@ -2489,21 +2540,29 @@ Provide an existing path or add --create to create it.`);
|
|
|
2489
2540
|
}
|
|
2490
2541
|
mkdirSync2(outDir, { recursive: true });
|
|
2491
2542
|
}
|
|
2492
|
-
const bgVersion = options.bg ?? options.preRenderedBg ?? (options.clear ? "v2" : void 0);
|
|
2493
2543
|
let builder;
|
|
2494
2544
|
const useColor = !options.clear;
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2545
|
+
const useSimple = options.simple;
|
|
2546
|
+
let bgVersion = options.bg ?? options.preRenderedBg ?? "v1";
|
|
2547
|
+
if (options.clear) {
|
|
2548
|
+
bgVersion = "v2";
|
|
2549
|
+
}
|
|
2550
|
+
const version2 = parseInt(bgVersion.replace(/\D+/g, ""), 10);
|
|
2551
|
+
if (version2 === 1) {
|
|
2552
|
+
if (useSimple) {
|
|
2553
|
+
builder = useColor ? new PogsColorSimpleV1() : new PogsClearSimpleV1();
|
|
2554
|
+
} else {
|
|
2498
2555
|
builder = useColor ? new PogsColorV1() : new PogsClearV1();
|
|
2556
|
+
}
|
|
2557
|
+
} else {
|
|
2558
|
+
if (useSimple) {
|
|
2559
|
+
builder = useColor ? new PogsColorSimpleV2() : new PogsClearSimpleV2();
|
|
2499
2560
|
} else {
|
|
2500
2561
|
builder = useColor ? new PogsColorV2() : new PogsClearV2();
|
|
2501
2562
|
}
|
|
2502
|
-
} else {
|
|
2503
|
-
builder = useColor ? new PogsColor() : new PogsClear();
|
|
2504
2563
|
}
|
|
2505
|
-
const vehicles = options.all ? await
|
|
2506
|
-
const bakers = Array.from({ length: CONCURRENCY }, () => builder.createBaker(
|
|
2564
|
+
const vehicles = options.all ? await app.getVehicles() : [await app.findVehicle(query)];
|
|
2565
|
+
const bakers = Array.from({ length: CONCURRENCY }, () => builder.createBaker(app));
|
|
2507
2566
|
let idx = 0;
|
|
2508
2567
|
await Promise.all(
|
|
2509
2568
|
bakers.map(async (baker) => {
|
|
@@ -2543,14 +2602,14 @@ function parseSize(input) {
|
|
|
2543
2602
|
throw new Error(`Invalid size "${input}". Use xs, s/small, m/medium, or l/large.`);
|
|
2544
2603
|
}
|
|
2545
2604
|
}
|
|
2546
|
-
function iconFetchCommand(
|
|
2605
|
+
function iconFetchCommand(app) {
|
|
2547
2606
|
return new Command17("fetch").description("Download vehicle icons into .data/icons/{size}/").argument("[query]", "tank_id (number), tag, or short_name \u2014 omit to fetch all").option("--size <size>", "icon size: s/small, m/medium (default), l/large", "medium").option("--all", "fetch icons for all vehicles").option("--force", "re-download icons that already exist locally").option("--concurrency <n>", "parallel downloads", "10").action(async (query, options) => {
|
|
2548
2607
|
if (!query && !options.all) {
|
|
2549
2608
|
console.error("Provide a query to fetch a single vehicle, or pass --all to fetch every vehicle.");
|
|
2550
2609
|
process.exit(1);
|
|
2551
2610
|
}
|
|
2552
2611
|
try {
|
|
2553
|
-
await
|
|
2612
|
+
await app.fetchIcons({
|
|
2554
2613
|
query,
|
|
2555
2614
|
size: parseSize(options.size),
|
|
2556
2615
|
force: options.force,
|
|
@@ -2569,14 +2628,14 @@ function iconFetchCommand(app2) {
|
|
|
2569
2628
|
|
|
2570
2629
|
// src/commands/icon/shrink.ts
|
|
2571
2630
|
import { Command as Command18 } from "commander";
|
|
2572
|
-
function iconShrinkCommand(
|
|
2631
|
+
function iconShrinkCommand(app) {
|
|
2573
2632
|
return new Command18("shrink").description("Fetch medium icons, trim transparent borders, save as xs in .data/icons/xs/").argument("[query]", "tank_id (number), tag, or short_name \u2014 omit to process all").option("--all", "process icons for all vehicles").option("--force", "re-process icons that are already cached").option("--concurrency <n>", "parallel downloads", "10").action(async (query, options) => {
|
|
2574
2633
|
if (!query && !options.all) {
|
|
2575
2634
|
console.error("Provide a query to shrink a single vehicle icon, or pass --all to process every vehicle.");
|
|
2576
2635
|
process.exit(1);
|
|
2577
2636
|
}
|
|
2578
2637
|
try {
|
|
2579
|
-
await
|
|
2638
|
+
await app.fetchIcons({
|
|
2580
2639
|
query,
|
|
2581
2640
|
size: "xs",
|
|
2582
2641
|
force: options.force ?? false,
|
|
@@ -2624,8 +2683,8 @@ var TomatoApi = class {
|
|
|
2624
2683
|
if (!forceUpdate && await this.hasData(vehicleId, filename)) {
|
|
2625
2684
|
return { vehicleId, fileName: filename, success: true, elapsed: Date.now() - t0, data: await this.loadData(vehicleId, filename) };
|
|
2626
2685
|
}
|
|
2627
|
-
return new Promise((
|
|
2628
|
-
this.queue.push(() => this.runVehicleVisuals(vehicleId, filename,
|
|
2686
|
+
return new Promise((resolve7) => {
|
|
2687
|
+
this.queue.push(() => this.runVehicleVisuals(vehicleId, filename, resolve7));
|
|
2629
2688
|
void this.drain();
|
|
2630
2689
|
});
|
|
2631
2690
|
}
|
|
@@ -2635,8 +2694,8 @@ var TomatoApi = class {
|
|
|
2635
2694
|
if (!forceUpdate && await this.hasData(vehicleId, filename)) {
|
|
2636
2695
|
return { vehicleId, fileName: filename, success: true, elapsed: Date.now() - t0, data: await this.loadData(vehicleId, filename) };
|
|
2637
2696
|
}
|
|
2638
|
-
return new Promise((
|
|
2639
|
-
this.queue.push(() => this.runVehicleLoadouts(vehicleId, filename,
|
|
2697
|
+
return new Promise((resolve7) => {
|
|
2698
|
+
this.queue.push(() => this.runVehicleLoadouts(vehicleId, filename, resolve7));
|
|
2640
2699
|
void this.drain();
|
|
2641
2700
|
});
|
|
2642
2701
|
}
|
|
@@ -2646,47 +2705,47 @@ var TomatoApi = class {
|
|
|
2646
2705
|
if (!forceUpdate && await this.hasData(vehicleId, filename)) {
|
|
2647
2706
|
return { vehicleId, fileName: filename, success: true, elapsed: Date.now() - t0, data: await this.loadData(vehicleId, filename) };
|
|
2648
2707
|
}
|
|
2649
|
-
return new Promise((
|
|
2650
|
-
this.queue.push(() => this.runVehicleProLoadouts(vehicleId, filename,
|
|
2708
|
+
return new Promise((resolve7) => {
|
|
2709
|
+
this.queue.push(() => this.runVehicleProLoadouts(vehicleId, filename, resolve7));
|
|
2651
2710
|
void this.drain();
|
|
2652
2711
|
});
|
|
2653
2712
|
}
|
|
2654
|
-
async runVehicleVisuals(vehicleId, filename,
|
|
2713
|
+
async runVehicleVisuals(vehicleId, filename, resolve7) {
|
|
2655
2714
|
const t0 = Date.now();
|
|
2656
2715
|
const url = `https://tomato.gg/wot/vehicles/visuals/${vehicleId}.json`;
|
|
2657
2716
|
try {
|
|
2658
2717
|
const data = await this.request(url);
|
|
2659
2718
|
await this.saveResponse(vehicleId, filename, data);
|
|
2660
|
-
|
|
2719
|
+
resolve7({ vehicleId, fileName: filename, success: true, elapsed: Date.now() - t0, data });
|
|
2661
2720
|
} catch (err) {
|
|
2662
|
-
|
|
2721
|
+
resolve7({ vehicleId, fileName: filename, success: false, elapsed: Date.now() - t0, data: void 0, error: err instanceof Error ? err : new Error(String(err)) });
|
|
2663
2722
|
throw err;
|
|
2664
2723
|
}
|
|
2665
2724
|
}
|
|
2666
|
-
async runVehicleLoadouts(vehicleId, filename,
|
|
2725
|
+
async runVehicleLoadouts(vehicleId, filename, resolve7) {
|
|
2667
2726
|
const t0 = Date.now();
|
|
2668
2727
|
const url = `https://api.tomato.gg/api/tank/loadout-performance/${vehicleId}?cache=true`;
|
|
2669
2728
|
try {
|
|
2670
2729
|
const data = await this.request(url);
|
|
2671
2730
|
await this.saveResponse(vehicleId, filename, data);
|
|
2672
|
-
|
|
2731
|
+
resolve7({ vehicleId, fileName: filename, success: true, elapsed: Date.now() - t0, data });
|
|
2673
2732
|
} catch (err) {
|
|
2674
|
-
|
|
2733
|
+
resolve7({ vehicleId, fileName: filename, success: false, elapsed: Date.now() - t0, data: void 0, error: err instanceof Error ? err : new Error(String(err)) });
|
|
2675
2734
|
throw err;
|
|
2676
2735
|
}
|
|
2677
2736
|
}
|
|
2678
|
-
async runVehicleProLoadouts(vehicleId, filename,
|
|
2737
|
+
async runVehicleProLoadouts(vehicleId, filename, resolve7) {
|
|
2679
2738
|
const t0 = Date.now();
|
|
2680
2739
|
const url = `https://api.tomato.gg/api/tank/top-loadouts/${vehicleId}?cache=true`;
|
|
2681
2740
|
try {
|
|
2682
2741
|
const data = await this.request(url);
|
|
2683
2742
|
await this.saveResponse(vehicleId, filename, data);
|
|
2684
|
-
|
|
2743
|
+
resolve7({ vehicleId, fileName: filename, success: true, elapsed: Date.now() - t0, data });
|
|
2685
2744
|
} catch (err) {
|
|
2686
2745
|
if (err instanceof HttpError && err.status === 404) {
|
|
2687
2746
|
await this.saveResponse(vehicleId, filename, null);
|
|
2688
2747
|
}
|
|
2689
|
-
|
|
2748
|
+
resolve7({ vehicleId, fileName: filename, success: false, elapsed: Date.now() - t0, data: void 0, error: err instanceof Error ? err : new Error(String(err)) });
|
|
2690
2749
|
throw err;
|
|
2691
2750
|
}
|
|
2692
2751
|
}
|
|
@@ -2818,11 +2877,11 @@ function printSummary(succeeded, failed) {
|
|
|
2818
2877
|
const summary = failed === 0 ? `${total} requests, ${succeeded} succeeded` : `${total} requests, ${succeeded} succeeded, ${failed} failed`;
|
|
2819
2878
|
console.log(summary);
|
|
2820
2879
|
}
|
|
2821
|
-
function tomatoFetchCommand(
|
|
2880
|
+
function tomatoFetchCommand(app, tomato2) {
|
|
2822
2881
|
return new Command19("fetch").description("Fetch Tomato.gg data for one vehicle or a filtered batch").argument("[query]", "tank_id (number), tag, or short_name").option("--tier <n>", "tier filter for batch mode", Number, 11).option("--lt", "include light tanks").option("--mt", "include medium tanks").option("--ht", "include heavy tanks").option("--td", "include tank destroyers (AT-SPG)").option("--at", "include SPGs (artillery)").action(async (query, options) => {
|
|
2823
2882
|
try {
|
|
2824
2883
|
if (query) {
|
|
2825
|
-
const vehicle2 = await
|
|
2884
|
+
const vehicle2 = await app.findVehicle(query);
|
|
2826
2885
|
console.error(`Fetching data for ${vehicle2.short_name} (${vehicle2.tank_id})\u2026`);
|
|
2827
2886
|
const results = await fetchVehicle(tomato2, vehicle2);
|
|
2828
2887
|
printErrors(results);
|
|
@@ -2841,7 +2900,7 @@ function tomatoFetchCommand(app2, tomato2) {
|
|
|
2841
2900
|
}
|
|
2842
2901
|
const selectedTypes = new Set(activeAliases.map(fromTypeAlias));
|
|
2843
2902
|
const tier = options.tier;
|
|
2844
|
-
const vehicles = await
|
|
2903
|
+
const vehicles = await app.getVehicles();
|
|
2845
2904
|
const targets = vehicles.filter((v) => v.tier === tier && selectedTypes.has(v.type));
|
|
2846
2905
|
if (targets.length === 0) {
|
|
2847
2906
|
console.error(`No vehicles match tier ${tier} with the given type filters.`);
|
|
@@ -2876,68 +2935,395 @@ function tomatoFetchCommand(app2, tomato2) {
|
|
|
2876
2935
|
}
|
|
2877
2936
|
|
|
2878
2937
|
// src/commands/bake/run.ts
|
|
2879
|
-
import {
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
import {
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2938
|
+
import { Command as Command21 } from "commander";
|
|
2939
|
+
|
|
2940
|
+
// src/commands/bake/all.ts
|
|
2941
|
+
import { join as join12 } from "path";
|
|
2942
|
+
|
|
2943
|
+
// src/lib/pipeline/pogs.ts
|
|
2944
|
+
import { access as access3, copyFile, mkdir as mkdir5, readdir as readdir3, rename, rm as rm2, unlink, writeFile as writeFile10 } from "fs/promises";
|
|
2945
|
+
import { join as join10 } from "path";
|
|
2946
|
+
|
|
2947
|
+
// src/lib/utils/game-resources.ts
|
|
2948
|
+
import { access as access2, mkdir as mkdir4, readdir as readdir2, readFile as readFile8, writeFile as writeFile9 } from "fs/promises";
|
|
2949
|
+
import { basename as basename5, join as join9 } from "path";
|
|
2950
|
+
import { unzip } from "fflate";
|
|
2951
|
+
var GUI_PKG_RE = /gui-part\d+\.pkg$/;
|
|
2952
|
+
var ICON_ATLAS_FILES = /* @__PURE__ */ new Set([
|
|
2953
|
+
"gui/flash/atlases/battleAtlas.dds",
|
|
2954
|
+
"gui/flash/atlases/battleAtlas.xml",
|
|
2955
|
+
"gui/flash/atlases/vehicleMarkerAtlas.dds",
|
|
2956
|
+
"gui/flash/atlases/vehicleMarkerAtlas.xml"
|
|
2957
|
+
]);
|
|
2958
|
+
async function extractIconAtlases(srcDir, outDir, onProgress) {
|
|
2959
|
+
await mkdir4(outDir, { recursive: true });
|
|
2960
|
+
const expectedFiles = [...ICON_ATLAS_FILES].map((f) => join9(outDir, basename5(f)));
|
|
2961
|
+
const allPresent = await Promise.all(expectedFiles.map((f) => access2(f).then(() => true, () => false)));
|
|
2962
|
+
if (allPresent.every(Boolean)) {
|
|
2963
|
+
return { pkgsFound: 0, filesExtracted: 0, skipped: true };
|
|
2964
|
+
}
|
|
2965
|
+
const entries = await readdir2(srcDir, { recursive: true });
|
|
2966
|
+
const pkgFiles = entries.filter((e) => GUI_PKG_RE.test(e)).map((e) => join9(srcDir, e));
|
|
2967
|
+
if (pkgFiles.length === 0) {
|
|
2968
|
+
throw new Error(`No gui-partN.pkg files found in: ${srcDir}`);
|
|
2969
|
+
}
|
|
2970
|
+
let filesExtracted = 0;
|
|
2971
|
+
for (const pkgFile of pkgFiles) {
|
|
2972
|
+
onProgress?.(`Checking: ${pkgFile}`);
|
|
2973
|
+
let data;
|
|
2904
2974
|
try {
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2975
|
+
data = await readFile8(pkgFile);
|
|
2976
|
+
} catch {
|
|
2977
|
+
onProgress?.(` Warning: failed to read package file, skipping.`);
|
|
2978
|
+
continue;
|
|
2979
|
+
}
|
|
2980
|
+
const extracted = await new Promise((resolve7, reject) => {
|
|
2981
|
+
unzip(data, { filter: (file) => ICON_ATLAS_FILES.has(file.name) }, (err, files) => {
|
|
2982
|
+
if (err) {
|
|
2983
|
+
reject(err);
|
|
2984
|
+
} else {
|
|
2985
|
+
resolve7(files);
|
|
2986
|
+
}
|
|
2908
2987
|
});
|
|
2909
|
-
}
|
|
2910
|
-
|
|
2988
|
+
});
|
|
2989
|
+
const names = Object.keys(extracted);
|
|
2990
|
+
if (names.length === 0) {
|
|
2991
|
+
onProgress?.(` No target files.`);
|
|
2992
|
+
continue;
|
|
2993
|
+
}
|
|
2994
|
+
onProgress?.(` Extracting ${names.length} file(s): ${names.join(" ")}`);
|
|
2995
|
+
await Promise.all(
|
|
2996
|
+
names.map((name) => writeFile9(join9(outDir, basename5(name)), extracted[name]))
|
|
2997
|
+
);
|
|
2998
|
+
filesExtracted += names.length;
|
|
2999
|
+
}
|
|
3000
|
+
return { pkgsFound: pkgFiles.length, filesExtracted, skipped: false };
|
|
3001
|
+
}
|
|
3002
|
+
|
|
3003
|
+
// src/lib/pipeline/pogs.ts
|
|
3004
|
+
var ATLAS_NAMES = ["battleAtlas", "vehicleMarkerAtlas"];
|
|
3005
|
+
var RENDER_CONCURRENCY = 5;
|
|
3006
|
+
var STATS_BATCH_SIZE = 5;
|
|
3007
|
+
var REQUIRED_SRC_FILES = ATLAS_NAMES.flatMap((name) => [`${name}.dds`, `${name}.xml`]);
|
|
3008
|
+
async function ensureAtlasAssets(srcDir, gameDir) {
|
|
3009
|
+
const present = await Promise.all(REQUIRED_SRC_FILES.map((f) => pathExists(join10(srcDir, f))));
|
|
3010
|
+
if (present.every(Boolean)) {
|
|
3011
|
+
return;
|
|
3012
|
+
}
|
|
3013
|
+
const missing = REQUIRED_SRC_FILES.filter((_, i) => !present[i]);
|
|
3014
|
+
if (!gameDir) {
|
|
3015
|
+
throw new Error(
|
|
3016
|
+
`Missing atlas files in ${srcDir}: ${missing.join(", ")}
|
|
3017
|
+
Run with --game-dir <wot-dir> to extract them automatically.`
|
|
3018
|
+
);
|
|
3019
|
+
}
|
|
3020
|
+
console.log("Some source atlas files are missing \u2014 extracting from game directory...");
|
|
3021
|
+
await extractIconAtlases(gameDir, srcDir, (msg) => console.log(msg));
|
|
3022
|
+
}
|
|
3023
|
+
async function pathExists(p) {
|
|
3024
|
+
return access3(p).then(() => true, () => false);
|
|
3025
|
+
}
|
|
3026
|
+
async function copyPngs(fromDir, toDir) {
|
|
3027
|
+
const entries = await readdir3(fromDir);
|
|
3028
|
+
await Promise.all(
|
|
3029
|
+
entries.filter((e) => e.endsWith(".png")).map((e) => copyFile(join10(fromDir, e), join10(toDir, e)))
|
|
3030
|
+
);
|
|
3031
|
+
}
|
|
3032
|
+
async function cleanBuild(outDir) {
|
|
3033
|
+
await rm2(join10(outDir, ".build"), { recursive: true, force: true });
|
|
3034
|
+
await rm2(join10(outDir, "res_mods"), { recursive: true, force: true });
|
|
3035
|
+
const entries = await readdir3(outDir).catch(() => []);
|
|
3036
|
+
await Promise.all(
|
|
3037
|
+
entries.filter((e) => /\.(png|xml|dds)$/.test(e)).map((e) => unlink(join10(outDir, e)))
|
|
3038
|
+
);
|
|
3039
|
+
}
|
|
3040
|
+
async function decodeDds(srcDir) {
|
|
3041
|
+
for (const name of ATLAS_NAMES) {
|
|
3042
|
+
const ddsPath = join10(srcDir, `${name}.dds`);
|
|
3043
|
+
const pngPath = join10(srcDir, `${name}.png`);
|
|
3044
|
+
if (await pathExists(ddsPath) && !await pathExists(pngPath)) {
|
|
3045
|
+
const out = await convertToPngFile(ddsPath);
|
|
3046
|
+
console.log(`Decoded \u2192 ${out}`);
|
|
3047
|
+
}
|
|
3048
|
+
}
|
|
3049
|
+
}
|
|
3050
|
+
async function extractAtlases(srcDir, buildDir, atlasManager2) {
|
|
3051
|
+
const atlasesDir = join10(buildDir, "atlases");
|
|
3052
|
+
await mkdir5(atlasesDir, { recursive: true });
|
|
3053
|
+
for (const name of ATLAS_NAMES) {
|
|
3054
|
+
const destDir = join10(atlasesDir, name);
|
|
3055
|
+
if (await pathExists(destDir)) {
|
|
3056
|
+
console.log(`skipping ${name} extraction \u2014 ${destDir} already exists`);
|
|
3057
|
+
continue;
|
|
3058
|
+
}
|
|
3059
|
+
const xmlPath = join10(srcDir, `${name}.xml`);
|
|
3060
|
+
const pngPath = join10(srcDir, `${name}.png`);
|
|
3061
|
+
const count = await atlasManager2.extractAll(xmlPath, pngPath, destDir);
|
|
3062
|
+
console.log(`Extracted ${count} textures \u2192 ${destDir}`);
|
|
3063
|
+
await Promise.all([
|
|
3064
|
+
copyFile(pngPath, join10(atlasesDir, `${name}.png`)),
|
|
3065
|
+
copyFile(xmlPath, join10(atlasesDir, `${name}.xml`))
|
|
3066
|
+
]);
|
|
3067
|
+
}
|
|
3068
|
+
}
|
|
3069
|
+
async function warmCache(app) {
|
|
3070
|
+
console.log("warm up vehicle data cache");
|
|
3071
|
+
const vehicles = await app.getVehicles();
|
|
3072
|
+
console.log(` ${vehicles.length} vehicles`);
|
|
3073
|
+
console.log("warm up vehicle profile cache");
|
|
3074
|
+
let profilesCount = 0;
|
|
3075
|
+
for (let i = 0; i < vehicles.length; i += STATS_BATCH_SIZE) {
|
|
3076
|
+
const batch = vehicles.slice(i, i + STATS_BATCH_SIZE);
|
|
3077
|
+
await Promise.all(
|
|
3078
|
+
batch.map(async (v) => {
|
|
3079
|
+
try {
|
|
3080
|
+
await app.getStatsForBestConfig(v.tank_id);
|
|
3081
|
+
profilesCount++;
|
|
3082
|
+
} catch {
|
|
3083
|
+
}
|
|
3084
|
+
})
|
|
3085
|
+
);
|
|
3086
|
+
}
|
|
3087
|
+
console.log(` ${profilesCount} profiles`);
|
|
3088
|
+
return { vehiclesCount: vehicles.length, profilesCount };
|
|
3089
|
+
}
|
|
3090
|
+
async function renderOneToFile2(vehicle2, baker, outDir) {
|
|
3091
|
+
const outPath = join10(outDir, `${vehicle2.nation}-${vehicle2.tag}.png`);
|
|
3092
|
+
const info = await (await baker.bake(vehicle2)).png().toFile(outPath);
|
|
3093
|
+
console.log(`${outPath} \u2014 ${info.width}\xD7${info.height}px`);
|
|
3094
|
+
}
|
|
3095
|
+
async function renderIcons(app, builder, buildDir, vehiclesCount, limit) {
|
|
3096
|
+
const iconsDir = join10(buildDir, "icons");
|
|
3097
|
+
const existing = await readdir3(iconsDir).catch(() => []);
|
|
3098
|
+
const pngCount = existing.filter((e) => e.endsWith(".png")).length;
|
|
3099
|
+
const targetCount = limit !== void 0 ? Math.min(limit, vehiclesCount) : vehiclesCount;
|
|
3100
|
+
if (pngCount >= targetCount) {
|
|
3101
|
+
console.log(`skipping icons render \u2014 ${iconsDir} already has ${pngCount} icons`);
|
|
3102
|
+
return;
|
|
3103
|
+
}
|
|
3104
|
+
console.log("generating icons");
|
|
3105
|
+
await mkdir5(iconsDir, { recursive: true });
|
|
3106
|
+
const vehicles = (await app.getVehicles()).slice(0, targetCount);
|
|
3107
|
+
const bakers = Array.from({ length: RENDER_CONCURRENCY }, () => builder.createBaker(app));
|
|
3108
|
+
let idx = 0;
|
|
3109
|
+
await Promise.all(
|
|
3110
|
+
bakers.map(async (baker) => {
|
|
3111
|
+
while (idx < vehicles.length) {
|
|
3112
|
+
const vehicle2 = vehicles[idx++];
|
|
3113
|
+
await renderOneToFile2(vehicle2, baker, iconsDir);
|
|
3114
|
+
}
|
|
3115
|
+
})
|
|
3116
|
+
);
|
|
3117
|
+
}
|
|
3118
|
+
async function overlayIcons(buildDir) {
|
|
3119
|
+
console.log("overlaying generated icons into atlas directories");
|
|
3120
|
+
const iconsDir = join10(buildDir, "icons");
|
|
3121
|
+
for (const name of ATLAS_NAMES) {
|
|
3122
|
+
await copyPngs(iconsDir, join10(buildDir, "atlases", name));
|
|
3123
|
+
}
|
|
3124
|
+
}
|
|
3125
|
+
var SUFFIX_RE = /_(7x7|bob|IGR)\.png$/;
|
|
3126
|
+
var SUFFIX_EXCLUDED = /* @__PURE__ */ new Set(["battleLoadingFormBgTips.png", "battleLoadingFormBgTips_7x7.png"]);
|
|
3127
|
+
async function replaceSuffixed(buildDir) {
|
|
3128
|
+
console.log("replacing suffixed variants in atlas with base icons");
|
|
3129
|
+
const iconsDir = join10(buildDir, "icons");
|
|
3130
|
+
for (const name of ATLAS_NAMES) {
|
|
3131
|
+
const atlasDir = join10(buildDir, "atlases", name);
|
|
3132
|
+
const entries = await readdir3(atlasDir);
|
|
3133
|
+
for (const filename of entries) {
|
|
3134
|
+
if (!SUFFIX_RE.test(filename) || SUFFIX_EXCLUDED.has(filename)) {
|
|
3135
|
+
continue;
|
|
3136
|
+
}
|
|
3137
|
+
const base = filename.replace(SUFFIX_RE, ".png");
|
|
3138
|
+
if (SUFFIX_EXCLUDED.has(base)) {
|
|
3139
|
+
continue;
|
|
3140
|
+
}
|
|
3141
|
+
const basePath = join10(atlasDir, base);
|
|
3142
|
+
if (!await pathExists(basePath)) {
|
|
3143
|
+
continue;
|
|
3144
|
+
}
|
|
3145
|
+
await copyFile(basePath, join10(atlasDir, filename));
|
|
3146
|
+
await copyFile(basePath, join10(iconsDir, filename));
|
|
3147
|
+
}
|
|
3148
|
+
}
|
|
3149
|
+
}
|
|
3150
|
+
async function packAtlases(outDir, buildDir, atlasManager2) {
|
|
3151
|
+
const atlasesOutDir = join10(outDir, "res_mods", "version", "gui", "flash", "atlases");
|
|
3152
|
+
await mkdir5(atlasesOutDir, { recursive: true });
|
|
3153
|
+
for (const name of ATLAS_NAMES) {
|
|
3154
|
+
const result = await atlasManager2.pack(join10(buildDir, "atlases", name));
|
|
3155
|
+
if (result.bins > 1) {
|
|
3156
|
+
console.warn(`Warning: ${name} textures span ${result.bins} bins \u2014 only the first will be written`);
|
|
3157
|
+
}
|
|
3158
|
+
const pngPath = join10(atlasesOutDir, `${name}.png`);
|
|
3159
|
+
await writeFile10(pngPath, result.pngBuffer);
|
|
3160
|
+
await writeFile10(join10(atlasesOutDir, `${name}.xml`), result.xml);
|
|
3161
|
+
await rename(pngPath, join10(atlasesOutDir, `${name}.dds`));
|
|
3162
|
+
console.log(`Packed ${result.count} textures \u2192 ${name}.dds (${result.width}\xD7${result.height})`);
|
|
3163
|
+
}
|
|
3164
|
+
}
|
|
3165
|
+
async function copyContour(outDir, buildDir) {
|
|
3166
|
+
const contourDir = join10(outDir, "res_mods", "version", "gui", "flash", "maps", "icons", "vehicle", "contour");
|
|
3167
|
+
await mkdir5(contourDir, { recursive: true });
|
|
3168
|
+
await copyPngs(join10(buildDir, "icons"), contourDir);
|
|
3169
|
+
}
|
|
3170
|
+
async function pogsPipeline(app, atlasManager2, builder, options) {
|
|
3171
|
+
const { srcDir, outDir, buildDir, clean, cleanAtlasDir, gameDir, prune, limit } = options;
|
|
3172
|
+
if (clean) {
|
|
3173
|
+
await cleanBuild(outDir);
|
|
3174
|
+
if (cleanAtlasDir) {
|
|
3175
|
+
await rm2(srcDir, { recursive: true, force: true });
|
|
3176
|
+
}
|
|
3177
|
+
}
|
|
3178
|
+
await ensureAtlasAssets(srcDir, gameDir);
|
|
3179
|
+
await decodeDds(srcDir);
|
|
3180
|
+
await extractAtlases(srcDir, buildDir, atlasManager2);
|
|
3181
|
+
const { vehiclesCount } = await warmCache(app);
|
|
3182
|
+
await renderIcons(app, builder, buildDir, vehiclesCount, limit);
|
|
3183
|
+
await overlayIcons(buildDir);
|
|
3184
|
+
await replaceSuffixed(buildDir);
|
|
3185
|
+
await packAtlases(outDir, buildDir, atlasManager2);
|
|
3186
|
+
await copyContour(outDir, buildDir);
|
|
3187
|
+
if (prune) {
|
|
3188
|
+
await rm2(buildDir, { recursive: true, force: true });
|
|
3189
|
+
}
|
|
3190
|
+
}
|
|
3191
|
+
|
|
3192
|
+
// src/commands/bake/bake-command.ts
|
|
3193
|
+
import { join as join11 } from "path";
|
|
3194
|
+
import { Command as Command20 } from "commander";
|
|
3195
|
+
function resolveBakeOptions(options) {
|
|
3196
|
+
const srcDir = options.atlasDir ?? join11(options.out, ".atlases");
|
|
3197
|
+
const clean = options.clean ?? options.fresh;
|
|
3198
|
+
const prune = options.prune ?? options.tidy;
|
|
3199
|
+
const limit = options.limit !== void 0 ? parseInt(options.limit, 10) : void 0;
|
|
3200
|
+
return {
|
|
3201
|
+
srcDir,
|
|
3202
|
+
outDir: options.out,
|
|
3203
|
+
buildDir: join11(options.out, ".build"),
|
|
3204
|
+
gameDir: options.gameDir,
|
|
3205
|
+
cleanAtlasDir: !options.atlasDir,
|
|
3206
|
+
clean,
|
|
3207
|
+
prune,
|
|
3208
|
+
limit
|
|
3209
|
+
};
|
|
3210
|
+
}
|
|
3211
|
+
function bakeSubcommand(name, description) {
|
|
3212
|
+
return new Command20(name).description(description).requiredOption("--out <dir>", "output directory for the final mod files").option("--atlas-dir <dir>", "directory with extracted atlas DDS/PNG/XML files (default: <out>/.atlases)").option("--game-dir <dir>", "WoT game directory \u2014 used to extract missing atlas files automatically").option("--clean", "wipe build artefacts before starting").option("--fresh", "alias for --clean").option("--prune", "remove intermediate build directory after completion").option("--tidy", "alias for --prune").option("--limit [n]", "render only the first N icons and stop (default N: 10)", "10");
|
|
3213
|
+
}
|
|
3214
|
+
|
|
3215
|
+
// src/commands/bake/all.ts
|
|
3216
|
+
function bakeAllCommand(app, atlasManager2) {
|
|
3217
|
+
return bakeSubcommand("all", "Bake all icon sets (clear + color)").action(async (options) => {
|
|
3218
|
+
const resolved = resolveBakeOptions(options);
|
|
3219
|
+
await pogsPipeline(app, atlasManager2, new PogsClearSimpleV2(), {
|
|
3220
|
+
...resolved,
|
|
3221
|
+
outDir: join12(options.out, "clear-simple"),
|
|
3222
|
+
buildDir: join12(options.out, "clear-simple", ".build")
|
|
3223
|
+
});
|
|
3224
|
+
await pogsPipeline(app, atlasManager2, new PogsClearV2(), {
|
|
3225
|
+
...resolved,
|
|
3226
|
+
outDir: join12(options.out, "clear-DMG-RLD-FSR-VR"),
|
|
3227
|
+
buildDir: join12(options.out, "clear-DMG-RLD-FSR-VR", ".build"),
|
|
3228
|
+
clean: false,
|
|
3229
|
+
cleanAtlasDir: false
|
|
3230
|
+
});
|
|
3231
|
+
await pogsPipeline(app, atlasManager2, new PogsColorSimpleV1(), {
|
|
3232
|
+
...resolved,
|
|
3233
|
+
outDir: join12(options.out, "color-simple"),
|
|
3234
|
+
buildDir: join12(options.out, "color-simple", ".build"),
|
|
3235
|
+
clean: false,
|
|
3236
|
+
cleanAtlasDir: false
|
|
3237
|
+
});
|
|
3238
|
+
await pogsPipeline(app, atlasManager2, new PogsColorV1(), {
|
|
3239
|
+
...resolved,
|
|
3240
|
+
outDir: join12(options.out, "color-DMG-RLD-FSR-VR"),
|
|
3241
|
+
buildDir: join12(options.out, "color-DMG-RLD-FSR-VR", ".build"),
|
|
3242
|
+
clean: false,
|
|
3243
|
+
cleanAtlasDir: false
|
|
3244
|
+
});
|
|
3245
|
+
});
|
|
3246
|
+
}
|
|
3247
|
+
|
|
3248
|
+
// src/commands/bake/clear.ts
|
|
3249
|
+
function bakeClearCommand(app, atlasManager2) {
|
|
3250
|
+
return bakeSubcommand("clear", "Bake PogS clear icon set (no colour background)").action(async (options) => {
|
|
3251
|
+
await pogsPipeline(app, atlasManager2, new PogsClearSimpleV2(), resolveBakeOptions(options));
|
|
3252
|
+
});
|
|
3253
|
+
}
|
|
3254
|
+
|
|
3255
|
+
// src/commands/bake/color.ts
|
|
3256
|
+
function bakeColorCommand(app, atlasManager2) {
|
|
3257
|
+
return bakeSubcommand("color", "Bake PogS colour icon set (DMG/FSR/VR/RLD labels, pre-rendered background v1)").action(async (options) => {
|
|
3258
|
+
await pogsPipeline(app, atlasManager2, new PogsColorV1(), resolveBakeOptions(options));
|
|
3259
|
+
});
|
|
3260
|
+
}
|
|
3261
|
+
|
|
3262
|
+
// src/commands/bake/run.ts
|
|
3263
|
+
function bakeCommand(app, atlasManager2) {
|
|
3264
|
+
const bake = new Command21("bake").description("Bake PogS icon sets");
|
|
3265
|
+
bake.addCommand(bakeAllCommand(app, atlasManager2));
|
|
3266
|
+
bake.addCommand(bakeClearCommand(app, atlasManager2));
|
|
3267
|
+
bake.addCommand(bakeColorCommand(app, atlasManager2));
|
|
3268
|
+
return bake;
|
|
3269
|
+
}
|
|
3270
|
+
|
|
3271
|
+
// src/commands/game/extract-icon-assets.ts
|
|
3272
|
+
import { join as join13 } from "path";
|
|
3273
|
+
import { Command as Command22 } from "commander";
|
|
3274
|
+
function extractIconAssetsCommand() {
|
|
3275
|
+
return new Command22("extract-icon-assets").alias("extract-icons").description(
|
|
3276
|
+
"Extract battle and vehicle marker atlas files from a WoT installation directory"
|
|
3277
|
+
).argument("<src-dir>", "WoT game directory (searched recursively for gui-partN.pkg files)").argument("<out-dir>", "destination directory for extracted files (created if absent)").action(async (srcDir, outDir) => {
|
|
3278
|
+
const { pkgsFound, filesExtracted, skipped } = await extractIconAtlases(
|
|
3279
|
+
srcDir,
|
|
3280
|
+
outDir,
|
|
3281
|
+
(msg) => console.log(msg)
|
|
3282
|
+
);
|
|
3283
|
+
if (skipped) {
|
|
3284
|
+
console.log(`All atlas files already present in ${outDir}, nothing to do.`);
|
|
3285
|
+
} else {
|
|
3286
|
+
console.log(`Done. Scanned ${pkgsFound} package(s), extracted ${filesExtracted} file(s) \u2192 ${outDir}`);
|
|
3287
|
+
}
|
|
3288
|
+
const ddsFiles = ["battleAtlas.dds", "vehicleMarkerAtlas.dds"];
|
|
3289
|
+
for (const ddsFile of ddsFiles) {
|
|
3290
|
+
const ddsPath = join13(outDir, ddsFile);
|
|
3291
|
+
try {
|
|
3292
|
+
const pngPath = await convertToPngFile(ddsPath);
|
|
3293
|
+
console.log(`Converted \u2192 ${pngPath}`);
|
|
3294
|
+
} catch (err) {
|
|
3295
|
+
console.error(`Warning: failed to convert ${ddsFile}: ${err instanceof Error ? err.message : String(err)}`);
|
|
3296
|
+
}
|
|
2911
3297
|
}
|
|
2912
3298
|
});
|
|
2913
3299
|
}
|
|
2914
3300
|
|
|
2915
3301
|
// src/index.ts
|
|
2916
3302
|
var _version = "";
|
|
2917
|
-
var version = _version || "0.2.
|
|
2918
|
-
config({ path:
|
|
2919
|
-
var
|
|
3303
|
+
var version = _version || "0.2.3";
|
|
3304
|
+
config({ path: resolve6(process.env.PIE_WOT_CWD ?? process.cwd(), ".env") });
|
|
3305
|
+
var wgData = new WGData();
|
|
2920
3306
|
var atlasManager = new AtlasManager();
|
|
2921
3307
|
var tomatoApi = new TomatoApi();
|
|
2922
|
-
var program = new
|
|
3308
|
+
var program = new Command23();
|
|
2923
3309
|
program.name("pie-wot").description("CLI utilities for World of Tanks data and assets").version(version).enablePositionalOptions();
|
|
2924
|
-
var vehicle = new
|
|
2925
|
-
vehicle.addCommand(listVehiclesCommand(
|
|
2926
|
-
vehicle.addCommand(exportCommand(
|
|
2927
|
-
vehicle.addCommand(vehicleStatsCommand(
|
|
2928
|
-
vehicle.addCommand(bestConfigCommand(
|
|
2929
|
-
vehicle.addCommand(charsCommand(
|
|
2930
|
-
vehicle.addCommand(longAliasesCommand(
|
|
2931
|
-
var atlas = new
|
|
3310
|
+
var vehicle = new Command23("vehicle").description("WoT vehicle data");
|
|
3311
|
+
vehicle.addCommand(listVehiclesCommand(wgData));
|
|
3312
|
+
vehicle.addCommand(exportCommand(wgData));
|
|
3313
|
+
vehicle.addCommand(vehicleStatsCommand(wgData));
|
|
3314
|
+
vehicle.addCommand(bestConfigCommand(wgData));
|
|
3315
|
+
vehicle.addCommand(charsCommand(wgData));
|
|
3316
|
+
vehicle.addCommand(longAliasesCommand(wgData));
|
|
3317
|
+
var atlas = new Command23("atlas").description("Texture atlas tools");
|
|
2932
3318
|
atlas.addCommand(inspectAtlasCommand(atlasManager));
|
|
2933
3319
|
atlas.addCommand(pickCommand(atlasManager));
|
|
2934
3320
|
atlas.addCommand(extractAtlasCommand(atlasManager));
|
|
2935
3321
|
atlas.addCommand(packAtlasCommand(atlasManager));
|
|
2936
|
-
var font = new
|
|
3322
|
+
var font = new Command23("font").description("Pixel font tools");
|
|
2937
3323
|
font.addCommand(renderCommand());
|
|
2938
|
-
var cache = new
|
|
2939
|
-
cache.addCommand(cachePurgeCommand(
|
|
2940
|
-
var dds = new
|
|
3324
|
+
var cache = new Command23("cache").description("API response cache");
|
|
3325
|
+
cache.addCommand(cachePurgeCommand(wgData));
|
|
3326
|
+
var dds = new Command23("dds").description("DDS texture tools");
|
|
2941
3327
|
dds.addCommand(ddsDecodeCommand());
|
|
2942
3328
|
dds.addCommand(ddsEncodeCommand());
|
|
2943
3329
|
program.addCommand(vehicle);
|
|
@@ -2945,14 +3331,17 @@ program.addCommand(atlas);
|
|
|
2945
3331
|
program.addCommand(font);
|
|
2946
3332
|
program.addCommand(cache);
|
|
2947
3333
|
program.addCommand(dds);
|
|
2948
|
-
var icon = new
|
|
3334
|
+
var icon = new Command23("icon").description("Vehicle icon generation tools");
|
|
2949
3335
|
icon.addCommand(dumpBackgroundCommand());
|
|
2950
|
-
icon.addCommand(iconRenderCommand(
|
|
2951
|
-
icon.addCommand(iconFetchCommand(
|
|
2952
|
-
icon.addCommand(iconShrinkCommand(
|
|
3336
|
+
icon.addCommand(iconRenderCommand(wgData));
|
|
3337
|
+
icon.addCommand(iconFetchCommand(wgData));
|
|
3338
|
+
icon.addCommand(iconShrinkCommand(wgData));
|
|
2953
3339
|
program.addCommand(icon);
|
|
2954
|
-
var tomato = new
|
|
2955
|
-
tomato.addCommand(tomatoFetchCommand(
|
|
3340
|
+
var tomato = new Command23("tomato").description("Tomato.gg data fetcher");
|
|
3341
|
+
tomato.addCommand(tomatoFetchCommand(wgData, tomatoApi));
|
|
2956
3342
|
program.addCommand(tomato);
|
|
2957
|
-
program.addCommand(bakeCommand());
|
|
3343
|
+
program.addCommand(bakeCommand(wgData, atlasManager));
|
|
3344
|
+
var game = new Command23("game").description("WoT game installation tools");
|
|
3345
|
+
game.addCommand(extractIconAssetsCommand());
|
|
3346
|
+
program.addCommand(game);
|
|
2958
3347
|
await program.parseAsync();
|