@omnigateway/pokemon 1.0.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -4
- package/omni-plugin.json +2 -2
- package/package.json +1 -1
- package/server/index.js +352 -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,9 @@ 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.
|
|
73
74
|
|
|
74
75
|
## Nintendo and Game Freak intellectual property
|
|
75
76
|
|
|
@@ -106,6 +107,27 @@ is deliberate: a plugin developed inside the monorepo can reach packages an
|
|
|
106
107
|
installed plugin cannot, and a build that only succeeds there proves nothing
|
|
107
108
|
about one that has to run anywhere else.
|
|
108
109
|
|
|
110
|
+
`@omnigateway/dashboard-sdk` is an **external** of the UI build, alongside React
|
|
111
|
+
and friends — the console serves one copy of each through its import map.
|
|
112
|
+
(`@omnigateway/plugin-api` is not: it is a runtime dependency of the *server*
|
|
113
|
+
half and is bundled into it, and the panel never imports it.)
|
|
114
|
+
|
|
115
|
+
The SDK is the easiest external to forget, because it is the one package here
|
|
116
|
+
that is obviously ours, and forgetting it fails silently: the SDK holds the
|
|
117
|
+
console's LIVE switch in a React context, so a bundled copy is a second context,
|
|
118
|
+
and the panel stops refreshing without an error anywhere.
|
|
119
|
+
`test/package.test.ts` checks the built bundle still imports it and carries no
|
|
120
|
+
`createContext` of its own.
|
|
121
|
+
|
|
122
|
+
## The console's LIVE switch
|
|
123
|
+
|
|
124
|
+
The panel polls, because growth arrives from requests it cannot hear about. It
|
|
125
|
+
polls on the console's cadence rather than its own: the chassis bar's LIVE
|
|
126
|
+
control pauses every screen at once, this panel included, and there is
|
|
127
|
+
deliberately no per-panel refresh setting. Paused means it stops refetching, not
|
|
128
|
+
that it stops working — an operator who pauses still sees the companion they
|
|
129
|
+
opened.
|
|
130
|
+
|
|
109
131
|
## Licence
|
|
110
132
|
|
|
111
133
|
MIT. See [LICENSE](LICENSE). The licence covers this plugin's code and not the
|
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.1.0",
|
|
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,27 @@ 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
|
+
mint: "mental-herb",
|
|
64
|
+
egg: "lucky-egg"
|
|
65
|
+
};
|
|
66
|
+
var ITEM_SPRITE_NAMES = new Map(Object.entries(ITEM_SPRITE_FILES));
|
|
49
67
|
var FRESH_EGG_BASE_PRICE = 1e9;
|
|
50
68
|
function freshEggPrice(tier) {
|
|
51
69
|
if (tier === null)
|
|
@@ -99,12 +117,14 @@ function decideGrant(input) {
|
|
|
99
117
|
var POKEAPI_ORIGIN = "https://pokeapi.co";
|
|
100
118
|
var SPRITE_ORIGIN = "https://raw.githubusercontent.com";
|
|
101
119
|
var SPRITE_DIR = "/PokeAPI/sprites/master/sprites/pokemon/versions/generation-v/black-white/animated";
|
|
120
|
+
var ITEM_SPRITE_DIR = "/PokeAPI/sprites/master/sprites/items";
|
|
102
121
|
var MAX_EVOLUTION_CHAIN_ID = 2000;
|
|
103
122
|
var INDEX_FETCH_CONCURRENCY = 8;
|
|
104
123
|
var INDEX_PATH = "species/index.json";
|
|
105
124
|
var speciesPath = (id) => `species/${id}.json`;
|
|
106
125
|
var chainPath = (chainId) => `species/chain-${chainId}.json`;
|
|
107
126
|
var spritePath = (id, shiny) => shiny ? `sprites/shiny/${id}.gif` : `sprites/${id}.gif`;
|
|
127
|
+
var itemSpritePath = (name) => `sprites/items/${name}.png`;
|
|
108
128
|
function isFetchableSpeciesId(id) {
|
|
109
129
|
return Number.isInteger(id) && hasAnimatedSprite(id);
|
|
110
130
|
}
|
|
@@ -348,6 +368,10 @@ async function loadDetail(deps, id, chains) {
|
|
|
348
368
|
function speciesDetail(deps, id) {
|
|
349
369
|
return loadDetail(deps, id, new Map);
|
|
350
370
|
}
|
|
371
|
+
function speciesDetails(deps, ids) {
|
|
372
|
+
const chains = new Map;
|
|
373
|
+
return Promise.all(ids.map((id) => loadDetail(deps, id, chains)));
|
|
374
|
+
}
|
|
351
375
|
async function cachedSpeciesName(deps, id) {
|
|
352
376
|
if (!isFetchableSpeciesId(id))
|
|
353
377
|
return null;
|
|
@@ -427,6 +451,22 @@ async function spriteBytes(deps, id, shiny) {
|
|
|
427
451
|
await writeCache(deps, path, bytes);
|
|
428
452
|
return bytes;
|
|
429
453
|
}
|
|
454
|
+
async function itemSpriteBytes(deps, item) {
|
|
455
|
+
const name = ITEM_SPRITE_NAMES.get(item);
|
|
456
|
+
if (name === undefined)
|
|
457
|
+
return null;
|
|
458
|
+
const path = itemSpritePath(name);
|
|
459
|
+
try {
|
|
460
|
+
const cached = await deps.files.read(path);
|
|
461
|
+
if (cached !== null && cached.length > 0)
|
|
462
|
+
return cached;
|
|
463
|
+
} catch {}
|
|
464
|
+
const bytes = await fetchBytes(deps, `${SPRITE_ORIGIN}${ITEM_SPRITE_DIR}/${name}.png`);
|
|
465
|
+
if (bytes === null)
|
|
466
|
+
return null;
|
|
467
|
+
await writeCache(deps, path, bytes);
|
|
468
|
+
return bytes;
|
|
469
|
+
}
|
|
430
470
|
|
|
431
471
|
// src/roll.ts
|
|
432
472
|
var NATURES = [
|
|
@@ -467,23 +507,33 @@ function mulberry32(seed) {
|
|
|
467
507
|
};
|
|
468
508
|
}
|
|
469
509
|
var COLLECTED_WEIGHT = 0.25;
|
|
510
|
+
var FORM_WEIGHT = 0.6;
|
|
470
511
|
function roll(input) {
|
|
471
512
|
const random = mulberry32(input.seed);
|
|
472
|
-
const
|
|
513
|
+
const eligibleWith = (withLure) => input.candidates.filter((candidate) => {
|
|
473
514
|
if (!hasAnimatedSprite(candidate.id))
|
|
474
515
|
return false;
|
|
475
516
|
if (candidate.id === DITTO_SPECIES_ID)
|
|
476
517
|
return false;
|
|
518
|
+
if (input.excludeFinal != null && candidate.finalId === input.excludeFinal)
|
|
519
|
+
return false;
|
|
520
|
+
if (withLure && input.collectedFinals.has(candidate.finalId))
|
|
521
|
+
return false;
|
|
477
522
|
if (input.guarantee === null)
|
|
478
523
|
return true;
|
|
479
524
|
const rarity2 = rarityFromCaptureRate(candidate.captureRate, false, false);
|
|
480
525
|
return sortRank(rarity2) >= sortRank(input.guarantee);
|
|
481
526
|
});
|
|
527
|
+
const wanted = input.onlyUncollected === true;
|
|
528
|
+
const lured = wanted ? eligibleWith(true) : [];
|
|
529
|
+
const usedLure = wanted && lured.length > 0;
|
|
530
|
+
const eligible = usedLure ? lured : eligibleWith(false);
|
|
482
531
|
if (eligible.length === 0)
|
|
483
532
|
return null;
|
|
484
533
|
const weights = eligible.map((candidate) => {
|
|
485
534
|
const base = Math.max(1, candidate.captureRate);
|
|
486
|
-
|
|
535
|
+
const collected = input.collectedFinals.has(candidate.finalId) ? base * COLLECTED_WEIGHT : base;
|
|
536
|
+
return input.preferLongLines === true ? collected * (1 + FORM_WEIGHT * (Math.max(1, candidate.forms) - 1)) : collected;
|
|
487
537
|
});
|
|
488
538
|
const total = weights.reduce((a, b) => a + b, 0);
|
|
489
539
|
let target = random() * total;
|
|
@@ -502,10 +552,16 @@ function roll(input) {
|
|
|
502
552
|
const rarity = rarityFromCaptureRate(chosen.captureRate, false, false);
|
|
503
553
|
const dittoEligible = rarity === "common" && chosen.forms >= 2;
|
|
504
554
|
const ditto = dittoEligible && random() < 1 / ODDS.dittoDisguise;
|
|
505
|
-
return { speciesId: chosen.id, isShiny, nature, ditto };
|
|
555
|
+
return { speciesId: chosen.id, isShiny, nature, ditto, usedLure };
|
|
506
556
|
}
|
|
507
557
|
|
|
508
558
|
// src/state.ts
|
|
559
|
+
function emptyInventory() {
|
|
560
|
+
const inventory = {};
|
|
561
|
+
for (const kind of ITEM_KINDS)
|
|
562
|
+
inventory[kind] = 0;
|
|
563
|
+
return inventory;
|
|
564
|
+
}
|
|
509
565
|
function freshState() {
|
|
510
566
|
return {
|
|
511
567
|
consumedTotal: 0,
|
|
@@ -513,7 +569,11 @@ function freshState() {
|
|
|
513
569
|
eggUsage: 0,
|
|
514
570
|
eggTier: null,
|
|
515
571
|
pendingHatch: null,
|
|
516
|
-
|
|
572
|
+
pendingReveal: null,
|
|
573
|
+
lure: false,
|
|
574
|
+
incense: false,
|
|
575
|
+
repel: null,
|
|
576
|
+
inventory: emptyInventory()
|
|
517
577
|
};
|
|
518
578
|
}
|
|
519
579
|
function isRecord(value) {
|
|
@@ -534,7 +594,7 @@ function parseState(raw) {
|
|
|
534
594
|
}
|
|
535
595
|
if (!isRecord(parsed))
|
|
536
596
|
return null;
|
|
537
|
-
const inventory =
|
|
597
|
+
const inventory = emptyInventory();
|
|
538
598
|
const storedInventory = parsed.inventory;
|
|
539
599
|
if (isRecord(storedInventory)) {
|
|
540
600
|
for (const kind of ITEM_KINDS) {
|
|
@@ -562,7 +622,10 @@ function parseState(raw) {
|
|
|
562
622
|
isShiny: storedActive.isShiny === true,
|
|
563
623
|
nature: nature ?? "hardy",
|
|
564
624
|
dittoDisguise: typeof storedActive.dittoDisguise === "number" ? storedActive.dittoDisguise : null,
|
|
565
|
-
dittoRevealed: storedActive.dittoRevealed === true
|
|
625
|
+
dittoRevealed: storedActive.dittoRevealed === true,
|
|
626
|
+
everstone: storedActive.everstone === true,
|
|
627
|
+
soothe: storedActive.soothe === true,
|
|
628
|
+
soothedRaw: Math.max(0, asInt(storedActive.soothedRaw, 0))
|
|
566
629
|
};
|
|
567
630
|
}
|
|
568
631
|
const storedPending = parsed.pendingHatch;
|
|
@@ -582,13 +645,28 @@ function parseState(raw) {
|
|
|
582
645
|
};
|
|
583
646
|
}
|
|
584
647
|
}
|
|
648
|
+
const storedReveal = parsed.pendingReveal;
|
|
649
|
+
let pendingReveal = null;
|
|
650
|
+
if (isRecord(storedReveal)) {
|
|
651
|
+
const rarity = asRarity(storedReveal.rarity);
|
|
652
|
+
const path = Array.isArray(storedReveal.path) ? storedReveal.path.filter((id) => typeof id === "number" && id > 0) : [];
|
|
653
|
+
if (rarity !== null && path.length > 0)
|
|
654
|
+
pendingReveal = { path, rarity };
|
|
655
|
+
}
|
|
656
|
+
const storedConsumed = parsed.consumedTotal;
|
|
657
|
+
if (typeof storedConsumed !== "number" || !Number.isFinite(storedConsumed))
|
|
658
|
+
return null;
|
|
585
659
|
const eggTier = asRarity(parsed.eggTier);
|
|
586
660
|
return {
|
|
587
|
-
consumedTotal: Math.max(0,
|
|
661
|
+
consumedTotal: Math.max(0, Math.trunc(storedConsumed)),
|
|
588
662
|
active,
|
|
589
663
|
eggUsage: Math.max(0, asInt(parsed.eggUsage, 0)),
|
|
590
664
|
eggTier: eggTier === null || eggTier === "legendary" ? null : eggTier,
|
|
591
665
|
pendingHatch,
|
|
666
|
+
pendingReveal,
|
|
667
|
+
lure: parsed.lure === true,
|
|
668
|
+
incense: parsed.incense === true,
|
|
669
|
+
repel: typeof parsed.repel === "number" && Number.isInteger(parsed.repel) && parsed.repel > 0 ? parsed.repel : null,
|
|
592
670
|
inventory
|
|
593
671
|
};
|
|
594
672
|
}
|
|
@@ -606,7 +684,19 @@ function advance(state, tokensTotal) {
|
|
|
606
684
|
const events = [];
|
|
607
685
|
let next = { ...state, consumedTotal: Math.trunc(tokensTotal) };
|
|
608
686
|
if (gained > 0) {
|
|
609
|
-
|
|
687
|
+
const active = next.active;
|
|
688
|
+
if (active === null) {
|
|
689
|
+
next = { ...next, eggUsage: next.eggUsage + gained };
|
|
690
|
+
} else if (!active.soothe) {
|
|
691
|
+
next = { ...next, active: { ...active, usedAtStage: active.usedAtStage + gained } };
|
|
692
|
+
} else {
|
|
693
|
+
const raw = active.soothedRaw + gained;
|
|
694
|
+
const owed = Math.floor(raw * SOOTHE_BONUS) - Math.floor(active.soothedRaw * SOOTHE_BONUS);
|
|
695
|
+
next = {
|
|
696
|
+
...next,
|
|
697
|
+
active: { ...active, soothedRaw: raw, usedAtStage: active.usedAtStage + gained + owed }
|
|
698
|
+
};
|
|
699
|
+
}
|
|
610
700
|
}
|
|
611
701
|
for (let step = 0;step < MAX_TRANSITIONS_PER_ADVANCE; step++) {
|
|
612
702
|
if (next.active === null) {
|
|
@@ -624,7 +714,10 @@ function advance(state, tokensTotal) {
|
|
|
624
714
|
isShiny: hatch.isShiny,
|
|
625
715
|
nature: hatch.nature,
|
|
626
716
|
dittoDisguise: hatch.ditto ? hatch.speciesId : null,
|
|
627
|
-
dittoRevealed: false
|
|
717
|
+
dittoRevealed: false,
|
|
718
|
+
everstone: false,
|
|
719
|
+
soothe: false,
|
|
720
|
+
soothedRaw: 0
|
|
628
721
|
};
|
|
629
722
|
events.push({
|
|
630
723
|
kind: "hatched",
|
|
@@ -636,10 +729,36 @@ function advance(state, tokensTotal) {
|
|
|
636
729
|
continue;
|
|
637
730
|
}
|
|
638
731
|
const mon = next.active;
|
|
732
|
+
if (mon.everstone)
|
|
733
|
+
break;
|
|
639
734
|
const needed = phaseThreshold(mon.rarity, mon.plannedPath.length, mon.stageIndex);
|
|
640
735
|
if (mon.usedAtStage < needed)
|
|
641
736
|
break;
|
|
642
737
|
const excess = mon.usedAtStage - needed;
|
|
738
|
+
if (mon.dittoDisguise !== null && !mon.dittoRevealed) {
|
|
739
|
+
if (next.pendingReveal === null)
|
|
740
|
+
break;
|
|
741
|
+
const reveal = next.pendingReveal;
|
|
742
|
+
events.push({
|
|
743
|
+
kind: "revealed",
|
|
744
|
+
disguisedAs: mon.plannedPath[mon.stageIndex] ?? mon.baseId,
|
|
745
|
+
speciesId: reveal.path[0]
|
|
746
|
+
});
|
|
747
|
+
next = {
|
|
748
|
+
...next,
|
|
749
|
+
active: {
|
|
750
|
+
...mon,
|
|
751
|
+
baseId: reveal.path[0],
|
|
752
|
+
plannedPath: reveal.path,
|
|
753
|
+
stageIndex: 0,
|
|
754
|
+
usedAtStage: excess,
|
|
755
|
+
rarity: reveal.rarity,
|
|
756
|
+
dittoRevealed: true
|
|
757
|
+
},
|
|
758
|
+
pendingReveal: null
|
|
759
|
+
};
|
|
760
|
+
continue;
|
|
761
|
+
}
|
|
643
762
|
if (mon.stageIndex < mon.plannedPath.length - 1) {
|
|
644
763
|
events.push({
|
|
645
764
|
kind: "evolved",
|
|
@@ -832,10 +951,16 @@ function consume(storage, apiKeyId, item, applyToState, now) {
|
|
|
832
951
|
return { ok: false, reason: "unreadable" };
|
|
833
952
|
if ((row.state.inventory[item] ?? 0) <= 0)
|
|
834
953
|
return { ok: false, reason: "none-held" };
|
|
835
|
-
const
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
954
|
+
const outcome = applyToState(row.state);
|
|
955
|
+
if ("refused" in outcome)
|
|
956
|
+
return { ok: false, reason: outcome.refused };
|
|
957
|
+
const nextState = {
|
|
958
|
+
...outcome.applied,
|
|
959
|
+
inventory: {
|
|
960
|
+
...outcome.applied.inventory,
|
|
961
|
+
[item]: (outcome.applied.inventory[item] ?? 0) - 1
|
|
962
|
+
}
|
|
963
|
+
};
|
|
839
964
|
storage.run("UPDATE {{companion}} SET state = ?, updated_at = ? WHERE api_key_id = ?", [
|
|
840
965
|
serialiseState(nextState),
|
|
841
966
|
now,
|
|
@@ -853,7 +978,10 @@ function purchase(storage, apiKeyId, entry, applyToState, now) {
|
|
|
853
978
|
return { ok: false, reason: "unreadable" };
|
|
854
979
|
if (wallet(row) < price)
|
|
855
980
|
return { ok: false, reason: "insufficient" };
|
|
856
|
-
const
|
|
981
|
+
const outcome = applyToState(row.state);
|
|
982
|
+
if ("refused" in outcome)
|
|
983
|
+
return { ok: false, reason: outcome.refused };
|
|
984
|
+
const nextState = outcome.applied;
|
|
857
985
|
storage.run("UPDATE {{companion}} SET state = ?, tokens_spent = tokens_spent + ?, updated_at = ? WHERE api_key_id = ?", [serialiseState(nextState), price, now, apiKeyId]);
|
|
858
986
|
return {
|
|
859
987
|
ok: true,
|
|
@@ -864,6 +992,9 @@ function purchase(storage, apiKeyId, entry, applyToState, now) {
|
|
|
864
992
|
|
|
865
993
|
// src/server.ts
|
|
866
994
|
var MAX_MULTIPLIER = 1000;
|
|
995
|
+
var WARM_PER_POLL = 8;
|
|
996
|
+
var WARM_BACKOFF_MS = 60000;
|
|
997
|
+
var WARM_BACKOFF_MAX_MS = 60 * 60000;
|
|
867
998
|
function multiplierFrom(config) {
|
|
868
999
|
const raw = config.multiplier;
|
|
869
1000
|
if (typeof raw !== "number" || !Number.isFinite(raw) || raw <= 0)
|
|
@@ -890,7 +1021,7 @@ var server_default = definePlugin({
|
|
|
890
1021
|
const existing = inFlight.get(apiKeyId);
|
|
891
1022
|
if (existing !== undefined)
|
|
892
1023
|
return existing;
|
|
893
|
-
const started = prefetchHatch(apiKeyId, state).finally(() => inFlight.delete(apiKeyId));
|
|
1024
|
+
const started = prefetchHatch(apiKeyId, state).then(() => prefetchReveal(apiKeyId, state)).finally(() => inFlight.delete(apiKeyId));
|
|
894
1025
|
inFlight.set(apiKeyId, started);
|
|
895
1026
|
return started;
|
|
896
1027
|
};
|
|
@@ -906,6 +1037,41 @@ var server_default = definePlugin({
|
|
|
906
1037
|
names.set(speciesId, found);
|
|
907
1038
|
return found;
|
|
908
1039
|
};
|
|
1040
|
+
const warming = new Set;
|
|
1041
|
+
const cold = new Map;
|
|
1042
|
+
const warmNames = (ids) => {
|
|
1043
|
+
if (net === undefined || files === undefined)
|
|
1044
|
+
return;
|
|
1045
|
+
const now = ctx.now();
|
|
1046
|
+
const batch = [];
|
|
1047
|
+
for (const id of ids) {
|
|
1048
|
+
if (batch.length >= WARM_PER_POLL)
|
|
1049
|
+
break;
|
|
1050
|
+
if (names.has(id) || warming.has(id))
|
|
1051
|
+
continue;
|
|
1052
|
+
const chilled = cold.get(id);
|
|
1053
|
+
if (chilled !== undefined && now < chilled.until)
|
|
1054
|
+
continue;
|
|
1055
|
+
batch.push(id);
|
|
1056
|
+
warming.add(id);
|
|
1057
|
+
}
|
|
1058
|
+
if (batch.length === 0)
|
|
1059
|
+
return;
|
|
1060
|
+
speciesDetails({ net, files }, batch).then((details) => {
|
|
1061
|
+
batch.forEach((id, index) => {
|
|
1062
|
+
if (details[index]?.names.en !== undefined) {
|
|
1063
|
+
cold.delete(id);
|
|
1064
|
+
return;
|
|
1065
|
+
}
|
|
1066
|
+
const failures = (cold.get(id)?.failures ?? 0) + 1;
|
|
1067
|
+
const wait = Math.min(WARM_BACKOFF_MS * 2 ** (failures - 1), WARM_BACKOFF_MAX_MS);
|
|
1068
|
+
cold.set(id, { until: now + wait, failures });
|
|
1069
|
+
});
|
|
1070
|
+
}).catch(() => {}).finally(() => {
|
|
1071
|
+
for (const id of batch)
|
|
1072
|
+
warming.delete(id);
|
|
1073
|
+
});
|
|
1074
|
+
};
|
|
909
1075
|
const settleAndRecord = (apiKeyId) => {
|
|
910
1076
|
const result = settle(storage, apiKeyId, ctx.now());
|
|
911
1077
|
if (result === null)
|
|
@@ -925,6 +1091,37 @@ var server_default = definePlugin({
|
|
|
925
1091
|
ctx.logger.info("companion graduated", { event: "companion.graduated", count: 1 });
|
|
926
1092
|
}
|
|
927
1093
|
};
|
|
1094
|
+
const prefetchReveal = async (apiKeyId, state) => {
|
|
1095
|
+
const mon = state.active;
|
|
1096
|
+
if (mon === null || mon.dittoDisguise === null || mon.dittoRevealed)
|
|
1097
|
+
return;
|
|
1098
|
+
if (state.pendingReveal !== null)
|
|
1099
|
+
return;
|
|
1100
|
+
if (net === undefined || files === undefined)
|
|
1101
|
+
return;
|
|
1102
|
+
const detail = await speciesDetail({ net, files }, DITTO_SPECIES_ID);
|
|
1103
|
+
if (detail === null)
|
|
1104
|
+
return;
|
|
1105
|
+
const current = readCompanion(storage, apiKeyId);
|
|
1106
|
+
if (current?.state == null)
|
|
1107
|
+
return;
|
|
1108
|
+
const latest = current.state.active;
|
|
1109
|
+
if (latest === null || latest.dittoDisguise === null || latest.dittoRevealed)
|
|
1110
|
+
return;
|
|
1111
|
+
if (current.state.pendingReveal !== null)
|
|
1112
|
+
return;
|
|
1113
|
+
storage.run("UPDATE {{companion}} SET state = ?, updated_at = ? WHERE api_key_id = ?", [
|
|
1114
|
+
JSON.stringify({
|
|
1115
|
+
...current.state,
|
|
1116
|
+
pendingReveal: {
|
|
1117
|
+
path: detail.chain,
|
|
1118
|
+
rarity: rarityFromCaptureRate(detail.captureRate, detail.isLegendary, detail.isMythical)
|
|
1119
|
+
}
|
|
1120
|
+
}),
|
|
1121
|
+
ctx.now(),
|
|
1122
|
+
apiKeyId
|
|
1123
|
+
]);
|
|
1124
|
+
};
|
|
928
1125
|
const prefetchHatch = async (apiKeyId, state) => {
|
|
929
1126
|
if (state.active !== null || state.pendingHatch !== null)
|
|
930
1127
|
return;
|
|
@@ -939,7 +1136,10 @@ var server_default = definePlugin({
|
|
|
939
1136
|
seed: hashSeed(`${apiKeyId}:${state.consumedTotal}`),
|
|
940
1137
|
guarantee: state.eggTier,
|
|
941
1138
|
hasShinyCharm: hasShinyCharm(state),
|
|
942
|
-
collectedFinals: collected
|
|
1139
|
+
collectedFinals: collected,
|
|
1140
|
+
onlyUncollected: state.lure,
|
|
1141
|
+
preferLongLines: state.incense,
|
|
1142
|
+
excludeFinal: state.repel
|
|
943
1143
|
});
|
|
944
1144
|
if (rolled === null)
|
|
945
1145
|
return;
|
|
@@ -951,6 +1151,10 @@ var server_default = definePlugin({
|
|
|
951
1151
|
const current = readCompanion(storage, apiKeyId);
|
|
952
1152
|
if (current?.state == null || current.state.pendingHatch !== null)
|
|
953
1153
|
return;
|
|
1154
|
+
if (current.state.active !== null)
|
|
1155
|
+
return;
|
|
1156
|
+
if (paidRollInputs(current.state) !== paidRollInputs(state))
|
|
1157
|
+
return;
|
|
954
1158
|
storage.run("UPDATE {{companion}} SET state = ?, updated_at = ? WHERE api_key_id = ?", [
|
|
955
1159
|
JSON.stringify({
|
|
956
1160
|
...current.state,
|
|
@@ -961,7 +1165,10 @@ var server_default = definePlugin({
|
|
|
961
1165
|
isShiny: rolled.isShiny,
|
|
962
1166
|
nature: rolled.nature,
|
|
963
1167
|
ditto: rolled.ditto
|
|
964
|
-
}
|
|
1168
|
+
},
|
|
1169
|
+
lure: state.lure && !rolled.usedLure,
|
|
1170
|
+
incense: false,
|
|
1171
|
+
repel: null
|
|
965
1172
|
}),
|
|
966
1173
|
ctx.now(),
|
|
967
1174
|
apiKeyId
|
|
@@ -1046,14 +1253,21 @@ var server_default = definePlugin({
|
|
|
1046
1253
|
prefetchOnce(apiKeyId, row.state).catch(() => {});
|
|
1047
1254
|
const active = row.state?.active ?? null;
|
|
1048
1255
|
const dex = readDex(storage, apiKeyId);
|
|
1256
|
+
const stageId = active === null ? null : active.plannedPath[active.stageIndex] ?? null;
|
|
1257
|
+
const stageName = await nameOf(stageId);
|
|
1258
|
+
const named = await Promise.all(dex.map(async (entry) => ({ ...entry, name: await nameOf(entry.finalId) })));
|
|
1259
|
+
warmNames([
|
|
1260
|
+
...stageId !== null && stageName === null ? [stageId] : [],
|
|
1261
|
+
...named.filter((entry) => entry.name === null).map((entry) => entry.finalId)
|
|
1262
|
+
]);
|
|
1049
1263
|
return {
|
|
1050
1264
|
json: {
|
|
1051
1265
|
state: row.state,
|
|
1052
1266
|
tokensTotal: row.tokensTotal,
|
|
1053
1267
|
wallet: wallet(row),
|
|
1054
1268
|
lastCreditAt: row.lastCreditAt,
|
|
1055
|
-
name:
|
|
1056
|
-
dex:
|
|
1269
|
+
name: stageName,
|
|
1270
|
+
dex: named,
|
|
1057
1271
|
shop: shopCatalogue(),
|
|
1058
1272
|
nextThreshold: active === null ? EGG_HATCH_THRESHOLD : phaseThreshold(active.rarity, active.plannedPath.length, active.stageIndex),
|
|
1059
1273
|
progress: active === null ? row.state?.eggUsage ?? 0 : active.usedAtStage
|
|
@@ -1083,6 +1297,27 @@ var server_default = definePlugin({
|
|
|
1083
1297
|
};
|
|
1084
1298
|
}
|
|
1085
1299
|
},
|
|
1300
|
+
{
|
|
1301
|
+
method: "GET",
|
|
1302
|
+
path: "/item-sprite/:item",
|
|
1303
|
+
handler: async (request) => {
|
|
1304
|
+
const item = request.params.item ?? "";
|
|
1305
|
+
if (!ITEM_SPRITE_NAMES.has(item)) {
|
|
1306
|
+
return { status: 404, json: { error: "no item icon" } };
|
|
1307
|
+
}
|
|
1308
|
+
if (net === undefined || files === undefined) {
|
|
1309
|
+
return { status: 503, json: { error: "sprites need the net and files capabilities" } };
|
|
1310
|
+
}
|
|
1311
|
+
const bytes = await itemSpriteBytes({ net, files }, item);
|
|
1312
|
+
if (bytes === null)
|
|
1313
|
+
return { status: 404, json: { error: "no item icon" } };
|
|
1314
|
+
return {
|
|
1315
|
+
bytes,
|
|
1316
|
+
contentType: "image/png",
|
|
1317
|
+
cacheControl: "public, max-age=31536000, immutable"
|
|
1318
|
+
};
|
|
1319
|
+
}
|
|
1320
|
+
},
|
|
1086
1321
|
{
|
|
1087
1322
|
method: "POST",
|
|
1088
1323
|
path: "/keys/:id/use",
|
|
@@ -1098,6 +1333,30 @@ var server_default = definePlugin({
|
|
|
1098
1333
|
return { json: { ok: true } };
|
|
1099
1334
|
}
|
|
1100
1335
|
},
|
|
1336
|
+
{
|
|
1337
|
+
method: "POST",
|
|
1338
|
+
path: "/keys/:id/unpin",
|
|
1339
|
+
handler: (request) => {
|
|
1340
|
+
const apiKeyId = request.params.id ?? "";
|
|
1341
|
+
const row = readCompanion(storage, apiKeyId);
|
|
1342
|
+
if (row === null)
|
|
1343
|
+
return { status: 404, json: { error: "no companion for that key" } };
|
|
1344
|
+
if (row.state === null)
|
|
1345
|
+
return { status: 409, json: { error: "unreadable" } };
|
|
1346
|
+
const active = row.state.active;
|
|
1347
|
+
if (active === null)
|
|
1348
|
+
return { status: 409, json: { error: "no-companion" } };
|
|
1349
|
+
if (!active.everstone)
|
|
1350
|
+
return { status: 409, json: { error: "nothing-new" } };
|
|
1351
|
+
storage.run("UPDATE {{companion}} SET state = ?, updated_at = ? WHERE api_key_id = ?", [
|
|
1352
|
+
JSON.stringify({ ...row.state, active: { ...active, everstone: false } }),
|
|
1353
|
+
ctx.now(),
|
|
1354
|
+
apiKeyId
|
|
1355
|
+
]);
|
|
1356
|
+
settleAndRecord(apiKeyId);
|
|
1357
|
+
return { json: { ok: true } };
|
|
1358
|
+
}
|
|
1359
|
+
},
|
|
1101
1360
|
{
|
|
1102
1361
|
method: "POST",
|
|
1103
1362
|
path: "/keys/:id/purchase",
|
|
@@ -1116,6 +1375,15 @@ var server_default = definePlugin({
|
|
|
1116
1375
|
return { routes };
|
|
1117
1376
|
}
|
|
1118
1377
|
});
|
|
1378
|
+
function paidRollInputs(state) {
|
|
1379
|
+
return JSON.stringify([
|
|
1380
|
+
state.eggTier,
|
|
1381
|
+
hasShinyCharm(state),
|
|
1382
|
+
state.lure,
|
|
1383
|
+
state.incense,
|
|
1384
|
+
state.repel
|
|
1385
|
+
]);
|
|
1386
|
+
}
|
|
1119
1387
|
function hashSeed(input) {
|
|
1120
1388
|
let hash = 2166136261;
|
|
1121
1389
|
for (let i = 0;i < input.length; i++) {
|
|
@@ -1136,7 +1404,7 @@ function shopCatalogue() {
|
|
|
1136
1404
|
price: freshEggPrice("uncommon")
|
|
1137
1405
|
},
|
|
1138
1406
|
{ entry: { kind: "egg", tier: "rare" }, price: freshEggPrice("rare") }
|
|
1139
|
-
];
|
|
1407
|
+
].sort((a, b) => a.price - b.price);
|
|
1140
1408
|
}
|
|
1141
1409
|
function parseShopEntry(body) {
|
|
1142
1410
|
if (typeof body !== "object" || body === null)
|
|
@@ -1158,32 +1426,86 @@ function parseShopEntry(body) {
|
|
|
1158
1426
|
}
|
|
1159
1427
|
function applyPurchase(state, entry) {
|
|
1160
1428
|
if (entry.kind === "egg") {
|
|
1161
|
-
|
|
1429
|
+
if (state.active === null)
|
|
1430
|
+
return { refused: "no-companion" };
|
|
1431
|
+
return {
|
|
1432
|
+
applied: { ...state, active: null, eggUsage: 0, eggTier: entry.tier, pendingHatch: null }
|
|
1433
|
+
};
|
|
1434
|
+
}
|
|
1435
|
+
if (!HELD_ITEMS.includes(entry.item) && (state.inventory[entry.item] ?? 0) > 0) {
|
|
1436
|
+
return { refused: "already-owned" };
|
|
1162
1437
|
}
|
|
1163
1438
|
return {
|
|
1164
|
-
|
|
1165
|
-
|
|
1439
|
+
applied: {
|
|
1440
|
+
...state,
|
|
1441
|
+
inventory: { ...state.inventory, [entry.item]: (state.inventory[entry.item] ?? 0) + 1 }
|
|
1442
|
+
}
|
|
1166
1443
|
};
|
|
1167
1444
|
}
|
|
1445
|
+
var HELD_ITEMS = [
|
|
1446
|
+
"rareCandy",
|
|
1447
|
+
"mint",
|
|
1448
|
+
"everstone",
|
|
1449
|
+
"lure",
|
|
1450
|
+
"sootheBell",
|
|
1451
|
+
"incense",
|
|
1452
|
+
"repel"
|
|
1453
|
+
];
|
|
1168
1454
|
function parseHeldItem(body) {
|
|
1169
1455
|
if (typeof body !== "object" || body === null)
|
|
1170
1456
|
return null;
|
|
1171
1457
|
const item = body.item;
|
|
1172
|
-
return item
|
|
1458
|
+
return HELD_ITEMS.includes(item) ? item : null;
|
|
1173
1459
|
}
|
|
1174
1460
|
function useItem(state, item) {
|
|
1461
|
+
if (item === "everstone" || item === "sootheBell" || item === "repel") {
|
|
1462
|
+
const active = state.active;
|
|
1463
|
+
if (active === null)
|
|
1464
|
+
return { refused: "no-companion" };
|
|
1465
|
+
if (item === "everstone") {
|
|
1466
|
+
if (active.everstone)
|
|
1467
|
+
return { refused: "nothing-new" };
|
|
1468
|
+
return { applied: { ...state, active: { ...active, everstone: true } } };
|
|
1469
|
+
}
|
|
1470
|
+
if (item === "sootheBell") {
|
|
1471
|
+
if (active.soothe)
|
|
1472
|
+
return { refused: "nothing-new" };
|
|
1473
|
+
return { applied: { ...state, active: { ...active, soothe: true } } };
|
|
1474
|
+
}
|
|
1475
|
+
const finalId = active.plannedPath[active.plannedPath.length - 1];
|
|
1476
|
+
if (finalId === undefined)
|
|
1477
|
+
return { refused: "no-companion" };
|
|
1478
|
+
if (finalId === DITTO_SPECIES_ID)
|
|
1479
|
+
return { refused: "nothing-new" };
|
|
1480
|
+
if (state.repel !== null)
|
|
1481
|
+
return { refused: "already-armed" };
|
|
1482
|
+
return { applied: { ...state, repel: finalId } };
|
|
1483
|
+
}
|
|
1484
|
+
if (item === "lure" || item === "incense") {
|
|
1485
|
+
if (item === "lure") {
|
|
1486
|
+
if (state.lure)
|
|
1487
|
+
return { refused: "nothing-new" };
|
|
1488
|
+
return { applied: { ...state, lure: true } };
|
|
1489
|
+
}
|
|
1490
|
+
if (state.incense)
|
|
1491
|
+
return { refused: "nothing-new" };
|
|
1492
|
+
return { applied: { ...state, incense: true } };
|
|
1493
|
+
}
|
|
1175
1494
|
if (item === "mint") {
|
|
1176
1495
|
if (state.active === null)
|
|
1177
|
-
return
|
|
1496
|
+
return { refused: "no-companion" };
|
|
1178
1497
|
const index = NATURES.indexOf(state.active.nature);
|
|
1179
1498
|
const nature = NATURES[(index + 1) % NATURES.length];
|
|
1180
|
-
return { ...state, active: { ...state.active, nature } };
|
|
1499
|
+
return { applied: { ...state, active: { ...state.active, nature } } };
|
|
1181
1500
|
}
|
|
1182
|
-
return
|
|
1183
|
-
...state,
|
|
1184
|
-
|
|
1501
|
+
return {
|
|
1502
|
+
applied: state.active === null ? { ...state, eggUsage: state.eggUsage + RARE_CANDY_XP } : {
|
|
1503
|
+
...state,
|
|
1504
|
+
active: { ...state.active, usedAtStage: state.active.usedAtStage + RARE_CANDY_XP }
|
|
1505
|
+
}
|
|
1185
1506
|
};
|
|
1186
1507
|
}
|
|
1187
1508
|
export {
|
|
1509
|
+
HELD_ITEMS,
|
|
1188
1510
|
server_default as default
|
|
1189
1511
|
};
|
package/ui/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
import{definePluginUI as It,useLive as $t}from"@omnigateway/dashboard-sdk";import{useMutation as fe,useQuery as at,useQueryClient as wt}from"@tanstack/react-query";import{useState as st}from"react";var dt={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 L(e){return ye.get(e)??{blurb:"",emoji:"❔",consumable:!0}}var ye=new Map(Object.entries(dt)),Dt=[...ye].filter(([,e])=>e.consumable).map(([e])=>e),ve="\uD83E\uDD5A";function ke(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 R(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 T(e,t){return e??`#${t}`}function E(e,t,o){return`${e??`Species ${t}`}${o?", shiny":""}`}function N(e){return e.replace(/([A-Z])/g," $1").toLowerCase()}function $e(e){if(e.kind==="item")return N(e.item);return e.tier===null?"fresh egg":`fresh egg (${e.tier}+)`}var we=[null,"common","uncommon","rare","legendary"],Ce=60000,Se=60*Ce;function M(e,t,o){if(!e)return"egg";if(t===null)return"sleep";let i=o-t;if(i<5*Ce)return"working";if(i<Se)return"idle";if(i<8*Se)return"tired";return"sleep"}import{useState as ut}from"react";import r 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",O=r.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
|
+
`,ue=r.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
|
+
`,Re=r(ue)`
|
|
25
|
+
&::after {
|
|
26
|
+
content: none;
|
|
27
|
+
}
|
|
28
|
+
`,Te=r.span`
|
|
29
|
+
flex: 1;
|
|
30
|
+
height: 1px;
|
|
31
|
+
background: var(--rule);
|
|
32
|
+
`,De=r.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
|
+
`,Ee=r.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
|
+
`,Pe=r.span`
|
|
66
|
+
color: var(--ink-faint);
|
|
67
|
+
letter-spacing: 0.08em;
|
|
68
|
+
white-space: nowrap;
|
|
69
|
+
`,I=r.div`
|
|
25
70
|
display: flex;
|
|
26
71
|
gap: ${l.md};
|
|
27
72
|
align-items: center;
|
|
28
73
|
flex-wrap: wrap;
|
|
29
|
-
`,
|
|
74
|
+
`,Ae=r(I)`
|
|
75
|
+
h2 {
|
|
76
|
+
margin: 0;
|
|
77
|
+
}
|
|
78
|
+
`,x=r.span`
|
|
79
|
+
color: var(--ink-dim);
|
|
80
|
+
`,_=r(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
|
+
`,Le=r.p`
|
|
30
92
|
color: var(--ink-dim);
|
|
31
|
-
|
|
93
|
+
margin: ${l.xs} 0 ${l.lg};
|
|
94
|
+
`,ce=r.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=r.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
|
+
`,Ne=r(I)`
|
|
113
|
+
margin: ${l.xs} 0 ${l.sm};
|
|
114
|
+
`,V=r(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=r.img`
|
|
119
|
+
width: ${B};
|
|
120
|
+
height: ${B};
|
|
56
121
|
image-rendering: pixelated;
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
width: 96px;
|
|
62
|
-
height: 96px;
|
|
122
|
+
`,q=r.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=r.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
|
+
`,k=r.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
|
+
`,K=r.span`
|
|
162
|
+
font-family: ${de};
|
|
99
163
|
font-variant-numeric: tabular-nums;
|
|
100
|
-
`,
|
|
164
|
+
`,Ke=r.div`
|
|
101
165
|
display: flex;
|
|
102
166
|
gap: 3px;
|
|
103
167
|
min-width: 240px;
|
|
104
168
|
max-width: 340px;
|
|
105
|
-
`,
|
|
169
|
+
`,Fe=r.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=r.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
|
+
`,Me=r.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=r.div`
|
|
129
193
|
display: flex;
|
|
130
194
|
flex-direction: column;
|
|
131
195
|
gap: 2px;
|
|
132
|
-
`,
|
|
196
|
+
`,Q=r.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
|
+
`,Y=r.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
|
+
`,Oe=r.div`
|
|
144
208
|
display: grid;
|
|
145
209
|
grid-template-columns: repeat(auto-fill, minmax(84px, 1fr));
|
|
146
210
|
gap: ${l.md};
|
|
147
|
-
`,
|
|
211
|
+
`,_e=r.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
|
+
`,Ge=r.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
|
+
`,He=r.div`
|
|
241
|
+
display: flex;
|
|
242
|
+
flex-direction: column;
|
|
243
|
+
align-items: flex-start;
|
|
244
|
+
gap: ${l.sm};
|
|
245
|
+
min-width: 0;
|
|
246
|
+
`,Ue=r.div`
|
|
247
|
+
display: flex;
|
|
248
|
+
align-items: center;
|
|
249
|
+
gap: ${l.sm};
|
|
250
|
+
flex-wrap: wrap;
|
|
251
|
+
`,je=r.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
|
+
`,Z=r.figcaption`
|
|
154
264
|
color: var(--ink-dim);
|
|
155
265
|
font-size: 11px;
|
|
156
266
|
text-align: center;
|
|
157
267
|
overflow-wrap: anywhere;
|
|
158
|
-
`,
|
|
268
|
+
`,Ve=r.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=r.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
|
+
`,W=r.span`
|
|
294
|
+
font-family: ${de};
|
|
185
295
|
font-size: 12px;
|
|
186
296
|
color: var(--ink-dim);
|
|
187
297
|
overflow-wrap: anywhere;
|
|
188
|
-
`,
|
|
298
|
+
`,X=r.div`
|
|
299
|
+
display: grid;
|
|
300
|
+
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
|
|
301
|
+
gap: ${l.md};
|
|
302
|
+
`,ee=r.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=r.div`
|
|
311
|
+
display: flex;
|
|
312
|
+
align-items: center;
|
|
313
|
+
gap: ${l.sm};
|
|
314
|
+
`,ne=r.span`
|
|
315
|
+
flex: 1;
|
|
316
|
+
overflow-wrap: anywhere;
|
|
317
|
+
`,oe=r.p`
|
|
318
|
+
margin: 0;
|
|
319
|
+
color: var(--ink-dim);
|
|
320
|
+
font-size: 12px;
|
|
321
|
+
line-height: 1.45;
|
|
322
|
+
`,re=r.div`
|
|
323
|
+
display: flex;
|
|
324
|
+
align-items: center;
|
|
325
|
+
justify-content: flex-end;
|
|
326
|
+
gap: ${l.sm};
|
|
327
|
+
margin-top: auto;
|
|
328
|
+
`,qe=r.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=r.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[i,d]=ut(!1);return Qe(qe,{"aria-hidden":"true",children:i?t:Qe(Je,{alt:"",onError:()=>d(!0),src:Ie(o,e)})})}import{jsx as P,jsxs as ae}from"react/jsx-runtime";function Ye({inventory:e,onUse:t,pending:o,pluginId:i}){let d=Object.entries(e).filter(([,p])=>p>0);if(d.length===0)return P(x,{children:"Nothing in the bag."});return P(X,{children:d.map(([p,n])=>{let a=L(p);return ae(ee,{children:[ae(te,{children:[P(ie,{emoji:a.emoji,item:p,pluginId:i}),P(ne,{children:N(p)}),ae(K,{children:["×",n]})]}),P(oe,{children:a.blurb}),P(re,{children:a.consumable?ae(k,{disabled:o,onClick:()=>t(p),type:"button",children:["Use ",N(p)]}):P(x,{children:"held"})})]},p)})})}import{Fragment as ct,useState as Ze}from"react";import{jsx as y,jsxs as w,Fragment as gt}from"react/jsx-runtime";function We({entries:e,pluginId:t}){let[o,i]=Ze(null),[d,p]=Ze(null);if(e.length===0)return y(x,{children:"Nothing graduated yet."});let n=o===null?e:e.filter((a)=>a.rarity===o);return w(gt,{children:[y(I,{children:we.map((a)=>y(k,{"aria-pressed":o===a,onClick:()=>{i(a),p(null)},type:"button",children:a??"all"},a??"all"))}),n.length===0?w(x,{children:["No ",o," graduates yet."]}):y(Oe,{children:n.map((a)=>{let s=d===a.id,S=`dex-detail-${a.id}`;return w(ct,{children:[w(_e,{$open:s,"aria-controls":S,"aria-expanded":s,onClick:()=>p(s?null:a.id),type:"button",children:[y("img",{alt:`${a.rarity}${a.isShiny?" shiny":""} ${a.name??`species ${a.finalId}`}`,src:R(t,a.finalId,a.isShiny),style:{width:"64px",height:"64px",imageRendering:"pixelated"}}),y(Z,{children:T(a.name,a.finalId)}),a.nature===null?null:y(Z,{children:a.nature})]}),s?y(mt,{entry:a,id:S,pluginId:t}):null]},a.id)})})]})}function mt({entry:e,id:t,pluginId:o}){return w(Ge,{id:t,children:[y("img",{alt:E(e.name,e.finalId,e.isShiny),src:R(o,e.finalId,e.isShiny),style:{width:"96px",height:"96px",imageRendering:"pixelated"}}),w(He,{children:[y("strong",{children:T(e.name,e.finalId)}),w(I,{children:[y(b,{children:e.rarity}),e.isShiny?y(V,{children:"✦ shiny"}):null,e.nature===null?null:y(b,{children:e.nature})]}),y(Ue,{children:e.chainOrder.map((i,d)=>w(je,{children:[y("img",{alt:E(null,i,!1),src:R(o,i,!1)}),y(Z,{children:i===e.finalId?T(e.name,i):`#${i}`})]},`${i}-${d}`))}),w(x,{children:["caught ",new Date(e.caughtAt).toLocaleDateString()]})]})]})}import{jsx as ge,jsxs as ht}from"react/jsx-runtime";function he({stages:e,stageIndex:t,progress:o,threshold:i,label:d,valueText:p}){let n=o/Math.max(1,i)*100;return ge(Ke,{"aria-label":d,"aria-valuemax":i,"aria-valuemin":0,"aria-valuenow":o,"aria-valuetext":p,role:"progressbar",children:Array.from({length:Math.max(1,e)},(a,s)=>ht(Fe,{children:[s<t?ge(me,{$pct:100}):null,s===t?ge(me,{$pct:n}):null]},s))})}function Xe(e,t,o,i){return`stage ${t+1} of ${e}, ${h(o)} of ${h(i)} tokens`}import{jsx as c,jsxs as m,Fragment as G}from"react/jsx-runtime";function et({view:e,activity:t,pluginId:o,onRelease:i,releasing:d}){let p=e.state;if(p===null)return null;let{active:n}=p,a=n===null?null:n.plannedPath[n.stageIndex]??null;return m(G,{children:[m(I,{children:[a===null?c(q,{"aria-label":"An egg, not yet hatched",role:"img"}):c(z,{alt:E(e.name,a,n?.isShiny===!0),src:R(o,a,n?.isShiny===!0)}),m("div",{children:[c("h3",{children:a===null?"Egg":T(e.name,a)}),m(Ne,{children:[n===null?p.eggTier===null?null:m(b,{children:[p.eggTier,"+ guaranteed"]}):m(G,{children:[c(b,{children:n.rarity}),n.isShiny?m(V,{children:[c("span",{"aria-hidden":"true",children:"✦"}),"shiny"]}):null,c(b,{children:n.nature}),n.dittoDisguise===null||n.dittoRevealed?null:c(b,{children:"?"}),n.everstone?c(b,{children:"everstone"}):null,n.soothe?c(b,{children:"soothe bell"}):null]}),c(b,{"aria-label":`Activity: ${t}`,role:"status",children:t})]}),n===null?m(G,{children:[c(he,{label:"Incubation",progress:e.progress,stageIndex:0,stages:1,threshold:e.nextThreshold,valueText:`${h(e.progress)} of ${h(e.nextThreshold)} tokens incubated`}),m(_,{children:[h(e.progress)," / ",h(e.nextThreshold)," tokens incubated"]})]}):m(G,{children:[c(he,{label:"Growth to the next evolution",progress:e.progress,stageIndex:n.stageIndex,stages:n.plannedPath.length,threshold:e.nextThreshold,valueText:Xe(n.plannedPath.length,n.stageIndex,e.progress,e.nextThreshold)}),m(_,{children:["Stage ",n.stageIndex+1," of ",n.plannedPath.length]}),n.everstone?m(G,{children:[m(_,{children:["Held at this stage · ",h(e.progress)," banked"]}),c(k,{disabled:d,onClick:i,type:"button",children:"Release"})]}):m(_,{children:[h(e.progress)," / ",h(e.nextThreshold)," to the next stage"]})]})]})]}),m(Me,{children:[m(J,{children:[c(Q,{children:"Earned"}),c(Y,{children:h(e.tokensTotal)})]}),m(J,{children:[c(Q,{children:"To spend"}),c(Y,{children:h(e.wallet)})]}),m(J,{children:[c(Q,{children:"Graduated"}),c(Y,{children:e.dex.length.toLocaleString()})]})]})]})}import{useState as ft}from"react";import{jsx as v,jsxs as se}from"react/jsx-runtime";function tt({keys:e,onPick:t,pluginId:o,rosterFailed:i}){let[d,p]=ft("");return se(O,{children:[v("h2",{children:"Companion"}),v(Le,{children:"Each API key raises its own Pokémon on the tokens it spends. Pick a key."}),e.length>0?v(Ve,{children:e.map((n)=>v(xt,{entry:n,onPick:t,pluginId:o},n.apiKeyId))}):v(x,{children:i?"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."}),v(ue,{children:"Or by key id"}),v("form",{onSubmit:(n)=>{n.preventDefault();let a=d.trim();if(a!=="")t(a)},children:se(I,{children:[v("input",{"aria-label":"API key id",onChange:(n)=>p(n.target.value),placeholder:"key id",value:d}),v(k,{type:"submit",children:"Show"})]})})]})}function xt({entry:e,onPick:t,pluginId:o}){let i=M(e.speciesId!==null,e.lastCreditAt,Date.now());return se(ze,{onClick:()=>t(e.apiKeyId),type:"button",children:[e.unreadable?v(Be,{"aria-label":"This key's save could not be read",role:"img"}):e.speciesId===null?v(q,{"aria-label":"An egg, not yet hatched",role:"img"}):v(z,{alt:E(e.name,e.speciesId,e.isShiny),src:R(o,e.speciesId,e.isShiny)}),v("strong",{children:e.unreadable?"Save unreadable":e.speciesId===null?"Egg":T(e.name,e.speciesId)}),e.rarity===null?null:v(b,{children:e.rarity}),v(W,{children:e.apiKeyId}),se(x,{children:[v(K,{children:h(e.tokensTotal)}),e.unreadable?null:` · ${i}`]})]})}import{useId as bt,useState as yt}from"react";import{jsx as H,jsxs as nt,Fragment as kt}from"react/jsx-runtime";var F={shop:!0,bag:!0,dex:!0},ot=(e)=>`plugin:${e}:sections`;function vt(e){try{let t=globalThis.localStorage?.getItem(ot(e));if(t===null||t===void 0)return F;let o=JSON.parse(t);if(typeof o!=="object"||o===null)return F;let i=o;return{shop:typeof i.shop==="boolean"?i.shop:F.shop,bag:typeof i.bag==="boolean"?i.bag:F.bag,dex:typeof i.dex==="boolean"?i.dex:F.dex}}catch{return F}}function rt(e){let[t,o]=yt(()=>vt(e));return{open:t,toggle:(d)=>{o((p)=>{let n={...p,[d]:!p[d]};try{globalThis.localStorage?.setItem(ot(e),JSON.stringify(n))}catch{}return n})}}}function le({title:e,count:t,open:o,onToggle:i,children:d}){let p=bt();return nt(kt,{children:[H(Re,{children:nt(De,{"aria-controls":p,"aria-expanded":o,onClick:i,type:"button",children:[H(Ee,{$open:o,children:"▶"}),e,H(Te,{}),H(Pe,{children:t})]})}),o?H("div",{id:p,children:d}):null]})}import{jsx as A,jsxs as pe}from"react/jsx-runtime";function it({offers:e,wallet:t,onBuy:o,pending:i,inventory:d,hasCompanion:p,pluginId:n}){return A(X,{children:e.map((a)=>{let{entry:s,price:S}=a,C=St(s,d),u=s.kind==="egg"&&!p,D=s.kind==="item"?N(s.item):"fresh egg";return pe(ee,{children:[pe(te,{children:[A(ie,{emoji:s.kind==="item"?L(s.item).emoji:ve,item:s.kind==="item"?s.item:"egg",pluginId:n}),A(ne,{children:D}),C?A(x,{children:"owned"}):A(K,{children:h(S)})]}),s.kind==="egg"&&s.tier!==null?pe(b,{children:[s.tier,"+"]}):null,A(oe,{children:s.kind==="item"?L(s.item).blurb:ke(s.tier)}),A(re,{children:pe(k,{disabled:C||u||t<S||i,onClick:()=>o(s),type:"button",children:["Buy ",$e(s)]})})]},s.kind==="item"?`item:${s.item}`:`egg:${s.tier??""}`)})})}function St(e,t){if(e.kind!=="item")return!1;return!L(e.item).consumable&&(t[e.item]??0)>0}import{jsx as f,jsxs as xe,Fragment as Tt}from"react/jsx-runtime";var lt=1e4;function Ct({pluginId:e,api:t}){let[o,i]=st({at:"start"}),d=wt(),{cadence:p}=$t(),n=at({queryKey:["roster"],queryFn:()=>t.get("keys"),refetchInterval:p(lt)}),a=n.data?.keys??[];if(o.at==="start"&&!n.isPending){let g=a.length===1?a[0]:void 0;i(g===void 0?{at:"roster"}:{at:"key",apiKeyId:g.apiKeyId})}let s=o.at==="key"?o.apiKeyId:null,S=at({queryKey:["companion",s],queryFn:()=>t.get(`keys/${s}`),enabled:s!==null,refetchInterval:p(lt)}),[C,u]=st(null),D=()=>{d.invalidateQueries({queryKey:["companion",s]}),d.invalidateQueries({queryKey:["roster"]})},U=fe({mutationFn:(g)=>t.post(`keys/${s}/purchase`,g),onMutate:()=>u(null),onError:(g)=>u(g instanceof Error?g.message:"the purchase was refused"),onSuccess:D}),j=fe({mutationFn:(g)=>t.post(`keys/${s}/use`,{item:g}),onMutate:()=>u(null),onError:(g)=>u(g instanceof Error?g.message:"the item could not be used"),onSuccess:D}),be=fe({mutationFn:()=>t.post(`keys/${s}/unpin`),onMutate:()=>u(null),onError:(g)=>u(g instanceof Error?g.message:"the companion could not be released"),onSuccess:D});if(n.isPending)return f(O,{children:"Loading…"});if(s===null)return f(tt,{keys:a,onPick:(g)=>i({at:"key",apiKeyId:g}),pluginId:e,rosterFailed:n.isError});return xe(O,{children:[xe(Ae,{children:[f("h2",{children:"Companion"}),f(W,{children:s}),f(k,{onClick:()=>i({at:"roster"}),type:"button",children:"All keys"})]}),f(Rt,{buy:U.mutate,buying:U.isPending,keyId:s,onRelease:()=>be.mutate(),onUse:j.mutate,pluginId:e,query:S,refusal:C,releasing:be.isPending,using:j.isPending})]})}function Rt({query:e,pluginId:t,buy:o,onUse:i,onRelease:d,buying:p,using:n,releasing:a,refusal:s}){let{open:S,toggle:C}=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 u=e.data;if(u.state===null)return f(ce,{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=M(u.state.active!==null,u.lastCreditAt,Date.now()),U=Object.values(u.state.inventory).filter((j)=>j>0).length;return xe(Tt,{children:[f(et,{activity:D,onRelease:d,pluginId:t,releasing:a,view:u}),f(le,{count:pt(u.shop.length,"offer"),onToggle:()=>C("shop"),open:S.shop,title:"Shop",children:f(it,{hasCompanion:u.state.active!==null,inventory:u.state.inventory,offers:u.shop,onBuy:o,pending:p,pluginId:t,wallet:u.wallet})}),f(le,{count:`${U} held`,onToggle:()=>C("bag"),open:S.bag,title:"Bag",children:f(Ye,{inventory:u.state.inventory,onUse:i,pending:n,pluginId:t})}),s===null?null:f(ce,{role:"alert",children:s}),f(le,{count:pt(u.dex.length,"graduate"),onToggle:()=>C("dex"),open:S.dex,title:"Pokédex",children:f(We,{entries:u.dex,pluginId:t})})]})}function pt(e,t){return`${e} ${t}${e===1?"":"s"}`}var An=It({mount:Ct});export{M as activityOf,An as default};
|