@omnigateway/pokemon 1.0.0 → 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 +406 -30
- package/ui/index.js +314 -41
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,16 @@ 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
|
+
}
|
|
374
|
+
async function cachedSpeciesName(deps, id) {
|
|
375
|
+
if (!isFetchableSpeciesId(id))
|
|
376
|
+
return null;
|
|
377
|
+
const cached = parseCachedDetail(await readJson(deps, speciesPath(id)), id);
|
|
378
|
+
return cached?.names.en ?? null;
|
|
379
|
+
}
|
|
351
380
|
function parseCachedIndex(raw) {
|
|
352
381
|
const entries = asArray(raw);
|
|
353
382
|
if (entries === null)
|
|
@@ -421,6 +450,22 @@ async function spriteBytes(deps, id, shiny) {
|
|
|
421
450
|
await writeCache(deps, path, bytes);
|
|
422
451
|
return bytes;
|
|
423
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
|
+
}
|
|
424
469
|
|
|
425
470
|
// src/roll.ts
|
|
426
471
|
var NATURES = [
|
|
@@ -461,23 +506,33 @@ function mulberry32(seed) {
|
|
|
461
506
|
};
|
|
462
507
|
}
|
|
463
508
|
var COLLECTED_WEIGHT = 0.25;
|
|
509
|
+
var FORM_WEIGHT = 0.6;
|
|
464
510
|
function roll(input) {
|
|
465
511
|
const random = mulberry32(input.seed);
|
|
466
|
-
const
|
|
512
|
+
const eligibleWith = (withLure) => input.candidates.filter((candidate) => {
|
|
467
513
|
if (!hasAnimatedSprite(candidate.id))
|
|
468
514
|
return false;
|
|
469
515
|
if (candidate.id === DITTO_SPECIES_ID)
|
|
470
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;
|
|
471
521
|
if (input.guarantee === null)
|
|
472
522
|
return true;
|
|
473
523
|
const rarity2 = rarityFromCaptureRate(candidate.captureRate, false, false);
|
|
474
524
|
return sortRank(rarity2) >= sortRank(input.guarantee);
|
|
475
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);
|
|
476
530
|
if (eligible.length === 0)
|
|
477
531
|
return null;
|
|
478
532
|
const weights = eligible.map((candidate) => {
|
|
479
533
|
const base = Math.max(1, candidate.captureRate);
|
|
480
|
-
|
|
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;
|
|
481
536
|
});
|
|
482
537
|
const total = weights.reduce((a, b) => a + b, 0);
|
|
483
538
|
let target = random() * total;
|
|
@@ -496,10 +551,16 @@ function roll(input) {
|
|
|
496
551
|
const rarity = rarityFromCaptureRate(chosen.captureRate, false, false);
|
|
497
552
|
const dittoEligible = rarity === "common" && chosen.forms >= 2;
|
|
498
553
|
const ditto = dittoEligible && random() < 1 / ODDS.dittoDisguise;
|
|
499
|
-
return { speciesId: chosen.id, isShiny, nature, ditto };
|
|
554
|
+
return { speciesId: chosen.id, isShiny, nature, ditto, usedLure };
|
|
500
555
|
}
|
|
501
556
|
|
|
502
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
|
+
}
|
|
503
564
|
function freshState() {
|
|
504
565
|
return {
|
|
505
566
|
consumedTotal: 0,
|
|
@@ -507,7 +568,11 @@ function freshState() {
|
|
|
507
568
|
eggUsage: 0,
|
|
508
569
|
eggTier: null,
|
|
509
570
|
pendingHatch: null,
|
|
510
|
-
|
|
571
|
+
pendingReveal: null,
|
|
572
|
+
lure: false,
|
|
573
|
+
incense: false,
|
|
574
|
+
repel: null,
|
|
575
|
+
inventory: emptyInventory()
|
|
511
576
|
};
|
|
512
577
|
}
|
|
513
578
|
function isRecord(value) {
|
|
@@ -528,7 +593,7 @@ function parseState(raw) {
|
|
|
528
593
|
}
|
|
529
594
|
if (!isRecord(parsed))
|
|
530
595
|
return null;
|
|
531
|
-
const inventory =
|
|
596
|
+
const inventory = emptyInventory();
|
|
532
597
|
const storedInventory = parsed.inventory;
|
|
533
598
|
if (isRecord(storedInventory)) {
|
|
534
599
|
for (const kind of ITEM_KINDS) {
|
|
@@ -556,7 +621,10 @@ function parseState(raw) {
|
|
|
556
621
|
isShiny: storedActive.isShiny === true,
|
|
557
622
|
nature: nature ?? "hardy",
|
|
558
623
|
dittoDisguise: typeof storedActive.dittoDisguise === "number" ? storedActive.dittoDisguise : null,
|
|
559
|
-
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))
|
|
560
628
|
};
|
|
561
629
|
}
|
|
562
630
|
const storedPending = parsed.pendingHatch;
|
|
@@ -576,13 +644,28 @@ function parseState(raw) {
|
|
|
576
644
|
};
|
|
577
645
|
}
|
|
578
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;
|
|
579
658
|
const eggTier = asRarity(parsed.eggTier);
|
|
580
659
|
return {
|
|
581
|
-
consumedTotal: Math.max(0,
|
|
660
|
+
consumedTotal: Math.max(0, Math.trunc(storedConsumed)),
|
|
582
661
|
active,
|
|
583
662
|
eggUsage: Math.max(0, asInt(parsed.eggUsage, 0)),
|
|
584
663
|
eggTier: eggTier === null || eggTier === "legendary" ? null : eggTier,
|
|
585
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,
|
|
586
669
|
inventory
|
|
587
670
|
};
|
|
588
671
|
}
|
|
@@ -600,7 +683,19 @@ function advance(state, tokensTotal) {
|
|
|
600
683
|
const events = [];
|
|
601
684
|
let next = { ...state, consumedTotal: Math.trunc(tokensTotal) };
|
|
602
685
|
if (gained > 0) {
|
|
603
|
-
|
|
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
|
+
}
|
|
604
699
|
}
|
|
605
700
|
for (let step = 0;step < MAX_TRANSITIONS_PER_ADVANCE; step++) {
|
|
606
701
|
if (next.active === null) {
|
|
@@ -618,7 +713,10 @@ function advance(state, tokensTotal) {
|
|
|
618
713
|
isShiny: hatch.isShiny,
|
|
619
714
|
nature: hatch.nature,
|
|
620
715
|
dittoDisguise: hatch.ditto ? hatch.speciesId : null,
|
|
621
|
-
dittoRevealed: false
|
|
716
|
+
dittoRevealed: false,
|
|
717
|
+
everstone: false,
|
|
718
|
+
soothe: false,
|
|
719
|
+
soothedRaw: 0
|
|
622
720
|
};
|
|
623
721
|
events.push({
|
|
624
722
|
kind: "hatched",
|
|
@@ -630,10 +728,36 @@ function advance(state, tokensTotal) {
|
|
|
630
728
|
continue;
|
|
631
729
|
}
|
|
632
730
|
const mon = next.active;
|
|
731
|
+
if (mon.everstone)
|
|
732
|
+
break;
|
|
633
733
|
const needed = phaseThreshold(mon.rarity, mon.plannedPath.length, mon.stageIndex);
|
|
634
734
|
if (mon.usedAtStage < needed)
|
|
635
735
|
break;
|
|
636
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
|
+
}
|
|
637
761
|
if (mon.stageIndex < mon.plannedPath.length - 1) {
|
|
638
762
|
events.push({
|
|
639
763
|
kind: "evolved",
|
|
@@ -726,6 +850,18 @@ function readCompanion(storage, apiKeyId) {
|
|
|
726
850
|
lastCreditAt: row.last_credit_at
|
|
727
851
|
};
|
|
728
852
|
}
|
|
853
|
+
function listCompanions(storage) {
|
|
854
|
+
const rows = storage.all(`SELECT api_key_id, state, tokens_total, tokens_spent, last_credit_at
|
|
855
|
+
FROM {{companion}}
|
|
856
|
+
ORDER BY last_credit_at IS NULL, last_credit_at DESC, tokens_total DESC, api_key_id ASC`);
|
|
857
|
+
return rows.map((row) => ({
|
|
858
|
+
apiKeyId: row.api_key_id,
|
|
859
|
+
state: parseState(row.state),
|
|
860
|
+
tokensTotal: row.tokens_total,
|
|
861
|
+
tokensSpent: row.tokens_spent,
|
|
862
|
+
lastCreditAt: row.last_credit_at
|
|
863
|
+
}));
|
|
864
|
+
}
|
|
729
865
|
function creditTokens(storage, apiKeyId, tokens, now) {
|
|
730
866
|
if (tokens <= 0)
|
|
731
867
|
return;
|
|
@@ -814,10 +950,16 @@ function consume(storage, apiKeyId, item, applyToState, now) {
|
|
|
814
950
|
return { ok: false, reason: "unreadable" };
|
|
815
951
|
if ((row.state.inventory[item] ?? 0) <= 0)
|
|
816
952
|
return { ok: false, reason: "none-held" };
|
|
817
|
-
const
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
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
|
+
};
|
|
821
963
|
storage.run("UPDATE {{companion}} SET state = ?, updated_at = ? WHERE api_key_id = ?", [
|
|
822
964
|
serialiseState(nextState),
|
|
823
965
|
now,
|
|
@@ -835,7 +977,10 @@ function purchase(storage, apiKeyId, entry, applyToState, now) {
|
|
|
835
977
|
return { ok: false, reason: "unreadable" };
|
|
836
978
|
if (wallet(row) < price)
|
|
837
979
|
return { ok: false, reason: "insufficient" };
|
|
838
|
-
const
|
|
980
|
+
const outcome = applyToState(row.state);
|
|
981
|
+
if ("refused" in outcome)
|
|
982
|
+
return { ok: false, reason: outcome.refused };
|
|
983
|
+
const nextState = outcome.applied;
|
|
839
984
|
storage.run("UPDATE {{companion}} SET state = ?, tokens_spent = tokens_spent + ?, updated_at = ? WHERE api_key_id = ?", [serialiseState(nextState), price, now, apiKeyId]);
|
|
840
985
|
return {
|
|
841
986
|
ok: true,
|
|
@@ -846,6 +991,9 @@ function purchase(storage, apiKeyId, entry, applyToState, now) {
|
|
|
846
991
|
|
|
847
992
|
// src/server.ts
|
|
848
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;
|
|
849
997
|
function multiplierFrom(config) {
|
|
850
998
|
const raw = config.multiplier;
|
|
851
999
|
if (typeof raw !== "number" || !Number.isFinite(raw) || raw <= 0)
|
|
@@ -872,10 +1020,57 @@ var server_default = definePlugin({
|
|
|
872
1020
|
const existing = inFlight.get(apiKeyId);
|
|
873
1021
|
if (existing !== undefined)
|
|
874
1022
|
return existing;
|
|
875
|
-
const started = prefetchHatch(apiKeyId, state).finally(() => inFlight.delete(apiKeyId));
|
|
1023
|
+
const started = prefetchHatch(apiKeyId, state).then(() => prefetchReveal(apiKeyId, state)).finally(() => inFlight.delete(apiKeyId));
|
|
876
1024
|
inFlight.set(apiKeyId, started);
|
|
877
1025
|
return started;
|
|
878
1026
|
};
|
|
1027
|
+
const names = new Map;
|
|
1028
|
+
const nameOf = async (speciesId) => {
|
|
1029
|
+
if (speciesId === null || files === undefined)
|
|
1030
|
+
return null;
|
|
1031
|
+
const known = names.get(speciesId);
|
|
1032
|
+
if (known !== undefined)
|
|
1033
|
+
return known;
|
|
1034
|
+
const found = await cachedSpeciesName({ files }, speciesId);
|
|
1035
|
+
if (found !== null)
|
|
1036
|
+
names.set(speciesId, found);
|
|
1037
|
+
return found;
|
|
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
|
+
};
|
|
879
1074
|
const settleAndRecord = (apiKeyId) => {
|
|
880
1075
|
const result = settle(storage, apiKeyId, ctx.now());
|
|
881
1076
|
if (result === null)
|
|
@@ -895,6 +1090,37 @@ var server_default = definePlugin({
|
|
|
895
1090
|
ctx.logger.info("companion graduated", { event: "companion.graduated", count: 1 });
|
|
896
1091
|
}
|
|
897
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
|
+
};
|
|
898
1124
|
const prefetchHatch = async (apiKeyId, state) => {
|
|
899
1125
|
if (state.active !== null || state.pendingHatch !== null)
|
|
900
1126
|
return;
|
|
@@ -909,7 +1135,10 @@ var server_default = definePlugin({
|
|
|
909
1135
|
seed: hashSeed(`${apiKeyId}:${state.consumedTotal}`),
|
|
910
1136
|
guarantee: state.eggTier,
|
|
911
1137
|
hasShinyCharm: hasShinyCharm(state),
|
|
912
|
-
collectedFinals: collected
|
|
1138
|
+
collectedFinals: collected,
|
|
1139
|
+
onlyUncollected: state.lure,
|
|
1140
|
+
preferLongLines: state.incense,
|
|
1141
|
+
excludeFinal: state.repel
|
|
913
1142
|
});
|
|
914
1143
|
if (rolled === null)
|
|
915
1144
|
return;
|
|
@@ -921,6 +1150,10 @@ var server_default = definePlugin({
|
|
|
921
1150
|
const current = readCompanion(storage, apiKeyId);
|
|
922
1151
|
if (current?.state == null || current.state.pendingHatch !== null)
|
|
923
1152
|
return;
|
|
1153
|
+
if (current.state.active !== null)
|
|
1154
|
+
return;
|
|
1155
|
+
if (paidRollInputs(current.state) !== paidRollInputs(state))
|
|
1156
|
+
return;
|
|
924
1157
|
storage.run("UPDATE {{companion}} SET state = ?, updated_at = ? WHERE api_key_id = ?", [
|
|
925
1158
|
JSON.stringify({
|
|
926
1159
|
...current.state,
|
|
@@ -931,7 +1164,10 @@ var server_default = definePlugin({
|
|
|
931
1164
|
isShiny: rolled.isShiny,
|
|
932
1165
|
nature: rolled.nature,
|
|
933
1166
|
ditto: rolled.ditto
|
|
934
|
-
}
|
|
1167
|
+
},
|
|
1168
|
+
lure: state.lure && !rolled.usedLure,
|
|
1169
|
+
incense: false,
|
|
1170
|
+
repel: null
|
|
935
1171
|
}),
|
|
936
1172
|
ctx.now(),
|
|
937
1173
|
apiKeyId
|
|
@@ -980,10 +1216,33 @@ var server_default = definePlugin({
|
|
|
980
1216
|
});
|
|
981
1217
|
}
|
|
982
1218
|
const routes = [
|
|
1219
|
+
{
|
|
1220
|
+
method: "GET",
|
|
1221
|
+
path: "/keys",
|
|
1222
|
+
handler: async () => {
|
|
1223
|
+
const rows = listCompanions(storage);
|
|
1224
|
+
const keys = await Promise.all(rows.map(async (row) => {
|
|
1225
|
+
const active = row.state?.active ?? null;
|
|
1226
|
+
const speciesId = active === null ? null : active.plannedPath[active.stageIndex] ?? null;
|
|
1227
|
+
return {
|
|
1228
|
+
apiKeyId: row.apiKeyId,
|
|
1229
|
+
speciesId,
|
|
1230
|
+
name: await nameOf(speciesId),
|
|
1231
|
+
rarity: active?.rarity ?? null,
|
|
1232
|
+
isShiny: active?.isShiny ?? false,
|
|
1233
|
+
tokensTotal: row.tokensTotal,
|
|
1234
|
+
wallet: wallet(row),
|
|
1235
|
+
lastCreditAt: row.lastCreditAt,
|
|
1236
|
+
unreadable: row.state === null
|
|
1237
|
+
};
|
|
1238
|
+
}));
|
|
1239
|
+
return { json: { keys } };
|
|
1240
|
+
}
|
|
1241
|
+
},
|
|
983
1242
|
{
|
|
984
1243
|
method: "GET",
|
|
985
1244
|
path: "/keys/:id",
|
|
986
|
-
handler: (request) => {
|
|
1245
|
+
handler: async (request) => {
|
|
987
1246
|
const apiKeyId = request.params.id ?? "";
|
|
988
1247
|
settleAndRecord(apiKeyId);
|
|
989
1248
|
const row = readCompanion(storage, apiKeyId);
|
|
@@ -992,13 +1251,22 @@ var server_default = definePlugin({
|
|
|
992
1251
|
if (row.state !== null)
|
|
993
1252
|
prefetchOnce(apiKeyId, row.state).catch(() => {});
|
|
994
1253
|
const active = row.state?.active ?? null;
|
|
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
|
+
]);
|
|
995
1262
|
return {
|
|
996
1263
|
json: {
|
|
997
1264
|
state: row.state,
|
|
998
1265
|
tokensTotal: row.tokensTotal,
|
|
999
1266
|
wallet: wallet(row),
|
|
1000
1267
|
lastCreditAt: row.lastCreditAt,
|
|
1001
|
-
|
|
1268
|
+
name: stageName,
|
|
1269
|
+
dex: named,
|
|
1002
1270
|
shop: shopCatalogue(),
|
|
1003
1271
|
nextThreshold: active === null ? EGG_HATCH_THRESHOLD : phaseThreshold(active.rarity, active.plannedPath.length, active.stageIndex),
|
|
1004
1272
|
progress: active === null ? row.state?.eggUsage ?? 0 : active.usedAtStage
|
|
@@ -1028,6 +1296,27 @@ var server_default = definePlugin({
|
|
|
1028
1296
|
};
|
|
1029
1297
|
}
|
|
1030
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
|
+
},
|
|
1031
1320
|
{
|
|
1032
1321
|
method: "POST",
|
|
1033
1322
|
path: "/keys/:id/use",
|
|
@@ -1043,6 +1332,30 @@ var server_default = definePlugin({
|
|
|
1043
1332
|
return { json: { ok: true } };
|
|
1044
1333
|
}
|
|
1045
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
|
+
},
|
|
1046
1359
|
{
|
|
1047
1360
|
method: "POST",
|
|
1048
1361
|
path: "/keys/:id/purchase",
|
|
@@ -1061,6 +1374,15 @@ var server_default = definePlugin({
|
|
|
1061
1374
|
return { routes };
|
|
1062
1375
|
}
|
|
1063
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
|
+
}
|
|
1064
1386
|
function hashSeed(input) {
|
|
1065
1387
|
let hash = 2166136261;
|
|
1066
1388
|
for (let i = 0;i < input.length; i++) {
|
|
@@ -1081,7 +1403,7 @@ function shopCatalogue() {
|
|
|
1081
1403
|
price: freshEggPrice("uncommon")
|
|
1082
1404
|
},
|
|
1083
1405
|
{ entry: { kind: "egg", tier: "rare" }, price: freshEggPrice("rare") }
|
|
1084
|
-
];
|
|
1406
|
+
].sort((a, b) => a.price - b.price);
|
|
1085
1407
|
}
|
|
1086
1408
|
function parseShopEntry(body) {
|
|
1087
1409
|
if (typeof body !== "object" || body === null)
|
|
@@ -1103,32 +1425,86 @@ function parseShopEntry(body) {
|
|
|
1103
1425
|
}
|
|
1104
1426
|
function applyPurchase(state, entry) {
|
|
1105
1427
|
if (entry.kind === "egg") {
|
|
1106
|
-
|
|
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" };
|
|
1107
1436
|
}
|
|
1108
1437
|
return {
|
|
1109
|
-
|
|
1110
|
-
|
|
1438
|
+
applied: {
|
|
1439
|
+
...state,
|
|
1440
|
+
inventory: { ...state.inventory, [entry.item]: (state.inventory[entry.item] ?? 0) + 1 }
|
|
1441
|
+
}
|
|
1111
1442
|
};
|
|
1112
1443
|
}
|
|
1444
|
+
var HELD_ITEMS = [
|
|
1445
|
+
"rareCandy",
|
|
1446
|
+
"mint",
|
|
1447
|
+
"everstone",
|
|
1448
|
+
"lure",
|
|
1449
|
+
"sootheBell",
|
|
1450
|
+
"incense",
|
|
1451
|
+
"repel"
|
|
1452
|
+
];
|
|
1113
1453
|
function parseHeldItem(body) {
|
|
1114
1454
|
if (typeof body !== "object" || body === null)
|
|
1115
1455
|
return null;
|
|
1116
1456
|
const item = body.item;
|
|
1117
|
-
return item
|
|
1457
|
+
return HELD_ITEMS.includes(item) ? item : null;
|
|
1118
1458
|
}
|
|
1119
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
|
+
}
|
|
1120
1493
|
if (item === "mint") {
|
|
1121
1494
|
if (state.active === null)
|
|
1122
|
-
return
|
|
1495
|
+
return { refused: "no-companion" };
|
|
1123
1496
|
const index = NATURES.indexOf(state.active.nature);
|
|
1124
1497
|
const nature = NATURES[(index + 1) % NATURES.length];
|
|
1125
|
-
return { ...state, active: { ...state.active, nature } };
|
|
1498
|
+
return { applied: { ...state, active: { ...state.active, nature } } };
|
|
1126
1499
|
}
|
|
1127
|
-
return
|
|
1128
|
-
...state,
|
|
1129
|
-
|
|
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
|
+
}
|
|
1130
1505
|
};
|
|
1131
1506
|
}
|
|
1132
1507
|
export {
|
|
1508
|
+
HELD_ITEMS,
|
|
1133
1509
|
server_default as default
|
|
1134
1510
|
};
|
package/ui/index.js
CHANGED
|
@@ -1,68 +1,341 @@
|
|
|
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
|
-
border-radius:
|
|
5
|
-
padding:
|
|
4
|
+
border-radius: 8px;
|
|
5
|
+
padding: ${l.lg};
|
|
6
6
|
color: var(--ink);
|
|
7
|
-
`,
|
|
7
|
+
`,ce=i.h3`
|
|
8
|
+
display: flex;
|
|
9
|
+
align-items: center;
|
|
10
|
+
gap: ${l.md};
|
|
11
|
+
margin: ${l.xl} 0 ${l.md};
|
|
12
|
+
font-size: 11px;
|
|
13
|
+
font-weight: 600;
|
|
14
|
+
letter-spacing: 0.12em;
|
|
15
|
+
text-transform: uppercase;
|
|
16
|
+
color: var(--ink-dim);
|
|
17
|
+
|
|
18
|
+
&::after {
|
|
19
|
+
content: "";
|
|
20
|
+
flex: 1;
|
|
21
|
+
height: 1px;
|
|
22
|
+
background: var(--rule);
|
|
23
|
+
}
|
|
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`
|
|
8
70
|
display: flex;
|
|
9
|
-
gap:
|
|
71
|
+
gap: ${l.md};
|
|
10
72
|
align-items: center;
|
|
11
73
|
flex-wrap: wrap;
|
|
12
|
-
`,
|
|
13
|
-
|
|
14
|
-
|
|
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`
|
|
92
|
+
color: var(--ink-dim);
|
|
93
|
+
margin: ${l.xs} 0 ${l.lg};
|
|
94
|
+
`,ge=i.p`
|
|
95
|
+
color: var(--warn);
|
|
96
|
+
background: var(--warn-wash);
|
|
97
|
+
border-radius: 6px;
|
|
98
|
+
padding: ${l.md};
|
|
99
|
+
margin: ${l.md} 0 0;
|
|
100
|
+
`,b=i.span`
|
|
101
|
+
display: inline-flex;
|
|
102
|
+
align-items: center;
|
|
103
|
+
gap: ${l.xs};
|
|
104
|
+
padding: 2px ${l.sm};
|
|
105
|
+
border: 1px solid var(--rule);
|
|
106
|
+
border-radius: 999px;
|
|
107
|
+
font-size: 11px;
|
|
108
|
+
letter-spacing: 0.08em;
|
|
109
|
+
text-transform: uppercase;
|
|
110
|
+
color: var(--ink-dim);
|
|
111
|
+
white-space: nowrap;
|
|
112
|
+
`,Le=i(w)`
|
|
113
|
+
margin: ${l.xs} 0 ${l.sm};
|
|
114
|
+
`,j=i(b)`
|
|
115
|
+
border-color: var(--rule-strong);
|
|
116
|
+
color: var(--ink);
|
|
117
|
+
font-weight: 600;
|
|
118
|
+
`,z=i.img`
|
|
119
|
+
width: ${B};
|
|
120
|
+
height: ${B};
|
|
15
121
|
image-rendering: pixelated;
|
|
122
|
+
`,q=i.div`
|
|
123
|
+
box-sizing: border-box;
|
|
124
|
+
width: ${B};
|
|
125
|
+
height: ${B};
|
|
126
|
+
border-radius: 50% 50% 45% 45%;
|
|
16
127
|
background: var(--panel-sunk);
|
|
17
|
-
border
|
|
18
|
-
`,
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
height:
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
height: 100%;
|
|
27
|
-
width: ${(t)=>Math.min(100,Math.max(0,t.$pct))}%;
|
|
28
|
-
`,a=s.span`
|
|
29
|
-
color: var(--ink-dim);
|
|
30
|
-
`,J=s.div`
|
|
31
|
-
display: grid;
|
|
32
|
-
grid-template-columns: repeat(auto-fill, minmax(72px, 1fr));
|
|
33
|
-
gap: 8px;
|
|
34
|
-
margin-top: 12px;
|
|
35
|
-
`,x=s.button`
|
|
128
|
+
border: 2px solid var(--rule-strong);
|
|
129
|
+
`,Be=i.div`
|
|
130
|
+
box-sizing: border-box;
|
|
131
|
+
width: ${B};
|
|
132
|
+
height: ${B};
|
|
133
|
+
border-radius: 6px;
|
|
134
|
+
background: var(--warn-wash);
|
|
135
|
+
border: 2px dashed var(--warn);
|
|
136
|
+
`,v=i.button`
|
|
36
137
|
background: var(--panel-raised);
|
|
37
138
|
border: 1px solid var(--rule);
|
|
38
|
-
border-radius:
|
|
139
|
+
border-radius: 6px;
|
|
39
140
|
color: var(--ink);
|
|
40
|
-
padding:
|
|
141
|
+
padding: ${l.sm} ${l.md};
|
|
142
|
+
font: inherit;
|
|
41
143
|
cursor: pointer;
|
|
144
|
+
|
|
145
|
+
&:hover:not(:disabled) {
|
|
146
|
+
border-color: var(--rule-strong);
|
|
147
|
+
}
|
|
148
|
+
&:focus-visible {
|
|
149
|
+
outline: 2px solid var(--accent);
|
|
150
|
+
outline-offset: 2px;
|
|
151
|
+
}
|
|
152
|
+
&[aria-pressed="true"] {
|
|
153
|
+
background: var(--accent-wash);
|
|
154
|
+
border-color: var(--accent);
|
|
155
|
+
color: var(--ink);
|
|
156
|
+
}
|
|
42
157
|
&:disabled {
|
|
43
158
|
color: var(--ink-faint);
|
|
44
159
|
cursor: not-allowed;
|
|
45
160
|
}
|
|
46
|
-
`,
|
|
47
|
-
|
|
48
|
-
|
|
161
|
+
`,O=i.span`
|
|
162
|
+
font-family: ${de};
|
|
163
|
+
font-variant-numeric: tabular-nums;
|
|
164
|
+
`,Oe=i.div`
|
|
165
|
+
display: flex;
|
|
166
|
+
gap: 3px;
|
|
167
|
+
min-width: 240px;
|
|
168
|
+
max-width: 340px;
|
|
169
|
+
`,Ke=i.div`
|
|
170
|
+
flex: 1;
|
|
171
|
+
height: 10px;
|
|
172
|
+
background: var(--panel-sunk);
|
|
173
|
+
border: 1px solid var(--rule);
|
|
174
|
+
border-radius: 3px;
|
|
175
|
+
overflow: hidden;
|
|
176
|
+
`,me=i.div`
|
|
177
|
+
height: 100%;
|
|
178
|
+
background: var(--accent);
|
|
179
|
+
width: ${(e)=>Math.min(100,Math.max(0,e.$pct))}%;
|
|
180
|
+
transition: width 480ms ease-out;
|
|
181
|
+
|
|
182
|
+
@media (prefers-reduced-motion: reduce) {
|
|
183
|
+
transition: none;
|
|
184
|
+
}
|
|
185
|
+
`,_e=i.dl`
|
|
186
|
+
display: flex;
|
|
187
|
+
flex-wrap: wrap;
|
|
188
|
+
gap: ${l.xl};
|
|
189
|
+
margin: ${l.lg} 0 0;
|
|
190
|
+
padding-top: ${l.lg};
|
|
191
|
+
border-top: 1px solid var(--rule);
|
|
192
|
+
`,J=i.div`
|
|
193
|
+
display: flex;
|
|
194
|
+
flex-direction: column;
|
|
195
|
+
gap: 2px;
|
|
196
|
+
`,Q=i.dt`
|
|
197
|
+
font-size: 11px;
|
|
198
|
+
letter-spacing: 0.08em;
|
|
199
|
+
text-transform: uppercase;
|
|
200
|
+
color: var(--ink-faint);
|
|
201
|
+
`,W=i.dd`
|
|
202
|
+
margin: 0;
|
|
203
|
+
font-family: ${de};
|
|
204
|
+
font-variant-numeric: tabular-nums;
|
|
205
|
+
font-size: 18px;
|
|
206
|
+
color: var(--ink);
|
|
207
|
+
`,Me=i.div`
|
|
208
|
+
display: grid;
|
|
209
|
+
grid-template-columns: repeat(auto-fill, minmax(84px, 1fr));
|
|
210
|
+
gap: ${l.md};
|
|
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`
|
|
49
252
|
margin: 0;
|
|
50
253
|
display: flex;
|
|
51
254
|
flex-direction: column;
|
|
52
255
|
align-items: center;
|
|
53
256
|
gap: 2px;
|
|
54
|
-
|
|
257
|
+
|
|
258
|
+
img {
|
|
259
|
+
width: 48px;
|
|
260
|
+
height: 48px;
|
|
261
|
+
image-rendering: pixelated;
|
|
262
|
+
}
|
|
263
|
+
`,Y=i.figcaption`
|
|
55
264
|
color: var(--ink-dim);
|
|
56
265
|
font-size: 11px;
|
|
57
266
|
text-align: center;
|
|
58
|
-
|
|
267
|
+
overflow-wrap: anywhere;
|
|
268
|
+
`,je=i.div`
|
|
269
|
+
display: grid;
|
|
270
|
+
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
|
|
271
|
+
gap: ${l.md};
|
|
272
|
+
`,ze=i.button`
|
|
59
273
|
display: flex;
|
|
274
|
+
flex-direction: column;
|
|
60
275
|
align-items: center;
|
|
61
|
-
gap:
|
|
62
|
-
|
|
63
|
-
width: 96px;
|
|
64
|
-
height: 96px;
|
|
65
|
-
border-radius: 50% 50% 45% 45%;
|
|
276
|
+
gap: ${l.sm};
|
|
277
|
+
padding: ${l.md};
|
|
66
278
|
background: var(--panel-raised);
|
|
67
|
-
border:
|
|
68
|
-
|
|
279
|
+
border: 1px solid var(--rule);
|
|
280
|
+
border-radius: 8px;
|
|
281
|
+
color: var(--ink);
|
|
282
|
+
font: inherit;
|
|
283
|
+
cursor: pointer;
|
|
284
|
+
text-align: center;
|
|
285
|
+
|
|
286
|
+
&:hover {
|
|
287
|
+
border-color: var(--rule-strong);
|
|
288
|
+
}
|
|
289
|
+
&:focus-visible {
|
|
290
|
+
outline: 2px solid var(--accent);
|
|
291
|
+
outline-offset: 2px;
|
|
292
|
+
}
|
|
293
|
+
`,Z=i.span`
|
|
294
|
+
font-family: ${de};
|
|
295
|
+
font-size: 12px;
|
|
296
|
+
color: var(--ink-dim);
|
|
297
|
+
overflow-wrap: anywhere;
|
|
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`
|
|
303
|
+
display: flex;
|
|
304
|
+
flex-direction: column;
|
|
305
|
+
gap: ${l.sm};
|
|
306
|
+
padding: ${l.md};
|
|
307
|
+
background: var(--panel-sunk);
|
|
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};
|