ultimatedarktower 4.1.0 → 5.0.0

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.
Files changed (53) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/esm/index.mjs +1490 -479
  3. package/dist/src/data/board/index.d.ts +9 -0
  4. package/dist/src/data/board/index.js +26 -0
  5. package/dist/src/data/board/index.js.map +1 -0
  6. package/dist/src/data/board/udtBoardAdjacency.js.map +1 -0
  7. package/dist/src/data/board/udtBoardAnchors.js.map +1 -0
  8. package/dist/src/data/board/udtGameBoard.js.map +1 -0
  9. package/dist/src/data/index.d.ts +19 -0
  10. package/dist/src/data/index.js +56 -0
  11. package/dist/src/data/index.js.map +1 -0
  12. package/dist/src/data/udtBoxInventory.d.ts +47 -0
  13. package/dist/src/data/udtBoxInventory.js +679 -0
  14. package/dist/src/data/udtBoxInventory.js.map +1 -0
  15. package/dist/src/{udtFoes.d.ts → data/udtFoes.d.ts} +1 -1
  16. package/dist/src/data/udtFoes.js.map +1 -0
  17. package/dist/src/data/udtGameContent.d.ts +418 -0
  18. package/dist/src/data/udtGameContent.js +293 -0
  19. package/dist/src/data/udtGameContent.js.map +1 -0
  20. package/dist/src/data/udtHeroes.js.map +1 -0
  21. package/dist/src/data/udtMonuments.js.map +1 -0
  22. package/dist/src/index.d.ts +2 -15
  23. package/dist/src/index.js +30 -55
  24. package/dist/src/index.js.map +1 -1
  25. package/dist/src/seed/index.d.ts +7 -0
  26. package/dist/src/seed/index.js +24 -0
  27. package/dist/src/seed/index.js.map +1 -0
  28. package/dist/src/seed/udtSeedParser.js.map +1 -0
  29. package/dist/src/seed/udtSystemRandom.js.map +1 -0
  30. package/package.json +1 -1
  31. package/dist/src/udtBoardAdjacency.js.map +0 -1
  32. package/dist/src/udtBoardAnchors.js.map +0 -1
  33. package/dist/src/udtFoes.js.map +0 -1
  34. package/dist/src/udtGameBoard.js.map +0 -1
  35. package/dist/src/udtHeroes.js.map +0 -1
  36. package/dist/src/udtMonuments.js.map +0 -1
  37. package/dist/src/udtSeedParser.js.map +0 -1
  38. package/dist/src/udtSystemRandom.js.map +0 -1
  39. /package/dist/src/{udtBoardAdjacency.d.ts → data/board/udtBoardAdjacency.d.ts} +0 -0
  40. /package/dist/src/{udtBoardAdjacency.js → data/board/udtBoardAdjacency.js} +0 -0
  41. /package/dist/src/{udtBoardAnchors.d.ts → data/board/udtBoardAnchors.d.ts} +0 -0
  42. /package/dist/src/{udtBoardAnchors.js → data/board/udtBoardAnchors.js} +0 -0
  43. /package/dist/src/{udtGameBoard.d.ts → data/board/udtGameBoard.d.ts} +0 -0
  44. /package/dist/src/{udtGameBoard.js → data/board/udtGameBoard.js} +0 -0
  45. /package/dist/src/{udtFoes.js → data/udtFoes.js} +0 -0
  46. /package/dist/src/{udtHeroes.d.ts → data/udtHeroes.d.ts} +0 -0
  47. /package/dist/src/{udtHeroes.js → data/udtHeroes.js} +0 -0
  48. /package/dist/src/{udtMonuments.d.ts → data/udtMonuments.d.ts} +0 -0
  49. /package/dist/src/{udtMonuments.js → data/udtMonuments.js} +0 -0
  50. /package/dist/src/{udtSeedParser.d.ts → seed/udtSeedParser.d.ts} +0 -0
  51. /package/dist/src/{udtSeedParser.js → seed/udtSeedParser.js} +0 -0
  52. /package/dist/src/{udtSystemRandom.d.ts → seed/udtSystemRandom.d.ts} +0 -0
  53. /package/dist/src/{udtSystemRandom.js → seed/udtSystemRandom.js} +0 -0
@@ -4170,279 +4170,23 @@ var IndexedDBSink = class {
4170
4170
  }
4171
4171
  };
4172
4172
 
4173
- // src/udtSeedParser.ts
4174
- var ALPHABET = "a123456789bcdefghijklmnpqrstuvwxyz";
4175
- var BASE = 34;
4176
- var SETUP_LENGTH = 6;
4177
- var RNG_SEED_LENGTH = 6;
4178
- var SEED_LENGTH = SETUP_LENGTH + RNG_SEED_LENGTH;
4179
- var CHAR_TO_VALUE = /* @__PURE__ */ new Map();
4180
- var VALUE_TO_CHAR = /* @__PURE__ */ new Map();
4181
- for (let i = 0; i < ALPHABET.length; i++) {
4182
- CHAR_TO_VALUE.set(ALPHABET[i], i);
4183
- VALUE_TO_CHAR.set(i, ALPHABET[i]);
4184
- }
4185
- var TIER1_FOES = ["Brigands", "Oreks", "Shadow Wolves", "Spine Fiends"];
4186
- var TIER2_FOES = ["Frost Trolls", "Clan of Neuri", "Lemures", "Widowmade Spiders"];
4187
- var TIER3_FOES = ["Dragons", "Mormos", "Striga", "Titans"];
4188
- var ADVERSARIES = [
4189
- "Ashstrider",
4190
- "Bane of Omens",
4191
- "Empress of Shades",
4192
- "Gaze Eternal",
4193
- "Gravemaw",
4194
- "Isa the Exile",
4195
- "Lingering Rot",
4196
- "Utuk'Ku"
4197
- ];
4198
- var ALLIES = [
4199
- "Gleb",
4200
- "Grigor",
4201
- "Hakan",
4202
- "Letha",
4203
- "Miras",
4204
- "Nimet",
4205
- "Tomas",
4206
- "Vasa",
4207
- "Yana",
4208
- "Zaida"
4209
- ];
4210
- var DIFFICULTIES = ["Heroic", "Gritty"];
4211
- var GAME_SOURCES = ["Core", "Competitive"];
4212
- function charToValue(c) {
4213
- const v = CHAR_TO_VALUE.get(c.toLowerCase());
4214
- if (v === void 0) {
4215
- throw new Error(`Invalid seed character: '${c}'`);
4216
- }
4217
- return v;
4218
- }
4219
- function valueToChar(v) {
4220
- const c = VALUE_TO_CHAR.get(v);
4221
- if (c === void 0) {
4222
- throw new Error(`Invalid seed value: ${v} (must be 0\u2013${BASE - 1})`);
4223
- }
4224
- return c;
4225
- }
4226
- function validateSeed(seed) {
4227
- const stripped = seed.replace(/[-\s]/g, "").toLowerCase();
4228
- if (stripped.length !== SEED_LENGTH) {
4229
- throw new Error(`Invalid seed length: expected ${SEED_LENGTH} characters, got ${stripped.length}`);
4230
- }
4231
- for (const c of stripped) {
4232
- if (!CHAR_TO_VALUE.has(c)) {
4233
- throw new Error(`Invalid seed character: '${c}'`);
4234
- }
4235
- }
4236
- const upper = stripped.toUpperCase();
4237
- return `${upper.slice(0, 4)}-${upper.slice(4, 8)}-${upper.slice(8, 12)}`;
4238
- }
4239
- function decodeSeed(seed) {
4240
- const normalized = validateSeed(seed);
4241
- const stripped = normalized.replace(/-/g, "").toLowerCase();
4242
- const setup = [];
4243
- for (let i = 0; i < SETUP_LENGTH; i++) {
4244
- setup.push(charToValue(stripped[i]));
4245
- }
4246
- let rngSeed = 0;
4247
- for (let i = 0; i < RNG_SEED_LENGTH; i++) {
4248
- const value = charToValue(stripped[SETUP_LENGTH + i]);
4249
- rngSeed += value * Math.round(Math.pow(BASE, i));
4250
- }
4251
- const foeByteA = setup[0];
4252
- const tier1 = foeByteA & 3;
4253
- const tier2 = (foeByteA & 12) >> 2;
4254
- const foeByteB = setup[1];
4255
- const tier3 = (foeByteA & 16) >> 4 | (foeByteB & 16) >> 3;
4256
- const adversaryIndex = foeByteB & 15;
4257
- const allyIndex = setup[2];
4258
- const extra = setup[3];
4259
- const difficultyIndex = extra & 1;
4260
- const expansionBits = (extra & 6) >> 1;
4261
- const sourceBits = (extra & 8) >> 2;
4262
- const playerCount = (setup[5] & 3) + 1;
4263
- const expansions = [];
4264
- if (expansionBits & 1) expansions.push("Monuments");
4265
- if (expansionBits & 2) expansions.push("Alliances");
4266
- let source;
4267
- switch (sourceBits) {
4268
- case 2:
4269
- source = "Competitive";
4270
- break;
4271
- default:
4272
- source = "Core";
4273
- break;
4274
- }
4275
- const seedBank = {
4276
- initializationSeed: rngSeed,
4277
- questSeed: rngSeed - 1,
4278
- seedString: normalized
4279
- };
4280
- return {
4281
- seed: normalized,
4282
- tier1Foe: TIER1_FOES[tier1],
4283
- tier2Foe: TIER2_FOES[tier2],
4284
- tier3Foe: TIER3_FOES[tier3],
4285
- adversary: ADVERSARIES[adversaryIndex],
4286
- ally: ALLIES[allyIndex],
4287
- difficulty: DIFFICULTIES[difficultyIndex],
4288
- source,
4289
- expansions,
4290
- playerCount,
4291
- rngSeed,
4292
- seedBank,
4293
- setup
4294
- };
4295
- }
4296
- function decodeRngSeed(seed) {
4297
- const normalized = validateSeed(seed);
4298
- const stripped = normalized.replace(/-/g, "").toLowerCase();
4299
- let rngSeed = 0;
4300
- for (let i = 0; i < RNG_SEED_LENGTH; i++) {
4301
- const value = charToValue(stripped[SETUP_LENGTH + i]);
4302
- rngSeed += value * Math.round(Math.pow(BASE, i));
4303
- }
4304
- return rngSeed;
4305
- }
4306
- function createSeed(config) {
4307
- let foeByteA = 0;
4308
- let foeByteB = 0;
4309
- const tier1Index = TIER1_FOES.indexOf(config.foes[0]);
4310
- const tier2Index = TIER2_FOES.indexOf(config.foes[1]);
4311
- const tier3Index = TIER3_FOES.indexOf(config.foes[2]);
4312
- if (tier1Index < 0) throw new Error(`Invalid Tier 1 foe: ${config.foes[0]}`);
4313
- if (tier2Index < 0) throw new Error(`Invalid Tier 2 foe: ${config.foes[1]}`);
4314
- if (tier3Index < 0) throw new Error(`Invalid Tier 3 foe: ${config.foes[2]}`);
4315
- foeByteA = tier1Index & 3;
4316
- foeByteA |= (tier2Index & 3) << 2;
4317
- foeByteA |= (tier3Index & 1) << 4;
4318
- foeByteB |= (tier3Index >> 1 & 1) << 4;
4319
- const adversaryIndex = ADVERSARIES.indexOf(config.adversary);
4320
- if (adversaryIndex < 0) throw new Error(`Invalid adversary: ${config.adversary}`);
4321
- foeByteB |= adversaryIndex & 15;
4322
- const allyIndex = ALLIES.indexOf(config.ally);
4323
- if (allyIndex < 0) throw new Error(`Invalid ally: ${config.ally}`);
4324
- let extraByte = 0;
4325
- if (config.difficulty === "Gritty") extraByte |= 1;
4326
- for (const expansion of config.expansions) {
4327
- switch (expansion) {
4328
- case "Monuments":
4329
- extraByte |= 2;
4330
- break;
4331
- case "Alliances":
4332
- extraByte |= 4;
4333
- break;
4334
- }
4335
- }
4336
- if (config.source === "Competitive") extraByte |= 8;
4337
- const versionByte = 0;
4338
- const playerCountByte = Math.max(0, Math.min(3, config.playerCount - 1));
4339
- let seedStr = valueToChar(foeByteA) + valueToChar(foeByteB) + valueToChar(allyIndex) + valueToChar(extraByte) + valueToChar(versionByte) + valueToChar(playerCountByte);
4340
- let rngValue = 0;
4341
- for (let i = 0; i < RNG_SEED_LENGTH; i++) {
4342
- const value = Math.floor(Math.random() * BASE);
4343
- seedStr += valueToChar(value);
4344
- rngValue += value * Math.round(Math.pow(BASE, i));
4345
- }
4346
- const upper = seedStr.toUpperCase();
4347
- const formatted = `${upper.slice(0, 4)}-${upper.slice(4, 8)}-${upper.slice(8, 12)}`;
4348
- return { seed: formatted, rngValue };
4349
- }
4350
- function encodeSeed(config, rngValue) {
4351
- let foeByteA = 0;
4352
- let foeByteB = 0;
4353
- const tier1Index = TIER1_FOES.indexOf(config.foes[0]);
4354
- const tier2Index = TIER2_FOES.indexOf(config.foes[1]);
4355
- const tier3Index = TIER3_FOES.indexOf(config.foes[2]);
4356
- if (tier1Index < 0) throw new Error(`Invalid Tier 1 foe: ${config.foes[0]}`);
4357
- if (tier2Index < 0) throw new Error(`Invalid Tier 2 foe: ${config.foes[1]}`);
4358
- if (tier3Index < 0) throw new Error(`Invalid Tier 3 foe: ${config.foes[2]}`);
4359
- foeByteA = tier1Index & 3;
4360
- foeByteA |= (tier2Index & 3) << 2;
4361
- foeByteA |= (tier3Index & 1) << 4;
4362
- foeByteB |= (tier3Index >> 1 & 1) << 4;
4363
- const adversaryIndex = ADVERSARIES.indexOf(config.adversary);
4364
- if (adversaryIndex < 0) throw new Error(`Invalid adversary: ${config.adversary}`);
4365
- foeByteB |= adversaryIndex & 15;
4366
- const allyIndex = ALLIES.indexOf(config.ally);
4367
- if (allyIndex < 0) throw new Error(`Invalid ally: ${config.ally}`);
4368
- let extraByte = 0;
4369
- if (config.difficulty === "Gritty") extraByte |= 1;
4370
- for (const expansion of config.expansions) {
4371
- switch (expansion) {
4372
- case "Monuments":
4373
- extraByte |= 2;
4374
- break;
4375
- case "Alliances":
4376
- extraByte |= 4;
4377
- break;
4378
- }
4379
- }
4380
- if (config.source === "Competitive") extraByte |= 8;
4381
- const versionByte = 0;
4382
- const playerCountByte = Math.max(0, Math.min(3, config.playerCount - 1));
4383
- let seedStr = valueToChar(foeByteA) + valueToChar(foeByteB) + valueToChar(allyIndex) + valueToChar(extraByte) + valueToChar(versionByte) + valueToChar(playerCountByte);
4384
- let remaining = rngValue;
4385
- for (let i = 0; i < RNG_SEED_LENGTH; i++) {
4386
- const digit = remaining % BASE;
4387
- seedStr += valueToChar(digit);
4388
- remaining = Math.floor(remaining / BASE);
4389
- }
4390
- const upper = seedStr.toUpperCase();
4391
- return `${upper.slice(0, 4)}-${upper.slice(4, 8)}-${upper.slice(8, 12)}`;
4392
- }
4393
- function compareSeedsRaw(seed1, seed2) {
4394
- const n1 = validateSeed(seed1);
4395
- const n2 = validateSeed(seed2);
4396
- const s1 = n1.replace(/-/g, "").toLowerCase();
4397
- const s2 = n2.replace(/-/g, "").toLowerCase();
4398
- const diffs = [];
4399
- for (let i = 0; i < SEED_LENGTH; i++) {
4400
- const v1 = charToValue(s1[i]);
4401
- const v2 = charToValue(s2[i]);
4402
- if (v1 !== v2) {
4403
- diffs.push({
4404
- charIndex: i,
4405
- value1: v1,
4406
- value2: v2,
4407
- char1: s1[i],
4408
- char2: s2[i]
4409
- });
4410
- }
4411
- }
4412
- return {
4413
- seed1: n1,
4414
- seed2: n2,
4415
- diffs,
4416
- setupDiffs: diffs.filter((d) => d.charIndex < SETUP_LENGTH),
4417
- rngDiffs: diffs.filter((d) => d.charIndex >= SETUP_LENGTH)
4418
- };
4419
- }
4420
- var SETUP_FIELD_LABELS = {
4421
- 0: "Tier1/Tier2/Tier3lo",
4422
- 1: "Adversary/Tier3hi",
4423
- 2: "Ally",
4424
- 3: "Difficulty/Expansions/Source",
4425
- 4: "Version",
4426
- 5: "PlayerCount"
4427
- };
4428
- function dumpSeedChars(seed) {
4429
- const normalized = validateSeed(seed);
4430
- const stripped = normalized.replace(/-/g, "").toLowerCase();
4431
- const chars = [];
4432
- for (let i = 0; i < SEED_LENGTH; i++) {
4433
- const isSetup = i < SETUP_LENGTH;
4434
- chars.push({
4435
- index: i,
4436
- char: stripped[i],
4437
- value: charToValue(stripped[i]),
4438
- section: isSetup ? "setup" : "rng",
4439
- field: isSetup ? SETUP_FIELD_LABELS[i] : void 0
4440
- });
4441
- }
4442
- return { seed: normalized, chars };
4443
- }
4173
+ // src/data/index.ts
4174
+ var data_exports = {};
4175
+ __export(data_exports, {
4176
+ board: () => board_exports,
4177
+ content: () => udtGameContent_exports,
4178
+ foes: () => udtFoes_exports,
4179
+ heroes: () => udtHeroes_exports,
4180
+ inventory: () => udtBoxInventory_exports,
4181
+ monuments: () => udtMonuments_exports
4182
+ });
4444
4183
 
4445
- // src/udtHeroes.ts
4184
+ // src/data/udtHeroes.ts
4185
+ var udtHeroes_exports = {};
4186
+ __export(udtHeroes_exports, {
4187
+ HEROES: () => HEROES,
4188
+ HERO_BY_ID: () => HERO_BY_ID
4189
+ });
4446
4190
  var HEROES = [
4447
4191
  // Base (4)
4448
4192
  { id: "brutal-warlord", name: "Brutal Warlord", source: "base" },
@@ -4470,7 +4214,12 @@ var HERO_BY_ID = Object.freeze(
4470
4214
  }, {})
4471
4215
  );
4472
4216
 
4473
- // src/udtMonuments.ts
4217
+ // src/data/udtMonuments.ts
4218
+ var udtMonuments_exports = {};
4219
+ __export(udtMonuments_exports, {
4220
+ MONUMENTS: () => MONUMENTS,
4221
+ MONUMENT_BY_ID: () => MONUMENT_BY_ID
4222
+ });
4474
4223
  var MONUMENTS = [
4475
4224
  { id: "arch-of-the-golden-sun", name: "Arch of the Golden Sun", source: "covenant" },
4476
4225
  { id: "argent-oak", name: "Argent Oak", source: "covenant" },
@@ -4488,7 +4237,16 @@ var MONUMENT_BY_ID = Object.freeze(
4488
4237
  }, {})
4489
4238
  );
4490
4239
 
4491
- // src/udtFoes.ts
4240
+ // src/data/udtFoes.ts
4241
+ var udtFoes_exports = {};
4242
+ __export(udtFoes_exports, {
4243
+ ADVERSARY_ROSTER: () => ADVERSARY_ROSTER,
4244
+ ALL_FOES: () => ALL_FOES,
4245
+ FOES: () => FOES,
4246
+ FOE_BY_ID: () => FOE_BY_ID,
4247
+ FOE_BY_NAME: () => FOE_BY_NAME,
4248
+ FOE_STATUSES: () => FOE_STATUSES
4249
+ });
4492
4250
  var FOE_STATUSES = ["panicked", "unsteady", "ready", "savage", "lethal"];
4493
4251
  var FOES = [
4494
4252
  // Tier 1 — level 2
@@ -4531,120 +4289,21 @@ var FOE_BY_NAME = Object.freeze(
4531
4289
  }, {})
4532
4290
  );
4533
4291
 
4534
- // src/udtSystemRandom.ts
4535
- var INT32_MAX = 2147483647;
4536
- var MSEED = 161803398;
4537
- function toInt32(n) {
4538
- return n | 0;
4539
- }
4540
- var SystemRandom = class {
4541
- /**
4542
- * Create a new PRNG instance with the given seed.
4543
- * Matches C# `new System.Random(seed)` exactly.
4544
- */
4545
- constructor(seed) {
4546
- this.seedArray = new Array(56).fill(0);
4547
- this.inext = 0;
4548
- this.inextp = 0;
4549
- this.initialize(seed);
4550
- }
4551
- /**
4552
- * Replicate .NET's System.Random constructor seeding algorithm.
4553
- */
4554
- initialize(seed) {
4555
- let subtraction;
4556
- if (seed === -2147483648) {
4557
- subtraction = INT32_MAX;
4558
- } else {
4559
- subtraction = Math.abs(seed);
4560
- }
4561
- let mj = toInt32(MSEED - subtraction);
4562
- this.seedArray[55] = mj;
4563
- let mk = 1;
4564
- for (let i = 1; i < 55; i++) {
4565
- const ii = 21 * i % 55;
4566
- this.seedArray[ii] = mk;
4567
- mk = toInt32(mj - mk);
4568
- if (mk < 0) mk = toInt32(mk + INT32_MAX);
4569
- mj = this.seedArray[ii];
4570
- }
4571
- for (let k = 1; k < 5; k++) {
4572
- for (let i = 1; i < 56; i++) {
4573
- this.seedArray[i] = toInt32(this.seedArray[i] - this.seedArray[1 + (i + 30) % 55]);
4574
- if (this.seedArray[i] < 0) {
4575
- this.seedArray[i] = toInt32(this.seedArray[i] + INT32_MAX);
4576
- }
4577
- }
4578
- }
4579
- this.inext = 0;
4580
- this.inextp = 21;
4581
- }
4582
- /**
4583
- * Internal sample — returns value in range [0, Int32.MaxValue).
4584
- * Matches C#'s InternalSample().
4585
- */
4586
- internalSample() {
4587
- let retVal;
4588
- let locINext = this.inext;
4589
- let locINextp = this.inextp;
4590
- if (++locINext >= 56) locINext = 1;
4591
- if (++locINextp >= 56) locINextp = 1;
4592
- retVal = toInt32(this.seedArray[locINext] - this.seedArray[locINextp]);
4593
- if (retVal === INT32_MAX) retVal--;
4594
- if (retVal < 0) retVal = toInt32(retVal + INT32_MAX);
4595
- this.seedArray[locINext] = retVal;
4596
- this.inext = locINext;
4597
- this.inextp = locINextp;
4598
- return retVal;
4599
- }
4600
- /**
4601
- * Sample — returns a double in range [0.0, 1.0).
4602
- * Matches C#'s Sample().
4603
- */
4604
- sample() {
4605
- return this.internalSample() * (1 / INT32_MAX);
4606
- }
4607
- /**
4608
- * Returns a non-negative random integer less than Int32.MaxValue.
4609
- * Matches C# `Random.Next()`.
4610
- */
4611
- next() {
4612
- return this.internalSample();
4613
- }
4614
- /**
4615
- * Returns a non-negative random integer less than maxValue.
4616
- * Matches C# `Random.Next(maxValue)`.
4617
- */
4618
- nextMax(maxValue) {
4619
- if (maxValue < 0) {
4620
- throw new Error("maxValue must be non-negative");
4621
- }
4622
- return toInt32(this.sample() * maxValue);
4623
- }
4624
- /**
4625
- * Returns a random integer in range [minValue, maxValue).
4626
- * Matches C# `Random.Next(minValue, maxValue)`.
4627
- */
4628
- nextRange(minValue, maxValue) {
4629
- if (minValue > maxValue) {
4630
- throw new Error("minValue must be less than or equal to maxValue");
4631
- }
4632
- const range = maxValue - minValue;
4633
- if (range <= INT32_MAX) {
4634
- return toInt32(this.sample() * range) + minValue;
4635
- }
4636
- return toInt32(this.internalSample() * (1 / INT32_MAX) * range) + minValue;
4637
- }
4638
- /**
4639
- * Returns a random double in range [0.0, 1.0).
4640
- * Matches C# `Random.NextDouble()`.
4641
- */
4642
- nextDouble() {
4643
- return this.sample();
4644
- }
4645
- };
4292
+ // src/data/board/index.ts
4293
+ var board_exports = {};
4294
+ __export(board_exports, {
4295
+ BOARD_ADJACENCY: () => BOARD_ADJACENCY,
4296
+ BOARD_ANCHORS: () => BOARD_ANCHORS,
4297
+ BOARD_GROUPINGS: () => BOARD_GROUPINGS,
4298
+ BOARD_IMAGE_INFO: () => BOARD_IMAGE_INFO,
4299
+ BOARD_LOCATIONS: () => BOARD_LOCATIONS,
4300
+ BOARD_LOCATION_BY_NAME: () => BOARD_LOCATION_BY_NAME,
4301
+ neighborsOf: () => neighborsOf,
4302
+ shortestPath: () => shortestPath,
4303
+ stepDistance: () => stepDistance
4304
+ });
4646
4305
 
4647
- // src/udtGameBoard.ts
4306
+ // src/data/board/udtGameBoard.ts
4648
4307
  var BOARD_GROUPINGS = {
4649
4308
  /** Dayside and Fivepint (North kingdom lakes). */
4650
4309
  LONG_WATER: "Long Water",
@@ -4721,7 +4380,7 @@ var BOARD_LOCATIONS = [
4721
4380
  ];
4722
4381
  var BOARD_LOCATION_BY_NAME = Object.fromEntries(BOARD_LOCATIONS.map((loc) => [loc.name, loc]));
4723
4382
 
4724
- // src/udtBoardAnchors.ts
4383
+ // src/data/board/udtBoardAnchors.ts
4725
4384
  var BOARD_IMAGE_INFO = {
4726
4385
  width: 4096,
4727
4386
  height: 4096,
@@ -5065,7 +4724,7 @@ var BOARD_ANCHORS = {
5065
4724
  }
5066
4725
  };
5067
4726
 
5068
- // src/udtBoardAdjacency.ts
4727
+ // src/data/board/udtBoardAdjacency.ts
5069
4728
  var BOARD_ADJACENCY = {
5070
4729
  "Howling Desert": [
5071
4730
  "Azkol's Bane",
@@ -5366,86 +5025,1462 @@ var BOARD_ADJACENCY = {
5366
5025
  "Lesser Tombstones",
5367
5026
  "The Decaying Wilds"
5368
5027
  ],
5369
- Duwani: ["Jewel Hills", "Lake of Songs", "Utar's Barrows"],
5370
- "Lesser Tombstones": [
5371
- "Big Sister",
5372
- "Greater Tombstones",
5373
- "Lake of Songs",
5374
- "Middle Sister",
5375
- "Three Rivers",
5376
- "Utar's Barrows"
5028
+ Duwani: ["Jewel Hills", "Lake of Songs", "Utar's Barrows"],
5029
+ "Lesser Tombstones": [
5030
+ "Big Sister",
5031
+ "Greater Tombstones",
5032
+ "Lake of Songs",
5033
+ "Middle Sister",
5034
+ "Three Rivers",
5035
+ "Utar's Barrows"
5036
+ ],
5037
+ "Lake of Songs": ["Duwani", "Lesser Tombstones", "Three Rivers", "Utar's Barrows"],
5038
+ "Three Rivers": ["Lake of Songs", "Lesser Tombstones", "Middle Sister", "Pine Barrens"]
5039
+ };
5040
+ function neighborsOf(loc) {
5041
+ return BOARD_ADJACENCY[loc] ?? [];
5042
+ }
5043
+ function stepDistance(a, b) {
5044
+ if (a === b) return 0;
5045
+ const visited = /* @__PURE__ */ new Set([a]);
5046
+ let frontier = [a];
5047
+ let dist = 0;
5048
+ while (frontier.length > 0) {
5049
+ dist++;
5050
+ const next = [];
5051
+ for (const node of frontier) {
5052
+ for (const n of neighborsOf(node)) {
5053
+ if (n === b) return dist;
5054
+ if (!visited.has(n)) {
5055
+ visited.add(n);
5056
+ next.push(n);
5057
+ }
5058
+ }
5059
+ }
5060
+ frontier = next;
5061
+ }
5062
+ return Infinity;
5063
+ }
5064
+ function shortestPath(a, b) {
5065
+ if (a === b) return [a];
5066
+ const prev = /* @__PURE__ */ new Map();
5067
+ const visited = /* @__PURE__ */ new Set([a]);
5068
+ let frontier = [a];
5069
+ while (frontier.length > 0) {
5070
+ const next = [];
5071
+ for (const node of frontier) {
5072
+ for (const n of neighborsOf(node)) {
5073
+ if (visited.has(n)) continue;
5074
+ visited.add(n);
5075
+ prev.set(n, node);
5076
+ if (n === b) {
5077
+ const path = [b];
5078
+ let cur = b;
5079
+ while (cur !== void 0 && cur !== a) {
5080
+ cur = prev.get(cur);
5081
+ if (cur !== void 0) path.push(cur);
5082
+ }
5083
+ return path.reverse();
5084
+ }
5085
+ next.push(n);
5086
+ }
5087
+ }
5088
+ frontier = next;
5089
+ }
5090
+ return [];
5091
+ }
5092
+
5093
+ // src/data/udtGameContent.ts
5094
+ var udtGameContent_exports = {};
5095
+ __export(udtGameContent_exports, {
5096
+ ADVERSARIES: () => ADVERSARIES,
5097
+ COMPANIONS: () => COMPANIONS,
5098
+ FOES: () => FOES2,
5099
+ HEROES: () => HEROES2,
5100
+ KINGDOM_VIRTUES: () => KINGDOM_VIRTUES,
5101
+ adversaries: () => adversaries,
5102
+ companions: () => companions,
5103
+ foes: () => foes,
5104
+ heroes: () => heroes,
5105
+ kingdomVirtues: () => kingdomVirtues
5106
+ });
5107
+ var HEROES2 = {
5108
+ Spymaster: {
5109
+ name: "Spymaster",
5110
+ expansion: "Base Game",
5111
+ bannerAction: "Place your hero on any space in your current kingdom.",
5112
+ defaultVirtues: [
5113
+ { name: "Alert", ability: "+1 Stealth Advantage." },
5114
+ { name: "Swift", ability: "Your base move is 4." }
5115
+ ],
5116
+ unlockableVirtues: [
5117
+ { name: "Resourceful", ability: "At the end of each month, gain 15 warriors." },
5118
+ {
5119
+ name: "Fortunate",
5120
+ ability: "You may Reinforce twice per turn at the same building."
5121
+ },
5122
+ {
5123
+ name: "Unseen",
5124
+ ability: "When you complete a monthly quest, you may remove a foe instead of gaining spirit."
5125
+ }
5126
+ ]
5127
+ },
5128
+ "Brutal Warlord": {
5129
+ name: "Brutal Warlord",
5130
+ expansion: "Base Game",
5131
+ bannerAction: "Gain 5 warriors.",
5132
+ defaultVirtues: [
5133
+ { name: "Baleful", ability: "+1 Melee Advantage." },
5134
+ { name: "Veteran", ability: "+1 Wild Advantage when you Battle." }
5135
+ ],
5136
+ unlockableVirtues: [
5137
+ { name: "Inspiring", ability: "After you Reinforce, also gain 6 warriors." },
5138
+ {
5139
+ name: "Callous",
5140
+ ability: "After you Battle, if you lost at least 10 warriors, gain a treasure from the market."
5141
+ },
5142
+ { name: "Relentless", ability: "If you double your move, gain +1 Wild Advantage." }
5143
+ ]
5144
+ },
5145
+ "Orphaned Scion": {
5146
+ name: "Orphaned Scion",
5147
+ expansion: "Base Game",
5148
+ bannerAction: "Gain 1 spirit.",
5149
+ defaultVirtues: [
5150
+ { name: "Arcane", ability: "+1 Magic Advantage." },
5151
+ { name: "Generous", ability: "After you Cleanse, remove 1 skull from any building." }
5152
+ ],
5153
+ unlockableVirtues: [
5154
+ {
5155
+ name: "Infused",
5156
+ ability: "At the start of your turn, remove 1 skull from a building in your home kingdom."
5157
+ },
5158
+ {
5159
+ name: "Blessed",
5160
+ ability: "Spend 1 spirit to prevent up to 6 warrior losses from a battle card or dungeon room."
5161
+ },
5162
+ {
5163
+ name: "Anointed",
5164
+ ability: "+1 Wild Advantage for each building with no skulls on or adjacent to your space."
5165
+ }
5166
+ ]
5167
+ },
5168
+ "Relic Hunter": {
5169
+ name: "Relic Hunter",
5170
+ expansion: "Base Game",
5171
+ bannerAction: "Gain 1 potion.",
5172
+ defaultVirtues: [
5173
+ { name: "Precise", ability: "+1 Humanoid Advantage." },
5174
+ {
5175
+ name: "Prepared",
5176
+ ability: "When you Reinforce at a bazaar, spend 1 less spirit to gain a treasure."
5177
+ }
5178
+ ],
5179
+ unlockableVirtues: [
5180
+ { name: "Crafty", ability: "When you spend a potion, double the number on it." },
5181
+ {
5182
+ name: "Lucky",
5183
+ ability: "When you spend (not lose) a treasure, gain the top card of the treasure deck."
5184
+ },
5185
+ { name: "Inventive", ability: "Spend 4 potions to remove a foe from your space." }
5186
+ ]
5187
+ },
5188
+ "Haunted Recluse": {
5189
+ name: "Haunted Recluse",
5190
+ expansion: "Alliances",
5191
+ bannerAction: "Move 1 skull from any building to any other building with 2 or fewer skulls.",
5192
+ defaultVirtues: [
5193
+ { name: "Spiritreaver", ability: "+1 Undead Advantage." },
5194
+ {
5195
+ name: "Skullweaver",
5196
+ ability: "When a skull emerges in your home kingdom, you can place it on any building with 2 or fewer skulls in any kingdom."
5197
+ }
5198
+ ],
5199
+ unlockableVirtues: [
5200
+ {
5201
+ name: "Shadowspinner",
5202
+ ability: "+1 Wild Advantage for each building with skulls on or adjacent to your space."
5203
+ },
5204
+ {
5205
+ name: "Soulreaper",
5206
+ ability: "Prevent up to 2 warrior losses per battle card for each skull on or adjacent to your space."
5207
+ },
5208
+ {
5209
+ name: "Sinbearer",
5210
+ ability: "At the end of the month you can spend up to 12 warriors to remove all skulls from your current kingdom."
5211
+ }
5212
+ ]
5213
+ },
5214
+ Archwright: {
5215
+ name: "Archwright",
5216
+ expansion: "Alliances",
5217
+ bannerAction: "Place a battlement on any space or move a battlement up to 2 spaces.",
5218
+ defaultVirtues: [
5219
+ { name: "Innovative", ability: "+1 Beast Advantage." },
5220
+ { name: "Clever", ability: "Battlements give you +2 Wild Advantages (instead of +1)." }
5221
+ ],
5222
+ unlockableVirtues: [
5223
+ {
5224
+ name: "Tactical",
5225
+ ability: "While on a battlement, you can Battle a foe on an adjacent space. (Terrain advantages use the space you are on.)"
5226
+ },
5227
+ {
5228
+ name: "Wily",
5229
+ ability: "Battlements give you advantages when you Quest (in addition to when you Battle)."
5230
+ },
5231
+ {
5232
+ name: "Exalted",
5233
+ ability: "While on a battlement, you may Cleanse to remove skulls from all adjacent buildings."
5234
+ }
5235
+ ]
5236
+ },
5237
+ "Relentless Warden": {
5238
+ name: "Relentless Warden",
5239
+ expansion: "Covenant",
5240
+ bannerAction: "Place quarry token on a foe if it is not already, else move quarry token up to 2 spaces.",
5241
+ defaultVirtues: [
5242
+ { name: "Perceptive", ability: "+1 Wild Advantage vs. your quarry." },
5243
+ {
5244
+ name: "Guarded",
5245
+ ability: "Prevent up to 3 warrior losses per battle card when you Battle your quarry."
5246
+ }
5247
+ ],
5248
+ unlockableVirtues: [
5249
+ { name: "Keen-Eyed", ability: "+2 Wild Advantages vs. your quarry." },
5250
+ {
5251
+ name: "Instinctive",
5252
+ ability: "You may remove your quarry token to ignore your quarry during its strike event."
5253
+ },
5254
+ {
5255
+ name: "Inspiring",
5256
+ ability: "When you defeat your quarry, remove all skulls on or adjacent to your space."
5257
+ }
5258
+ ]
5259
+ },
5260
+ "Undaunted Aegis": {
5261
+ name: "Undaunted Aegis",
5262
+ expansion: "Covenant",
5263
+ bannerAction: "For each corruption you have, gain 3 warriors. You may spend 10 warriors to remove one of your corruptions.",
5264
+ defaultVirtues: [
5265
+ {
5266
+ name: "Ascetic",
5267
+ ability: "Gain 1 spirit for each battle card you spend no advantages on."
5268
+ },
5269
+ {
5270
+ name: "Iron-Willed",
5271
+ ability: "You can have an additional corruption. Start the game with 1 random corruption."
5272
+ }
5273
+ ],
5274
+ unlockableVirtues: [
5275
+ { name: "Emboldened", ability: "+1 Wild Advantage for each corruption you have." },
5276
+ {
5277
+ name: "Resolute",
5278
+ ability: "When you Reinforce, spend 1 less spirit for each corruption you have."
5279
+ },
5280
+ {
5281
+ name: "Steeled",
5282
+ ability: "Once per turn, if another hero would gain a corruption, you may gain it instead and gain 2 spirit."
5283
+ }
5284
+ ]
5285
+ },
5286
+ "Devious Swindler": {
5287
+ name: "Devious Swindler",
5288
+ expansion: "Covenant",
5289
+ bannerAction: "Roll the haggle die and gain the result.",
5290
+ defaultVirtues: [
5291
+ {
5292
+ name: "Inventive",
5293
+ ability: "When you Battle, gain all advantages in the treasure market."
5294
+ },
5295
+ {
5296
+ name: "Joyful",
5297
+ ability: "When you roll the haggle die, ignore the Cancelled symbol."
5298
+ }
5299
+ ],
5300
+ unlockableVirtues: [
5301
+ {
5302
+ name: "Fortuitous",
5303
+ ability: "After you roll the haggle die, you may reroll once and take either result."
5304
+ },
5305
+ {
5306
+ name: "Opportunistic",
5307
+ ability: "When any player gains a treasure from the treasure market, you gain a blessing."
5308
+ },
5309
+ {
5310
+ name: "Calculating",
5311
+ ability: "You may ignore warrior and spirit losses on critical hit battle cards."
5312
+ }
5313
+ ]
5314
+ },
5315
+ "Reverent Astromancer": {
5316
+ name: "Reverent Astromancer",
5317
+ expansion: "Covenant",
5318
+ bannerAction: "Remove a skull on or adjacent to your space.",
5319
+ defaultVirtues: [
5320
+ {
5321
+ name: "Well Versed",
5322
+ ability: "If you remove a skull with your Banner action, gain a blessing."
5323
+ },
5324
+ {
5325
+ name: "Pious",
5326
+ ability: "At the start of each month, prepare spells equal to the month number."
5327
+ }
5328
+ ],
5329
+ unlockableVirtues: [
5330
+ { name: "Exalted", ability: "You can prepare invocations." },
5331
+ { name: "Zealous", ability: "Whenever you cast a spell, gain a blessing." },
5332
+ {
5333
+ name: "Bounteous",
5334
+ ability: "Once per turn, when you cast a spell, gain the top card of the treasure deck."
5335
+ }
5336
+ ]
5337
+ }
5338
+ };
5339
+ var heroes = Object.values(HEROES2);
5340
+ var FOES2 = {
5341
+ Brigands: { name: "Brigands", level: 2 },
5342
+ Oreks: { name: "Oreks", level: 2 },
5343
+ "Shadow Wolves": { name: "Shadow Wolves", level: 2 },
5344
+ "Spine Fiend": { name: "Spine Fiend", level: 2 },
5345
+ "Clan Of Neuri": { name: "Clan Of Neuri", level: 3 },
5346
+ "Frost Troll": { name: "Frost Troll", level: 3 },
5347
+ Lemure: { name: "Lemure", level: 3 },
5348
+ "Widowmade Spider": { name: "Widowmade Spider", level: 3 },
5349
+ Dragon: { name: "Dragon", level: 4 },
5350
+ Mormo: { name: "Mormo", level: 4 },
5351
+ Striga: { name: "Striga", level: 4 },
5352
+ Titan: { name: "Titan", level: 4 }
5353
+ };
5354
+ var foes = Object.values(FOES2);
5355
+ var ADVERSARIES = {
5356
+ Ashstrider: { name: "Ashstrider", level: 5 },
5357
+ "Bane Of Omens": { name: "Bane Of Omens", level: 5 },
5358
+ "Empress Of Shades": { name: "Empress Of Shades", level: 5 },
5359
+ "Gaze Eternal": { name: "Gaze Eternal", level: 5 },
5360
+ Gravemaw: { name: "Gravemaw", level: 5 },
5361
+ "Isa The Exile": { name: "Isa The Exile", level: 5 },
5362
+ "Lingering Rot": { name: "Lingering Rot", level: 5 },
5363
+ "U'tuk-Ku The Ice Herald": { name: "U'tuk-Ku The Ice Herald", level: 5 }
5364
+ };
5365
+ var adversaries = Object.values(ADVERSARIES);
5366
+ var COMPANIONS = {
5367
+ Gleb: { name: "Gleb", title: "The Outlaw King" },
5368
+ Grigor: { name: "Grigor", title: "The Unbreakable" },
5369
+ Hakan: { name: "Hakan", title: "The Artificer" },
5370
+ Letha: { name: "Letha", title: "The Dryad" },
5371
+ Miras: { name: "Miras", title: "The Horselord" },
5372
+ Nimet: { name: "Nimet", title: "The Fathomless" },
5373
+ Tomas: { name: "Tomas", title: "The Scout" },
5374
+ Vasa: { name: "Vasa", title: "The Devine" },
5375
+ Yana: { name: "Yana", title: "The Assassin" },
5376
+ Zaida: { name: "Zaida", title: "The Efreet" }
5377
+ };
5378
+ var companions = Object.values(COMPANIONS);
5379
+ var KINGDOM_VIRTUES = {
5380
+ East: { name: "Champion of the East", ability: "+2 Wild Advantages in hills." },
5381
+ North: { name: "Champion of the North", ability: "+2 Wild Advantages in mountains." },
5382
+ South: { name: "Champion of the South", ability: "+2 Wild Advantages in deserts." },
5383
+ West: { name: "Champion of the West", ability: "+2 Wild Advantages in forests." }
5384
+ };
5385
+ var kingdomVirtues = Object.values(KINGDOM_VIRTUES);
5386
+
5387
+ // src/data/udtBoxInventory.ts
5388
+ var udtBoxInventory_exports = {};
5389
+ __export(udtBoxInventory_exports, {
5390
+ EXPANSIONS: () => EXPANSIONS,
5391
+ coffers: () => coffers,
5392
+ coffers2: () => coffers2,
5393
+ expansions: () => expansions,
5394
+ skullsPack: () => skullsPack,
5395
+ sleeves: () => sleeves
5396
+ });
5397
+ var expansions = [
5398
+ {
5399
+ name: "Base Game",
5400
+ categories: [
5401
+ {
5402
+ name: "Manuals / Sheets",
5403
+ section: "Misc",
5404
+ components: [
5405
+ { name: "Are You Ready To Take On The Tower", count: 1 },
5406
+ { name: "Re-Pack Sheet", count: 1 },
5407
+ { name: "Rulebook", count: 1 }
5408
+ ]
5409
+ },
5410
+ {
5411
+ name: "Tower",
5412
+ section: "Misc",
5413
+ components: [
5414
+ { name: "Seals", count: 12 },
5415
+ { name: "Tower", count: 1 }
5416
+ ]
5417
+ },
5418
+ {
5419
+ name: "Boards",
5420
+ section: "Misc",
5421
+ components: [
5422
+ { name: "Brutal Warlord", count: 1 },
5423
+ { name: "Game Board", count: 1 },
5424
+ { name: "Orphaned Scion", count: 1 },
5425
+ { name: "Relic Hunter", count: 1 },
5426
+ { name: "Spymaster", count: 1 }
5427
+ ]
5428
+ },
5429
+ {
5430
+ name: "Minis",
5431
+ section: "Misc",
5432
+ components: [
5433
+ { name: "Bazaars", count: 4 },
5434
+ { name: "Citadels", count: 4 },
5435
+ { name: "Hero Figures", count: 4 },
5436
+ { name: "Sanctuaries", count: 4 },
5437
+ { name: "Villages", count: 4 }
5438
+ ]
5439
+ },
5440
+ {
5441
+ name: "Mini Bases",
5442
+ section: "Misc",
5443
+ components: [
5444
+ { color: "Blue", count: 1 },
5445
+ { color: "Brown", count: 1 },
5446
+ { color: "Green", count: 1 },
5447
+ { color: "White", count: 1 }
5448
+ ]
5449
+ },
5450
+ {
5451
+ name: "Dungeons",
5452
+ section: "Tokens",
5453
+ components: [
5454
+ { name: "Cave", count: 1 },
5455
+ { name: "Encampment", count: 1 },
5456
+ { name: "Fortress", count: 1 },
5457
+ { name: "Ruins", count: 1 },
5458
+ { name: "Shrine", count: 1 },
5459
+ { name: "Tomb", count: 1 }
5460
+ ]
5461
+ },
5462
+ {
5463
+ name: "Foes / Adversaries",
5464
+ section: "Tokens",
5465
+ components: [
5466
+ { name: "Brigands", level: 2, count: 8 },
5467
+ { name: "Oreks", level: 2, count: 6 },
5468
+ { name: "Shadow Wolves", level: 2, count: 8 },
5469
+ { name: "Spine Fiend", level: 2, count: 6 },
5470
+ { name: "Clan Of Neuri", level: 3, count: 5 },
5471
+ { name: "Frost Troll", level: 3, count: 4 },
5472
+ { name: "Lemure", level: 3, count: 6 },
5473
+ { name: "Widowmade Spider", level: 3, count: 5 },
5474
+ { name: "Dragon", level: 4, count: 2 },
5475
+ { name: "Mormo", level: 4, count: 4 },
5476
+ { name: "Striga", level: 4, count: 2 },
5477
+ { name: "Titan", level: 4, count: 1 },
5478
+ { name: "Ashstrider", level: 5, count: 1 },
5479
+ { name: "Bane Of Omens", level: 5, count: 1 },
5480
+ { name: "Empress Of Shades", level: 5, count: 1 },
5481
+ { name: "Gaze Eternal", level: 5, count: 1 },
5482
+ { name: "Gravemaw", level: 5, count: 1 },
5483
+ { name: "Isa The Exile", level: 5, count: 1 },
5484
+ { name: "Lingering Rot", level: 5, count: 1 },
5485
+ { name: "U'tuk-Ku The Ice Herald", level: 5, count: 1 }
5486
+ ]
5487
+ },
5488
+ {
5489
+ name: "Quest Markers & Bases",
5490
+ section: "Tokens",
5491
+ components: [
5492
+ { name: "Adversary", count: 1 },
5493
+ { name: "Companion", count: 1 },
5494
+ { name: "Main Goal", count: 1 }
5495
+ ]
5496
+ },
5497
+ {
5498
+ name: "Special",
5499
+ section: "Tokens",
5500
+ components: [
5501
+ { name: "Caravans", type: "Eastern", count: 1 },
5502
+ { name: "Caravans", type: "Northern", count: 1 },
5503
+ { name: "Caravans", type: "Western", count: 1 },
5504
+ { name: "River Of Fire", count: 4 },
5505
+ { name: "Siege Trees", count: 8 },
5506
+ { name: "Spores", count: 8 }
5507
+ ]
5508
+ },
5509
+ {
5510
+ name: "Spirits",
5511
+ section: "Tokens",
5512
+ components: [
5513
+ { name: "1's", count: 25 },
5514
+ { name: "5's", count: 19 }
5515
+ ]
5516
+ },
5517
+ {
5518
+ name: "Virtues",
5519
+ section: "Tokens",
5520
+ components: [
5521
+ { name: "Anointed", count: 1 },
5522
+ { name: "Blessed", count: 1 },
5523
+ { name: "Callous", count: 1 },
5524
+ { name: "Champion Of The East", count: 1 },
5525
+ { name: "Champion Of The North", count: 1 },
5526
+ { name: "Champion Of The South", count: 1 },
5527
+ { name: "Champion Of The West", count: 1 },
5528
+ { name: "Crafty", count: 1 },
5529
+ { name: "Fortunate", count: 1 },
5530
+ { name: "Infused", count: 1 },
5531
+ { name: "Inspiring", count: 1 },
5532
+ { name: "Inventive", count: 1 },
5533
+ { name: "Lucky", count: 1 },
5534
+ { name: "Relentless", count: 1 },
5535
+ { name: "Resourceful", count: 1 },
5536
+ { name: "Unseen", count: 1 }
5537
+ ]
5538
+ },
5539
+ {
5540
+ name: "Warriors",
5541
+ section: "Tokens",
5542
+ components: [
5543
+ { name: "1's", count: 30 },
5544
+ { name: "5's", count: 25 }
5545
+ ]
5546
+ },
5547
+ {
5548
+ name: "Companion",
5549
+ section: "Cards",
5550
+ components: [
5551
+ { name: "Gleb", description: "The Outlaw King", count: 1 },
5552
+ { name: "Grigor", description: "The Unbreakable", count: 1 },
5553
+ { name: "Hakan", description: "The Artificer", count: 1 },
5554
+ { name: "Letha", description: "The Dryad", count: 1 },
5555
+ { name: "Miras", description: "The Horselord", count: 1 },
5556
+ { name: "Nimet", description: "The Fathomless", count: 1 },
5557
+ { name: "Tomas", description: "The Scout", count: 1 },
5558
+ { name: "Vasa", description: "The Devine", count: 1 },
5559
+ { name: "Yana", description: "The Assassin", count: 1 },
5560
+ { name: "Zaida", description: "The Efreet", count: 1 }
5561
+ ]
5562
+ },
5563
+ {
5564
+ name: "Corruption",
5565
+ section: "Cards",
5566
+ components: [
5567
+ { name: "Cruel", count: 1 },
5568
+ { name: "Cursed", count: 1 },
5569
+ { name: "Feeble", count: 1 },
5570
+ { name: "Feral", count: 1 },
5571
+ { name: "Feverish", count: 1 },
5572
+ { name: "Greedy", count: 1 },
5573
+ { name: "Lost", count: 1 },
5574
+ { name: "Selfish", count: 1 },
5575
+ { name: "Suspicious", count: 1 },
5576
+ { name: "Tempted", count: 1 },
5577
+ { name: "Uncertain", count: 1 },
5578
+ { name: "Weak", count: 1 }
5579
+ ]
5580
+ },
5581
+ {
5582
+ name: "Foes",
5583
+ section: "Cards",
5584
+ components: [
5585
+ { name: "Brigands", level: 2, count: 1 },
5586
+ { name: "Oreks", level: 2, count: 1 },
5587
+ { name: "Shadow Wolves", level: 2, count: 1 },
5588
+ { name: "Spine Fiend", level: 2, count: 1 },
5589
+ { name: "Clan Of Neuri", level: 3, count: 1 },
5590
+ { name: "Frost Troll", level: 3, count: 1 },
5591
+ { name: "Lemure", level: 3, count: 1 },
5592
+ { name: "Widowmade Spider", level: 3, count: 1 },
5593
+ { name: "Dragon", level: 4, count: 1 },
5594
+ { name: "Mormo", level: 4, count: 1 },
5595
+ { name: "Striga", level: 4, count: 1 },
5596
+ { name: "Titan", level: 4, count: 1 },
5597
+ { name: "Ashstrider", level: 5, count: 1 },
5598
+ { name: "Bane Of Omens", level: 5, count: 1 },
5599
+ { name: "Empress Of Shades", level: 5, count: 1 },
5600
+ { name: "Gaze Eternal", level: 5, count: 1 },
5601
+ { name: "Gravemaw", level: 5, count: 1 },
5602
+ { name: "Isa The Exile", level: 5, count: 1 },
5603
+ { name: "Lingering Rot", level: 5, count: 1 },
5604
+ { name: "U'tuk-Ku The Ice Herald", level: 5, count: 1 }
5605
+ ]
5606
+ },
5607
+ {
5608
+ name: "Gear",
5609
+ section: "Cards",
5610
+ components: [
5611
+ { name: "Blessed Scepters", count: 3 },
5612
+ { name: "Brass Talismans", count: 3 },
5613
+ { name: "Dusky Cloaks", count: 3 },
5614
+ { name: "LeaTher Armor", count: 3 },
5615
+ { name: "Longswords", count: 3 },
5616
+ { name: "Trusted Maps", count: 3 }
5617
+ ]
5618
+ },
5619
+ {
5620
+ name: "Heroic Tests",
5621
+ section: "Cards",
5622
+ components: [
5623
+ { name: "Finish Building The Shrine", type: "Compassion", count: 1 },
5624
+ { name: "Suffer With The Silent Sisters", type: "Compassion", count: 1 },
5625
+ { name: "Guide Abandoned Pilgrims", type: "Compassion", count: 1 },
5626
+ { name: "Hold A Moonless Vigil", type: "Compassion", count: 1 },
5627
+ { name: "Perform The Song Of Peril", type: "Prowess", count: 1 },
5628
+ { name: "Race To The Golden Obelisk", type: "Prowess", count: 1 },
5629
+ { name: "Solve The Riddle Of The Marid", type: "Prowess", count: 1 },
5630
+ { name: "Survive The Drowned Barrows", type: "Prowess", count: 1 },
5631
+ { name: "Consecrate Akartus", type: "Sacrifice", count: 1 },
5632
+ { name: "Lay Plovo's Ghost To Rest", type: "Sacrifice", count: 1 },
5633
+ { name: "Repair The Weeping Damn", type: "Sacrifice", count: 1 },
5634
+ { name: "Supply The WatchTowers", type: "Sacrifice", count: 1 },
5635
+ { name: "Activate The Ley Lines", type: "Valor", count: 1 },
5636
+ { name: "Impress The Winter Fey", type: "Valor", count: 1 },
5637
+ { name: "Protect The Radiant Castle", type: "Valor", count: 1 },
5638
+ { name: "Win Egan's Tournament", type: "Valor", count: 1 }
5639
+ ]
5640
+ },
5641
+ {
5642
+ name: "Potions",
5643
+ section: "Cards",
5644
+ components: [
5645
+ { name: "Potion Of Dragon Teeth", count: 3 },
5646
+ { name: "Potion Of Fortune's Favor", count: 3 },
5647
+ { name: "Potion Of one Thousand Strides", count: 3 },
5648
+ { name: "Potion Of Purifying Breath", count: 3 },
5649
+ { name: "Potion Of The Golden Sun", count: 3 },
5650
+ { name: "Potion Of The Siren's Song", count: 3 }
5651
+ ]
5652
+ },
5653
+ {
5654
+ name: "Quest Items",
5655
+ section: "Cards",
5656
+ components: [
5657
+ { name: "Amulet Of Annihilation", count: 1 },
5658
+ { name: "Amulet Of Hope", count: 4 },
5659
+ { name: "Bezoar", count: 1 },
5660
+ { name: "Dragon Scales", count: 1 },
5661
+ { name: "Fulminating Silver", count: 1 },
5662
+ { name: "Golden Wolf Pelt", count: 1 },
5663
+ { name: "Herbal Remedy", count: 1 },
5664
+ { name: "Horn Of The Elements", count: 1 },
5665
+ { name: "Mark Of The Outlaw", count: 1 },
5666
+ { name: "Orb Of Pure Snow", count: 1 },
5667
+ { name: "Relic Of Light", count: 1 },
5668
+ { name: "Smuggler's Coin", count: 1 },
5669
+ { name: "The Black Mark", count: 1 },
5670
+ { name: "Tomas's Map", count: 1 },
5671
+ { name: "Tools Of The Saboteur", count: 1 },
5672
+ { name: "Turquoise Urn", count: 1 },
5673
+ { name: "Wraps Of Invisibility", count: 1 }
5674
+ ]
5675
+ },
5676
+ {
5677
+ name: "Treasures",
5678
+ section: "Cards",
5679
+ components: [
5680
+ { name: "Acorns Of The White Oak", count: 1 },
5681
+ { name: "Amulet Of The Marid", count: 1 },
5682
+ { name: "Axe Of Soul Rending", count: 1 },
5683
+ { name: "Azkol's Banner", count: 1 },
5684
+ { name: "Azkol's Horn", count: 1 },
5685
+ { name: "Azkol's Idol", count: 1 },
5686
+ { name: "Circlet Of Conviction", count: 1 },
5687
+ { name: "Cloak Of Stars", count: 1 },
5688
+ { name: "Crown Of Azkol", count: 1 },
5689
+ { name: "Golden Mace Of Azkol", count: 1 },
5690
+ { name: "Hallowed Reliquary", count: 1 },
5691
+ { name: "Kamaria's Carpet", count: 1 },
5692
+ { name: "Lamp Of Darkness", count: 1 },
5693
+ { name: "Lamp Of Hope", count: 1 },
5694
+ { name: "Necklace Of Haggling", count: 1 },
5695
+ { name: "Oakstone Bow", count: 1 },
5696
+ { name: "Scroll Of Burning Sands", count: 1 },
5697
+ { name: "Scroll Of The Great Serpent", count: 1 },
5698
+ { name: "Scroll Of Twilight Shadow", count: 1 },
5699
+ { name: "Spear Of Atish", count: 1 },
5700
+ { name: "Tears Of The Shedu", count: 1 },
5701
+ { name: "White Cauldron", count: 1 }
5702
+ ]
5703
+ }
5704
+ ]
5705
+ },
5706
+ {
5707
+ name: "Alliances",
5708
+ categories: [
5709
+ {
5710
+ name: "Boards",
5711
+ section: "Misc",
5712
+ components: [
5713
+ { name: "Arcane Scouts", count: 1 },
5714
+ { name: "Archwright", count: 1 },
5715
+ { name: "Druids Circle", count: 1 },
5716
+ { name: "Heroic Action", count: 4 },
5717
+ { name: "Hunted Recluse", count: 1 },
5718
+ { name: "Influence Vessel", count: 1 },
5719
+ { name: "Paladins Order", count: 1 },
5720
+ { name: "Thieves Guild", count: 1 }
5721
+ ]
5722
+ },
5723
+ {
5724
+ name: "Mini Bases",
5725
+ section: "Misc",
5726
+ components: [
5727
+ { color: "Brown", count: 1 },
5728
+ { color: "Yellow", count: 1 }
5729
+ ]
5730
+ },
5731
+ {
5732
+ name: "Flags",
5733
+ section: "Misc",
5734
+ components: [
5735
+ { type: "Arcane Scouts", count: 1 },
5736
+ { type: "Druids Circle", count: 1 },
5737
+ { type: "Paladins Order", count: 1 },
5738
+ { type: "Thieves Guild", count: 1 }
5739
+ ]
5740
+ },
5741
+ {
5742
+ name: "Skulls",
5743
+ section: "Misc",
5744
+ components: [
5745
+ { color: "Blue (Frost)", count: 11 },
5746
+ { color: "Green (Blight)", count: 11 },
5747
+ { color: "Purple (Omen)", count: 11 },
5748
+ { color: "Red (Fire)", count: 11 }
5749
+ ]
5750
+ },
5751
+ {
5752
+ name: "Influence",
5753
+ section: "Tokens",
5754
+ components: [
5755
+ { name: "1's", count: 17 },
5756
+ { name: "5's", count: 23 }
5757
+ ]
5758
+ },
5759
+ {
5760
+ name: "Virtues",
5761
+ section: "Tokens",
5762
+ components: [
5763
+ { name: "Exalted", count: 1 },
5764
+ { name: "Shadowspinner", count: 1 },
5765
+ { name: "Sinbearer", count: 1 },
5766
+ { name: "Soulreaper", count: 1 },
5767
+ { name: "Tactical", count: 1 },
5768
+ { name: "Wily", count: 1 }
5769
+ ]
5770
+ },
5771
+ {
5772
+ name: "Companion",
5773
+ section: "Cards",
5774
+ components: [
5775
+ { name: "Amani", description: "The Vizier", count: 1 },
5776
+ { name: "Berat", description: "The Wizard", count: 1 },
5777
+ { name: "Burgoyn", description: "The Herbalist", count: 1 },
5778
+ { name: "Ema", description: "The Grand Merchant", count: 1 },
5779
+ { name: "Haraswa", description: "The Pegasus", count: 1 },
5780
+ { name: "Lukas", description: "The Plunderer", count: 1 },
5781
+ { name: "Maxim", description: "The Beast", count: 1 },
5782
+ { name: "Omar", description: "The Healer", count: 1 },
5783
+ { name: "Oola", description: "The Nomad", count: 1 },
5784
+ { name: "Ruska", description: "The Barbarian", count: 1 },
5785
+ { name: "Sanzhar", description: "The Zealot", count: 1 },
5786
+ { name: "Xyr", description: "The Oracle", count: 1 }
5787
+ ]
5788
+ },
5789
+ {
5790
+ name: "Treasures",
5791
+ section: "Cards",
5792
+ components: [
5793
+ { name: "Coffer Of The Master Thief", count: 1 },
5794
+ { name: "Crystal Blade", count: 1 },
5795
+ { name: "Crystal Platemail", count: 1 },
5796
+ { name: "Crystal Shield", count: 1 },
5797
+ { name: "Diadem Of The Emmisary", count: 1 },
5798
+ { name: "Druid's Incense", count: 1 },
5799
+ { name: "Everlasting Brazier", count: 1 },
5800
+ { name: "Ewer Of The Silent Child", count: 1 },
5801
+ { name: "Forbidden Grimoire", count: 1 },
5802
+ { name: "Iron Hound Of Azkol", count: 1 },
5803
+ { name: "Jeweled Goblet Of Azkol", count: 1 },
5804
+ { name: "Paladin's Greatshield", count: 1 },
5805
+ { name: "Ring Of The Emmisary", count: 1 },
5806
+ { name: "Robes Of The Last Sultan", count: 1 },
5807
+ { name: "Scroll Of Forged Friendship", count: 1 },
5808
+ { name: "Standard Of The Scouts", count: 1 },
5809
+ { name: "Staff Of Wishes", count: 1 },
5810
+ { name: "Trebblok's Hammer", count: 1 },
5811
+ { name: "Vestments Of The Emmisary", count: 1 },
5812
+ { name: "Zemayir's Teeth", count: 1 }
5813
+ ]
5814
+ }
5815
+ ]
5816
+ },
5817
+ {
5818
+ name: "Covenant",
5819
+ categories: [
5820
+ {
5821
+ name: "Boards",
5822
+ section: "Misc",
5823
+ components: [
5824
+ { name: "Devious Swindler", count: 1 },
5825
+ { name: "Relentless Warden", count: 1 },
5826
+ { name: "Reverent Astromancer", count: 1 },
5827
+ { name: "Undaunted Aegis", count: 1 }
5828
+ ]
5829
+ },
5830
+ {
5831
+ name: "Mini Bases",
5832
+ section: "Misc",
5833
+ components: [
5834
+ { color: "Blue", count: 1 },
5835
+ { color: "Brown", count: 1 },
5836
+ { color: "Green", count: 1 },
5837
+ { color: "Orange", count: 1 }
5838
+ ]
5839
+ },
5840
+ {
5841
+ name: "Monuments",
5842
+ section: "Misc",
5843
+ components: [
5844
+ { type: "Arch of the Golden Sun", count: 1 },
5845
+ { type: "Argent Oak", count: 1 },
5846
+ { type: "Cenotaph of the First Prophet", count: 1 },
5847
+ { type: "Colossus of Bjorn", count: 1 },
5848
+ { type: "Endless Necropolis", count: 1 },
5849
+ { type: "Moonstone Temple", count: 1 },
5850
+ { type: "Nightmare Cage", count: 1 },
5851
+ { type: "Tower Shard", count: 1 }
5852
+ ]
5853
+ },
5854
+ {
5855
+ name: "Foundation",
5856
+ section: "Tokens",
5857
+ components: [
5858
+ { name: "Arch of the Golden Sun / Nightmare Cage", count: 1 },
5859
+ { name: "Argent Oak / Moonstone Temple", count: 1 },
5860
+ { name: "Cenotaph of the First Prophet / Tower Shard", count: 1 },
5861
+ { name: "Colossus of Bjorn / Endless Necropolis", count: 1 }
5862
+ ]
5863
+ },
5864
+ {
5865
+ name: "Virtues",
5866
+ section: "Tokens",
5867
+ components: [
5868
+ { name: "Bounteous", count: 1 },
5869
+ { name: "Exalted", count: 1 },
5870
+ { name: "Zealous", count: 1 },
5871
+ { name: "Emboldened", count: 1 },
5872
+ { name: "Resolute", count: 1 },
5873
+ { name: "Steeled", count: 1 },
5874
+ { name: "Keen-Eyed", count: 1 },
5875
+ { name: "Inspiring", count: 1 },
5876
+ { name: "Instinctive", count: 1 },
5877
+ { name: "Calculating", count: 1 },
5878
+ { name: "Opportunistic", count: 1 },
5879
+ { name: "Fortuitous", count: 1 }
5880
+ ]
5881
+ },
5882
+ {
5883
+ name: "Corruption",
5884
+ section: "Cards",
5885
+ components: [
5886
+ { name: "Aquaphobic", count: 1 },
5887
+ { name: "Arrogant", count: 1 },
5888
+ { name: "Crestfallen", count: 1 },
5889
+ { name: "Disreputable", count: 1 },
5890
+ { name: "Fatigued", count: 1 },
5891
+ { name: "Indolent", count: 1 },
5892
+ { name: "Inobservant", count: 1 },
5893
+ { name: "Reckless", count: 1 },
5894
+ { name: "Shaken", count: 1 },
5895
+ { name: "Snobby", count: 1 },
5896
+ { name: "Timid", count: 1 },
5897
+ { name: "Vain", count: 1 }
5898
+ ]
5899
+ },
5900
+ {
5901
+ name: "Invocation",
5902
+ section: "Cards",
5903
+ components: [
5904
+ { name: "Abate the Darkness", count: 1 },
5905
+ { name: "Celestial Jaunt", count: 1 },
5906
+ { name: "Commanding Rebuke", count: 1 },
5907
+ { name: "Smite the Wicked", count: 1 }
5908
+ ]
5909
+ },
5910
+ {
5911
+ name: "Monument",
5912
+ section: "Cards",
5913
+ components: [
5914
+ { name: "Arch of the Golden Sun", description: "Bazaar", count: 1 },
5915
+ { name: "Argent Oak", description: "Sanctuary", count: 1 },
5916
+ { name: "Cenotaph of the First Prophet", description: "Citadel", count: 1 },
5917
+ { name: "Colossus of Bjorn", description: "Village", count: 1 },
5918
+ { name: "Endless Necropolis", description: "Village", count: 1 },
5919
+ { name: "Moonstone Temple", description: "Sanctuary", count: 1 },
5920
+ { name: "Nightmare Cage", description: "Bazaar", count: 1 },
5921
+ { name: "Tower Shard", description: "Citadel", count: 1 }
5922
+ ]
5923
+ },
5924
+ {
5925
+ name: "Spell",
5926
+ section: "Cards",
5927
+ components: [
5928
+ { name: "Aura of Friendship", count: 1 },
5929
+ { name: "Bestow Blessing", count: 1 },
5930
+ { name: "Bounty of the Gods", count: 1 },
5931
+ { name: "Ritual of Warding", count: 1 },
5932
+ { name: "Soothing Word", count: 1 },
5933
+ { name: "Winds of Change", count: 1 }
5934
+ ]
5935
+ },
5936
+ {
5937
+ name: "Treasures",
5938
+ section: "Cards",
5939
+ components: [
5940
+ { name: "Archwright's Sledge", count: 1 },
5941
+ { name: "Azkol's Chakram", count: 1 },
5942
+ { name: "Azkol's Ichor", count: 1 },
5943
+ { name: "Azkol's Scroll", count: 1 },
5944
+ { name: "Azkol's Vambraces", count: 1 },
5945
+ { name: "Beacon Stone", count: 1 },
5946
+ { name: "Brutal Warlord's Bell", count: 1 },
5947
+ { name: "Everfilled Chest", count: 1 },
5948
+ { name: "Grim Whisper", count: 1 },
5949
+ { name: "Haunted Recluse's Effigy", count: 1 },
5950
+ { name: "Opal of Protection", count: 1 },
5951
+ { name: "Orhpaned Scion's Charm", count: 1 },
5952
+ { name: "Relic Hunter's Flagon", count: 1 },
5953
+ { name: "Sanctified Flask", count: 1 },
5954
+ { name: "Spymaster's Journal", count: 1 },
5955
+ { name: "Tent of Revelry", count: 1 },
5956
+ { name: "The Iron Wall", count: 1 },
5957
+ { name: "Wand of Celerity", count: 1 },
5958
+ { name: "Wand of Conflagration", count: 1 },
5959
+ { name: "Wand of Pacification", count: 1 }
5960
+ ]
5961
+ }
5962
+ ]
5963
+ },
5964
+ {
5965
+ name: "Dark Horde",
5966
+ categories: [
5967
+ {
5968
+ name: "Storage Tray 1 (Top)",
5969
+ section: "Minis",
5970
+ components: [
5971
+ { name: "Briagands", count: 8 },
5972
+ { name: "Clan Of Neuri", count: 5 },
5973
+ { name: "Isa The Exile", count: 1 },
5974
+ { name: "Lemure", count: 6 },
5975
+ { name: "Mormo", count: 4 },
5976
+ { name: "Oreks", count: 6 },
5977
+ { name: "Shadow Wolves", count: 8 },
5978
+ { name: "Spine Fiend", count: 6 },
5979
+ { name: "Widowmade Spider", count: 5 }
5980
+ ]
5981
+ },
5982
+ {
5983
+ name: "Storage Tray 2 (Bottom)",
5984
+ section: "Minis",
5985
+ components: [
5986
+ { name: "Frost Trolls", count: 4 },
5987
+ { name: "Gravemaw", count: 1 },
5988
+ { name: "Dragon", count: 2 },
5989
+ { name: "Empress Of Shades", count: 1 },
5990
+ { name: "Bane Of Omens", count: 1 },
5991
+ { name: "Lingering Rot", count: 1 },
5992
+ { name: "Striga", count: 2 },
5993
+ { name: "Ashstrider", count: 1 },
5994
+ { name: "Titan", count: 1 },
5995
+ { name: "Gaze Eternal", count: 1 },
5996
+ { name: "U'tuk-Ku The Ice Herald", count: 1 },
5997
+ { name: "Main Goal Marker", count: 1 },
5998
+ { name: "Guild Quest Marker", count: 1 },
5999
+ { name: "Adversary Quest Marker", count: 1 },
6000
+ { name: "Companion Quest Marker", count: 1 }
6001
+ ]
6002
+ }
6003
+ ]
6004
+ }
6005
+ ];
6006
+ var EXPANSIONS = expansions.reduce(
6007
+ (acc, e) => {
6008
+ acc[e.name] = e;
6009
+ return acc;
6010
+ },
6011
+ {}
6012
+ );
6013
+ var coffers = [
6014
+ {
6015
+ resource: "Influence",
6016
+ denominations: [
6017
+ { name: "1's", count: 8 },
6018
+ { name: "5's", count: 17 }
6019
+ ],
6020
+ total: 25
6021
+ },
6022
+ {
6023
+ resource: "Spirits",
6024
+ denominations: [
6025
+ { name: "1's", count: 24 },
6026
+ { name: "5's", count: 16 }
6027
+ ],
6028
+ total: 40
6029
+ },
6030
+ {
6031
+ resource: "Warriors",
6032
+ denominations: [
6033
+ { name: "1's", count: 28 },
6034
+ { name: "5's", count: 22 }
6035
+ ],
6036
+ total: 50
6037
+ }
6038
+ ];
6039
+ var coffers2 = {
6040
+ tokens: [
6041
+ { name: "Advantage", count: 10 },
6042
+ { name: "Charge", count: 10 },
6043
+ { name: "Foundation", count: 4 },
6044
+ { name: "Guild", count: 4 },
6045
+ { name: "Protection", count: 6 },
6046
+ { name: "Quarry", count: 1 },
6047
+ { name: "Wasteland", count: 32 }
6048
+ ],
6049
+ total: 67
6050
+ };
6051
+ var skullsPack = {
6052
+ tokens: [
6053
+ { name: "White (Normal)", count: 10 },
6054
+ { name: "Black (Doom)", count: 2 },
6055
+ { name: "Blue (Frost)", count: 2 },
6056
+ { name: "Green (Blight)", count: 2 },
6057
+ { name: "Purple (Omen)", count: 2 },
6058
+ { name: "Red (Fire)", count: 2 }
5377
6059
  ],
5378
- "Lake of Songs": ["Duwani", "Lesser Tombstones", "Three Rivers", "Utar's Barrows"],
5379
- "Three Rivers": ["Lake of Songs", "Lesser Tombstones", "Middle Sister", "Pine Barrens"]
6060
+ total: 20
5380
6061
  };
5381
- function neighborsOf(loc) {
5382
- return BOARD_ADJACENCY[loc] ?? [];
6062
+ var sleeves = [
6063
+ { name: "Printed Large Sleeves", purposes: ["Monuments", "Treasure Cards"] },
6064
+ {
6065
+ name: "Printed Mini Sleeves",
6066
+ purposes: ["Gear", "Heroic Tests", "Invocations", "Potions", "Quest Items", "Spells"]
6067
+ },
6068
+ { name: "Clear Large Sleeves", purposes: ["Companions", "Foes"] },
6069
+ { name: "Clear Mini Sleeves", purposes: ["Blessings", "Corruptions"] }
6070
+ ];
6071
+
6072
+ // src/seed/index.ts
6073
+ var seed_exports = {};
6074
+ __export(seed_exports, {
6075
+ ADVERSARIES: () => ADVERSARIES2,
6076
+ ALLIES: () => ALLIES,
6077
+ DIFFICULTIES: () => DIFFICULTIES,
6078
+ GAME_SOURCES: () => GAME_SOURCES,
6079
+ SystemRandom: () => SystemRandom,
6080
+ TIER1_FOES: () => TIER1_FOES,
6081
+ TIER2_FOES: () => TIER2_FOES,
6082
+ TIER3_FOES: () => TIER3_FOES,
6083
+ charToValue: () => charToValue,
6084
+ compareSeedsRaw: () => compareSeedsRaw,
6085
+ createSeed: () => createSeed,
6086
+ decodeRngSeed: () => decodeRngSeed,
6087
+ decodeSeed: () => decodeSeed,
6088
+ dumpSeedChars: () => dumpSeedChars,
6089
+ encodeSeed: () => encodeSeed,
6090
+ validateSeed: () => validateSeed,
6091
+ valueToChar: () => valueToChar
6092
+ });
6093
+
6094
+ // src/seed/udtSeedParser.ts
6095
+ var ALPHABET = "a123456789bcdefghijklmnpqrstuvwxyz";
6096
+ var BASE = 34;
6097
+ var SETUP_LENGTH = 6;
6098
+ var RNG_SEED_LENGTH = 6;
6099
+ var SEED_LENGTH = SETUP_LENGTH + RNG_SEED_LENGTH;
6100
+ var CHAR_TO_VALUE = /* @__PURE__ */ new Map();
6101
+ var VALUE_TO_CHAR = /* @__PURE__ */ new Map();
6102
+ for (let i = 0; i < ALPHABET.length; i++) {
6103
+ CHAR_TO_VALUE.set(ALPHABET[i], i);
6104
+ VALUE_TO_CHAR.set(i, ALPHABET[i]);
6105
+ }
6106
+ var TIER1_FOES = ["Brigands", "Oreks", "Shadow Wolves", "Spine Fiends"];
6107
+ var TIER2_FOES = ["Frost Trolls", "Clan of Neuri", "Lemures", "Widowmade Spiders"];
6108
+ var TIER3_FOES = ["Dragons", "Mormos", "Striga", "Titans"];
6109
+ var ADVERSARIES2 = [
6110
+ "Ashstrider",
6111
+ "Bane of Omens",
6112
+ "Empress of Shades",
6113
+ "Gaze Eternal",
6114
+ "Gravemaw",
6115
+ "Isa the Exile",
6116
+ "Lingering Rot",
6117
+ "Utuk'Ku"
6118
+ ];
6119
+ var ALLIES = [
6120
+ "Gleb",
6121
+ "Grigor",
6122
+ "Hakan",
6123
+ "Letha",
6124
+ "Miras",
6125
+ "Nimet",
6126
+ "Tomas",
6127
+ "Vasa",
6128
+ "Yana",
6129
+ "Zaida"
6130
+ ];
6131
+ var DIFFICULTIES = ["Heroic", "Gritty"];
6132
+ var GAME_SOURCES = ["Core", "Competitive"];
6133
+ function charToValue(c) {
6134
+ const v = CHAR_TO_VALUE.get(c.toLowerCase());
6135
+ if (v === void 0) {
6136
+ throw new Error(`Invalid seed character: '${c}'`);
6137
+ }
6138
+ return v;
6139
+ }
6140
+ function valueToChar(v) {
6141
+ const c = VALUE_TO_CHAR.get(v);
6142
+ if (c === void 0) {
6143
+ throw new Error(`Invalid seed value: ${v} (must be 0\u2013${BASE - 1})`);
6144
+ }
6145
+ return c;
6146
+ }
6147
+ function validateSeed(seed) {
6148
+ const stripped = seed.replace(/[-\s]/g, "").toLowerCase();
6149
+ if (stripped.length !== SEED_LENGTH) {
6150
+ throw new Error(`Invalid seed length: expected ${SEED_LENGTH} characters, got ${stripped.length}`);
6151
+ }
6152
+ for (const c of stripped) {
6153
+ if (!CHAR_TO_VALUE.has(c)) {
6154
+ throw new Error(`Invalid seed character: '${c}'`);
6155
+ }
6156
+ }
6157
+ const upper = stripped.toUpperCase();
6158
+ return `${upper.slice(0, 4)}-${upper.slice(4, 8)}-${upper.slice(8, 12)}`;
6159
+ }
6160
+ function decodeSeed(seed) {
6161
+ const normalized = validateSeed(seed);
6162
+ const stripped = normalized.replace(/-/g, "").toLowerCase();
6163
+ const setup = [];
6164
+ for (let i = 0; i < SETUP_LENGTH; i++) {
6165
+ setup.push(charToValue(stripped[i]));
6166
+ }
6167
+ let rngSeed = 0;
6168
+ for (let i = 0; i < RNG_SEED_LENGTH; i++) {
6169
+ const value = charToValue(stripped[SETUP_LENGTH + i]);
6170
+ rngSeed += value * Math.round(Math.pow(BASE, i));
6171
+ }
6172
+ const foeByteA = setup[0];
6173
+ const tier1 = foeByteA & 3;
6174
+ const tier2 = (foeByteA & 12) >> 2;
6175
+ const foeByteB = setup[1];
6176
+ const tier3 = (foeByteA & 16) >> 4 | (foeByteB & 16) >> 3;
6177
+ const adversaryIndex = foeByteB & 15;
6178
+ const allyIndex = setup[2];
6179
+ const extra = setup[3];
6180
+ const difficultyIndex = extra & 1;
6181
+ const expansionBits = (extra & 6) >> 1;
6182
+ const sourceBits = (extra & 8) >> 2;
6183
+ const playerCount = (setup[5] & 3) + 1;
6184
+ const expansions2 = [];
6185
+ if (expansionBits & 1) expansions2.push("Monuments");
6186
+ if (expansionBits & 2) expansions2.push("Alliances");
6187
+ let source;
6188
+ switch (sourceBits) {
6189
+ case 2:
6190
+ source = "Competitive";
6191
+ break;
6192
+ default:
6193
+ source = "Core";
6194
+ break;
6195
+ }
6196
+ const seedBank = {
6197
+ initializationSeed: rngSeed,
6198
+ questSeed: rngSeed - 1,
6199
+ seedString: normalized
6200
+ };
6201
+ return {
6202
+ seed: normalized,
6203
+ tier1Foe: TIER1_FOES[tier1],
6204
+ tier2Foe: TIER2_FOES[tier2],
6205
+ tier3Foe: TIER3_FOES[tier3],
6206
+ adversary: ADVERSARIES2[adversaryIndex],
6207
+ ally: ALLIES[allyIndex],
6208
+ difficulty: DIFFICULTIES[difficultyIndex],
6209
+ source,
6210
+ expansions: expansions2,
6211
+ playerCount,
6212
+ rngSeed,
6213
+ seedBank,
6214
+ setup
6215
+ };
6216
+ }
6217
+ function decodeRngSeed(seed) {
6218
+ const normalized = validateSeed(seed);
6219
+ const stripped = normalized.replace(/-/g, "").toLowerCase();
6220
+ let rngSeed = 0;
6221
+ for (let i = 0; i < RNG_SEED_LENGTH; i++) {
6222
+ const value = charToValue(stripped[SETUP_LENGTH + i]);
6223
+ rngSeed += value * Math.round(Math.pow(BASE, i));
6224
+ }
6225
+ return rngSeed;
6226
+ }
6227
+ function createSeed(config) {
6228
+ let foeByteA = 0;
6229
+ let foeByteB = 0;
6230
+ const tier1Index = TIER1_FOES.indexOf(config.foes[0]);
6231
+ const tier2Index = TIER2_FOES.indexOf(config.foes[1]);
6232
+ const tier3Index = TIER3_FOES.indexOf(config.foes[2]);
6233
+ if (tier1Index < 0) throw new Error(`Invalid Tier 1 foe: ${config.foes[0]}`);
6234
+ if (tier2Index < 0) throw new Error(`Invalid Tier 2 foe: ${config.foes[1]}`);
6235
+ if (tier3Index < 0) throw new Error(`Invalid Tier 3 foe: ${config.foes[2]}`);
6236
+ foeByteA = tier1Index & 3;
6237
+ foeByteA |= (tier2Index & 3) << 2;
6238
+ foeByteA |= (tier3Index & 1) << 4;
6239
+ foeByteB |= (tier3Index >> 1 & 1) << 4;
6240
+ const adversaryIndex = ADVERSARIES2.indexOf(config.adversary);
6241
+ if (adversaryIndex < 0) throw new Error(`Invalid adversary: ${config.adversary}`);
6242
+ foeByteB |= adversaryIndex & 15;
6243
+ const allyIndex = ALLIES.indexOf(config.ally);
6244
+ if (allyIndex < 0) throw new Error(`Invalid ally: ${config.ally}`);
6245
+ let extraByte = 0;
6246
+ if (config.difficulty === "Gritty") extraByte |= 1;
6247
+ for (const expansion of config.expansions) {
6248
+ switch (expansion) {
6249
+ case "Monuments":
6250
+ extraByte |= 2;
6251
+ break;
6252
+ case "Alliances":
6253
+ extraByte |= 4;
6254
+ break;
6255
+ }
6256
+ }
6257
+ if (config.source === "Competitive") extraByte |= 8;
6258
+ const versionByte = 0;
6259
+ const playerCountByte = Math.max(0, Math.min(3, config.playerCount - 1));
6260
+ let seedStr = valueToChar(foeByteA) + valueToChar(foeByteB) + valueToChar(allyIndex) + valueToChar(extraByte) + valueToChar(versionByte) + valueToChar(playerCountByte);
6261
+ let rngValue = 0;
6262
+ for (let i = 0; i < RNG_SEED_LENGTH; i++) {
6263
+ const value = Math.floor(Math.random() * BASE);
6264
+ seedStr += valueToChar(value);
6265
+ rngValue += value * Math.round(Math.pow(BASE, i));
6266
+ }
6267
+ const upper = seedStr.toUpperCase();
6268
+ const formatted = `${upper.slice(0, 4)}-${upper.slice(4, 8)}-${upper.slice(8, 12)}`;
6269
+ return { seed: formatted, rngValue };
5383
6270
  }
5384
- function stepDistance(a, b) {
5385
- if (a === b) return 0;
5386
- const visited = /* @__PURE__ */ new Set([a]);
5387
- let frontier = [a];
5388
- let dist = 0;
5389
- while (frontier.length > 0) {
5390
- dist++;
5391
- const next = [];
5392
- for (const node of frontier) {
5393
- for (const n of neighborsOf(node)) {
5394
- if (n === b) return dist;
5395
- if (!visited.has(n)) {
5396
- visited.add(n);
5397
- next.push(n);
5398
- }
5399
- }
6271
+ function encodeSeed(config, rngValue) {
6272
+ let foeByteA = 0;
6273
+ let foeByteB = 0;
6274
+ const tier1Index = TIER1_FOES.indexOf(config.foes[0]);
6275
+ const tier2Index = TIER2_FOES.indexOf(config.foes[1]);
6276
+ const tier3Index = TIER3_FOES.indexOf(config.foes[2]);
6277
+ if (tier1Index < 0) throw new Error(`Invalid Tier 1 foe: ${config.foes[0]}`);
6278
+ if (tier2Index < 0) throw new Error(`Invalid Tier 2 foe: ${config.foes[1]}`);
6279
+ if (tier3Index < 0) throw new Error(`Invalid Tier 3 foe: ${config.foes[2]}`);
6280
+ foeByteA = tier1Index & 3;
6281
+ foeByteA |= (tier2Index & 3) << 2;
6282
+ foeByteA |= (tier3Index & 1) << 4;
6283
+ foeByteB |= (tier3Index >> 1 & 1) << 4;
6284
+ const adversaryIndex = ADVERSARIES2.indexOf(config.adversary);
6285
+ if (adversaryIndex < 0) throw new Error(`Invalid adversary: ${config.adversary}`);
6286
+ foeByteB |= adversaryIndex & 15;
6287
+ const allyIndex = ALLIES.indexOf(config.ally);
6288
+ if (allyIndex < 0) throw new Error(`Invalid ally: ${config.ally}`);
6289
+ let extraByte = 0;
6290
+ if (config.difficulty === "Gritty") extraByte |= 1;
6291
+ for (const expansion of config.expansions) {
6292
+ switch (expansion) {
6293
+ case "Monuments":
6294
+ extraByte |= 2;
6295
+ break;
6296
+ case "Alliances":
6297
+ extraByte |= 4;
6298
+ break;
5400
6299
  }
5401
- frontier = next;
5402
6300
  }
5403
- return Infinity;
6301
+ if (config.source === "Competitive") extraByte |= 8;
6302
+ const versionByte = 0;
6303
+ const playerCountByte = Math.max(0, Math.min(3, config.playerCount - 1));
6304
+ let seedStr = valueToChar(foeByteA) + valueToChar(foeByteB) + valueToChar(allyIndex) + valueToChar(extraByte) + valueToChar(versionByte) + valueToChar(playerCountByte);
6305
+ let remaining = rngValue;
6306
+ for (let i = 0; i < RNG_SEED_LENGTH; i++) {
6307
+ const digit = remaining % BASE;
6308
+ seedStr += valueToChar(digit);
6309
+ remaining = Math.floor(remaining / BASE);
6310
+ }
6311
+ const upper = seedStr.toUpperCase();
6312
+ return `${upper.slice(0, 4)}-${upper.slice(4, 8)}-${upper.slice(8, 12)}`;
5404
6313
  }
5405
- function shortestPath(a, b) {
5406
- if (a === b) return [a];
5407
- const prev = /* @__PURE__ */ new Map();
5408
- const visited = /* @__PURE__ */ new Set([a]);
5409
- let frontier = [a];
5410
- while (frontier.length > 0) {
5411
- const next = [];
5412
- for (const node of frontier) {
5413
- for (const n of neighborsOf(node)) {
5414
- if (visited.has(n)) continue;
5415
- visited.add(n);
5416
- prev.set(n, node);
5417
- if (n === b) {
5418
- const path = [b];
5419
- let cur = b;
5420
- while (cur !== void 0 && cur !== a) {
5421
- cur = prev.get(cur);
5422
- if (cur !== void 0) path.push(cur);
5423
- }
5424
- return path.reverse();
6314
+ function compareSeedsRaw(seed1, seed2) {
6315
+ const n1 = validateSeed(seed1);
6316
+ const n2 = validateSeed(seed2);
6317
+ const s1 = n1.replace(/-/g, "").toLowerCase();
6318
+ const s2 = n2.replace(/-/g, "").toLowerCase();
6319
+ const diffs = [];
6320
+ for (let i = 0; i < SEED_LENGTH; i++) {
6321
+ const v1 = charToValue(s1[i]);
6322
+ const v2 = charToValue(s2[i]);
6323
+ if (v1 !== v2) {
6324
+ diffs.push({
6325
+ charIndex: i,
6326
+ value1: v1,
6327
+ value2: v2,
6328
+ char1: s1[i],
6329
+ char2: s2[i]
6330
+ });
6331
+ }
6332
+ }
6333
+ return {
6334
+ seed1: n1,
6335
+ seed2: n2,
6336
+ diffs,
6337
+ setupDiffs: diffs.filter((d) => d.charIndex < SETUP_LENGTH),
6338
+ rngDiffs: diffs.filter((d) => d.charIndex >= SETUP_LENGTH)
6339
+ };
6340
+ }
6341
+ var SETUP_FIELD_LABELS = {
6342
+ 0: "Tier1/Tier2/Tier3lo",
6343
+ 1: "Adversary/Tier3hi",
6344
+ 2: "Ally",
6345
+ 3: "Difficulty/Expansions/Source",
6346
+ 4: "Version",
6347
+ 5: "PlayerCount"
6348
+ };
6349
+ function dumpSeedChars(seed) {
6350
+ const normalized = validateSeed(seed);
6351
+ const stripped = normalized.replace(/-/g, "").toLowerCase();
6352
+ const chars = [];
6353
+ for (let i = 0; i < SEED_LENGTH; i++) {
6354
+ const isSetup = i < SETUP_LENGTH;
6355
+ chars.push({
6356
+ index: i,
6357
+ char: stripped[i],
6358
+ value: charToValue(stripped[i]),
6359
+ section: isSetup ? "setup" : "rng",
6360
+ field: isSetup ? SETUP_FIELD_LABELS[i] : void 0
6361
+ });
6362
+ }
6363
+ return { seed: normalized, chars };
6364
+ }
6365
+
6366
+ // src/seed/udtSystemRandom.ts
6367
+ var INT32_MAX = 2147483647;
6368
+ var MSEED = 161803398;
6369
+ function toInt32(n) {
6370
+ return n | 0;
6371
+ }
6372
+ var SystemRandom = class {
6373
+ /**
6374
+ * Create a new PRNG instance with the given seed.
6375
+ * Matches C# `new System.Random(seed)` exactly.
6376
+ */
6377
+ constructor(seed) {
6378
+ this.seedArray = new Array(56).fill(0);
6379
+ this.inext = 0;
6380
+ this.inextp = 0;
6381
+ this.initialize(seed);
6382
+ }
6383
+ /**
6384
+ * Replicate .NET's System.Random constructor seeding algorithm.
6385
+ */
6386
+ initialize(seed) {
6387
+ let subtraction;
6388
+ if (seed === -2147483648) {
6389
+ subtraction = INT32_MAX;
6390
+ } else {
6391
+ subtraction = Math.abs(seed);
6392
+ }
6393
+ let mj = toInt32(MSEED - subtraction);
6394
+ this.seedArray[55] = mj;
6395
+ let mk = 1;
6396
+ for (let i = 1; i < 55; i++) {
6397
+ const ii = 21 * i % 55;
6398
+ this.seedArray[ii] = mk;
6399
+ mk = toInt32(mj - mk);
6400
+ if (mk < 0) mk = toInt32(mk + INT32_MAX);
6401
+ mj = this.seedArray[ii];
6402
+ }
6403
+ for (let k = 1; k < 5; k++) {
6404
+ for (let i = 1; i < 56; i++) {
6405
+ this.seedArray[i] = toInt32(this.seedArray[i] - this.seedArray[1 + (i + 30) % 55]);
6406
+ if (this.seedArray[i] < 0) {
6407
+ this.seedArray[i] = toInt32(this.seedArray[i] + INT32_MAX);
5425
6408
  }
5426
- next.push(n);
5427
6409
  }
5428
6410
  }
5429
- frontier = next;
6411
+ this.inext = 0;
6412
+ this.inextp = 21;
5430
6413
  }
5431
- return [];
5432
- }
6414
+ /**
6415
+ * Internal sample — returns value in range [0, Int32.MaxValue).
6416
+ * Matches C#'s InternalSample().
6417
+ */
6418
+ internalSample() {
6419
+ let retVal;
6420
+ let locINext = this.inext;
6421
+ let locINextp = this.inextp;
6422
+ if (++locINext >= 56) locINext = 1;
6423
+ if (++locINextp >= 56) locINextp = 1;
6424
+ retVal = toInt32(this.seedArray[locINext] - this.seedArray[locINextp]);
6425
+ if (retVal === INT32_MAX) retVal--;
6426
+ if (retVal < 0) retVal = toInt32(retVal + INT32_MAX);
6427
+ this.seedArray[locINext] = retVal;
6428
+ this.inext = locINext;
6429
+ this.inextp = locINextp;
6430
+ return retVal;
6431
+ }
6432
+ /**
6433
+ * Sample — returns a double in range [0.0, 1.0).
6434
+ * Matches C#'s Sample().
6435
+ */
6436
+ sample() {
6437
+ return this.internalSample() * (1 / INT32_MAX);
6438
+ }
6439
+ /**
6440
+ * Returns a non-negative random integer less than Int32.MaxValue.
6441
+ * Matches C# `Random.Next()`.
6442
+ */
6443
+ next() {
6444
+ return this.internalSample();
6445
+ }
6446
+ /**
6447
+ * Returns a non-negative random integer less than maxValue.
6448
+ * Matches C# `Random.Next(maxValue)`.
6449
+ */
6450
+ nextMax(maxValue) {
6451
+ if (maxValue < 0) {
6452
+ throw new Error("maxValue must be non-negative");
6453
+ }
6454
+ return toInt32(this.sample() * maxValue);
6455
+ }
6456
+ /**
6457
+ * Returns a random integer in range [minValue, maxValue).
6458
+ * Matches C# `Random.Next(minValue, maxValue)`.
6459
+ */
6460
+ nextRange(minValue, maxValue) {
6461
+ if (minValue > maxValue) {
6462
+ throw new Error("minValue must be less than or equal to maxValue");
6463
+ }
6464
+ const range = maxValue - minValue;
6465
+ if (range <= INT32_MAX) {
6466
+ return toInt32(this.sample() * range) + minValue;
6467
+ }
6468
+ return toInt32(this.internalSample() * (1 / INT32_MAX) * range) + minValue;
6469
+ }
6470
+ /**
6471
+ * Returns a random double in range [0.0, 1.0).
6472
+ * Matches C# `Random.NextDouble()`.
6473
+ */
6474
+ nextDouble() {
6475
+ return this.sample();
6476
+ }
6477
+ };
5433
6478
 
5434
6479
  // src/index.ts
5435
6480
  var index_default = UltimateDarkTower_default;
5436
6481
  export {
5437
- ADVERSARIES,
5438
- ADVERSARY_ROSTER,
5439
- ALLIES,
5440
- ALL_FOES,
5441
6482
  AUDIO_COMMAND_POS,
5442
6483
  BATTERY_STATUS_FREQUENCY,
5443
- BOARD_ADJACENCY,
5444
- BOARD_ANCHORS,
5445
- BOARD_GROUPINGS,
5446
- BOARD_IMAGE_INFO,
5447
- BOARD_LOCATIONS,
5448
- BOARD_LOCATION_BY_NAME,
5449
6484
  BluetoothAdapterFactory,
5450
6485
  BluetoothConnectionError,
5451
6486
  BluetoothDeviceNotFoundError,
@@ -5459,7 +6494,6 @@ export {
5459
6494
  DEFAULT_CONNECTION_MONITORING_FREQUENCY,
5460
6495
  DEFAULT_CONNECTION_MONITORING_TIMEOUT,
5461
6496
  DEFAULT_RETRY_SEND_COMMAND_MAX,
5462
- DIFFICULTIES,
5463
6497
  DIS_FIRMWARE_REVISION_UUID,
5464
6498
  DIS_HARDWARE_REVISION_UUID,
5465
6499
  DIS_IEEE_REGULATORY_UUID,
@@ -5472,14 +6506,7 @@ export {
5472
6506
  DIS_SYSTEM_ID_UUID,
5473
6507
  DOMOutput,
5474
6508
  DRUM_PACKETS,
5475
- FOES,
5476
- FOE_BY_ID,
5477
- FOE_BY_NAME,
5478
- FOE_STATUSES,
5479
- GAME_SOURCES,
5480
6509
  GLYPHS,
5481
- HEROES,
5482
- HERO_BY_ID,
5483
6510
  InMemorySink,
5484
6511
  IndexedDBSink,
5485
6512
  LAYER_TO_POSITION,
@@ -5488,16 +6515,10 @@ export {
5488
6515
  LIGHT_EFFECTS,
5489
6516
  LIGHT_INDEX_TO_DIRECTION,
5490
6517
  Logger,
5491
- MONUMENTS,
5492
- MONUMENT_BY_ID,
5493
6518
  RING_LIGHT_POSITIONS,
5494
6519
  SKULL_DROP_COUNT_POS,
5495
6520
  STATE_DATA_LENGTH,
5496
- SystemRandom,
5497
6521
  TC,
5498
- TIER1_FOES,
5499
- TIER2_FOES,
5500
- TIER3_FOES,
5501
6522
  TOWER_AUDIO_LIBRARY,
5502
6523
  TOWER_COMMANDS,
5503
6524
  TOWER_COMMAND_HEADER_SIZE,
@@ -5520,26 +6541,16 @@ export {
5520
6541
  VOLUME_DESCRIPTIONS,
5521
6542
  VOLUME_ICONS,
5522
6543
  bytesToHex,
5523
- charToValue,
5524
- compareSeedsRaw,
5525
6544
  createDefaultTowerState,
5526
- createSeed,
5527
- decodeRngSeed,
5528
- decodeSeed,
6545
+ data_exports as data,
5529
6546
  index_default as default,
5530
6547
  drumPositionCmds,
5531
- dumpSeedChars,
5532
- encodeSeed,
5533
6548
  isCalibrated,
5534
6549
  logger,
5535
6550
  milliVoltsToPercentage,
5536
6551
  milliVoltsToPercentageNumber,
5537
- neighborsOf,
5538
6552
  parseDifferentialReadings,
5539
6553
  rtdt_pack_state,
5540
6554
  rtdt_unpack_state,
5541
- shortestPath,
5542
- stepDistance,
5543
- validateSeed,
5544
- valueToChar
6555
+ seed_exports as seed
5545
6556
  };