@pietrovich/wot-utils 0.2.3 → 0.2.5
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 +9 -19
- package/dist/index.js +168 -98
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
# pie-wot
|
|
2
2
|
|
|
3
|
-
A CLI toolkit for World of Tanks modders. Fetches
|
|
3
|
+
A CLI toolkit for World of Tanks modders. Fetches fresh vehicle data from the Wargaming API, manipulates DDS textures
|
|
4
|
+
and texture atlases, and generates custom icon sets — all from the terminal, no build step required.
|
|
4
5
|
|
|
5
|
-
The long-term goal is to make it trivial to maintain and publish a custom icon set: when new vehicles are added to
|
|
6
|
+
The long-term goal is to make it trivial to maintain and publish a custom icon set: when new vehicles are added to
|
|
7
|
+
the game or tank characteristics change, re-running a handful of commands should be enough to produce an updated,
|
|
8
|
+
publish-ready set. The [PogS icon set](https://github.com/pavelmaca/WoT-PogsIconSet) by @pavelmaca served as the reference implementation and first supported style.
|
|
6
9
|
|
|
7
|
-
## Pre-baked ready
|
|
10
|
+
## Pre-baked, ready-to-use sets
|
|
8
11
|
|
|
9
12
|
If you just want the icons without running the pipeline yourself, pre-baked sets are published periodically at [pietrovich/wot-pogs-like-icon-sets](https://github.com/pietrovich/wot-pogs-like-icon-sets) and kept in sync with game updates.
|
|
10
13
|
|
|
@@ -17,7 +20,7 @@ cp .env.example .env # add your WG_APP_ID
|
|
|
17
20
|
|
|
18
21
|
Get a free application ID at [developers.wargaming.net](https://developers.wargaming.net/).
|
|
19
22
|
|
|
20
|
-
> **Node.js ≥ 24**
|
|
23
|
+
> **Node.js ≥ 24** recommended. May work with older versions
|
|
21
24
|
|
|
22
25
|
## Uninstall
|
|
23
26
|
|
|
@@ -47,11 +50,11 @@ pie-wot dds decode <file> # decode a DDS texture to PNG
|
|
|
47
50
|
pie-wot dds encode <file> # encode a PNG to DXT5/BC3 DDS
|
|
48
51
|
|
|
49
52
|
pie-wot icon dump-background # generate per-type background images (PogS style)
|
|
50
|
-
pie-wot icon render <query> # render a vehicle icon
|
|
53
|
+
pie-wot icon render <query> # render a vehicle icon (use --simple for simplified variant)
|
|
51
54
|
|
|
52
55
|
pie-wot font render [font] [text] # render text as PNG using a pixel font
|
|
53
56
|
|
|
54
|
-
pie-wot cache purge # clear the local API response cache
|
|
57
|
+
pie-wot cache purge # clear the local WG API response cache
|
|
55
58
|
|
|
56
59
|
pie-wot bake <script> # run a bundled build script (see below)
|
|
57
60
|
```
|
|
@@ -111,19 +114,6 @@ npm start -- <command>
|
|
|
111
114
|
npm start -- vehicle list
|
|
112
115
|
```
|
|
113
116
|
|
|
114
|
-
### LOCAL_DEV
|
|
115
|
-
|
|
116
|
-
The bundled baker scripts (`scripts/`) detect whether the `pie-wot` global binary is available and use it by default. Set `LOCAL_DEV=true` in your `.env` (or environment) to force them to use `npm start` from the local clone instead:
|
|
117
|
-
|
|
118
|
-
```
|
|
119
|
-
LOCAL_DEV=true
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
This is useful when iterating on the CLI itself — changes to `src/` take effect immediately without a publish/reinstall cycle.
|
|
123
|
-
|
|
124
|
-
## How it works
|
|
125
|
-
|
|
126
|
-
API responses are cached locally (SHA-256 keyed, no TTL) so repeated runs don't hammer the Wargaming API. DDS encode/decode uses a trimmed BC1/BC3 codec derived from [photopea/UTEX.js](https://github.com/photopea/UTEX.js). Image compositing uses [sharp](https://sharp.pixelplumbing.com/).
|
|
127
117
|
|
|
128
118
|
## Credits
|
|
129
119
|
|
package/dist/index.js
CHANGED
|
@@ -2227,35 +2227,6 @@ function nameText() {
|
|
|
2227
2227
|
};
|
|
2228
2228
|
}
|
|
2229
2229
|
|
|
2230
|
-
// src/lib/icons/pogs/PogsClear.ts
|
|
2231
|
-
var iconAligner = createAligner(PogsConstants, "bl.+", [18, 1]);
|
|
2232
|
-
var PogsClear = class {
|
|
2233
|
-
createBaker(app) {
|
|
2234
|
-
return new ImageBaker(
|
|
2235
|
-
PogsConstants,
|
|
2236
|
-
[barAndShield(), vehicleIcon(app, iconAligner), tierText(), nameText()]
|
|
2237
|
-
);
|
|
2238
|
-
}
|
|
2239
|
-
};
|
|
2240
|
-
|
|
2241
|
-
// src/lib/icons/pogs/PogsColor.ts
|
|
2242
|
-
var iconAligner2 = createAligner(PogsConstants, "bl.+", [18, 1]);
|
|
2243
|
-
var PogsColor = class {
|
|
2244
|
-
createBaker(app) {
|
|
2245
|
-
return new ImageBaker(
|
|
2246
|
-
PogsConstants,
|
|
2247
|
-
[
|
|
2248
|
-
gradientBackground(),
|
|
2249
|
-
barAndShield(),
|
|
2250
|
-
vehicleIcon(app, iconAligner2),
|
|
2251
|
-
tierText(),
|
|
2252
|
-
nameText()
|
|
2253
|
-
],
|
|
2254
|
-
(s) => s.removeAlpha()
|
|
2255
|
-
);
|
|
2256
|
-
}
|
|
2257
|
-
};
|
|
2258
|
-
|
|
2259
2230
|
// src/lib/icons/layers/pre-rendered-background.ts
|
|
2260
2231
|
import { readFile as readFile6 } from "fs/promises";
|
|
2261
2232
|
import { resolve as resolve4 } from "path";
|
|
@@ -2292,8 +2263,41 @@ function preRenderedBackground(version2, flavor = "") {
|
|
|
2292
2263
|
};
|
|
2293
2264
|
}
|
|
2294
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
|
+
|
|
2295
2299
|
// src/lib/icons/pogs/PogsClearV1.ts
|
|
2296
|
-
var
|
|
2300
|
+
var iconAligner2 = createAligner(PogsConstants, "bl.+", [18, "(bh - 1).+"]);
|
|
2297
2301
|
var PogsClearV1 = class {
|
|
2298
2302
|
version = 1;
|
|
2299
2303
|
createBaker(app) {
|
|
@@ -2301,7 +2305,7 @@ var PogsClearV1 = class {
|
|
|
2301
2305
|
PogsConstants,
|
|
2302
2306
|
[
|
|
2303
2307
|
preRenderedBackground(this.version, "clear"),
|
|
2304
|
-
vehicleIcon(app,
|
|
2308
|
+
vehicleIcon(app, iconAligner2),
|
|
2305
2309
|
tierText(),
|
|
2306
2310
|
nameText()
|
|
2307
2311
|
]
|
|
@@ -2309,11 +2313,31 @@ var PogsClearV1 = class {
|
|
|
2309
2313
|
}
|
|
2310
2314
|
};
|
|
2311
2315
|
|
|
2312
|
-
// src/lib/icons/pogs/
|
|
2313
|
-
var
|
|
2316
|
+
// src/lib/icons/pogs/PogsClearSimpleV2.ts
|
|
2317
|
+
var PogsClearSimpleV2 = class extends PogsClearSimpleV1 {
|
|
2314
2318
|
version = 2;
|
|
2315
2319
|
};
|
|
2316
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
|
+
|
|
2317
2341
|
// src/lib/icons/layers/text-view-range.ts
|
|
2318
2342
|
var defaultAligner3 = createAligner(PogsConstants, "br.+", [37, "bh - 1"]);
|
|
2319
2343
|
function textViewRange(app, aligner = defaultAligner3) {
|
|
@@ -2356,17 +2380,16 @@ function textReload(app, aligner = defaultAligner4) {
|
|
|
2356
2380
|
};
|
|
2357
2381
|
}
|
|
2358
2382
|
|
|
2359
|
-
// src/lib/icons/layers/text-
|
|
2360
|
-
var defaultAligner5 = createAligner(PogsConstants, "
|
|
2361
|
-
function
|
|
2383
|
+
// src/lib/icons/layers/text-penetration.ts
|
|
2384
|
+
var defaultAligner5 = createAligner(PogsConstants, "rt", ["r", 2]);
|
|
2385
|
+
function textPenetration(app, aligner = defaultAligner5) {
|
|
2362
2386
|
return async (_box, _prev, vehicle2) => {
|
|
2363
2387
|
const profiles = await app.getStatsForBestConfig(vehicle2);
|
|
2364
|
-
const
|
|
2365
|
-
if (
|
|
2388
|
+
const text = profiles[vehicle2.tank_id]?.ammo?.[0].penetration?.[1];
|
|
2389
|
+
if (text === void 0) {
|
|
2366
2390
|
return null;
|
|
2367
2391
|
}
|
|
2368
|
-
const
|
|
2369
|
-
const { data, width, height } = await renderWithShadow("pogs4px", text, Colors.white);
|
|
2392
|
+
const { data, width, height } = await renderWithShadow("pogs4px", text, Colors.beige);
|
|
2370
2393
|
const { left, top } = aligner.align({ width, height });
|
|
2371
2394
|
return {
|
|
2372
2395
|
input: data,
|
|
@@ -2378,20 +2401,17 @@ function textHullArmor(app, aligner = defaultAligner5) {
|
|
|
2378
2401
|
};
|
|
2379
2402
|
}
|
|
2380
2403
|
|
|
2381
|
-
// src/lib/icons/layers/text-
|
|
2382
|
-
var defaultAligner6 = createAligner(PogsConstants, "
|
|
2383
|
-
function
|
|
2384
|
-
return async (_box,
|
|
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) => {
|
|
2385
2408
|
const profiles = await app.getStatsForBestConfig(vehicle2);
|
|
2386
|
-
const
|
|
2387
|
-
if (
|
|
2409
|
+
const text = profiles[vehicle2.tank_id]?.ammo?.[0].damage?.[1];
|
|
2410
|
+
if (text === void 0) {
|
|
2388
2411
|
return null;
|
|
2389
2412
|
}
|
|
2390
|
-
const
|
|
2391
|
-
const {
|
|
2392
|
-
const offsetX = -1 * prev.meta.width + width;
|
|
2393
|
-
const shifted = aligner.shift(offsetX, 0);
|
|
2394
|
-
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 });
|
|
2395
2415
|
return {
|
|
2396
2416
|
input: data,
|
|
2397
2417
|
raw: { width, height, channels: 4 },
|
|
@@ -2402,16 +2422,17 @@ function textTurretArmor(app, aligner = defaultAligner6) {
|
|
|
2402
2422
|
};
|
|
2403
2423
|
}
|
|
2404
2424
|
|
|
2405
|
-
// src/lib/icons/layers/text-
|
|
2406
|
-
var defaultAligner7 = createAligner(PogsConstants, "
|
|
2407
|
-
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) {
|
|
2408
2428
|
return async (_box, _prev, vehicle2) => {
|
|
2409
2429
|
const profiles = await app.getStatsForBestConfig(vehicle2);
|
|
2410
|
-
const
|
|
2411
|
-
if (
|
|
2430
|
+
const hull = profiles[vehicle2.tank_id]?.armor?.hull;
|
|
2431
|
+
if (hull === void 0) {
|
|
2412
2432
|
return null;
|
|
2413
2433
|
}
|
|
2414
|
-
const
|
|
2434
|
+
const text = `${hull.front}*${hull.sides}*${hull.rear}`;
|
|
2435
|
+
const { data, width, height } = await renderWithShadow("pogs4px", text, Colors.white);
|
|
2415
2436
|
const { left, top } = aligner.align({ width, height });
|
|
2416
2437
|
return {
|
|
2417
2438
|
input: data,
|
|
@@ -2423,17 +2444,20 @@ function textPenetration(app, aligner = defaultAligner7) {
|
|
|
2423
2444
|
};
|
|
2424
2445
|
}
|
|
2425
2446
|
|
|
2426
|
-
// src/lib/icons/layers/text-
|
|
2427
|
-
var defaultAligner8 = createAligner(PogsConstants, "
|
|
2428
|
-
function
|
|
2429
|
-
return async (_box,
|
|
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) => {
|
|
2430
2451
|
const profiles = await app.getStatsForBestConfig(vehicle2);
|
|
2431
|
-
const
|
|
2432
|
-
if (
|
|
2452
|
+
const turret = profiles[vehicle2.tank_id]?.armor?.turret;
|
|
2453
|
+
if (!turret) {
|
|
2433
2454
|
return null;
|
|
2434
2455
|
}
|
|
2435
|
-
const
|
|
2436
|
-
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 });
|
|
2437
2461
|
return {
|
|
2438
2462
|
input: data,
|
|
2439
2463
|
raw: { width, height, channels: 4 },
|
|
@@ -2444,23 +2468,26 @@ function textDamage(app, aligner = defaultAligner8) {
|
|
|
2444
2468
|
};
|
|
2445
2469
|
}
|
|
2446
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
|
+
|
|
2447
2483
|
// src/lib/icons/pogs/PogsColorV1.ts
|
|
2448
|
-
var PogsColorV1 = class {
|
|
2449
|
-
version = 1;
|
|
2484
|
+
var PogsColorV1 = class extends PogsColorSimpleV1 {
|
|
2450
2485
|
createBaker(data) {
|
|
2451
2486
|
return new ImageBaker(
|
|
2452
2487
|
PogsConstants,
|
|
2453
2488
|
[
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
textViewRange(data),
|
|
2457
|
-
textReload(data),
|
|
2458
|
-
textPenetration(data),
|
|
2459
|
-
textDamage(data),
|
|
2460
|
-
textHullArmor(data),
|
|
2461
|
-
textTurretArmor(data),
|
|
2462
|
-
tierText(),
|
|
2463
|
-
nameText()
|
|
2489
|
+
...this.getBaseLayers(data),
|
|
2490
|
+
...getLayers(data)
|
|
2464
2491
|
],
|
|
2465
2492
|
(s) => s.removeAlpha()
|
|
2466
2493
|
);
|
|
@@ -2472,6 +2499,24 @@ var PogsColorV2 = class extends PogsColorV1 {
|
|
|
2472
2499
|
version = 2;
|
|
2473
2500
|
};
|
|
2474
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
|
+
|
|
2475
2520
|
// src/commands/icon/render.ts
|
|
2476
2521
|
var CONCURRENCY = 5;
|
|
2477
2522
|
async function renderOneToFile(vehicle2, baker, outDir) {
|
|
@@ -2480,7 +2525,7 @@ async function renderOneToFile(vehicle2, baker, outDir) {
|
|
|
2480
2525
|
console.log(`${outPath} \u2014 ${info.width}\xD7${info.height}px`);
|
|
2481
2526
|
}
|
|
2482
2527
|
function iconRenderCommand(app) {
|
|
2483
|
-
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) => {
|
|
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) => {
|
|
2484
2529
|
try {
|
|
2485
2530
|
if (!query && !options.all) {
|
|
2486
2531
|
console.error("Provide a query argument or use --all to render all vehicles.");
|
|
@@ -2495,18 +2540,26 @@ Provide an existing path or add --create to create it.`);
|
|
|
2495
2540
|
}
|
|
2496
2541
|
mkdirSync2(outDir, { recursive: true });
|
|
2497
2542
|
}
|
|
2498
|
-
const bgVersion = options.bg ?? options.preRenderedBg ?? (options.clear ? "v2" : void 0);
|
|
2499
2543
|
let builder;
|
|
2500
2544
|
const useColor = !options.clear;
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
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 {
|
|
2504
2555
|
builder = useColor ? new PogsColorV1() : new PogsClearV1();
|
|
2556
|
+
}
|
|
2557
|
+
} else {
|
|
2558
|
+
if (useSimple) {
|
|
2559
|
+
builder = useColor ? new PogsColorSimpleV2() : new PogsClearSimpleV2();
|
|
2505
2560
|
} else {
|
|
2506
2561
|
builder = useColor ? new PogsColorV2() : new PogsClearV2();
|
|
2507
2562
|
}
|
|
2508
|
-
} else {
|
|
2509
|
-
builder = useColor ? new PogsColor() : new PogsClear();
|
|
2510
2563
|
}
|
|
2511
2564
|
const vehicles = options.all ? await app.getVehicles() : [await app.findVehicle(query)];
|
|
2512
2565
|
const bakers = Array.from({ length: CONCURRENCY }, () => builder.createBaker(app));
|
|
@@ -3039,17 +3092,18 @@ async function renderOneToFile2(vehicle2, baker, outDir) {
|
|
|
3039
3092
|
const info = await (await baker.bake(vehicle2)).png().toFile(outPath);
|
|
3040
3093
|
console.log(`${outPath} \u2014 ${info.width}\xD7${info.height}px`);
|
|
3041
3094
|
}
|
|
3042
|
-
async function renderIcons(app, builder, buildDir, vehiclesCount) {
|
|
3095
|
+
async function renderIcons(app, builder, buildDir, vehiclesCount, limit) {
|
|
3043
3096
|
const iconsDir = join10(buildDir, "icons");
|
|
3044
3097
|
const existing = await readdir3(iconsDir).catch(() => []);
|
|
3045
3098
|
const pngCount = existing.filter((e) => e.endsWith(".png")).length;
|
|
3046
|
-
|
|
3099
|
+
const targetCount = limit !== void 0 ? Math.min(limit, vehiclesCount) : vehiclesCount;
|
|
3100
|
+
if (pngCount >= targetCount) {
|
|
3047
3101
|
console.log(`skipping icons render \u2014 ${iconsDir} already has ${pngCount} icons`);
|
|
3048
3102
|
return;
|
|
3049
3103
|
}
|
|
3050
3104
|
console.log("generating icons");
|
|
3051
3105
|
await mkdir5(iconsDir, { recursive: true });
|
|
3052
|
-
const vehicles = await app.getVehicles();
|
|
3106
|
+
const vehicles = (await app.getVehicles()).slice(0, targetCount);
|
|
3053
3107
|
const bakers = Array.from({ length: RENDER_CONCURRENCY }, () => builder.createBaker(app));
|
|
3054
3108
|
let idx = 0;
|
|
3055
3109
|
await Promise.all(
|
|
@@ -3114,7 +3168,7 @@ async function copyContour(outDir, buildDir) {
|
|
|
3114
3168
|
await copyPngs(join10(buildDir, "icons"), contourDir);
|
|
3115
3169
|
}
|
|
3116
3170
|
async function pogsPipeline(app, atlasManager2, builder, options) {
|
|
3117
|
-
const { srcDir, outDir, buildDir, clean, cleanAtlasDir, gameDir, prune } = options;
|
|
3171
|
+
const { srcDir, outDir, buildDir, clean, cleanAtlasDir, gameDir, prune, limit } = options;
|
|
3118
3172
|
if (clean) {
|
|
3119
3173
|
await cleanBuild(outDir);
|
|
3120
3174
|
if (cleanAtlasDir) {
|
|
@@ -3125,7 +3179,7 @@ async function pogsPipeline(app, atlasManager2, builder, options) {
|
|
|
3125
3179
|
await decodeDds(srcDir);
|
|
3126
3180
|
await extractAtlases(srcDir, buildDir, atlasManager2);
|
|
3127
3181
|
const { vehiclesCount } = await warmCache(app);
|
|
3128
|
-
await renderIcons(app, builder, buildDir, vehiclesCount);
|
|
3182
|
+
await renderIcons(app, builder, buildDir, vehiclesCount, limit);
|
|
3129
3183
|
await overlayIcons(buildDir);
|
|
3130
3184
|
await replaceSuffixed(buildDir);
|
|
3131
3185
|
await packAtlases(outDir, buildDir, atlasManager2);
|
|
@@ -3137,11 +3191,12 @@ async function pogsPipeline(app, atlasManager2, builder, options) {
|
|
|
3137
3191
|
|
|
3138
3192
|
// src/commands/bake/bake-command.ts
|
|
3139
3193
|
import { join as join11 } from "path";
|
|
3140
|
-
import { Command as Command20 } from "commander";
|
|
3194
|
+
import { Command as Command20, Option } from "commander";
|
|
3141
3195
|
function resolveBakeOptions(options) {
|
|
3142
3196
|
const srcDir = options.atlasDir ?? join11(options.out, ".atlases");
|
|
3143
3197
|
const clean = options.clean ?? options.fresh;
|
|
3144
3198
|
const prune = options.prune ?? options.tidy;
|
|
3199
|
+
const limit = options.limit !== void 0 ? parseInt(options.limit, 10) : void 0;
|
|
3145
3200
|
return {
|
|
3146
3201
|
srcDir,
|
|
3147
3202
|
outDir: options.out,
|
|
@@ -3149,26 +3204,41 @@ function resolveBakeOptions(options) {
|
|
|
3149
3204
|
gameDir: options.gameDir,
|
|
3150
3205
|
cleanAtlasDir: !options.atlasDir,
|
|
3151
3206
|
clean,
|
|
3152
|
-
prune
|
|
3207
|
+
prune,
|
|
3208
|
+
limit
|
|
3153
3209
|
};
|
|
3154
3210
|
}
|
|
3155
3211
|
function bakeSubcommand(name, description) {
|
|
3156
|
-
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");
|
|
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").addOption(new Option("--limit [n]", "render only the first N icons and stop (default N: 10)").preset("10"));
|
|
3157
3213
|
}
|
|
3158
3214
|
|
|
3159
3215
|
// src/commands/bake/all.ts
|
|
3160
3216
|
function bakeAllCommand(app, atlasManager2) {
|
|
3161
3217
|
return bakeSubcommand("all", "Bake all icon sets (clear + color)").action(async (options) => {
|
|
3162
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
|
+
});
|
|
3163
3224
|
await pogsPipeline(app, atlasManager2, new PogsClearV2(), {
|
|
3164
3225
|
...resolved,
|
|
3165
|
-
outDir: join12(options.out, "clear"),
|
|
3166
|
-
buildDir: join12(options.out, "clear", ".build")
|
|
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
|
|
3167
3237
|
});
|
|
3168
3238
|
await pogsPipeline(app, atlasManager2, new PogsColorV1(), {
|
|
3169
3239
|
...resolved,
|
|
3170
|
-
outDir: join12(options.out, "color"),
|
|
3171
|
-
buildDir: join12(options.out, "color", ".build"),
|
|
3240
|
+
outDir: join12(options.out, "color-DMG-RLD-FSR-VR"),
|
|
3241
|
+
buildDir: join12(options.out, "color-DMG-RLD-FSR-VR", ".build"),
|
|
3172
3242
|
clean: false,
|
|
3173
3243
|
cleanAtlasDir: false
|
|
3174
3244
|
});
|
|
@@ -3178,7 +3248,7 @@ function bakeAllCommand(app, atlasManager2) {
|
|
|
3178
3248
|
// src/commands/bake/clear.ts
|
|
3179
3249
|
function bakeClearCommand(app, atlasManager2) {
|
|
3180
3250
|
return bakeSubcommand("clear", "Bake PogS clear icon set (no colour background)").action(async (options) => {
|
|
3181
|
-
await pogsPipeline(app, atlasManager2, new
|
|
3251
|
+
await pogsPipeline(app, atlasManager2, new PogsClearSimpleV2(), resolveBakeOptions(options));
|
|
3182
3252
|
});
|
|
3183
3253
|
}
|
|
3184
3254
|
|
|
@@ -3230,7 +3300,7 @@ function extractIconAssetsCommand() {
|
|
|
3230
3300
|
|
|
3231
3301
|
// src/index.ts
|
|
3232
3302
|
var _version = "";
|
|
3233
|
-
var version = _version || "0.2.
|
|
3303
|
+
var version = _version || "0.2.4";
|
|
3234
3304
|
config({ path: resolve6(process.env.PIE_WOT_CWD ?? process.cwd(), ".env") });
|
|
3235
3305
|
var wgData = new WGData();
|
|
3236
3306
|
var atlasManager = new AtlasManager();
|
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
},
|
|
6
6
|
"private": false,
|
|
7
7
|
"repository": "github:pietrovich/wot-utils",
|
|
8
|
-
"version": "0.2.
|
|
8
|
+
"version": "0.2.5",
|
|
9
9
|
"description": "CLI utilities for World of Tanks data and assets",
|
|
10
10
|
"type": "module",
|
|
11
11
|
"bin": {
|
|
@@ -58,5 +58,8 @@
|
|
|
58
58
|
},
|
|
59
59
|
"engines": {
|
|
60
60
|
"node": ">=24.0.0"
|
|
61
|
+
},
|
|
62
|
+
"overrides": {
|
|
63
|
+
"esbuild": ">=0.28.1"
|
|
61
64
|
}
|
|
62
65
|
}
|