@omnigateway/pokemon 1.0.1 → 1.0.2
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 +6 -4
- package/omni-plugin.json +1 -1
- package/package.json +1 -1
- package/server/index.js +351 -30
- package/ui/index.js +189 -43
package/README.md
CHANGED
|
@@ -56,8 +56,8 @@ check:
|
|
|
56
56
|
| Capability | Why |
|
|
57
57
|
| --- | --- |
|
|
58
58
|
| `storage` | Three tables on the plugin's own migration track, named `plugin_pokemon_<name>` by the host: the companion row per key, the Dex, and the grant ledger. |
|
|
59
|
-
| `files` | The species index and cached sprites live in the plugin's scoped data directory, **not** in a table. That directory is excluded from database snapshots, exactly as `request_bodies/` is — the alternative would put tens of megabytes of artwork into every snapshot an operator downloads, and it re-fetches itself anyway. |
|
|
60
|
-
| `net:outbound` | Species data
|
|
59
|
+
| `files` | The species index and cached sprites — companions and item icons alike — live in the plugin's scoped data directory, **not** in a table. That directory is excluded from database snapshots, exactly as `request_bodies/` is — the alternative would put tens of megabytes of artwork into every snapshot an operator downloads, and it re-fetches itself anyway. |
|
|
60
|
+
| `net:outbound` | Species data, companion sprites and item icons are fetched at runtime. The manifest also declares the origins, `https://pokeapi.co` and `https://raw.githubusercontent.com`; the host hands the plugin a `fetch` bound to that allowlist and refuses anything else. |
|
|
61
61
|
| `events:request` | Growth is credited from `RequestCompleted` — all four token classes, which are disjoint, so summing them double-counts nothing. |
|
|
62
62
|
| `events:limit` | A key parked at a `5h` or `1w` ceiling earns a rare candy, rated by the window's own length. A `1m` ceiling pays nothing: a minute is not a span in which work happened. |
|
|
63
63
|
|
|
@@ -68,8 +68,10 @@ accidental overreach is impossible and that the plugin's intent is auditable
|
|
|
68
68
|
from one readable file. It constrains honest code and not hostile code.
|
|
69
69
|
|
|
70
70
|
The plugin degrades rather than failing when a capability is absent. With no
|
|
71
|
-
`net`, an incubating egg holds its progress instead of losing it, and
|
|
72
|
-
|
|
71
|
+
`net`, an incubating egg holds its progress instead of losing it, and both
|
|
72
|
+
sprite routes answer `503` — the panel then draws each item as an emoji, which
|
|
73
|
+
is the same thing it draws before the cache has filled and the same thing it
|
|
74
|
+
draws for `mint`, whose icon does not exist upstream at all.
|
|
73
75
|
|
|
74
76
|
## Nintendo and Game Freak intellectual property
|
|
75
77
|
|
package/omni-plugin.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@omnigateway/pokemon",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "A Pokémon companion for OmniGateway. Each gateway key raises one that hatches, evolves, and graduates into a Pokédex on the tokens that key spends.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Harismawan <mail@harismawan.com>",
|
package/server/index.js
CHANGED
|
@@ -43,9 +43,26 @@ var RARE_CANDY_XP = 1e8;
|
|
|
43
43
|
var ITEM_PRICES = {
|
|
44
44
|
rareCandy: 500000000,
|
|
45
45
|
mint: 1e8,
|
|
46
|
-
shinyCharm: 3000000000
|
|
46
|
+
shinyCharm: 3000000000,
|
|
47
|
+
everstone: 1e9,
|
|
48
|
+
lure: 1e9,
|
|
49
|
+
sootheBell: 3000000000,
|
|
50
|
+
incense: 500000000,
|
|
51
|
+
repel: 500000000
|
|
47
52
|
};
|
|
53
|
+
var SOOTHE_BONUS = 0.25;
|
|
48
54
|
var ITEM_KINDS = Object.keys(ITEM_PRICES);
|
|
55
|
+
var ITEM_SPRITE_FILES = {
|
|
56
|
+
rareCandy: "rare-candy",
|
|
57
|
+
shinyCharm: "shiny-charm",
|
|
58
|
+
everstone: "everstone",
|
|
59
|
+
sootheBell: "soothe-bell",
|
|
60
|
+
repel: "repel",
|
|
61
|
+
incense: "luck-incense",
|
|
62
|
+
lure: "honey",
|
|
63
|
+
egg: "lucky-egg"
|
|
64
|
+
};
|
|
65
|
+
var ITEM_SPRITE_NAMES = new Map(Object.entries(ITEM_SPRITE_FILES));
|
|
49
66
|
var FRESH_EGG_BASE_PRICE = 1e9;
|
|
50
67
|
function freshEggPrice(tier) {
|
|
51
68
|
if (tier === null)
|
|
@@ -99,12 +116,14 @@ function decideGrant(input) {
|
|
|
99
116
|
var POKEAPI_ORIGIN = "https://pokeapi.co";
|
|
100
117
|
var SPRITE_ORIGIN = "https://raw.githubusercontent.com";
|
|
101
118
|
var SPRITE_DIR = "/PokeAPI/sprites/master/sprites/pokemon/versions/generation-v/black-white/animated";
|
|
119
|
+
var ITEM_SPRITE_DIR = "/PokeAPI/sprites/master/sprites/items";
|
|
102
120
|
var MAX_EVOLUTION_CHAIN_ID = 2000;
|
|
103
121
|
var INDEX_FETCH_CONCURRENCY = 8;
|
|
104
122
|
var INDEX_PATH = "species/index.json";
|
|
105
123
|
var speciesPath = (id) => `species/${id}.json`;
|
|
106
124
|
var chainPath = (chainId) => `species/chain-${chainId}.json`;
|
|
107
125
|
var spritePath = (id, shiny) => shiny ? `sprites/shiny/${id}.gif` : `sprites/${id}.gif`;
|
|
126
|
+
var itemSpritePath = (name) => `sprites/items/${name}.png`;
|
|
108
127
|
function isFetchableSpeciesId(id) {
|
|
109
128
|
return Number.isInteger(id) && hasAnimatedSprite(id);
|
|
110
129
|
}
|
|
@@ -348,6 +367,10 @@ async function loadDetail(deps, id, chains) {
|
|
|
348
367
|
function speciesDetail(deps, id) {
|
|
349
368
|
return loadDetail(deps, id, new Map);
|
|
350
369
|
}
|
|
370
|
+
function speciesDetails(deps, ids) {
|
|
371
|
+
const chains = new Map;
|
|
372
|
+
return Promise.all(ids.map((id) => loadDetail(deps, id, chains)));
|
|
373
|
+
}
|
|
351
374
|
async function cachedSpeciesName(deps, id) {
|
|
352
375
|
if (!isFetchableSpeciesId(id))
|
|
353
376
|
return null;
|
|
@@ -427,6 +450,22 @@ async function spriteBytes(deps, id, shiny) {
|
|
|
427
450
|
await writeCache(deps, path, bytes);
|
|
428
451
|
return bytes;
|
|
429
452
|
}
|
|
453
|
+
async function itemSpriteBytes(deps, item) {
|
|
454
|
+
const name = ITEM_SPRITE_NAMES.get(item);
|
|
455
|
+
if (name === undefined)
|
|
456
|
+
return null;
|
|
457
|
+
const path = itemSpritePath(name);
|
|
458
|
+
try {
|
|
459
|
+
const cached = await deps.files.read(path);
|
|
460
|
+
if (cached !== null && cached.length > 0)
|
|
461
|
+
return cached;
|
|
462
|
+
} catch {}
|
|
463
|
+
const bytes = await fetchBytes(deps, `${SPRITE_ORIGIN}${ITEM_SPRITE_DIR}/${name}.png`);
|
|
464
|
+
if (bytes === null)
|
|
465
|
+
return null;
|
|
466
|
+
await writeCache(deps, path, bytes);
|
|
467
|
+
return bytes;
|
|
468
|
+
}
|
|
430
469
|
|
|
431
470
|
// src/roll.ts
|
|
432
471
|
var NATURES = [
|
|
@@ -467,23 +506,33 @@ function mulberry32(seed) {
|
|
|
467
506
|
};
|
|
468
507
|
}
|
|
469
508
|
var COLLECTED_WEIGHT = 0.25;
|
|
509
|
+
var FORM_WEIGHT = 0.6;
|
|
470
510
|
function roll(input) {
|
|
471
511
|
const random = mulberry32(input.seed);
|
|
472
|
-
const
|
|
512
|
+
const eligibleWith = (withLure) => input.candidates.filter((candidate) => {
|
|
473
513
|
if (!hasAnimatedSprite(candidate.id))
|
|
474
514
|
return false;
|
|
475
515
|
if (candidate.id === DITTO_SPECIES_ID)
|
|
476
516
|
return false;
|
|
517
|
+
if (input.excludeFinal != null && candidate.finalId === input.excludeFinal)
|
|
518
|
+
return false;
|
|
519
|
+
if (withLure && input.collectedFinals.has(candidate.finalId))
|
|
520
|
+
return false;
|
|
477
521
|
if (input.guarantee === null)
|
|
478
522
|
return true;
|
|
479
523
|
const rarity2 = rarityFromCaptureRate(candidate.captureRate, false, false);
|
|
480
524
|
return sortRank(rarity2) >= sortRank(input.guarantee);
|
|
481
525
|
});
|
|
526
|
+
const wanted = input.onlyUncollected === true;
|
|
527
|
+
const lured = wanted ? eligibleWith(true) : [];
|
|
528
|
+
const usedLure = wanted && lured.length > 0;
|
|
529
|
+
const eligible = usedLure ? lured : eligibleWith(false);
|
|
482
530
|
if (eligible.length === 0)
|
|
483
531
|
return null;
|
|
484
532
|
const weights = eligible.map((candidate) => {
|
|
485
533
|
const base = Math.max(1, candidate.captureRate);
|
|
486
|
-
|
|
534
|
+
const collected = input.collectedFinals.has(candidate.finalId) ? base * COLLECTED_WEIGHT : base;
|
|
535
|
+
return input.preferLongLines === true ? collected * (1 + FORM_WEIGHT * (Math.max(1, candidate.forms) - 1)) : collected;
|
|
487
536
|
});
|
|
488
537
|
const total = weights.reduce((a, b) => a + b, 0);
|
|
489
538
|
let target = random() * total;
|
|
@@ -502,10 +551,16 @@ function roll(input) {
|
|
|
502
551
|
const rarity = rarityFromCaptureRate(chosen.captureRate, false, false);
|
|
503
552
|
const dittoEligible = rarity === "common" && chosen.forms >= 2;
|
|
504
553
|
const ditto = dittoEligible && random() < 1 / ODDS.dittoDisguise;
|
|
505
|
-
return { speciesId: chosen.id, isShiny, nature, ditto };
|
|
554
|
+
return { speciesId: chosen.id, isShiny, nature, ditto, usedLure };
|
|
506
555
|
}
|
|
507
556
|
|
|
508
557
|
// src/state.ts
|
|
558
|
+
function emptyInventory() {
|
|
559
|
+
const inventory = {};
|
|
560
|
+
for (const kind of ITEM_KINDS)
|
|
561
|
+
inventory[kind] = 0;
|
|
562
|
+
return inventory;
|
|
563
|
+
}
|
|
509
564
|
function freshState() {
|
|
510
565
|
return {
|
|
511
566
|
consumedTotal: 0,
|
|
@@ -513,7 +568,11 @@ function freshState() {
|
|
|
513
568
|
eggUsage: 0,
|
|
514
569
|
eggTier: null,
|
|
515
570
|
pendingHatch: null,
|
|
516
|
-
|
|
571
|
+
pendingReveal: null,
|
|
572
|
+
lure: false,
|
|
573
|
+
incense: false,
|
|
574
|
+
repel: null,
|
|
575
|
+
inventory: emptyInventory()
|
|
517
576
|
};
|
|
518
577
|
}
|
|
519
578
|
function isRecord(value) {
|
|
@@ -534,7 +593,7 @@ function parseState(raw) {
|
|
|
534
593
|
}
|
|
535
594
|
if (!isRecord(parsed))
|
|
536
595
|
return null;
|
|
537
|
-
const inventory =
|
|
596
|
+
const inventory = emptyInventory();
|
|
538
597
|
const storedInventory = parsed.inventory;
|
|
539
598
|
if (isRecord(storedInventory)) {
|
|
540
599
|
for (const kind of ITEM_KINDS) {
|
|
@@ -562,7 +621,10 @@ function parseState(raw) {
|
|
|
562
621
|
isShiny: storedActive.isShiny === true,
|
|
563
622
|
nature: nature ?? "hardy",
|
|
564
623
|
dittoDisguise: typeof storedActive.dittoDisguise === "number" ? storedActive.dittoDisguise : null,
|
|
565
|
-
dittoRevealed: storedActive.dittoRevealed === true
|
|
624
|
+
dittoRevealed: storedActive.dittoRevealed === true,
|
|
625
|
+
everstone: storedActive.everstone === true,
|
|
626
|
+
soothe: storedActive.soothe === true,
|
|
627
|
+
soothedRaw: Math.max(0, asInt(storedActive.soothedRaw, 0))
|
|
566
628
|
};
|
|
567
629
|
}
|
|
568
630
|
const storedPending = parsed.pendingHatch;
|
|
@@ -582,13 +644,28 @@ function parseState(raw) {
|
|
|
582
644
|
};
|
|
583
645
|
}
|
|
584
646
|
}
|
|
647
|
+
const storedReveal = parsed.pendingReveal;
|
|
648
|
+
let pendingReveal = null;
|
|
649
|
+
if (isRecord(storedReveal)) {
|
|
650
|
+
const rarity = asRarity(storedReveal.rarity);
|
|
651
|
+
const path = Array.isArray(storedReveal.path) ? storedReveal.path.filter((id) => typeof id === "number" && id > 0) : [];
|
|
652
|
+
if (rarity !== null && path.length > 0)
|
|
653
|
+
pendingReveal = { path, rarity };
|
|
654
|
+
}
|
|
655
|
+
const storedConsumed = parsed.consumedTotal;
|
|
656
|
+
if (typeof storedConsumed !== "number" || !Number.isFinite(storedConsumed))
|
|
657
|
+
return null;
|
|
585
658
|
const eggTier = asRarity(parsed.eggTier);
|
|
586
659
|
return {
|
|
587
|
-
consumedTotal: Math.max(0,
|
|
660
|
+
consumedTotal: Math.max(0, Math.trunc(storedConsumed)),
|
|
588
661
|
active,
|
|
589
662
|
eggUsage: Math.max(0, asInt(parsed.eggUsage, 0)),
|
|
590
663
|
eggTier: eggTier === null || eggTier === "legendary" ? null : eggTier,
|
|
591
664
|
pendingHatch,
|
|
665
|
+
pendingReveal,
|
|
666
|
+
lure: parsed.lure === true,
|
|
667
|
+
incense: parsed.incense === true,
|
|
668
|
+
repel: typeof parsed.repel === "number" && Number.isInteger(parsed.repel) && parsed.repel > 0 ? parsed.repel : null,
|
|
592
669
|
inventory
|
|
593
670
|
};
|
|
594
671
|
}
|
|
@@ -606,7 +683,19 @@ function advance(state, tokensTotal) {
|
|
|
606
683
|
const events = [];
|
|
607
684
|
let next = { ...state, consumedTotal: Math.trunc(tokensTotal) };
|
|
608
685
|
if (gained > 0) {
|
|
609
|
-
|
|
686
|
+
const active = next.active;
|
|
687
|
+
if (active === null) {
|
|
688
|
+
next = { ...next, eggUsage: next.eggUsage + gained };
|
|
689
|
+
} else if (!active.soothe) {
|
|
690
|
+
next = { ...next, active: { ...active, usedAtStage: active.usedAtStage + gained } };
|
|
691
|
+
} else {
|
|
692
|
+
const raw = active.soothedRaw + gained;
|
|
693
|
+
const owed = Math.floor(raw * SOOTHE_BONUS) - Math.floor(active.soothedRaw * SOOTHE_BONUS);
|
|
694
|
+
next = {
|
|
695
|
+
...next,
|
|
696
|
+
active: { ...active, soothedRaw: raw, usedAtStage: active.usedAtStage + gained + owed }
|
|
697
|
+
};
|
|
698
|
+
}
|
|
610
699
|
}
|
|
611
700
|
for (let step = 0;step < MAX_TRANSITIONS_PER_ADVANCE; step++) {
|
|
612
701
|
if (next.active === null) {
|
|
@@ -624,7 +713,10 @@ function advance(state, tokensTotal) {
|
|
|
624
713
|
isShiny: hatch.isShiny,
|
|
625
714
|
nature: hatch.nature,
|
|
626
715
|
dittoDisguise: hatch.ditto ? hatch.speciesId : null,
|
|
627
|
-
dittoRevealed: false
|
|
716
|
+
dittoRevealed: false,
|
|
717
|
+
everstone: false,
|
|
718
|
+
soothe: false,
|
|
719
|
+
soothedRaw: 0
|
|
628
720
|
};
|
|
629
721
|
events.push({
|
|
630
722
|
kind: "hatched",
|
|
@@ -636,10 +728,36 @@ function advance(state, tokensTotal) {
|
|
|
636
728
|
continue;
|
|
637
729
|
}
|
|
638
730
|
const mon = next.active;
|
|
731
|
+
if (mon.everstone)
|
|
732
|
+
break;
|
|
639
733
|
const needed = phaseThreshold(mon.rarity, mon.plannedPath.length, mon.stageIndex);
|
|
640
734
|
if (mon.usedAtStage < needed)
|
|
641
735
|
break;
|
|
642
736
|
const excess = mon.usedAtStage - needed;
|
|
737
|
+
if (mon.dittoDisguise !== null && !mon.dittoRevealed) {
|
|
738
|
+
if (next.pendingReveal === null)
|
|
739
|
+
break;
|
|
740
|
+
const reveal = next.pendingReveal;
|
|
741
|
+
events.push({
|
|
742
|
+
kind: "revealed",
|
|
743
|
+
disguisedAs: mon.plannedPath[mon.stageIndex] ?? mon.baseId,
|
|
744
|
+
speciesId: reveal.path[0]
|
|
745
|
+
});
|
|
746
|
+
next = {
|
|
747
|
+
...next,
|
|
748
|
+
active: {
|
|
749
|
+
...mon,
|
|
750
|
+
baseId: reveal.path[0],
|
|
751
|
+
plannedPath: reveal.path,
|
|
752
|
+
stageIndex: 0,
|
|
753
|
+
usedAtStage: excess,
|
|
754
|
+
rarity: reveal.rarity,
|
|
755
|
+
dittoRevealed: true
|
|
756
|
+
},
|
|
757
|
+
pendingReveal: null
|
|
758
|
+
};
|
|
759
|
+
continue;
|
|
760
|
+
}
|
|
643
761
|
if (mon.stageIndex < mon.plannedPath.length - 1) {
|
|
644
762
|
events.push({
|
|
645
763
|
kind: "evolved",
|
|
@@ -832,10 +950,16 @@ function consume(storage, apiKeyId, item, applyToState, now) {
|
|
|
832
950
|
return { ok: false, reason: "unreadable" };
|
|
833
951
|
if ((row.state.inventory[item] ?? 0) <= 0)
|
|
834
952
|
return { ok: false, reason: "none-held" };
|
|
835
|
-
const
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
953
|
+
const outcome = applyToState(row.state);
|
|
954
|
+
if ("refused" in outcome)
|
|
955
|
+
return { ok: false, reason: outcome.refused };
|
|
956
|
+
const nextState = {
|
|
957
|
+
...outcome.applied,
|
|
958
|
+
inventory: {
|
|
959
|
+
...outcome.applied.inventory,
|
|
960
|
+
[item]: (outcome.applied.inventory[item] ?? 0) - 1
|
|
961
|
+
}
|
|
962
|
+
};
|
|
839
963
|
storage.run("UPDATE {{companion}} SET state = ?, updated_at = ? WHERE api_key_id = ?", [
|
|
840
964
|
serialiseState(nextState),
|
|
841
965
|
now,
|
|
@@ -853,7 +977,10 @@ function purchase(storage, apiKeyId, entry, applyToState, now) {
|
|
|
853
977
|
return { ok: false, reason: "unreadable" };
|
|
854
978
|
if (wallet(row) < price)
|
|
855
979
|
return { ok: false, reason: "insufficient" };
|
|
856
|
-
const
|
|
980
|
+
const outcome = applyToState(row.state);
|
|
981
|
+
if ("refused" in outcome)
|
|
982
|
+
return { ok: false, reason: outcome.refused };
|
|
983
|
+
const nextState = outcome.applied;
|
|
857
984
|
storage.run("UPDATE {{companion}} SET state = ?, tokens_spent = tokens_spent + ?, updated_at = ? WHERE api_key_id = ?", [serialiseState(nextState), price, now, apiKeyId]);
|
|
858
985
|
return {
|
|
859
986
|
ok: true,
|
|
@@ -864,6 +991,9 @@ function purchase(storage, apiKeyId, entry, applyToState, now) {
|
|
|
864
991
|
|
|
865
992
|
// src/server.ts
|
|
866
993
|
var MAX_MULTIPLIER = 1000;
|
|
994
|
+
var WARM_PER_POLL = 8;
|
|
995
|
+
var WARM_BACKOFF_MS = 60000;
|
|
996
|
+
var WARM_BACKOFF_MAX_MS = 60 * 60000;
|
|
867
997
|
function multiplierFrom(config) {
|
|
868
998
|
const raw = config.multiplier;
|
|
869
999
|
if (typeof raw !== "number" || !Number.isFinite(raw) || raw <= 0)
|
|
@@ -890,7 +1020,7 @@ var server_default = definePlugin({
|
|
|
890
1020
|
const existing = inFlight.get(apiKeyId);
|
|
891
1021
|
if (existing !== undefined)
|
|
892
1022
|
return existing;
|
|
893
|
-
const started = prefetchHatch(apiKeyId, state).finally(() => inFlight.delete(apiKeyId));
|
|
1023
|
+
const started = prefetchHatch(apiKeyId, state).then(() => prefetchReveal(apiKeyId, state)).finally(() => inFlight.delete(apiKeyId));
|
|
894
1024
|
inFlight.set(apiKeyId, started);
|
|
895
1025
|
return started;
|
|
896
1026
|
};
|
|
@@ -906,6 +1036,41 @@ var server_default = definePlugin({
|
|
|
906
1036
|
names.set(speciesId, found);
|
|
907
1037
|
return found;
|
|
908
1038
|
};
|
|
1039
|
+
const warming = new Set;
|
|
1040
|
+
const cold = new Map;
|
|
1041
|
+
const warmNames = (ids) => {
|
|
1042
|
+
if (net === undefined || files === undefined)
|
|
1043
|
+
return;
|
|
1044
|
+
const now = ctx.now();
|
|
1045
|
+
const batch = [];
|
|
1046
|
+
for (const id of ids) {
|
|
1047
|
+
if (batch.length >= WARM_PER_POLL)
|
|
1048
|
+
break;
|
|
1049
|
+
if (names.has(id) || warming.has(id))
|
|
1050
|
+
continue;
|
|
1051
|
+
const chilled = cold.get(id);
|
|
1052
|
+
if (chilled !== undefined && now < chilled.until)
|
|
1053
|
+
continue;
|
|
1054
|
+
batch.push(id);
|
|
1055
|
+
warming.add(id);
|
|
1056
|
+
}
|
|
1057
|
+
if (batch.length === 0)
|
|
1058
|
+
return;
|
|
1059
|
+
speciesDetails({ net, files }, batch).then((details) => {
|
|
1060
|
+
batch.forEach((id, index) => {
|
|
1061
|
+
if (details[index]?.names.en !== undefined) {
|
|
1062
|
+
cold.delete(id);
|
|
1063
|
+
return;
|
|
1064
|
+
}
|
|
1065
|
+
const failures = (cold.get(id)?.failures ?? 0) + 1;
|
|
1066
|
+
const wait = Math.min(WARM_BACKOFF_MS * 2 ** (failures - 1), WARM_BACKOFF_MAX_MS);
|
|
1067
|
+
cold.set(id, { until: now + wait, failures });
|
|
1068
|
+
});
|
|
1069
|
+
}).catch(() => {}).finally(() => {
|
|
1070
|
+
for (const id of batch)
|
|
1071
|
+
warming.delete(id);
|
|
1072
|
+
});
|
|
1073
|
+
};
|
|
909
1074
|
const settleAndRecord = (apiKeyId) => {
|
|
910
1075
|
const result = settle(storage, apiKeyId, ctx.now());
|
|
911
1076
|
if (result === null)
|
|
@@ -925,6 +1090,37 @@ var server_default = definePlugin({
|
|
|
925
1090
|
ctx.logger.info("companion graduated", { event: "companion.graduated", count: 1 });
|
|
926
1091
|
}
|
|
927
1092
|
};
|
|
1093
|
+
const prefetchReveal = async (apiKeyId, state) => {
|
|
1094
|
+
const mon = state.active;
|
|
1095
|
+
if (mon === null || mon.dittoDisguise === null || mon.dittoRevealed)
|
|
1096
|
+
return;
|
|
1097
|
+
if (state.pendingReveal !== null)
|
|
1098
|
+
return;
|
|
1099
|
+
if (net === undefined || files === undefined)
|
|
1100
|
+
return;
|
|
1101
|
+
const detail = await speciesDetail({ net, files }, DITTO_SPECIES_ID);
|
|
1102
|
+
if (detail === null)
|
|
1103
|
+
return;
|
|
1104
|
+
const current = readCompanion(storage, apiKeyId);
|
|
1105
|
+
if (current?.state == null)
|
|
1106
|
+
return;
|
|
1107
|
+
const latest = current.state.active;
|
|
1108
|
+
if (latest === null || latest.dittoDisguise === null || latest.dittoRevealed)
|
|
1109
|
+
return;
|
|
1110
|
+
if (current.state.pendingReveal !== null)
|
|
1111
|
+
return;
|
|
1112
|
+
storage.run("UPDATE {{companion}} SET state = ?, updated_at = ? WHERE api_key_id = ?", [
|
|
1113
|
+
JSON.stringify({
|
|
1114
|
+
...current.state,
|
|
1115
|
+
pendingReveal: {
|
|
1116
|
+
path: detail.chain,
|
|
1117
|
+
rarity: rarityFromCaptureRate(detail.captureRate, detail.isLegendary, detail.isMythical)
|
|
1118
|
+
}
|
|
1119
|
+
}),
|
|
1120
|
+
ctx.now(),
|
|
1121
|
+
apiKeyId
|
|
1122
|
+
]);
|
|
1123
|
+
};
|
|
928
1124
|
const prefetchHatch = async (apiKeyId, state) => {
|
|
929
1125
|
if (state.active !== null || state.pendingHatch !== null)
|
|
930
1126
|
return;
|
|
@@ -939,7 +1135,10 @@ var server_default = definePlugin({
|
|
|
939
1135
|
seed: hashSeed(`${apiKeyId}:${state.consumedTotal}`),
|
|
940
1136
|
guarantee: state.eggTier,
|
|
941
1137
|
hasShinyCharm: hasShinyCharm(state),
|
|
942
|
-
collectedFinals: collected
|
|
1138
|
+
collectedFinals: collected,
|
|
1139
|
+
onlyUncollected: state.lure,
|
|
1140
|
+
preferLongLines: state.incense,
|
|
1141
|
+
excludeFinal: state.repel
|
|
943
1142
|
});
|
|
944
1143
|
if (rolled === null)
|
|
945
1144
|
return;
|
|
@@ -951,6 +1150,10 @@ var server_default = definePlugin({
|
|
|
951
1150
|
const current = readCompanion(storage, apiKeyId);
|
|
952
1151
|
if (current?.state == null || current.state.pendingHatch !== null)
|
|
953
1152
|
return;
|
|
1153
|
+
if (current.state.active !== null)
|
|
1154
|
+
return;
|
|
1155
|
+
if (paidRollInputs(current.state) !== paidRollInputs(state))
|
|
1156
|
+
return;
|
|
954
1157
|
storage.run("UPDATE {{companion}} SET state = ?, updated_at = ? WHERE api_key_id = ?", [
|
|
955
1158
|
JSON.stringify({
|
|
956
1159
|
...current.state,
|
|
@@ -961,7 +1164,10 @@ var server_default = definePlugin({
|
|
|
961
1164
|
isShiny: rolled.isShiny,
|
|
962
1165
|
nature: rolled.nature,
|
|
963
1166
|
ditto: rolled.ditto
|
|
964
|
-
}
|
|
1167
|
+
},
|
|
1168
|
+
lure: state.lure && !rolled.usedLure,
|
|
1169
|
+
incense: false,
|
|
1170
|
+
repel: null
|
|
965
1171
|
}),
|
|
966
1172
|
ctx.now(),
|
|
967
1173
|
apiKeyId
|
|
@@ -1046,14 +1252,21 @@ var server_default = definePlugin({
|
|
|
1046
1252
|
prefetchOnce(apiKeyId, row.state).catch(() => {});
|
|
1047
1253
|
const active = row.state?.active ?? null;
|
|
1048
1254
|
const dex = readDex(storage, apiKeyId);
|
|
1255
|
+
const stageId = active === null ? null : active.plannedPath[active.stageIndex] ?? null;
|
|
1256
|
+
const stageName = await nameOf(stageId);
|
|
1257
|
+
const named = await Promise.all(dex.map(async (entry) => ({ ...entry, name: await nameOf(entry.finalId) })));
|
|
1258
|
+
warmNames([
|
|
1259
|
+
...stageId !== null && stageName === null ? [stageId] : [],
|
|
1260
|
+
...named.filter((entry) => entry.name === null).map((entry) => entry.finalId)
|
|
1261
|
+
]);
|
|
1049
1262
|
return {
|
|
1050
1263
|
json: {
|
|
1051
1264
|
state: row.state,
|
|
1052
1265
|
tokensTotal: row.tokensTotal,
|
|
1053
1266
|
wallet: wallet(row),
|
|
1054
1267
|
lastCreditAt: row.lastCreditAt,
|
|
1055
|
-
name:
|
|
1056
|
-
dex:
|
|
1268
|
+
name: stageName,
|
|
1269
|
+
dex: named,
|
|
1057
1270
|
shop: shopCatalogue(),
|
|
1058
1271
|
nextThreshold: active === null ? EGG_HATCH_THRESHOLD : phaseThreshold(active.rarity, active.plannedPath.length, active.stageIndex),
|
|
1059
1272
|
progress: active === null ? row.state?.eggUsage ?? 0 : active.usedAtStage
|
|
@@ -1083,6 +1296,27 @@ var server_default = definePlugin({
|
|
|
1083
1296
|
};
|
|
1084
1297
|
}
|
|
1085
1298
|
},
|
|
1299
|
+
{
|
|
1300
|
+
method: "GET",
|
|
1301
|
+
path: "/item-sprite/:item",
|
|
1302
|
+
handler: async (request) => {
|
|
1303
|
+
const item = request.params.item ?? "";
|
|
1304
|
+
if (!ITEM_SPRITE_NAMES.has(item)) {
|
|
1305
|
+
return { status: 404, json: { error: "no item icon" } };
|
|
1306
|
+
}
|
|
1307
|
+
if (net === undefined || files === undefined) {
|
|
1308
|
+
return { status: 503, json: { error: "sprites need the net and files capabilities" } };
|
|
1309
|
+
}
|
|
1310
|
+
const bytes = await itemSpriteBytes({ net, files }, item);
|
|
1311
|
+
if (bytes === null)
|
|
1312
|
+
return { status: 404, json: { error: "no item icon" } };
|
|
1313
|
+
return {
|
|
1314
|
+
bytes,
|
|
1315
|
+
contentType: "image/png",
|
|
1316
|
+
cacheControl: "public, max-age=31536000, immutable"
|
|
1317
|
+
};
|
|
1318
|
+
}
|
|
1319
|
+
},
|
|
1086
1320
|
{
|
|
1087
1321
|
method: "POST",
|
|
1088
1322
|
path: "/keys/:id/use",
|
|
@@ -1098,6 +1332,30 @@ var server_default = definePlugin({
|
|
|
1098
1332
|
return { json: { ok: true } };
|
|
1099
1333
|
}
|
|
1100
1334
|
},
|
|
1335
|
+
{
|
|
1336
|
+
method: "POST",
|
|
1337
|
+
path: "/keys/:id/unpin",
|
|
1338
|
+
handler: (request) => {
|
|
1339
|
+
const apiKeyId = request.params.id ?? "";
|
|
1340
|
+
const row = readCompanion(storage, apiKeyId);
|
|
1341
|
+
if (row === null)
|
|
1342
|
+
return { status: 404, json: { error: "no companion for that key" } };
|
|
1343
|
+
if (row.state === null)
|
|
1344
|
+
return { status: 409, json: { error: "unreadable" } };
|
|
1345
|
+
const active = row.state.active;
|
|
1346
|
+
if (active === null)
|
|
1347
|
+
return { status: 409, json: { error: "no-companion" } };
|
|
1348
|
+
if (!active.everstone)
|
|
1349
|
+
return { status: 409, json: { error: "nothing-new" } };
|
|
1350
|
+
storage.run("UPDATE {{companion}} SET state = ?, updated_at = ? WHERE api_key_id = ?", [
|
|
1351
|
+
JSON.stringify({ ...row.state, active: { ...active, everstone: false } }),
|
|
1352
|
+
ctx.now(),
|
|
1353
|
+
apiKeyId
|
|
1354
|
+
]);
|
|
1355
|
+
settleAndRecord(apiKeyId);
|
|
1356
|
+
return { json: { ok: true } };
|
|
1357
|
+
}
|
|
1358
|
+
},
|
|
1101
1359
|
{
|
|
1102
1360
|
method: "POST",
|
|
1103
1361
|
path: "/keys/:id/purchase",
|
|
@@ -1116,6 +1374,15 @@ var server_default = definePlugin({
|
|
|
1116
1374
|
return { routes };
|
|
1117
1375
|
}
|
|
1118
1376
|
});
|
|
1377
|
+
function paidRollInputs(state) {
|
|
1378
|
+
return JSON.stringify([
|
|
1379
|
+
state.eggTier,
|
|
1380
|
+
hasShinyCharm(state),
|
|
1381
|
+
state.lure,
|
|
1382
|
+
state.incense,
|
|
1383
|
+
state.repel
|
|
1384
|
+
]);
|
|
1385
|
+
}
|
|
1119
1386
|
function hashSeed(input) {
|
|
1120
1387
|
let hash = 2166136261;
|
|
1121
1388
|
for (let i = 0;i < input.length; i++) {
|
|
@@ -1136,7 +1403,7 @@ function shopCatalogue() {
|
|
|
1136
1403
|
price: freshEggPrice("uncommon")
|
|
1137
1404
|
},
|
|
1138
1405
|
{ entry: { kind: "egg", tier: "rare" }, price: freshEggPrice("rare") }
|
|
1139
|
-
];
|
|
1406
|
+
].sort((a, b) => a.price - b.price);
|
|
1140
1407
|
}
|
|
1141
1408
|
function parseShopEntry(body) {
|
|
1142
1409
|
if (typeof body !== "object" || body === null)
|
|
@@ -1158,32 +1425,86 @@ function parseShopEntry(body) {
|
|
|
1158
1425
|
}
|
|
1159
1426
|
function applyPurchase(state, entry) {
|
|
1160
1427
|
if (entry.kind === "egg") {
|
|
1161
|
-
|
|
1428
|
+
if (state.active === null)
|
|
1429
|
+
return { refused: "no-companion" };
|
|
1430
|
+
return {
|
|
1431
|
+
applied: { ...state, active: null, eggUsage: 0, eggTier: entry.tier, pendingHatch: null }
|
|
1432
|
+
};
|
|
1433
|
+
}
|
|
1434
|
+
if (!HELD_ITEMS.includes(entry.item) && (state.inventory[entry.item] ?? 0) > 0) {
|
|
1435
|
+
return { refused: "already-owned" };
|
|
1162
1436
|
}
|
|
1163
1437
|
return {
|
|
1164
|
-
|
|
1165
|
-
|
|
1438
|
+
applied: {
|
|
1439
|
+
...state,
|
|
1440
|
+
inventory: { ...state.inventory, [entry.item]: (state.inventory[entry.item] ?? 0) + 1 }
|
|
1441
|
+
}
|
|
1166
1442
|
};
|
|
1167
1443
|
}
|
|
1444
|
+
var HELD_ITEMS = [
|
|
1445
|
+
"rareCandy",
|
|
1446
|
+
"mint",
|
|
1447
|
+
"everstone",
|
|
1448
|
+
"lure",
|
|
1449
|
+
"sootheBell",
|
|
1450
|
+
"incense",
|
|
1451
|
+
"repel"
|
|
1452
|
+
];
|
|
1168
1453
|
function parseHeldItem(body) {
|
|
1169
1454
|
if (typeof body !== "object" || body === null)
|
|
1170
1455
|
return null;
|
|
1171
1456
|
const item = body.item;
|
|
1172
|
-
return item
|
|
1457
|
+
return HELD_ITEMS.includes(item) ? item : null;
|
|
1173
1458
|
}
|
|
1174
1459
|
function useItem(state, item) {
|
|
1460
|
+
if (item === "everstone" || item === "sootheBell" || item === "repel") {
|
|
1461
|
+
const active = state.active;
|
|
1462
|
+
if (active === null)
|
|
1463
|
+
return { refused: "no-companion" };
|
|
1464
|
+
if (item === "everstone") {
|
|
1465
|
+
if (active.everstone)
|
|
1466
|
+
return { refused: "nothing-new" };
|
|
1467
|
+
return { applied: { ...state, active: { ...active, everstone: true } } };
|
|
1468
|
+
}
|
|
1469
|
+
if (item === "sootheBell") {
|
|
1470
|
+
if (active.soothe)
|
|
1471
|
+
return { refused: "nothing-new" };
|
|
1472
|
+
return { applied: { ...state, active: { ...active, soothe: true } } };
|
|
1473
|
+
}
|
|
1474
|
+
const finalId = active.plannedPath[active.plannedPath.length - 1];
|
|
1475
|
+
if (finalId === undefined)
|
|
1476
|
+
return { refused: "no-companion" };
|
|
1477
|
+
if (finalId === DITTO_SPECIES_ID)
|
|
1478
|
+
return { refused: "nothing-new" };
|
|
1479
|
+
if (state.repel !== null)
|
|
1480
|
+
return { refused: "already-armed" };
|
|
1481
|
+
return { applied: { ...state, repel: finalId } };
|
|
1482
|
+
}
|
|
1483
|
+
if (item === "lure" || item === "incense") {
|
|
1484
|
+
if (item === "lure") {
|
|
1485
|
+
if (state.lure)
|
|
1486
|
+
return { refused: "nothing-new" };
|
|
1487
|
+
return { applied: { ...state, lure: true } };
|
|
1488
|
+
}
|
|
1489
|
+
if (state.incense)
|
|
1490
|
+
return { refused: "nothing-new" };
|
|
1491
|
+
return { applied: { ...state, incense: true } };
|
|
1492
|
+
}
|
|
1175
1493
|
if (item === "mint") {
|
|
1176
1494
|
if (state.active === null)
|
|
1177
|
-
return
|
|
1495
|
+
return { refused: "no-companion" };
|
|
1178
1496
|
const index = NATURES.indexOf(state.active.nature);
|
|
1179
1497
|
const nature = NATURES[(index + 1) % NATURES.length];
|
|
1180
|
-
return { ...state, active: { ...state.active, nature } };
|
|
1498
|
+
return { applied: { ...state, active: { ...state.active, nature } } };
|
|
1181
1499
|
}
|
|
1182
|
-
return
|
|
1183
|
-
...state,
|
|
1184
|
-
|
|
1500
|
+
return {
|
|
1501
|
+
applied: state.active === null ? { ...state, eggUsage: state.eggUsage + RARE_CANDY_XP } : {
|
|
1502
|
+
...state,
|
|
1503
|
+
active: { ...state.active, usedAtStage: state.active.usedAtStage + RARE_CANDY_XP }
|
|
1504
|
+
}
|
|
1185
1505
|
};
|
|
1186
1506
|
}
|
|
1187
1507
|
export {
|
|
1508
|
+
HELD_ITEMS,
|
|
1188
1509
|
server_default as default
|
|
1189
1510
|
};
|
package/ui/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
function
|
|
1
|
+
function ue(e){return e}import{useMutation as xe,useQuery as at,useQueryClient as vt}from"@tanstack/react-query";import{useState as st}from"react";var pt={rareCandy:{blurb:"Injects 100.0M growth. Priced at five times what it grants.",emoji:"\uD83C\uDF6C",consumable:!0},mint:{blurb:"Rerolls this companion's nature. Cosmetic, and cheap enough to reroll again.",emoji:"\uD83C\uDF3F",consumable:!0},shinyCharm:{blurb:"Kept, never spent. Raises every future hatch from 1 in 64 to 1 in 48.",emoji:"✨",consumable:!1},everstone:{blurb:"Pins this companion: it will not evolve, reveal, or graduate.",emoji:"\uD83E\uDEA8",consumable:!0},lure:{blurb:"Next egg prefers a species the Dex has not collected. A preference, never a veto.",emoji:"\uD83C\uDF6F",consumable:!0},sootheBell:{blurb:"This companion grows 25% faster. Bound to it, and never repays its price.",emoji:"\uD83D\uDD14",consumable:!0},incense:{blurb:"Next egg leans toward a longer evolution line. Buys events, not value.",emoji:"\uD83D\uDD6F️",consumable:!0},repel:{blurb:"Next egg will not hatch one named line.",emoji:"\uD83D\uDEAB",consumable:!0}};function N(e){return ye.get(e)??{blurb:"",emoji:"❔",consumable:!0}}var ye=new Map(Object.entries(pt)),Gt=[...ye].filter(([,e])=>e.consumable).map(([e])=>e),ke="\uD83E\uDD5A";function Se(e){return e===null?"Sends this companion off and starts again.":`Sends this companion off for an egg guaranteed to hatch ${e} or better.`}function T(e,t,o){return`/api/plugins/${e}/sprite/${t}${o?"?shiny=1":""}`}function Ie(e,t){return`/api/plugins/${e}/item-sprite/${t}`}function h(e){if(e>=1e9)return`${(e/1e9).toFixed(2)}B`;if(e>=1e6)return`${(e/1e6).toFixed(1)}M`;return e.toLocaleString()}function R(e,t){return e??`#${t}`}function P(e,t,o){return`${e??`Species ${t}`}${o?", shiny":""}`}function L(e){return e.replace(/([A-Z])/g," $1").toLowerCase()}function we(e){if(e.kind==="item")return L(e.item);return e.tier===null?"fresh egg":`fresh egg (${e.tier}+)`}var $e=[null,"common","uncommon","rare","legendary"],Ce=60000,ve=60*Ce;function _(e,t,o){if(!e)return"egg";if(t===null)return"sleep";let a=o-t;if(a<5*Ce)return"working";if(a<ve)return"idle";if(a<8*ve)return"tired";return"sleep"}import{useState as ut}from"react";import i from"styled-components";var l={xs:"4px",sm:"8px",md:"12px",lg:"20px",xl:"32px"},B="192px",de="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",M=i.section`
|
|
2
2
|
background: var(--panel);
|
|
3
3
|
border: 1px solid var(--rule);
|
|
4
4
|
border-radius: 8px;
|
|
5
5
|
padding: ${l.lg};
|
|
6
6
|
color: var(--ink);
|
|
7
|
-
`,
|
|
7
|
+
`,ce=i.h3`
|
|
8
8
|
display: flex;
|
|
9
9
|
align-items: center;
|
|
10
10
|
gap: ${l.md};
|
|
@@ -21,20 +21,83 @@ function V(e){return e}import{useMutation as we,useQuery as ve,useQueryClient as
|
|
|
21
21
|
height: 1px;
|
|
22
22
|
background: var(--rule);
|
|
23
23
|
}
|
|
24
|
-
`,
|
|
24
|
+
`,Te=i(ce)`
|
|
25
|
+
&::after {
|
|
26
|
+
content: none;
|
|
27
|
+
}
|
|
28
|
+
`,Re=i.span`
|
|
29
|
+
flex: 1;
|
|
30
|
+
height: 1px;
|
|
31
|
+
background: var(--rule);
|
|
32
|
+
`,Pe=i.button`
|
|
33
|
+
display: flex;
|
|
34
|
+
align-items: center;
|
|
35
|
+
gap: ${l.sm};
|
|
36
|
+
flex: 1;
|
|
37
|
+
padding: 0;
|
|
38
|
+
background: none;
|
|
39
|
+
border: 0;
|
|
40
|
+
color: inherit;
|
|
41
|
+
font: inherit;
|
|
42
|
+
letter-spacing: inherit;
|
|
43
|
+
text-transform: inherit;
|
|
44
|
+
text-align: left;
|
|
45
|
+
cursor: pointer;
|
|
46
|
+
|
|
47
|
+
&:hover {
|
|
48
|
+
color: var(--ink);
|
|
49
|
+
}
|
|
50
|
+
&:focus-visible {
|
|
51
|
+
outline: 2px solid var(--accent);
|
|
52
|
+
outline-offset: 4px;
|
|
53
|
+
border-radius: 2px;
|
|
54
|
+
}
|
|
55
|
+
`,Ae=i.span`
|
|
56
|
+
display: inline-block;
|
|
57
|
+
font-size: 9px;
|
|
58
|
+
line-height: 1;
|
|
59
|
+
transform: rotate(${(e)=>e.$open?"90deg":"0deg"});
|
|
60
|
+
transition: transform 120ms ease-out;
|
|
61
|
+
|
|
62
|
+
@media (prefers-reduced-motion: reduce) {
|
|
63
|
+
transition: none;
|
|
64
|
+
}
|
|
65
|
+
`,Ee=i.span`
|
|
66
|
+
color: var(--ink-faint);
|
|
67
|
+
letter-spacing: 0.08em;
|
|
68
|
+
white-space: nowrap;
|
|
69
|
+
`,w=i.div`
|
|
25
70
|
display: flex;
|
|
26
71
|
gap: ${l.md};
|
|
27
72
|
align-items: center;
|
|
28
73
|
flex-wrap: wrap;
|
|
29
|
-
`,
|
|
74
|
+
`,De=i(w)`
|
|
75
|
+
h2 {
|
|
76
|
+
margin: 0;
|
|
77
|
+
}
|
|
78
|
+
`,x=i.span`
|
|
79
|
+
color: var(--ink-dim);
|
|
80
|
+
`,F=i(x)`
|
|
81
|
+
display: block;
|
|
82
|
+
margin-top: ${l.sm};
|
|
83
|
+
|
|
84
|
+
/* Two facts about one meter are closer to each other than either is to the
|
|
85
|
+
meter, because that is what they are: "stage 1 of 2" and the tokens under
|
|
86
|
+
it are one reading, not two. Spaced evenly they read as a list of unrelated
|
|
87
|
+
numbers that happen to be stacked. */
|
|
88
|
+
& + & {
|
|
89
|
+
margin-top: ${l.xs};
|
|
90
|
+
}
|
|
91
|
+
`,Ne=i.p`
|
|
30
92
|
color: var(--ink-dim);
|
|
31
|
-
|
|
93
|
+
margin: ${l.xs} 0 ${l.lg};
|
|
94
|
+
`,ge=i.p`
|
|
32
95
|
color: var(--warn);
|
|
33
96
|
background: var(--warn-wash);
|
|
34
97
|
border-radius: 6px;
|
|
35
98
|
padding: ${l.md};
|
|
36
99
|
margin: ${l.md} 0 0;
|
|
37
|
-
`,b=
|
|
100
|
+
`,b=i.span`
|
|
38
101
|
display: inline-flex;
|
|
39
102
|
align-items: center;
|
|
40
103
|
gap: ${l.xs};
|
|
@@ -46,30 +109,31 @@ function V(e){return e}import{useMutation as we,useQuery as ve,useQueryClient as
|
|
|
46
109
|
text-transform: uppercase;
|
|
47
110
|
color: var(--ink-dim);
|
|
48
111
|
white-space: nowrap;
|
|
49
|
-
`,
|
|
112
|
+
`,Le=i(w)`
|
|
113
|
+
margin: ${l.xs} 0 ${l.sm};
|
|
114
|
+
`,j=i(b)`
|
|
50
115
|
border-color: var(--rule-strong);
|
|
51
116
|
color: var(--ink);
|
|
52
117
|
font-weight: 600;
|
|
53
|
-
`,
|
|
54
|
-
width:
|
|
55
|
-
height:
|
|
118
|
+
`,z=i.img`
|
|
119
|
+
width: ${B};
|
|
120
|
+
height: ${B};
|
|
56
121
|
image-rendering: pixelated;
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
width: 96px;
|
|
62
|
-
height: 96px;
|
|
122
|
+
`,q=i.div`
|
|
123
|
+
box-sizing: border-box;
|
|
124
|
+
width: ${B};
|
|
125
|
+
height: ${B};
|
|
63
126
|
border-radius: 50% 50% 45% 45%;
|
|
64
127
|
background: var(--panel-sunk);
|
|
65
128
|
border: 2px solid var(--rule-strong);
|
|
66
|
-
`,
|
|
67
|
-
|
|
68
|
-
|
|
129
|
+
`,Be=i.div`
|
|
130
|
+
box-sizing: border-box;
|
|
131
|
+
width: ${B};
|
|
132
|
+
height: ${B};
|
|
69
133
|
border-radius: 6px;
|
|
70
134
|
background: var(--warn-wash);
|
|
71
135
|
border: 2px dashed var(--warn);
|
|
72
|
-
`,
|
|
136
|
+
`,v=i.button`
|
|
73
137
|
background: var(--panel-raised);
|
|
74
138
|
border: 1px solid var(--rule);
|
|
75
139
|
border-radius: 6px;
|
|
@@ -94,22 +158,22 @@ function V(e){return e}import{useMutation as we,useQuery as ve,useQueryClient as
|
|
|
94
158
|
color: var(--ink-faint);
|
|
95
159
|
cursor: not-allowed;
|
|
96
160
|
}
|
|
97
|
-
`,
|
|
98
|
-
font-family: ${
|
|
161
|
+
`,O=i.span`
|
|
162
|
+
font-family: ${de};
|
|
99
163
|
font-variant-numeric: tabular-nums;
|
|
100
|
-
`,
|
|
164
|
+
`,Oe=i.div`
|
|
101
165
|
display: flex;
|
|
102
166
|
gap: 3px;
|
|
103
167
|
min-width: 240px;
|
|
104
168
|
max-width: 340px;
|
|
105
|
-
`,
|
|
169
|
+
`,Ke=i.div`
|
|
106
170
|
flex: 1;
|
|
107
171
|
height: 10px;
|
|
108
172
|
background: var(--panel-sunk);
|
|
109
173
|
border: 1px solid var(--rule);
|
|
110
174
|
border-radius: 3px;
|
|
111
175
|
overflow: hidden;
|
|
112
|
-
`,
|
|
176
|
+
`,me=i.div`
|
|
113
177
|
height: 100%;
|
|
114
178
|
background: var(--accent);
|
|
115
179
|
width: ${(e)=>Math.min(100,Math.max(0,e.$pct))}%;
|
|
@@ -118,48 +182,94 @@ function V(e){return e}import{useMutation as we,useQuery as ve,useQueryClient as
|
|
|
118
182
|
@media (prefers-reduced-motion: reduce) {
|
|
119
183
|
transition: none;
|
|
120
184
|
}
|
|
121
|
-
`,
|
|
185
|
+
`,_e=i.dl`
|
|
122
186
|
display: flex;
|
|
123
187
|
flex-wrap: wrap;
|
|
124
188
|
gap: ${l.xl};
|
|
125
189
|
margin: ${l.lg} 0 0;
|
|
126
190
|
padding-top: ${l.lg};
|
|
127
191
|
border-top: 1px solid var(--rule);
|
|
128
|
-
`,
|
|
192
|
+
`,J=i.div`
|
|
129
193
|
display: flex;
|
|
130
194
|
flex-direction: column;
|
|
131
195
|
gap: 2px;
|
|
132
|
-
`,
|
|
196
|
+
`,Q=i.dt`
|
|
133
197
|
font-size: 11px;
|
|
134
198
|
letter-spacing: 0.08em;
|
|
135
199
|
text-transform: uppercase;
|
|
136
200
|
color: var(--ink-faint);
|
|
137
|
-
`,
|
|
201
|
+
`,W=i.dd`
|
|
138
202
|
margin: 0;
|
|
139
|
-
font-family: ${
|
|
203
|
+
font-family: ${de};
|
|
140
204
|
font-variant-numeric: tabular-nums;
|
|
141
205
|
font-size: 18px;
|
|
142
206
|
color: var(--ink);
|
|
143
|
-
`,
|
|
207
|
+
`,Me=i.div`
|
|
144
208
|
display: grid;
|
|
145
209
|
grid-template-columns: repeat(auto-fill, minmax(84px, 1fr));
|
|
146
210
|
gap: ${l.md};
|
|
147
|
-
`,
|
|
211
|
+
`,Fe=i.button`
|
|
212
|
+
margin: 0;
|
|
213
|
+
display: flex;
|
|
214
|
+
flex-direction: column;
|
|
215
|
+
align-items: center;
|
|
216
|
+
gap: 2px;
|
|
217
|
+
padding: ${l.xs};
|
|
218
|
+
background: ${(e)=>e.$open?"var(--accent-wash)":"none"};
|
|
219
|
+
border: 1px solid ${(e)=>e.$open?"var(--accent)":"transparent"};
|
|
220
|
+
border-radius: 8px;
|
|
221
|
+
color: inherit;
|
|
222
|
+
font: inherit;
|
|
223
|
+
cursor: pointer;
|
|
224
|
+
|
|
225
|
+
&:hover {
|
|
226
|
+
border-color: ${(e)=>e.$open?"var(--accent)":"var(--rule)"};
|
|
227
|
+
}
|
|
228
|
+
&:focus-visible {
|
|
229
|
+
outline: 2px solid var(--accent);
|
|
230
|
+
outline-offset: 2px;
|
|
231
|
+
}
|
|
232
|
+
`,Ue=i.div`
|
|
233
|
+
grid-column: 1 / -1;
|
|
234
|
+
display: flex;
|
|
235
|
+
gap: ${l.lg};
|
|
236
|
+
padding: ${l.md};
|
|
237
|
+
background: var(--panel-sunk);
|
|
238
|
+
border: 1px solid var(--rule);
|
|
239
|
+
border-radius: 8px;
|
|
240
|
+
`,Ge=i.div`
|
|
241
|
+
display: flex;
|
|
242
|
+
flex-direction: column;
|
|
243
|
+
align-items: flex-start;
|
|
244
|
+
gap: ${l.sm};
|
|
245
|
+
min-width: 0;
|
|
246
|
+
`,He=i.div`
|
|
247
|
+
display: flex;
|
|
248
|
+
align-items: center;
|
|
249
|
+
gap: ${l.sm};
|
|
250
|
+
flex-wrap: wrap;
|
|
251
|
+
`,Ve=i.figure`
|
|
148
252
|
margin: 0;
|
|
149
253
|
display: flex;
|
|
150
254
|
flex-direction: column;
|
|
151
255
|
align-items: center;
|
|
152
256
|
gap: 2px;
|
|
153
|
-
|
|
257
|
+
|
|
258
|
+
img {
|
|
259
|
+
width: 48px;
|
|
260
|
+
height: 48px;
|
|
261
|
+
image-rendering: pixelated;
|
|
262
|
+
}
|
|
263
|
+
`,Y=i.figcaption`
|
|
154
264
|
color: var(--ink-dim);
|
|
155
265
|
font-size: 11px;
|
|
156
266
|
text-align: center;
|
|
157
267
|
overflow-wrap: anywhere;
|
|
158
|
-
`,
|
|
268
|
+
`,je=i.div`
|
|
159
269
|
display: grid;
|
|
160
|
-
grid-template-columns: repeat(auto-fill, minmax(
|
|
270
|
+
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
|
|
161
271
|
gap: ${l.md};
|
|
162
|
-
`,
|
|
272
|
+
`,ze=i.button`
|
|
163
273
|
display: flex;
|
|
164
274
|
flex-direction: column;
|
|
165
275
|
align-items: center;
|
|
@@ -180,16 +290,52 @@ function V(e){return e}import{useMutation as we,useQuery as ve,useQueryClient as
|
|
|
180
290
|
outline: 2px solid var(--accent);
|
|
181
291
|
outline-offset: 2px;
|
|
182
292
|
}
|
|
183
|
-
`,
|
|
184
|
-
font-family: ${
|
|
293
|
+
`,Z=i.span`
|
|
294
|
+
font-family: ${de};
|
|
185
295
|
font-size: 12px;
|
|
186
296
|
color: var(--ink-dim);
|
|
187
297
|
overflow-wrap: anywhere;
|
|
188
|
-
`,
|
|
298
|
+
`,X=i.div`
|
|
299
|
+
display: grid;
|
|
300
|
+
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
|
|
301
|
+
gap: ${l.md};
|
|
302
|
+
`,ee=i.div`
|
|
189
303
|
display: flex;
|
|
190
|
-
|
|
304
|
+
flex-direction: column;
|
|
191
305
|
gap: ${l.sm};
|
|
192
|
-
padding: ${l.
|
|
306
|
+
padding: ${l.md};
|
|
193
307
|
background: var(--panel-sunk);
|
|
194
|
-
border
|
|
195
|
-
|
|
308
|
+
border: 1px solid var(--rule);
|
|
309
|
+
border-radius: 8px;
|
|
310
|
+
`,te=i.div`
|
|
311
|
+
display: flex;
|
|
312
|
+
align-items: center;
|
|
313
|
+
gap: ${l.sm};
|
|
314
|
+
`,ne=i.span`
|
|
315
|
+
flex: 1;
|
|
316
|
+
overflow-wrap: anywhere;
|
|
317
|
+
`,oe=i.p`
|
|
318
|
+
margin: 0;
|
|
319
|
+
color: var(--ink-dim);
|
|
320
|
+
font-size: 12px;
|
|
321
|
+
line-height: 1.45;
|
|
322
|
+
`,re=i.div`
|
|
323
|
+
display: flex;
|
|
324
|
+
align-items: center;
|
|
325
|
+
justify-content: flex-end;
|
|
326
|
+
gap: ${l.sm};
|
|
327
|
+
margin-top: auto;
|
|
328
|
+
`,qe=i.span`
|
|
329
|
+
display: inline-flex;
|
|
330
|
+
align-items: center;
|
|
331
|
+
justify-content: center;
|
|
332
|
+
flex: none;
|
|
333
|
+
width: 32px;
|
|
334
|
+
height: 32px;
|
|
335
|
+
font-size: 20px;
|
|
336
|
+
line-height: 1;
|
|
337
|
+
`,Je=i.img`
|
|
338
|
+
width: 32px;
|
|
339
|
+
height: 32px;
|
|
340
|
+
image-rendering: pixelated;
|
|
341
|
+
`;import{jsx as Qe}from"react/jsx-runtime";function ie({item:e,emoji:t,pluginId:o}){let[a,u]=ut(!1);return Qe(qe,{"aria-hidden":"true",children:a?t:Qe(Je,{alt:"",onError:()=>u(!0),src:Ie(o,e)})})}import{jsx as A,jsxs as ae}from"react/jsx-runtime";function We({inventory:e,onUse:t,pending:o,pluginId:a}){let u=Object.entries(e).filter(([,s])=>s>0);if(u.length===0)return A(x,{children:"Nothing in the bag."});return A(X,{children:u.map(([s,r])=>{let n=N(s);return ae(ee,{children:[ae(te,{children:[A(ie,{emoji:n.emoji,item:s,pluginId:a}),A(ne,{children:L(s)}),ae(O,{children:["×",r]})]}),A(oe,{children:n.blurb}),A(re,{children:n.consumable?ae(v,{disabled:o,onClick:()=>t(s),type:"button",children:["Use ",L(s)]}):A(x,{children:"held"})})]},s)})})}import{Fragment as dt,useState as Ye}from"react";import{jsx as y,jsxs as C,Fragment as gt}from"react/jsx-runtime";function Ze({entries:e,pluginId:t}){let[o,a]=Ye(null),[u,s]=Ye(null);if(e.length===0)return y(x,{children:"Nothing graduated yet."});let r=o===null?e:e.filter((n)=>n.rarity===o);return C(gt,{children:[y(w,{children:$e.map((n)=>y(v,{"aria-pressed":o===n,onClick:()=>{a(n),s(null)},type:"button",children:n??"all"},n??"all"))}),r.length===0?C(x,{children:["No ",o," graduates yet."]}):y(Me,{children:r.map((n)=>{let p=u===n.id,I=`dex-detail-${n.id}`;return C(dt,{children:[C(Fe,{$open:p,"aria-controls":I,"aria-expanded":p,onClick:()=>s(p?null:n.id),type:"button",children:[y("img",{alt:`${n.rarity}${n.isShiny?" shiny":""} ${n.name??`species ${n.finalId}`}`,src:T(t,n.finalId,n.isShiny),style:{width:"64px",height:"64px",imageRendering:"pixelated"}}),y(Y,{children:R(n.name,n.finalId)}),n.nature===null?null:y(Y,{children:n.nature})]}),p?y(ct,{entry:n,id:I,pluginId:t}):null]},n.id)})})]})}function ct({entry:e,id:t,pluginId:o}){return C(Ue,{id:t,children:[y("img",{alt:P(e.name,e.finalId,e.isShiny),src:T(o,e.finalId,e.isShiny),style:{width:"96px",height:"96px",imageRendering:"pixelated"}}),C(Ge,{children:[y("strong",{children:R(e.name,e.finalId)}),C(w,{children:[y(b,{children:e.rarity}),e.isShiny?y(j,{children:"✦ shiny"}):null,e.nature===null?null:y(b,{children:e.nature})]}),y(He,{children:e.chainOrder.map((a,u)=>C(Ve,{children:[y("img",{alt:P(null,a,!1),src:T(o,a,!1)}),y(Y,{children:a===e.finalId?R(e.name,a):`#${a}`})]},`${a}-${u}`))}),C(x,{children:["caught ",new Date(e.caughtAt).toLocaleDateString()]})]})]})}import{jsx as he,jsxs as mt}from"react/jsx-runtime";function fe({stages:e,stageIndex:t,progress:o,threshold:a,label:u,valueText:s}){let r=o/Math.max(1,a)*100;return he(Oe,{"aria-label":u,"aria-valuemax":a,"aria-valuemin":0,"aria-valuenow":o,"aria-valuetext":s,role:"progressbar",children:Array.from({length:Math.max(1,e)},(n,p)=>mt(Ke,{children:[p<t?he(me,{$pct:100}):null,p===t?he(me,{$pct:r}):null]},p))})}function Xe(e,t,o,a){return`stage ${t+1} of ${e}, ${h(o)} of ${h(a)} tokens`}import{jsx as c,jsxs as g,Fragment as U}from"react/jsx-runtime";function et({view:e,activity:t,pluginId:o,onRelease:a,releasing:u}){let s=e.state;if(s===null)return null;let{active:r}=s,n=r===null?null:r.plannedPath[r.stageIndex]??null;return g(U,{children:[g(w,{children:[n===null?c(q,{"aria-label":"An egg, not yet hatched",role:"img"}):c(z,{alt:P(e.name,n,r?.isShiny===!0),src:T(o,n,r?.isShiny===!0)}),g("div",{children:[c("h3",{children:n===null?"Egg":R(e.name,n)}),g(Le,{children:[r===null?s.eggTier===null?null:g(b,{children:[s.eggTier,"+ guaranteed"]}):g(U,{children:[c(b,{children:r.rarity}),r.isShiny?g(j,{children:[c("span",{"aria-hidden":"true",children:"✦"}),"shiny"]}):null,c(b,{children:r.nature}),r.dittoDisguise===null||r.dittoRevealed?null:c(b,{children:"?"}),r.everstone?c(b,{children:"everstone"}):null,r.soothe?c(b,{children:"soothe bell"}):null]}),c(b,{"aria-label":`Activity: ${t}`,role:"status",children:t})]}),r===null?g(U,{children:[c(fe,{label:"Incubation",progress:e.progress,stageIndex:0,stages:1,threshold:e.nextThreshold,valueText:`${h(e.progress)} of ${h(e.nextThreshold)} tokens incubated`}),g(F,{children:[h(e.progress)," / ",h(e.nextThreshold)," tokens incubated"]})]}):g(U,{children:[c(fe,{label:"Growth to the next evolution",progress:e.progress,stageIndex:r.stageIndex,stages:r.plannedPath.length,threshold:e.nextThreshold,valueText:Xe(r.plannedPath.length,r.stageIndex,e.progress,e.nextThreshold)}),g(F,{children:["Stage ",r.stageIndex+1," of ",r.plannedPath.length]}),r.everstone?g(U,{children:[g(F,{children:["Held at this stage · ",h(e.progress)," banked"]}),c(v,{disabled:u,onClick:a,type:"button",children:"Release"})]}):g(F,{children:[h(e.progress)," / ",h(e.nextThreshold)," to the next stage"]})]})]})]}),g(_e,{children:[g(J,{children:[c(Q,{children:"Earned"}),c(W,{children:h(e.tokensTotal)})]}),g(J,{children:[c(Q,{children:"To spend"}),c(W,{children:h(e.wallet)})]}),g(J,{children:[c(Q,{children:"Graduated"}),c(W,{children:e.dex.length.toLocaleString()})]})]})]})}import{useState as ht}from"react";import{jsx as k,jsxs as se}from"react/jsx-runtime";function tt({keys:e,onPick:t,pluginId:o,rosterFailed:a}){let[u,s]=ht("");return se(M,{children:[k("h2",{children:"Companion"}),k(Ne,{children:"Each API key raises its own Pokémon on the tokens it spends. Pick a key."}),e.length>0?k(je,{children:e.map((r)=>k(ft,{entry:r,onPick:t,pluginId:o},r.apiKeyId))}):k(x,{children:a?"The list of keys could not be loaded. Enter a key id below to reach a companion directly.":"No key has spent a token yet. A companion appears the first time a key serves a request."}),k(ce,{children:"Or by key id"}),k("form",{onSubmit:(r)=>{r.preventDefault();let n=u.trim();if(n!=="")t(n)},children:se(w,{children:[k("input",{"aria-label":"API key id",onChange:(r)=>s(r.target.value),placeholder:"key id",value:u}),k(v,{type:"submit",children:"Show"})]})})]})}function ft({entry:e,onPick:t,pluginId:o}){let a=_(e.speciesId!==null,e.lastCreditAt,Date.now());return se(ze,{onClick:()=>t(e.apiKeyId),type:"button",children:[e.unreadable?k(Be,{"aria-label":"This key's save could not be read",role:"img"}):e.speciesId===null?k(q,{"aria-label":"An egg, not yet hatched",role:"img"}):k(z,{alt:P(e.name,e.speciesId,e.isShiny),src:T(o,e.speciesId,e.isShiny)}),k("strong",{children:e.unreadable?"Save unreadable":e.speciesId===null?"Egg":R(e.name,e.speciesId)}),e.rarity===null?null:k(b,{children:e.rarity}),k(Z,{children:e.apiKeyId}),se(x,{children:[k(O,{children:h(e.tokensTotal)}),e.unreadable?null:` · ${a}`]})]})}import{useId as xt,useState as bt}from"react";import{jsx as G,jsxs as nt,Fragment as kt}from"react/jsx-runtime";var K={shop:!0,bag:!0,dex:!0},ot=(e)=>`plugin:${e}:sections`;function yt(e){try{let t=globalThis.localStorage?.getItem(ot(e));if(t===null||t===void 0)return K;let o=JSON.parse(t);if(typeof o!=="object"||o===null)return K;let a=o;return{shop:typeof a.shop==="boolean"?a.shop:K.shop,bag:typeof a.bag==="boolean"?a.bag:K.bag,dex:typeof a.dex==="boolean"?a.dex:K.dex}}catch{return K}}function rt(e){let[t,o]=bt(()=>yt(e));return{open:t,toggle:(u)=>{o((s)=>{let r={...s,[u]:!s[u]};try{globalThis.localStorage?.setItem(ot(e),JSON.stringify(r))}catch{}return r})}}}function le({title:e,count:t,open:o,onToggle:a,children:u}){let s=xt();return nt(kt,{children:[G(Te,{children:nt(Pe,{"aria-controls":s,"aria-expanded":o,onClick:a,type:"button",children:[G(Ae,{$open:o,children:"▶"}),e,G(Re,{}),G(Ee,{children:t})]})}),o?G("div",{id:s,children:u}):null]})}import{jsx as E,jsxs as pe}from"react/jsx-runtime";function it({offers:e,wallet:t,onBuy:o,pending:a,inventory:u,hasCompanion:s,pluginId:r}){return E(X,{children:e.map((n)=>{let{entry:p,price:I}=n,S=St(p,u),d=p.kind==="egg"&&!s,D=p.kind==="item"?L(p.item):"fresh egg";return pe(ee,{children:[pe(te,{children:[E(ie,{emoji:p.kind==="item"?N(p.item).emoji:ke,item:p.kind==="item"?p.item:"egg",pluginId:r}),E(ne,{children:D}),S?E(x,{children:"owned"}):E(O,{children:h(I)})]}),p.kind==="egg"&&p.tier!==null?pe(b,{children:[p.tier,"+"]}):null,E(oe,{children:p.kind==="item"?N(p.item).blurb:Se(p.tier)}),E(re,{children:pe(v,{disabled:S||d||t<I||a,onClick:()=>o(p),type:"button",children:["Buy ",we(p)]})})]},p.kind==="item"?`item:${p.item}`:`egg:${p.tier??""}`)})})}function St(e,t){if(e.kind!=="item")return!1;return!N(e.item).consumable&&(t[e.item]??0)>0}import{jsx as f,jsxs as be,Fragment as Ct}from"react/jsx-runtime";var It=15000;function wt({pluginId:e,api:t}){let[o,a]=st({at:"start"}),u=vt(),s=at({queryKey:["roster"],queryFn:()=>t.get("keys")}),r=s.data?.keys??[];if(o.at==="start"&&!s.isPending){let m=r.length===1?r[0]:void 0;a(m===void 0?{at:"roster"}:{at:"key",apiKeyId:m.apiKeyId})}let n=o.at==="key"?o.apiKeyId:null,p=at({queryKey:["companion",n],queryFn:()=>t.get(`keys/${n}`),enabled:n!==null,refetchInterval:It}),[I,S]=st(null),d=()=>{u.invalidateQueries({queryKey:["companion",n]}),u.invalidateQueries({queryKey:["roster"]})},D=xe({mutationFn:(m)=>t.post(`keys/${n}/purchase`,m),onMutate:()=>S(null),onError:(m)=>S(m instanceof Error?m.message:"the purchase was refused"),onSuccess:d}),H=xe({mutationFn:(m)=>t.post(`keys/${n}/use`,{item:m}),onMutate:()=>S(null),onError:(m)=>S(m instanceof Error?m.message:"the item could not be used"),onSuccess:d}),V=xe({mutationFn:()=>t.post(`keys/${n}/unpin`),onMutate:()=>S(null),onError:(m)=>S(m instanceof Error?m.message:"the companion could not be released"),onSuccess:d});if(s.isPending)return f(M,{children:"Loading…"});if(n===null)return f(tt,{keys:r,onPick:(m)=>a({at:"key",apiKeyId:m}),pluginId:e,rosterFailed:s.isError});return be(M,{children:[be(De,{children:[f("h2",{children:"Companion"}),f(Z,{children:n}),f(v,{onClick:()=>a({at:"roster"}),type:"button",children:"All keys"})]}),f($t,{buy:D.mutate,buying:D.isPending,keyId:n,onRelease:()=>V.mutate(),onUse:H.mutate,pluginId:e,query:p,refusal:I,releasing:V.isPending,using:H.isPending})]})}function $t({query:e,pluginId:t,buy:o,onUse:a,onRelease:u,buying:s,using:r,releasing:n,refusal:p}){let{open:I,toggle:S}=rt(t);if(e.isPending)return f(x,{children:"Loading…"});if(e.isError||e.data===void 0)return f(x,{children:"No companion for that key yet."});let d=e.data;if(d.state===null)return f(ge,{children:"This key's save could not be read. It has been left untouched rather than replaced — nothing has been lost, but it needs looking at."});let D=_(d.state.active!==null,d.lastCreditAt,Date.now()),H=Object.values(d.state.inventory).filter((V)=>V>0).length;return be(Ct,{children:[f(et,{activity:D,onRelease:u,pluginId:t,releasing:n,view:d}),f(le,{count:lt(d.shop.length,"offer"),onToggle:()=>S("shop"),open:I.shop,title:"Shop",children:f(it,{hasCompanion:d.state.active!==null,inventory:d.state.inventory,offers:d.shop,onBuy:o,pending:s,pluginId:t,wallet:d.wallet})}),f(le,{count:`${H} held`,onToggle:()=>S("bag"),open:I.bag,title:"Bag",children:f(We,{inventory:d.state.inventory,onUse:a,pending:r,pluginId:t})}),p===null?null:f(ge,{role:"alert",children:p}),f(le,{count:lt(d.dex.length,"graduate"),onToggle:()=>S("dex"),open:I.dex,title:"Pokédex",children:f(Ze,{entries:d.dex,pluginId:t})})]})}function lt(e,t){return`${e} ${t}${e===1?"":"s"}`}var jn=ue({mount:wt});export{_ as activityOf,jn as default};
|