jaml-ui 0.14.4 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/DESIGN.md +9 -11
  2. package/dist/assets.d.ts +7 -0
  3. package/dist/assets.js +11 -0
  4. package/dist/components/AnalyzerExplorer.d.ts +4 -1
  5. package/dist/components/AnalyzerExplorer.js +14 -48
  6. package/dist/components/GameCard.js +8 -7
  7. package/dist/components/JamlAestheticSelector.d.ts +4 -0
  8. package/dist/components/JamlAestheticSelector.js +6 -19
  9. package/dist/components/JamlAnalyzerFullscreen.d.ts +7 -1
  10. package/dist/components/JamlAnalyzerFullscreen.js +18 -47
  11. package/dist/components/JamlIde.js +12 -24
  12. package/dist/components/JamlIdeVisual.js +3 -56
  13. package/dist/components/JamlMapPreview.d.ts +6 -1
  14. package/dist/components/JamlMapPreview.js +99 -21
  15. package/dist/components/JamlSeedInput.d.ts +5 -0
  16. package/dist/components/JamlSeedInput.js +11 -14
  17. package/dist/components/MotelyVersionBadge.d.ts +1 -3
  18. package/dist/components/MotelyVersionBadge.js +4 -16
  19. package/dist/components/jamlMap/JamlMapEditorDemo.d.ts +8 -0
  20. package/dist/components/jamlMap/JamlMapEditorDemo.js +170 -0
  21. package/dist/components/jamlMap/JokerPicker.d.ts +7 -0
  22. package/dist/components/jamlMap/JokerPicker.js +258 -0
  23. package/dist/components/jamlMap/MysterySlot.d.ts +32 -0
  24. package/dist/components/jamlMap/MysterySlot.js +109 -0
  25. package/dist/components/jamlMap/index.d.ts +3 -0
  26. package/dist/components/jamlMap/index.js +3 -0
  27. package/dist/core.d.ts +0 -2
  28. package/dist/core.js +0 -2
  29. package/dist/decode/motelyItemDecoder.d.ts +10 -23
  30. package/dist/decode/motelyItemDecoder.js +103 -248
  31. package/dist/decode/motelySprite.d.ts +19 -0
  32. package/dist/decode/motelySprite.js +84 -0
  33. package/dist/hooks/analyzerStreamRegistry.js +30 -82
  34. package/dist/hooks/useAnalyzer.d.ts +10 -3
  35. package/dist/hooks/useAnalyzer.js +11 -6
  36. package/dist/hooks/useIntersectionObserver.d.ts +14 -0
  37. package/dist/hooks/useIntersectionObserver.js +50 -0
  38. package/dist/index.d.ts +3 -8
  39. package/dist/index.js +2 -7
  40. package/dist/motely.d.ts +2 -1
  41. package/dist/motely.js +2 -1
  42. package/dist/motelyDisplay.d.ts +4 -623
  43. package/dist/motelyDisplay.js +26 -165
  44. package/dist/r3f/Card3D.d.ts +2 -2
  45. package/dist/r3f/Card3D.js +13 -46
  46. package/dist/r3f/JimboBillboard.d.ts +10 -0
  47. package/dist/r3f/JimboBillboard.js +29 -0
  48. package/dist/r3f/JimboText3D.d.ts +9 -0
  49. package/dist/r3f/JimboText3D.js +8 -0
  50. package/dist/r3f.d.ts +2 -0
  51. package/dist/r3f.js +2 -0
  52. package/dist/render/CanvasRenderer.js +7 -171
  53. package/dist/sprites/spriteData.d.ts +1 -0
  54. package/dist/sprites/spriteData.js +1 -0
  55. package/dist/sprites/spriteMapper.d.ts +78 -1
  56. package/dist/sprites/spriteMapper.js +52 -0
  57. package/dist/ui/JimboBadge.d.ts +13 -0
  58. package/dist/ui/JimboBadge.js +8 -0
  59. package/dist/ui/JimboFloating.d.ts +8 -0
  60. package/dist/ui/JimboFloating.js +17 -0
  61. package/dist/ui/JimboToggleList.d.ts +11 -0
  62. package/dist/ui/JimboToggleList.js +5 -0
  63. package/dist/ui/codeBlock.js +2 -3
  64. package/dist/ui/footer.d.ts +4 -0
  65. package/dist/ui/footer.js +6 -4
  66. package/dist/ui/hooks.d.ts +89 -0
  67. package/dist/ui/hooks.js +551 -0
  68. package/dist/ui/jimboBackground.js +2 -131
  69. package/dist/ui/jimboCopyRow.d.ts +4 -0
  70. package/dist/ui/jimboCopyRow.js +5 -22
  71. package/dist/ui/jimboFilterBar.d.ts +1 -4
  72. package/dist/ui/jimboFilterBar.js +2 -61
  73. package/dist/ui/jimboFlankNav.d.ts +1 -2
  74. package/dist/ui/jimboFlankNav.js +5 -30
  75. package/dist/ui/jimboTabs.d.ts +1 -5
  76. package/dist/ui/jimboTabs.js +6 -41
  77. package/dist/ui/jimboText.d.ts +1 -1
  78. package/dist/ui/jimboText.js +15 -32
  79. package/dist/ui/jimboTooltip.d.ts +1 -12
  80. package/dist/ui/jimboTooltip.js +6 -82
  81. package/dist/ui/panel.d.ts +3 -1
  82. package/dist/ui/panel.js +11 -47
  83. package/dist/ui/showcase.d.ts +4 -0
  84. package/dist/ui/showcase.js +9 -36
  85. package/dist/ui/sprites.d.ts +14 -0
  86. package/dist/ui/sprites.js +54 -13
  87. package/dist/ui.d.ts +4 -0
  88. package/dist/ui.js +5 -0
  89. package/package.json +130 -122
  90. package/dist/components/JamlSpeedometer.d.ts +0 -11
  91. package/dist/components/JamlSpeedometer.js +0 -54
  92. package/dist/decode/packedBalatroItem.d.ts +0 -13
  93. package/dist/decode/packedBalatroItem.js +0 -26
  94. package/dist/hooks/loadMotelyWasm.d.ts +0 -7
  95. package/dist/hooks/loadMotelyWasm.js +0 -16
  96. package/dist/utils/itemUtils.d.ts +0 -11
  97. package/dist/utils/itemUtils.js +0 -71
@@ -1,290 +1,145 @@
1
1
  /**
2
- * Motely item decoder.
3
- *
4
- * MotelyItem.Value is a packed integer. The MotelyItemType enum
5
- * uses packed integers where the top nibble encodes category:
6
- * 0x1000 = Standardcard, 0x2000 = Spectral, 0x3000 = Tarot,
7
- * 0x4000 = Planet, 0x5000 = Joker, 0xF000 = Invalid
2
+ * Motely item decoder — thin wrapper over motely-wasm runtime enums.
3
+ * No hand-rolled bitmask tables. The WASM enum IS the source of truth.
8
4
  */
9
5
  import { Motely } from "motely-wasm";
10
- import { getItemCategory, getItemDisplayName } from "../utils/itemUtils.js";
11
- // ─── Category from MotelyItemType integer ────────────────────────────────────
12
- const CATEGORY_MASK = 0xf000;
13
- const VALUE_TYPE_MASK = 0xffff;
14
- const VALUE_SEAL_MASK = 0x70000;
15
- const VALUE_ENHANCEMENT_MASK = 0x780000;
16
- const VALUE_EDITION_MASK = 0x3800000;
17
- const CATEGORY_TO_TYPE = {
18
- 0x1000: "Standard card",
19
- 0x2000: "Spectral",
20
- 0x3000: "Tarot",
21
- 0x4000: "Planet",
22
- 0x5000: "Joker",
6
+ // ─── Helpers using motely-wasm runtime enums directly ────────────────────────
7
+ function enumKey(e, value) {
8
+ const k = e[String(value)];
9
+ return typeof k === "string" ? k : null;
10
+ }
11
+ function spaceSplit(value) {
12
+ return value.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2");
13
+ }
14
+ const CATEGORY_MAP = {
15
+ [Motely.MotelyItemTypeCategory.Standardcard]: "playing",
16
+ [Motely.MotelyItemTypeCategory.SpectralCard]: "spectral",
17
+ [Motely.MotelyItemTypeCategory.TarotCard]: "tarot",
18
+ [Motely.MotelyItemTypeCategory.PlanetCard]: "planet",
19
+ [Motely.MotelyItemTypeCategory.Joker]: "joker",
23
20
  };
24
- // ─── Reverse lookup: MotelyItemType integer → string name ────────────────────
25
- const _itemTypeToName = new Map();
26
- function ensureItemTypeMap() {
27
- if (_itemTypeToName.size > 0)
28
- return;
29
- const e = Motely.MotelyItemType;
30
- for (const [key, val] of Object.entries(e)) {
31
- if (typeof val === "number" && typeof key === "string" && !/^\d+$/.test(key)) {
32
- _itemTypeToName.set(val, key);
33
- }
34
- }
35
- }
36
- function asRuntimeItem(input) {
37
- return input !== null && typeof input === "object" ? input : null;
38
- }
39
- function finiteNumber(value) {
40
- return typeof value === "number" && Number.isFinite(value) ? value : null;
41
- }
42
- function runtimeEnumName(enumObject, value) {
43
- if (value === null || !enumObject || typeof enumObject !== "object")
44
- return null;
45
- const enumKey = enumObject[String(value)];
46
- return typeof enumKey === "string" && enumKey.length > 0 ? enumKey : null;
47
- }
48
- function parseStandardcardEnumKey(enumKey) {
49
- const match = /^([CDHS])(10|[2-9JQKA])$/.exec(enumKey);
50
- if (!match)
51
- return null;
52
- const suitMap = {
53
- C: "Clubs",
54
- D: "Diamonds",
55
- H: "Hearts",
56
- S: "Spades",
57
- };
58
- const rankMap = {
59
- J: "Jack",
60
- Q: "Queen",
61
- K: "King",
62
- A: "Ace",
63
- };
64
- return {
65
- rank: rankMap[match[2]] ?? match[2],
66
- suit: suitMap[match[1]],
67
- };
68
- }
69
- function rankNameFromEnum(enumKey) {
70
- if (!enumKey)
71
- return null;
72
- const rankMap = {
73
- Two: "2",
74
- Three: "3",
75
- Four: "4",
76
- Five: "5",
77
- Six: "6",
78
- Seven: "7",
79
- Eight: "8",
80
- Nine: "9",
81
- Ten: "10",
82
- Jack: "Jack",
83
- Queen: "Queen",
84
- King: "King",
85
- Ace: "Ace",
86
- };
87
- return rankMap[enumKey] ?? null;
88
- }
21
+ // ─── Core decoder ────────────────────────────────────────────────────────────
89
22
  function resolvePackedValue(input) {
23
+ if (input == null)
24
+ return null;
90
25
  if (typeof input === "number")
91
26
  return Number.isFinite(input) ? input : null;
92
- const runtimeItem = asRuntimeItem(input);
93
- return finiteNumber(runtimeItem?.value);
27
+ return input.value ?? input.type ?? null;
94
28
  }
95
- function resolveEditionValue(input) {
96
- const runtimeItem = asRuntimeItem(input);
97
- const direct = finiteNumber(runtimeItem?.edition);
98
- if (direct !== null)
99
- return direct;
100
- const packedValue = resolvePackedValue(input);
101
- return packedValue !== null ? packedValue & VALUE_EDITION_MASK : null;
102
- }
103
- function resolveSealValue(input) {
104
- const runtimeItem = asRuntimeItem(input);
105
- const direct = finiteNumber(runtimeItem?.seal);
106
- if (direct !== null)
107
- return direct;
108
- const packedValue = resolvePackedValue(input);
109
- return packedValue !== null ? packedValue & VALUE_SEAL_MASK : null;
29
+ export function resolveMotelyItemType(input) {
30
+ const val = resolvePackedValue(input);
31
+ return val !== null ? val & 0xffff : null;
110
32
  }
111
- function resolveEnhancementValue(input) {
112
- const runtimeItem = asRuntimeItem(input);
113
- const direct = finiteNumber(runtimeItem?.enhancement);
114
- if (direct !== null)
115
- return direct;
116
- const packedValue = resolvePackedValue(input);
117
- return packedValue !== null ? packedValue & VALUE_ENHANCEMENT_MASK : null;
33
+ export function motelyItemTypeName(input) {
34
+ const itemType = resolveMotelyItemType(input);
35
+ if (itemType === null)
36
+ return "Unknown";
37
+ return enumKey(Motely.MotelyItemType, itemType) ?? `item#${itemType}`;
118
38
  }
119
- export function resolveMotelyItemType(input) {
120
- if (typeof input === "number")
121
- return Number.isFinite(input) ? (input & VALUE_TYPE_MASK) : null;
122
- const runtimeItem = asRuntimeItem(input);
123
- const directType = finiteNumber(runtimeItem?.type);
124
- if (directType !== null)
125
- return directType & VALUE_TYPE_MASK;
126
- const packedValue = finiteNumber(runtimeItem?.value);
127
- return packedValue !== null ? packedValue & VALUE_TYPE_MASK : null;
39
+ export function motelyItemCategory(itemType) {
40
+ const catValue = (itemType >> 12) & 0xf;
41
+ return CATEGORY_MAP[catValue] ?? "unknown";
128
42
  }
129
43
  export function motelyItemRenderCategory(input) {
130
- const enumKey = motelyItemTypeName(input);
131
- return enumKey === "Unknown" ? "unknown" : getItemCategory(enumKey);
44
+ const itemType = resolveMotelyItemType(input);
45
+ if (itemType === null)
46
+ return "unknown";
47
+ return motelyItemCategory(itemType);
48
+ }
49
+ export function motelyItemDisplayName(input) {
50
+ return spaceSplit(motelyItemTypeName(input));
132
51
  }
133
52
  export function motelyItemEditionName(input) {
134
- const enumKey = runtimeEnumName(Motely.MotelyItemEdition, resolveEditionValue(input));
135
- return enumKey === null || enumKey === "None" ? null : enumKey;
53
+ if (input == null)
54
+ return null;
55
+ const val = typeof input === "number" ? input : input.edition;
56
+ if (val == null)
57
+ return null;
58
+ const key = enumKey(Motely.MotelyItemEdition, typeof val === "number" ? val : 0);
59
+ if (!key || key === "None")
60
+ return null;
61
+ return key;
136
62
  }
137
63
  export function motelyItemSealName(input) {
138
- const enumKey = runtimeEnumName(Motely.MotelyItemSeal, resolveSealValue(input));
139
- return enumKey === null || enumKey === "None" ? null : enumKey;
64
+ if (input == null)
65
+ return null;
66
+ const val = typeof input === "number" ? null : input.seal;
67
+ if (val == null)
68
+ return null;
69
+ const key = enumKey(Motely.MotelyItemSeal, val);
70
+ if (!key || key === "None")
71
+ return null;
72
+ return key;
140
73
  }
141
74
  export function motelyItemEnhancementName(input) {
142
- const enumKey = runtimeEnumName(Motely.MotelyItemEnhancement, resolveEnhancementValue(input));
143
- return enumKey === null || enumKey === "None" ? null : enumKey;
144
- }
145
- export function motelyStandardcardSuitName(input) {
146
- const runtimeItem = asRuntimeItem(input);
147
- const directSuit = runtimeEnumName(Motely.MotelyStandardcardSuit, finiteNumber(runtimeItem?.suit));
148
- if (directSuit === "Clubs" || directSuit === "Diamonds" || directSuit === "Hearts" || directSuit === "Spades") {
149
- return directSuit;
150
- }
151
- const parsed = parseStandardcardEnumKey(motelyItemTypeName(input));
152
- return parsed?.suit ?? null;
75
+ if (input == null)
76
+ return null;
77
+ const val = typeof input === "number" ? null : input.enhancement;
78
+ if (val == null)
79
+ return null;
80
+ const key = enumKey(Motely.MotelyItemEnhancement, val);
81
+ if (!key || key === "None")
82
+ return null;
83
+ return key;
153
84
  }
154
85
  export function motelyStandardcardRankName(input) {
155
- const runtimeItem = asRuntimeItem(input);
156
- const directRank = runtimeEnumName(Motely.MotelyStandardcardRank, finiteNumber(runtimeItem?.rank));
157
- const normalizedDirect = rankNameFromEnum(directRank);
158
- if (normalizedDirect !== null)
159
- return normalizedDirect;
160
- const parsed = parseStandardcardEnumKey(motelyItemTypeName(input));
161
- return parsed?.rank ?? null;
162
- }
163
- /** Get the enum key name for a MotelyItemType value. */
164
- export function motelyItemTypeName(input) {
165
- const itemType = resolveMotelyItemType(input);
166
- if (itemType === null)
167
- return "Unknown";
168
- ensureItemTypeMap();
169
- return _itemTypeToName.get(itemType) ?? "Unknown";
86
+ if (input == null)
87
+ return null;
88
+ const val = typeof input === "number" ? null : input.rank;
89
+ if (val == null)
90
+ return null;
91
+ return enumKey(Motely.MotelyStandardcardRank, val);
170
92
  }
171
- /** Get the category string for a MotelyItemType value. */
172
- export function motelyItemCategory(input) {
173
- const itemType = resolveMotelyItemType(input);
174
- if (itemType === null)
175
- return "Unknown";
176
- const renderCategory = motelyItemRenderCategory(itemType);
177
- if (renderCategory === "playing")
178
- return "Standard card";
179
- if (renderCategory === "spectral")
180
- return "Spectral";
181
- if (renderCategory === "tarot")
182
- return "Tarot";
183
- if (renderCategory === "planet")
184
- return "Planet";
185
- if (renderCategory === "joker")
186
- return "Joker";
187
- return CATEGORY_TO_TYPE[itemType & CATEGORY_MASK] ?? "Unknown";
93
+ export function motelyStandardcardSuitName(input) {
94
+ if (input == null)
95
+ return null;
96
+ const val = typeof input === "number" ? null : input.suit;
97
+ if (val == null)
98
+ return null;
99
+ return enumKey(Motely.MotelyStandardcardSuit, val);
188
100
  }
189
- /** Convert PascalCase enum key to display name. */
190
- export function motelyItemDisplayName(input) {
191
- const enumKey = motelyItemTypeName(input);
192
- return enumKey === "Unknown" ? "Unknown" : getItemDisplayName(enumKey);
101
+ export function decodeMotelyItemName(input) {
102
+ return motelyItemTypeName(input);
193
103
  }
194
- // ─── Module-level cache ──────────────────────────────────────────────────────
195
- const _cache = new Map();
196
- const _decodedBaseCache = new Map();
197
104
  export function decodeMotelyItem(input) {
198
105
  const itemType = resolveMotelyItemType(input);
199
106
  if (itemType === null)
200
107
  return null;
201
- let base = _decodedBaseCache.get(itemType) ?? null;
202
- if (!_decodedBaseCache.has(itemType)) {
203
- const enumKey = motelyItemTypeName(itemType);
204
- if (enumKey === "Unknown") {
205
- _decodedBaseCache.set(itemType, null);
206
- return null;
207
- }
208
- const category = motelyItemRenderCategory(itemType);
209
- const rank = motelyStandardcardRankName(itemType);
210
- const suit = motelyStandardcardSuitName(itemType);
211
- base = {
212
- itemType,
213
- enumKey,
214
- displayName: category === "playing" && rank && suit ? `${rank} of ${suit}` : getItemDisplayName(enumKey),
215
- category,
216
- rank,
217
- suit,
218
- };
219
- _decodedBaseCache.set(itemType, base);
220
- }
221
- if (!base)
222
- return null;
223
- const decoded = {
224
- ...base,
108
+ const enumKeyStr = enumKey(Motely.MotelyItemType, itemType) ?? `Unknown_${itemType}`;
109
+ const category = motelyItemCategory(itemType);
110
+ const displayName = spaceSplit(enumKeyStr);
111
+ return {
112
+ itemType,
113
+ enumKey: enumKeyStr,
114
+ displayName,
115
+ category,
225
116
  edition: motelyItemEditionName(input),
226
117
  seal: motelyItemSealName(input),
227
118
  enhancement: motelyItemEnhancementName(input),
119
+ rank: motelyStandardcardRankName(input),
120
+ suit: motelyStandardcardSuitName(input),
228
121
  };
229
- return decoded;
230
122
  }
231
- export function decodeMotelyItemToJamlCard(input, scale = 1) {
123
+ export function decodeMotelyItemToJamlCard(input, scale) {
232
124
  const decoded = decodeMotelyItem(input);
233
- if (!decoded || decoded.category === "unknown")
125
+ if (!decoded)
234
126
  return null;
235
- if (decoded.category === "playing") {
236
- if (!decoded.rank || !decoded.suit)
237
- return null;
238
- return {
239
- type: "playing",
240
- card: {
241
- name: `${decoded.rank} of ${decoded.suit}`,
242
- edition: decoded.edition ?? undefined,
243
- seal: decoded.seal ? `${decoded.seal} Seal` : undefined,
244
- enhancements: decoded.enhancement ? [decoded.enhancement] : undefined,
245
- rank: decoded.rank,
246
- suit: decoded.suit,
247
- scale,
248
- },
249
- };
250
- }
127
+ const type = decoded.category === "joker" ? "joker"
128
+ : decoded.category === "playing" ? "playing"
129
+ : "consumable";
251
130
  return {
252
- type: decoded.category === "joker" ? "joker" : "consumable",
131
+ type,
253
132
  card: {
254
133
  name: decoded.displayName,
255
134
  edition: decoded.edition ?? undefined,
135
+ seal: decoded.seal ?? undefined,
136
+ enhancements: decoded.enhancement ? [decoded.enhancement] : undefined,
137
+ rank: decoded.rank ?? undefined,
138
+ suit: decoded.suit ?? undefined,
256
139
  scale,
257
140
  },
258
141
  };
259
142
  }
260
- /**
261
- * Decode a MotelyItemType integer to a display name for sprite lookup.
262
- * Cached per value.
263
- */
264
- export function decodeMotelyItemName(input) {
265
- const itemType = resolveMotelyItemType(input);
266
- if (itemType === null)
267
- return null;
268
- if (_cache.has(itemType))
269
- return _cache.get(itemType) ?? null;
270
- const category = motelyItemCategory(itemType);
271
- if (category === "Unknown") {
272
- _cache.set(itemType, null);
273
- return null;
274
- }
275
- const decoded = decodeMotelyItem(input);
276
- const name = decoded?.displayName ?? motelyItemDisplayName(itemType);
277
- _cache.set(itemType, name);
278
- return name;
279
- }
280
- /** Warm the cache for a batch of item type values. */
281
- export function warmMotelyItemCache(itemTypes) {
282
- for (const t of itemTypes) {
283
- decodeMotelyItemName(t);
284
- decodeMotelyItem(t);
285
- }
286
- }
287
- /** Number of unique items decoded so far. */
288
- export function motelyItemCacheSize() {
289
- return _cache.size;
290
- }
143
+ // Cache warming is now a no-op since we use runtime enums directly
144
+ export function warmMotelyItemCache() { }
145
+ export function motelyItemCacheSize() { return 0; }
@@ -0,0 +1,19 @@
1
+ import { type MotelyRenderableCategory } from "./motelyItemDecoder.js";
2
+ export interface MotelySpriteData {
3
+ atlasPath: string;
4
+ gridCol: number;
5
+ gridRow: number;
6
+ gridCols: number;
7
+ gridRows: number;
8
+ displayName: string;
9
+ category: MotelyRenderableCategory;
10
+ }
11
+ /**
12
+ * Given a raw motely-wasm item value (which may be a bitpacked integer or raw MotelyItemType),
13
+ * resolves it to a sprite atlas path and grid coordinates for rendering.
14
+ */
15
+ export declare function motelyItemToSprite(rawValue: number): MotelySpriteData | null;
16
+ /**
17
+ * Resolves a sprite by name and category without needing a Motely integer.
18
+ */
19
+ export declare function getMotelySpriteByName(name: string, category?: MotelyRenderableCategory): MotelySpriteData | null;
@@ -0,0 +1,84 @@
1
+ import { decodeMotelyItem } from "./motelyItemDecoder.js";
2
+ import { getSpriteData, SHEET_META } from "../sprites/spriteMapper.js";
3
+ import { RANK_MAP, SUIT_MAP } from "../sprites/spriteData.js";
4
+ import { resolveJamlAssetUrl } from "../assets.js";
5
+ /**
6
+ * Given a raw motely-wasm item value (which may be a bitpacked integer or raw MotelyItemType),
7
+ * resolves it to a sprite atlas path and grid coordinates for rendering.
8
+ */
9
+ export function motelyItemToSprite(rawValue) {
10
+ const decoded = decodeMotelyItem(rawValue);
11
+ if (!decoded)
12
+ return null;
13
+ if (decoded.category === "playing" && decoded.rank && decoded.suit) {
14
+ const col = RANK_MAP[decoded.rank];
15
+ const row = SUIT_MAP[decoded.suit];
16
+ if (col !== undefined && row !== undefined) {
17
+ return {
18
+ atlasPath: resolveJamlAssetUrl('deck'),
19
+ gridCol: col,
20
+ gridRow: row,
21
+ gridCols: 13,
22
+ gridRows: 4,
23
+ displayName: decoded.displayName,
24
+ category: "playing"
25
+ };
26
+ }
27
+ }
28
+ const sprite = getSpriteData(decoded.displayName);
29
+ if (!sprite)
30
+ return null;
31
+ const meta = SHEET_META[sprite.type];
32
+ if (!meta)
33
+ return null;
34
+ return {
35
+ atlasPath: resolveJamlAssetUrl(meta.assetKey),
36
+ gridCol: sprite.pos.x,
37
+ gridRow: sprite.pos.y,
38
+ gridCols: meta.cols,
39
+ gridRows: meta.rows,
40
+ displayName: decoded.displayName,
41
+ category: decoded.category
42
+ };
43
+ }
44
+ /**
45
+ * Resolves a sprite by name and category without needing a Motely integer.
46
+ */
47
+ export function getMotelySpriteByName(name, category = "unknown") {
48
+ if (category === "playing") {
49
+ // Attempt to parse "Rank of Suit"
50
+ const match = /^(.*?)\s+of\s+(.*?)$/i.exec(name);
51
+ if (match) {
52
+ const rank = match[1];
53
+ const suit = match[2];
54
+ const col = RANK_MAP[rank];
55
+ const row = SUIT_MAP[suit];
56
+ if (col !== undefined && row !== undefined) {
57
+ return {
58
+ atlasPath: resolveJamlAssetUrl('deck'),
59
+ gridCol: col,
60
+ gridRow: row,
61
+ gridCols: 13,
62
+ gridRows: 4,
63
+ displayName: name,
64
+ category: "playing"
65
+ };
66
+ }
67
+ }
68
+ }
69
+ const sprite = getSpriteData(name);
70
+ if (!sprite)
71
+ return null;
72
+ const meta = SHEET_META[sprite.type];
73
+ if (!meta)
74
+ return null;
75
+ return {
76
+ atlasPath: resolveJamlAssetUrl(meta.assetKey),
77
+ gridCol: sprite.pos.x,
78
+ gridRow: sprite.pos.y,
79
+ gridCols: meta.cols,
80
+ gridRows: meta.rows,
81
+ displayName: name,
82
+ category
83
+ };
84
+ }
@@ -1,5 +1,4 @@
1
1
  "use client";
2
- /* eslint-disable @typescript-eslint/no-explicit-any */
3
2
  import { motelyItemDisplayNameFromValue } from "../motelyDisplay.js";
4
3
  export const ANALYZER_STREAM_META = {
5
4
  shop: { key: "shop", label: "Shop", tone: "default", defaultEnabled: true },
@@ -28,120 +27,69 @@ export function buildStreamHandle(live, ante, key) {
28
27
  const ctx = live.ctx;
29
28
  const Motely = live.Motely;
30
29
  const runState = live.runStates[ante];
31
- let stream = null;
32
30
  let cursor = 0;
33
31
  const idBase = `${ante}-${key}`;
34
32
  function nameFromValue(value) {
35
33
  return motelyItemDisplayNameFromValue(value);
36
34
  }
37
- function joker(streamFactoryName, nextName) {
35
+ function streamHandle(createStream, getNextValue) {
36
+ let stream = null;
38
37
  return {
39
38
  initStream: () => {
40
- stream = ctx[streamFactoryName](ante, Motely.MotelyJokerStreamFlags.Default);
39
+ stream = createStream();
41
40
  cursor = 0;
42
41
  },
43
42
  nextItem: () => {
44
- const r = ctx[nextName](stream);
45
- const value = r.joker?.value ?? r.item?.value ?? 0;
43
+ if (stream === null)
44
+ throw new Error(`Analyzer stream ${key} was read before initialization.`);
45
+ const value = getNextValue(stream);
46
46
  const id = `${idBase}-${cursor++}`;
47
47
  return { id, name: nameFromValue(value), value };
48
48
  },
49
49
  };
50
50
  }
51
- function fixedRarityJoker(streamFactoryName, nextName) {
52
- return {
53
- initStream: () => {
54
- stream = ctx[streamFactoryName](ante, Motely.MotelyJokerFixedRarityStreamFlags.Default);
55
- cursor = 0;
56
- },
57
- nextItem: () => {
58
- const r = ctx[nextName](stream);
59
- const value = r.joker?.value ?? 0;
60
- const id = `${idBase}-${cursor++}`;
61
- return { id, name: nameFromValue(value), value };
62
- },
63
- };
51
+ function joker(createStream, getNext) {
52
+ return streamHandle(createStream, (stream) => getNext(stream).item.value);
64
53
  }
65
- function tarot(streamFactoryName, nextName) {
66
- return {
67
- initStream: () => {
68
- stream = ctx[streamFactoryName](ante);
69
- cursor = 0;
70
- },
71
- nextItem: () => {
72
- const r = ctx[nextName](stream);
73
- const value = r.tarot?.value ?? r.item?.value ?? 0;
74
- const id = `${idBase}-${cursor++}`;
75
- return { id, name: nameFromValue(value), value };
76
- },
77
- };
54
+ function fixedRarityJoker(createStream, getNext) {
55
+ return streamHandle(createStream, (stream) => getNext(stream).item.value);
78
56
  }
79
- function planet(streamFactoryName, nextName) {
80
- return {
81
- initStream: () => {
82
- stream = ctx[streamFactoryName](ante);
83
- cursor = 0;
84
- },
85
- nextItem: () => {
86
- const r = ctx[nextName](stream);
87
- const value = r.planet?.value ?? 0;
88
- const id = `${idBase}-${cursor++}`;
89
- return { id, name: nameFromValue(value), value };
90
- },
91
- };
57
+ function tarot(createStream, getNext) {
58
+ return streamHandle(createStream, (stream) => getNext(stream).item.value);
92
59
  }
93
- function spectral(streamFactoryName, nextName) {
94
- return {
95
- initStream: () => {
96
- stream = ctx[streamFactoryName](ante);
97
- cursor = 0;
98
- },
99
- nextItem: () => {
100
- const r = ctx[nextName](stream);
101
- const value = r.spectral?.value ?? 0;
102
- const id = `${idBase}-${cursor++}`;
103
- return { id, name: nameFromValue(value), value };
104
- },
105
- };
60
+ function planet(createStream, getNext) {
61
+ return streamHandle(createStream, (stream) => getNext(stream).item.value);
62
+ }
63
+ function spectral(createStream, getNext) {
64
+ return streamHandle(createStream, (stream) => getNext(stream).item.value);
106
65
  }
107
66
  switch (key) {
108
67
  case "shop":
109
- return {
110
- initStream: () => {
111
- stream = ctx.createShopItemStream(ante, runState, Motely.MotelyShopStreamFlags.Default, Motely.MotelyJokerStreamFlags.Default);
112
- cursor = 0;
113
- },
114
- nextItem: () => {
115
- const r = ctx.getNextShopItem(stream);
116
- const value = r.item.value;
117
- const id = `${idBase}-${cursor++}`;
118
- return { id, name: nameFromValue(value), value };
119
- },
120
- };
68
+ return streamHandle(() => ctx.createShopItemStream(ante, runState, Motely.MotelyShopStreamFlags.Default, Motely.MotelyJokerStreamFlags.Default), (stream) => ctx.getNextShopItem(stream).item.value);
121
69
  case "shopJoker":
122
- return joker("createShopJokerStream", "getNextShopJoker");
70
+ return joker(() => ctx.createShopJokerStream(ante, Motely.MotelyJokerStreamFlags.Default), (stream) => ctx.getNextShopJoker(stream));
123
71
  case "soulJoker":
124
- return fixedRarityJoker("createSoulJokerStream", "getNextSoulJoker");
72
+ return fixedRarityJoker(() => ctx.createSoulJokerStream(ante, Motely.MotelyJokerFixedRarityStreamFlags.Default), (stream) => ctx.getNextSoulJoker(stream));
125
73
  case "rareTagJoker":
126
- return fixedRarityJoker("createRareTagJokerStream", "getNextRareTagJoker");
74
+ return fixedRarityJoker(() => ctx.createRareTagJokerStream(ante, Motely.MotelyJokerFixedRarityStreamFlags.Default), (stream) => ctx.getNextRareTagJoker(stream));
127
75
  case "uncommonTagJoker":
128
- return fixedRarityJoker("createUncommonTagJokerStream", "getNextUncommonTagJoker");
76
+ return fixedRarityJoker(() => ctx.createUncommonTagJokerStream(ante, Motely.MotelyJokerFixedRarityStreamFlags.Default), (stream) => ctx.getNextUncommonTagJoker(stream));
129
77
  case "riffRaffJoker":
130
- return fixedRarityJoker("createRiffRaffJokerStream", "getNextRiffRaffJoker");
78
+ return fixedRarityJoker(() => ctx.createRiffRaffJokerStream(ante, Motely.MotelyJokerFixedRarityStreamFlags.Default), (stream) => ctx.getNextRiffRaffJoker(stream));
131
79
  case "buffoonJoker":
132
- return joker("createBuffoonPackJokerStream", "getNextBuffoonPackJoker");
80
+ return joker(() => ctx.createBuffoonPackJokerStream(ante, Motely.MotelyJokerStreamFlags.Default), (stream) => ctx.getNextBuffoonPackJoker(stream));
133
81
  case "judgementJoker":
134
- return joker("createJudgementJokerStream", "getNextJudgementJoker");
82
+ return joker(() => ctx.createJudgementJokerStream(ante, Motely.MotelyJokerStreamFlags.Default), (stream) => ctx.getNextJudgementJoker(stream));
135
83
  case "wraithJoker":
136
- return joker("createWraithJokerStream", "getNextWraithJoker");
84
+ return joker(() => ctx.createWraithJokerStream(ante, Motely.MotelyJokerStreamFlags.Default), (stream) => ctx.getNextWraithJoker(stream));
137
85
  case "shopTarot":
138
- return tarot("createShopTarotStream", "getNextShopTarot");
86
+ return tarot(() => ctx.createShopTarotStream(ante), (stream) => ctx.getNextShopTarot(stream));
139
87
  case "shopPlanet":
140
- return planet("createShopPlanetStream", "getNextShopPlanet");
88
+ return planet(() => ctx.createShopPlanetStream(ante), (stream) => ctx.getNextShopPlanet(stream));
141
89
  case "shopSpectral":
142
- return spectral("createShopSpectralStream", "getNextShopSpectral");
90
+ return spectral(() => ctx.createShopSpectralStream(ante), (stream) => ctx.getNextShopSpectral(stream));
143
91
  case "purpleSealTarot":
144
- return tarot("createPurpleSealTarotStream", "getNextPurpleSealTarot");
92
+ return tarot(() => ctx.createPurpleSealTarotStream(ante), (stream) => ctx.getNextPurpleSealTarot(stream));
145
93
  default:
146
94
  return null;
147
95
  }