@react-text-game/core 0.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.
Files changed (178) hide show
  1. package/README.md +744 -0
  2. package/dist/baseGameObject.d.ts +90 -0
  3. package/dist/baseGameObject.d.ts.map +1 -0
  4. package/dist/baseGameObject.js +109 -0
  5. package/dist/baseGameObject.js.map +1 -0
  6. package/dist/constants.d.ts +12 -0
  7. package/dist/constants.d.ts.map +1 -0
  8. package/dist/constants.js +12 -0
  9. package/dist/constants.js.map +1 -0
  10. package/dist/game.d.ts +294 -0
  11. package/dist/game.d.ts.map +1 -0
  12. package/dist/game.js +489 -0
  13. package/dist/game.js.map +1 -0
  14. package/dist/helpers.d.ts +2 -0
  15. package/dist/helpers.d.ts.map +1 -0
  16. package/dist/helpers.js +6 -0
  17. package/dist/helpers.js.map +1 -0
  18. package/dist/hooks/index.d.ts +4 -0
  19. package/dist/hooks/index.d.ts.map +1 -0
  20. package/dist/hooks/index.js +4 -0
  21. package/dist/hooks/index.js.map +1 -0
  22. package/dist/hooks/useCurrentPassage.d.ts +10 -0
  23. package/dist/hooks/useCurrentPassage.d.ts.map +1 -0
  24. package/dist/hooks/useCurrentPassage.js +17 -0
  25. package/dist/hooks/useCurrentPassage.js.map +1 -0
  26. package/dist/hooks/useGameEntity.d.ts +21 -0
  27. package/dist/hooks/useGameEntity.d.ts.map +1 -0
  28. package/dist/hooks/useGameEntity.js +70 -0
  29. package/dist/hooks/useGameEntity.js.map +1 -0
  30. package/dist/hooks/useGameIsStarted.d.ts +12 -0
  31. package/dist/hooks/useGameIsStarted.d.ts.map +1 -0
  32. package/dist/hooks/useGameIsStarted.js +18 -0
  33. package/dist/hooks/useGameIsStarted.js.map +1 -0
  34. package/dist/index.d.ts +12 -0
  35. package/dist/index.d.ts.map +1 -0
  36. package/dist/index.js +10 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/logger.d.ts +8 -0
  39. package/dist/logger.d.ts.map +1 -0
  40. package/dist/logger.js +36 -0
  41. package/dist/logger.js.map +1 -0
  42. package/dist/options.d.ts +13 -0
  43. package/dist/options.d.ts.map +1 -0
  44. package/dist/options.js +15 -0
  45. package/dist/options.js.map +1 -0
  46. package/dist/passages/interactiveMap/fabric.d.ts +4 -0
  47. package/dist/passages/interactiveMap/fabric.d.ts.map +1 -0
  48. package/dist/passages/interactiveMap/fabric.js +3 -0
  49. package/dist/passages/interactiveMap/fabric.js.map +1 -0
  50. package/dist/passages/interactiveMap/index.d.ts +4 -0
  51. package/dist/passages/interactiveMap/index.d.ts.map +1 -0
  52. package/dist/passages/interactiveMap/index.js +4 -0
  53. package/dist/passages/interactiveMap/index.js.map +1 -0
  54. package/dist/passages/interactiveMap/interactiveMap.d.ts +89 -0
  55. package/dist/passages/interactiveMap/interactiveMap.d.ts.map +1 -0
  56. package/dist/passages/interactiveMap/interactiveMap.js +103 -0
  57. package/dist/passages/interactiveMap/interactiveMap.js.map +1 -0
  58. package/dist/passages/interactiveMap/types.d.ts +822 -0
  59. package/dist/passages/interactiveMap/types.d.ts.map +1 -0
  60. package/dist/passages/interactiveMap/types.js +2 -0
  61. package/dist/passages/interactiveMap/types.js.map +1 -0
  62. package/dist/passages/passage.d.ts +57 -0
  63. package/dist/passages/passage.d.ts.map +1 -0
  64. package/dist/passages/passage.js +64 -0
  65. package/dist/passages/passage.js.map +1 -0
  66. package/dist/passages/story/fabric.d.ts +4 -0
  67. package/dist/passages/story/fabric.d.ts.map +1 -0
  68. package/dist/passages/story/fabric.js +3 -0
  69. package/dist/passages/story/fabric.js.map +1 -0
  70. package/dist/passages/story/index.d.ts +5 -0
  71. package/dist/passages/story/index.d.ts.map +1 -0
  72. package/dist/passages/story/index.js +5 -0
  73. package/dist/passages/story/index.js.map +1 -0
  74. package/dist/passages/story/start.d.ts +14 -0
  75. package/dist/passages/story/start.d.ts.map +1 -0
  76. package/dist/passages/story/start.js +22 -0
  77. package/dist/passages/story/start.js.map +1 -0
  78. package/dist/passages/story/story.d.ts +84 -0
  79. package/dist/passages/story/story.d.ts.map +1 -0
  80. package/dist/passages/story/story.js +88 -0
  81. package/dist/passages/story/story.js.map +1 -0
  82. package/dist/passages/story/types.d.ts +911 -0
  83. package/dist/passages/story/types.d.ts.map +1 -0
  84. package/dist/passages/story/types.js +2 -0
  85. package/dist/passages/story/types.js.map +1 -0
  86. package/dist/passages/types/index.d.ts +3 -0
  87. package/dist/passages/types/index.d.ts.map +1 -0
  88. package/dist/passages/types/index.js +2 -0
  89. package/dist/passages/types/index.js.map +1 -0
  90. package/dist/passages/widget.d.ts +62 -0
  91. package/dist/passages/widget.d.ts.map +1 -0
  92. package/dist/passages/widget.js +66 -0
  93. package/dist/passages/widget.js.map +1 -0
  94. package/dist/saves/constants.d.ts +17 -0
  95. package/dist/saves/constants.d.ts.map +1 -0
  96. package/dist/saves/constants.js +17 -0
  97. package/dist/saves/constants.js.map +1 -0
  98. package/dist/saves/db.d.ts +119 -0
  99. package/dist/saves/db.d.ts.map +1 -0
  100. package/dist/saves/db.js +231 -0
  101. package/dist/saves/db.js.map +1 -0
  102. package/dist/saves/helpers.d.ts +28 -0
  103. package/dist/saves/helpers.d.ts.map +1 -0
  104. package/dist/saves/helpers.js +84 -0
  105. package/dist/saves/helpers.js.map +1 -0
  106. package/dist/saves/hooks/index.d.ts +10 -0
  107. package/dist/saves/hooks/index.d.ts.map +1 -0
  108. package/dist/saves/hooks/index.js +10 -0
  109. package/dist/saves/hooks/index.js.map +1 -0
  110. package/dist/saves/hooks/useDeleteAllSlots.d.ts +18 -0
  111. package/dist/saves/hooks/useDeleteAllSlots.d.ts.map +1 -0
  112. package/dist/saves/hooks/useDeleteAllSlots.js +18 -0
  113. package/dist/saves/hooks/useDeleteAllSlots.js.map +1 -0
  114. package/dist/saves/hooks/useDeleteGame.d.ts +22 -0
  115. package/dist/saves/hooks/useDeleteGame.d.ts.map +1 -0
  116. package/dist/saves/hooks/useDeleteGame.js +33 -0
  117. package/dist/saves/hooks/useDeleteGame.js.map +1 -0
  118. package/dist/saves/hooks/useExportSaves.d.ts +27 -0
  119. package/dist/saves/hooks/useExportSaves.d.ts.map +1 -0
  120. package/dist/saves/hooks/useExportSaves.js +54 -0
  121. package/dist/saves/hooks/useExportSaves.js.map +1 -0
  122. package/dist/saves/hooks/useImportSaves.d.ts +29 -0
  123. package/dist/saves/hooks/useImportSaves.d.ts.map +1 -0
  124. package/dist/saves/hooks/useImportSaves.js +108 -0
  125. package/dist/saves/hooks/useImportSaves.js.map +1 -0
  126. package/dist/saves/hooks/useLastLoadGame.d.ts +39 -0
  127. package/dist/saves/hooks/useLastLoadGame.d.ts.map +1 -0
  128. package/dist/saves/hooks/useLastLoadGame.js +72 -0
  129. package/dist/saves/hooks/useLastLoadGame.js.map +1 -0
  130. package/dist/saves/hooks/useLoadGame.d.ts +22 -0
  131. package/dist/saves/hooks/useLoadGame.d.ts.map +1 -0
  132. package/dist/saves/hooks/useLoadGame.js +40 -0
  133. package/dist/saves/hooks/useLoadGame.js.map +1 -0
  134. package/dist/saves/hooks/useRestartGame.d.ts +20 -0
  135. package/dist/saves/hooks/useRestartGame.d.ts.map +1 -0
  136. package/dist/saves/hooks/useRestartGame.js +29 -0
  137. package/dist/saves/hooks/useRestartGame.js.map +1 -0
  138. package/dist/saves/hooks/useSaveGame.d.ts +22 -0
  139. package/dist/saves/hooks/useSaveGame.d.ts.map +1 -0
  140. package/dist/saves/hooks/useSaveGame.js +34 -0
  141. package/dist/saves/hooks/useSaveGame.js.map +1 -0
  142. package/dist/saves/hooks/useSaveSlots.d.ts +45 -0
  143. package/dist/saves/hooks/useSaveSlots.d.ts.map +1 -0
  144. package/dist/saves/hooks/useSaveSlots.js +42 -0
  145. package/dist/saves/hooks/useSaveSlots.js.map +1 -0
  146. package/dist/saves/index.d.ts +4 -0
  147. package/dist/saves/index.d.ts.map +1 -0
  148. package/dist/saves/index.js +3 -0
  149. package/dist/saves/index.js.map +1 -0
  150. package/dist/saves/types.d.ts +52 -0
  151. package/dist/saves/types.d.ts.map +1 -0
  152. package/dist/saves/types.js +2 -0
  153. package/dist/saves/types.js.map +1 -0
  154. package/dist/storage.d.ts +124 -0
  155. package/dist/storage.d.ts.map +1 -0
  156. package/dist/storage.js +229 -0
  157. package/dist/storage.js.map +1 -0
  158. package/dist/tests/game.test.d.ts +2 -0
  159. package/dist/tests/game.test.d.ts.map +1 -0
  160. package/dist/tests/game.test.js +602 -0
  161. package/dist/tests/game.test.js.map +1 -0
  162. package/dist/tests/interactiveMap.test.d.ts +2 -0
  163. package/dist/tests/interactiveMap.test.d.ts.map +1 -0
  164. package/dist/tests/interactiveMap.test.js +1003 -0
  165. package/dist/tests/interactiveMap.test.js.map +1 -0
  166. package/dist/tests/storage.test.d.ts +2 -0
  167. package/dist/tests/storage.test.d.ts.map +1 -0
  168. package/dist/tests/storage.test.js +328 -0
  169. package/dist/tests/storage.test.js.map +1 -0
  170. package/dist/tests/story.test.d.ts +2 -0
  171. package/dist/tests/story.test.d.ts.map +1 -0
  172. package/dist/tests/story.test.js +698 -0
  173. package/dist/tests/story.test.js.map +1 -0
  174. package/dist/types.d.ts +19 -0
  175. package/dist/types.d.ts.map +1 -0
  176. package/dist/types.js +2 -0
  177. package/dist/types.js.map +1 -0
  178. package/package.json +60 -0
@@ -0,0 +1,822 @@
1
+ import { ButtonColor, ButtonVariant, MaybeCallable } from "../../types";
2
+ /**
3
+ * Base interface shared by all hotspot types.
4
+ * Provides common properties for interaction, identification, and state management.
5
+ */
6
+ interface BaseHotspot {
7
+ /**
8
+ * Optional unique identifier for this hotspot.
9
+ * Can be used for debugging, analytics, or programmatic hotspot manipulation.
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * id: 'village-entrance'
14
+ * id: 'shop-button'
15
+ * ```
16
+ */
17
+ id?: string;
18
+ /**
19
+ * Callback function executed when the hotspot is clicked.
20
+ * Called only when the hotspot is not disabled.
21
+ *
22
+ * @example
23
+ * ```typescript
24
+ * // Navigate to another passage
25
+ * action: () => Game.jumpTo('village')
26
+ *
27
+ * // Perform complex game logic
28
+ * action: () => {
29
+ * player.gold -= 50;
30
+ * player.inventory.add('sword');
31
+ * Game.jumpTo('shop-exit');
32
+ * }
33
+ * ```
34
+ */
35
+ action: () => void;
36
+ /**
37
+ * Controls whether the hotspot is interactive.
38
+ * Can be a static boolean or a function for dynamic state.
39
+ *
40
+ * @defaultValue false
41
+ *
42
+ * @example
43
+ * ```typescript
44
+ * // Static disabled state
45
+ * isDisabled: true
46
+ *
47
+ * // Dynamic based on game state
48
+ * isDisabled: () => player.gold < 50
49
+ * isDisabled: () => !player.hasKey
50
+ * ```
51
+ *
52
+ * @remarks
53
+ * When disabled:
54
+ * - Hotspot cannot be clicked
55
+ * - Visual appearance changes (usually dimmed/grayed out)
56
+ * - For image hotspots, the "disabled" image variant is shown if provided
57
+ * - Tooltip still displays to explain why it's disabled
58
+ */
59
+ isDisabled?: boolean | (() => boolean);
60
+ /**
61
+ * Optional tooltip configuration.
62
+ * Displays additional information when hovering over the hotspot.
63
+ */
64
+ tooltip?: {
65
+ /**
66
+ * The text to display in the tooltip.
67
+ * Can be static string or a function for dynamic content.
68
+ *
69
+ * @example
70
+ * ```typescript
71
+ * // Static tooltip
72
+ * content: 'Click to enter the village'
73
+ *
74
+ * // Dynamic tooltip based on state
75
+ * content: () => player.hasKey
76
+ * ? 'Unlock the door'
77
+ * : 'You need a key to unlock this door'
78
+ * ```
79
+ */
80
+ content: string | (() => string);
81
+ /**
82
+ * Position of the tooltip relative to the hotspot.
83
+ *
84
+ * @defaultValue `"top"`
85
+ */
86
+ position?: "top" | "bottom" | "left" | "right";
87
+ };
88
+ }
89
+ /**
90
+ * Text-based button hotspot for interactive maps.
91
+ * Displays as a styled button with customizable text and appearance.
92
+ *
93
+ * @example
94
+ * ```typescript
95
+ * // Simple label hotspot
96
+ * {
97
+ * type: 'label',
98
+ * content: 'Village Entrance',
99
+ * action: () => Game.jumpTo('village')
100
+ * }
101
+ *
102
+ * // Dynamic label with custom styling
103
+ * {
104
+ * type: 'label',
105
+ * content: () => `Gold: ${player.gold}`,
106
+ * action: () => openInventory(),
107
+ * props: {
108
+ * variant: 'bordered',
109
+ * color: 'warning'
110
+ * }
111
+ * }
112
+ * ```
113
+ */
114
+ export interface LabelHotspot extends BaseHotspot {
115
+ /**
116
+ * Discriminator property identifying this as a label hotspot.
117
+ */
118
+ type: "label";
119
+ /**
120
+ * The text to display on the button.
121
+ * Can be static string or a function for dynamic content.
122
+ *
123
+ * @example
124
+ * ```typescript
125
+ * // Static label
126
+ * content: 'Enter Shop'
127
+ *
128
+ * // Dynamic label
129
+ * content: () => `Health: ${player.health}/100`
130
+ * content: () => player.hasVisited ? 'Return to Town' : 'Discover Town'
131
+ * ```
132
+ */
133
+ content: string | (() => string);
134
+ /**
135
+ * Optional configuration for button styling and appearance.
136
+ */
137
+ props?: {
138
+ /**
139
+ * CSS class name overrides.
140
+ */
141
+ classNames?: {
142
+ /**
143
+ * CSS class for the button element.
144
+ *
145
+ * @example
146
+ * ```typescript
147
+ * button: 'text-lg font-bold px-6 py-3'
148
+ * ```
149
+ */
150
+ button?: string;
151
+ };
152
+ /**
153
+ * Visual style variant for the button.
154
+ *
155
+ * @defaultValue `"solid"`
156
+ *
157
+ * @see {@link ButtonVariant} for available options
158
+ */
159
+ variant?: ButtonVariant;
160
+ /**
161
+ * Color scheme for the button.
162
+ * Maps to semantic color tokens in the UI theme.
163
+ *
164
+ * @defaultValue `"primary"`
165
+ *
166
+ * @see {@link ButtonColor} for available options
167
+ */
168
+ color?: ButtonColor;
169
+ };
170
+ }
171
+ /**
172
+ * Image-based hotspot with state-dependent visuals.
173
+ * Displays different images for idle, hover, active, and disabled states.
174
+ *
175
+ * @example
176
+ * ```typescript
177
+ * // Basic image hotspot with hover effect
178
+ * {
179
+ * type: 'image',
180
+ * content: {
181
+ * idle: '/icons/chest.png',
182
+ * hover: '/icons/chest-glow.png',
183
+ * active: '/icons/chest-open.png'
184
+ * },
185
+ * action: () => openChest()
186
+ * }
187
+ *
188
+ * // Dynamic image with disabled state
189
+ * {
190
+ * type: 'image',
191
+ * content: {
192
+ * idle: '/icons/door.png',
193
+ * hover: '/icons/door-highlight.png',
194
+ * disabled: '/icons/door-locked.png'
195
+ * },
196
+ * isDisabled: () => !player.hasKey,
197
+ * action: () => Game.jumpTo('next-room'),
198
+ * tooltip: {
199
+ * content: () => player.hasKey ? 'Enter' : 'Locked - Find the key'
200
+ * }
201
+ * }
202
+ *
203
+ * // Scaled image hotspot
204
+ * {
205
+ * type: 'image',
206
+ * content: { idle: '/icons/small-item.png' },
207
+ * props: { zoom: '150%' },
208
+ * action: () => pickupItem()
209
+ * }
210
+ * ```
211
+ */
212
+ export interface ImageHotspot extends BaseHotspot {
213
+ /**
214
+ * Discriminator property identifying this as an image hotspot.
215
+ */
216
+ type: "image";
217
+ /**
218
+ * Image URLs/paths for different hotspot states.
219
+ * At minimum, the `idle` state is required.
220
+ */
221
+ content: {
222
+ /**
223
+ * Image displayed in the default/resting state.
224
+ * Always shown when no other state is active.
225
+ *
226
+ * @example
227
+ * ```typescript
228
+ * idle: '/icons/button-default.png'
229
+ * idle: () => `/icons/${currentTheme}/button.png`
230
+ * ```
231
+ */
232
+ idle: string | (() => string);
233
+ /**
234
+ * Optional image displayed when the hotspot is hovered.
235
+ * If not provided, the idle image is shown on hover.
236
+ *
237
+ * @example
238
+ * ```typescript
239
+ * hover: '/icons/button-hover.png'
240
+ * hover: () => `/icons/button-${hoverColor}.png`
241
+ * ```
242
+ */
243
+ hover?: string | (() => string);
244
+ /**
245
+ * Optional image displayed briefly when the hotspot is clicked.
246
+ * Creates visual feedback for the click action.
247
+ * If not provided, the hover or idle image is shown on click.
248
+ *
249
+ * @example
250
+ * ```typescript
251
+ * active: '/icons/button-pressed.png'
252
+ * active: '/icons/button-flash.png'
253
+ * ```
254
+ *
255
+ * @remarks
256
+ * The active state is shown for ~100ms when clicked, then returns to idle/hover.
257
+ */
258
+ active?: string | (() => string);
259
+ /**
260
+ * Optional image displayed when the hotspot is disabled.
261
+ * If not provided, the idle image is shown with reduced opacity when disabled.
262
+ *
263
+ * @example
264
+ * ```typescript
265
+ * disabled: '/icons/button-grayed.png'
266
+ * disabled: '/icons/button-locked.png'
267
+ * ```
268
+ */
269
+ disabled?: string | (() => string);
270
+ };
271
+ /**
272
+ * Optional configuration for sizing and styling.
273
+ */
274
+ props?: {
275
+ /**
276
+ * CSS zoom level for the hotspot image.
277
+ * Useful for making small images more visible without recreating assets.
278
+ *
279
+ * @example
280
+ * ```typescript
281
+ * zoom: '150%' // Make image 1.5x larger
282
+ * zoom: '200%' // Double the size
283
+ * zoom: '75%' // Make smaller
284
+ * ```
285
+ *
286
+ * @remarks
287
+ * Zoom is applied via CSS and may affect image quality.
288
+ * For best results, use appropriately-sized source images.
289
+ */
290
+ zoom?: `${number}%`;
291
+ /**
292
+ * CSS class name overrides for different states.
293
+ */
294
+ classNames?: {
295
+ /**
296
+ * CSS class for the hotspot container element.
297
+ *
298
+ * @example
299
+ * ```typescript
300
+ * container: 'shadow-lg rounded-full'
301
+ * ```
302
+ */
303
+ container?: string;
304
+ /**
305
+ * CSS class for the idle state image.
306
+ */
307
+ idle?: string;
308
+ /**
309
+ * CSS class for the hover state image.
310
+ */
311
+ hover?: string;
312
+ /**
313
+ * CSS class for the active/clicked state image.
314
+ */
315
+ active?: string;
316
+ /**
317
+ * CSS class for the disabled state image.
318
+ */
319
+ disabled?: string;
320
+ };
321
+ };
322
+ }
323
+ /**
324
+ * Position mixin for hotspots placed on the map image itself.
325
+ * Positions are percentage-based (0-100) relative to the map dimensions.
326
+ */
327
+ interface BaseMapHotspot {
328
+ /**
329
+ * Position coordinates on the map.
330
+ * Values are percentages (0-100) of the map's width and height.
331
+ */
332
+ position: {
333
+ /**
334
+ * Horizontal position as a percentage (0-100) from the left edge.
335
+ * Can be static number or a function for dynamic positioning.
336
+ *
337
+ * @example
338
+ * ```typescript
339
+ * x: 50 // Center horizontally
340
+ * x: 25 // 25% from the left
341
+ * x: () => player.isAtNight ? 30 : 70 // Dynamic based on state
342
+ * ```
343
+ *
344
+ * @remarks
345
+ * - 0 = left edge of the map
346
+ * - 50 = horizontal center
347
+ * - 100 = right edge of the map
348
+ */
349
+ x: number | (() => number);
350
+ /**
351
+ * Vertical position as a percentage (0-100) from the top edge.
352
+ * Can be static number or a function for dynamic positioning.
353
+ *
354
+ * @example
355
+ * ```typescript
356
+ * y: 50 // Center vertically
357
+ * y: 75 // 75% from the top
358
+ * y: () => player.level * 10 // Dynamic positioning
359
+ * ```
360
+ *
361
+ * @remarks
362
+ * - 0 = top edge of the map
363
+ * - 50 = vertical center
364
+ * - 100 = bottom edge of the map
365
+ */
366
+ y: number | (() => number);
367
+ };
368
+ }
369
+ /**
370
+ * Label hotspot positioned on the map image.
371
+ * Combines text button functionality with percentage-based map positioning.
372
+ *
373
+ * @example
374
+ * ```typescript
375
+ * {
376
+ * type: 'label',
377
+ * content: 'Village',
378
+ * position: { x: 30, y: 40 },
379
+ * action: () => Game.jumpTo('village')
380
+ * }
381
+ * ```
382
+ */
383
+ export interface MapLabelHotspot extends LabelHotspot, BaseMapHotspot {
384
+ }
385
+ /**
386
+ * Image hotspot positioned on the map image.
387
+ * Combines image-based interaction with percentage-based map positioning.
388
+ *
389
+ * @example
390
+ * ```typescript
391
+ * {
392
+ * type: 'image',
393
+ * content: {
394
+ * idle: '/icons/treasure.png',
395
+ * hover: '/icons/treasure-glow.png'
396
+ * },
397
+ * position: { x: 60, y: 70 },
398
+ * action: () => openTreasure()
399
+ * }
400
+ * ```
401
+ */
402
+ export interface MapImageHotspot extends ImageHotspot, BaseMapHotspot {
403
+ }
404
+ /**
405
+ * Position mixin for hotspots placed on the edges/sides of the map.
406
+ * Side hotspots appear outside the main map area in fixed positions.
407
+ */
408
+ interface SideHotspot {
409
+ /**
410
+ * Which edge of the map to place this hotspot.
411
+ *
412
+ * @remarks
413
+ * Side hotspots are useful for:
414
+ * - Persistent UI elements that shouldn't overlap the map
415
+ * - Navigation controls
416
+ * - Status displays
417
+ * - Menu buttons
418
+ *
419
+ * Multiple hotspots on the same side are stacked in order.
420
+ */
421
+ position: "left" | "right" | "top" | "bottom";
422
+ }
423
+ /**
424
+ * Label hotspot positioned on the edge of the map.
425
+ * Appears outside the main map area, on one of the four sides.
426
+ *
427
+ * @example
428
+ * ```typescript
429
+ * {
430
+ * type: 'label',
431
+ * content: 'Menu',
432
+ * position: 'top',
433
+ * action: () => openMenu()
434
+ * }
435
+ * ```
436
+ */
437
+ export interface SideLabelHotspot extends LabelHotspot, SideHotspot {
438
+ }
439
+ /**
440
+ * Image hotspot positioned on the edge of the map.
441
+ * Appears outside the main map area, on one of the four sides.
442
+ *
443
+ * @example
444
+ * ```typescript
445
+ * {
446
+ * type: 'image',
447
+ * content: { idle: '/icons/compass.png' },
448
+ * position: 'bottom',
449
+ * action: () => toggleCompass()
450
+ * }
451
+ * ```
452
+ */
453
+ export interface SideImageHotspot extends ImageHotspot, SideHotspot {
454
+ }
455
+ /**
456
+ * Contextual menu hotspot that displays multiple label buttons at a specific position.
457
+ * Useful for creating radial menus, action lists, or grouped choices on the map.
458
+ *
459
+ * @example
460
+ * ```typescript
461
+ * // Context menu at a location
462
+ * {
463
+ * type: 'menu',
464
+ * position: { x: 50, y: 50 },
465
+ * direction: 'vertical',
466
+ * items: [
467
+ * { type: 'label', content: 'Examine', action: () => examine() },
468
+ * { type: 'label', content: 'Take', action: () => take() },
469
+ * { type: 'label', content: 'Leave', action: () => leave() }
470
+ * ]
471
+ * }
472
+ *
473
+ * // Dynamic menu with conditional items
474
+ * {
475
+ * type: 'menu',
476
+ * position: { x: () => cursor.x, y: () => cursor.y },
477
+ * items: [
478
+ * { type: 'label', content: 'Attack', action: () => attack() },
479
+ * () => player.hasMagic ? {
480
+ * type: 'label',
481
+ * content: 'Cast Spell',
482
+ * action: () => castSpell()
483
+ * } : undefined,
484
+ * { type: 'label', content: 'Defend', action: () => defend() }
485
+ * ]
486
+ * }
487
+ * ```
488
+ */
489
+ export interface MapMenu {
490
+ /**
491
+ * Discriminator property identifying this as a menu hotspot.
492
+ */
493
+ type: "menu";
494
+ /**
495
+ * Array of menu items to display.
496
+ * Each item is a LabelHotspot or a function returning one.
497
+ * Functions that return `undefined` are filtered out (useful for conditional menu items).
498
+ *
499
+ * @example
500
+ * ```typescript
501
+ * // Static menu items
502
+ * items: [
503
+ * { type: 'label', content: 'Option 1', action: () => {} },
504
+ * { type: 'label', content: 'Option 2', action: () => {} }
505
+ * ]
506
+ *
507
+ * // Conditional menu items
508
+ * items: [
509
+ * { type: 'label', content: 'Always visible', action: () => {} },
510
+ * () => player.hasItem ? {
511
+ * type: 'label',
512
+ * content: 'Use Item',
513
+ * action: () => useItem()
514
+ * } : undefined
515
+ * ]
516
+ * ```
517
+ */
518
+ items: Array<MaybeCallable<LabelHotspot | undefined>>;
519
+ /**
520
+ * Position of the menu on the map.
521
+ * Values are percentages (0-100) relative to the map dimensions.
522
+ */
523
+ position: {
524
+ /**
525
+ * Horizontal position as a percentage (0-100) from the left edge.
526
+ * Can be static number or a function for dynamic positioning.
527
+ *
528
+ * @example
529
+ * ```typescript
530
+ * x: 50 // Center horizontally
531
+ * x: () => player.cursorX // Follow cursor
532
+ * ```
533
+ */
534
+ x: MaybeCallable<number>;
535
+ /**
536
+ * Vertical position as a percentage (0-100) from the top edge.
537
+ * Can be static number or a function for dynamic positioning.
538
+ *
539
+ * @example
540
+ * ```typescript
541
+ * y: 50 // Center vertically
542
+ * y: () => player.cursorY // Follow cursor
543
+ * ```
544
+ */
545
+ y: MaybeCallable<number>;
546
+ };
547
+ /**
548
+ * Layout direction for menu items.
549
+ *
550
+ * @defaultValue `"vertical"`
551
+ *
552
+ * @remarks
553
+ * - `"vertical"` - Items stacked top to bottom (default)
554
+ * - `"horizontal"` - Items arranged left to right
555
+ */
556
+ direction?: "horizontal" | "vertical";
557
+ /**
558
+ * Optional styling configuration.
559
+ */
560
+ props?: {
561
+ /**
562
+ * CSS class name for the menu container.
563
+ *
564
+ * @example
565
+ * ```typescript
566
+ * className: 'bg-card/90 backdrop-blur-sm rounded-lg shadow-xl p-2'
567
+ * ```
568
+ */
569
+ className?: string;
570
+ };
571
+ }
572
+ /**
573
+ * Union type of all possible hotspot types.
574
+ * Used for type-safe hotspot arrays in interactive maps.
575
+ *
576
+ * @remarks
577
+ * This discriminated union allows TypeScript to narrow hotspot types
578
+ * based on the `type` property when rendering or processing hotspots.
579
+ */
580
+ export type AnyHotspot = MapLabelHotspot | MapImageHotspot | SideLabelHotspot | SideImageHotspot | MapMenu;
581
+ /**
582
+ * Function type for dynamic hotspot generation.
583
+ * Returns a hotspot or undefined (for conditional hotspots).
584
+ *
585
+ * @returns Hotspot configuration or undefined if the hotspot should not be displayed
586
+ *
587
+ * @example
588
+ * ```typescript
589
+ * // Conditional hotspot based on game state
590
+ * const dynamicHotspot: HotspotCallback = () =>
591
+ * player.hasKey ? {
592
+ * type: 'label',
593
+ * content: 'Unlock Door',
594
+ * position: { x: 50, y: 50 },
595
+ * action: () => unlockDoor()
596
+ * } : undefined;
597
+ * ```
598
+ */
599
+ export type HotspotCallback = () => AnyHotspot | undefined;
600
+ /**
601
+ * Configuration options for creating an interactive map passage.
602
+ * Defines the map image, background, hotspots, and styling.
603
+ *
604
+ * @example
605
+ * ```typescript
606
+ * const mapOptions: InteractiveMapOptions = {
607
+ * caption: 'World Map',
608
+ * image: '/maps/world.jpg',
609
+ * bgImage: '/maps/world-bg.jpg',
610
+ * props: { bgOpacity: 0.3 },
611
+ * hotspots: [
612
+ * {
613
+ * type: 'label',
614
+ * content: 'Village',
615
+ * position: { x: 30, y: 40 },
616
+ * action: () => Game.jumpTo('village')
617
+ * },
618
+ * () => player.hasDiscovered('forest') ? {
619
+ * type: 'image',
620
+ * content: { idle: '/icons/forest.png' },
621
+ * position: { x: 60, y: 70 },
622
+ * action: () => Game.jumpTo('forest')
623
+ * } : undefined
624
+ * ],
625
+ * classNames: {
626
+ * container: 'bg-gradient-to-b from-sky-900 to-indigo-900'
627
+ * }
628
+ * };
629
+ * ```
630
+ */
631
+ export type InteractiveMapOptions = {
632
+ /**
633
+ * Optional caption or title for the map.
634
+ * Displayed above the map area (implementation depends on UI).
635
+ *
636
+ * @example
637
+ * ```typescript
638
+ * caption: 'Kingdom of Eldoria'
639
+ * caption: 'Floor 1 - Dungeon'
640
+ * ```
641
+ */
642
+ caption?: string;
643
+ /**
644
+ * URL or path to the main map image.
645
+ * Can be static string or a function for dynamic map selection.
646
+ *
647
+ * @example
648
+ * ```typescript
649
+ * // Static map
650
+ * image: '/maps/world.jpg'
651
+ *
652
+ * // Dynamic based on time of day
653
+ * image: () => isNight ? '/maps/world-night.jpg' : '/maps/world-day.jpg'
654
+ *
655
+ * // Based on player progress
656
+ * image: () => `/maps/world-level-${player.level}.jpg`
657
+ * ```
658
+ */
659
+ image: string | (() => string);
660
+ /**
661
+ * Array of hotspots to display on the map.
662
+ * Can include static hotspots or functions that return hotspots dynamically.
663
+ * Functions returning `undefined` are filtered out (useful for conditional hotspots).
664
+ *
665
+ * @example
666
+ * ```typescript
667
+ * hotspots: [
668
+ * // Static hotspot - always visible
669
+ * {
670
+ * type: 'label',
671
+ * content: 'Home',
672
+ * position: { x: 50, y: 50 },
673
+ * action: () => Game.jumpTo('home')
674
+ * },
675
+ *
676
+ * // Dynamic hotspot - conditional visibility
677
+ * () => player.hasKey ? {
678
+ * type: 'label',
679
+ * content: 'Secret Room',
680
+ * position: { x: 80, y: 30 },
681
+ * action: () => Game.jumpTo('secret')
682
+ * } : undefined,
683
+ *
684
+ * // Side hotspot
685
+ * {
686
+ * type: 'label',
687
+ * content: 'Menu',
688
+ * position: 'top',
689
+ * action: () => openMenu()
690
+ * }
691
+ * ]
692
+ * ```
693
+ */
694
+ hotspots: Array<MaybeCallable<AnyHotspot | undefined>>;
695
+ /**
696
+ * Optional background image URL or path.
697
+ * Displayed behind the main map image with configurable opacity.
698
+ * Can be static string or a function for dynamic backgrounds.
699
+ *
700
+ * @example
701
+ * ```typescript
702
+ * // Static background
703
+ * bgImage: '/backgrounds/parchment.jpg'
704
+ *
705
+ * // Dynamic background
706
+ * bgImage: () => `/backgrounds/${currentSeason}.jpg`
707
+ * ```
708
+ *
709
+ * @remarks
710
+ * Use this for:
711
+ * - Decorative borders or frames
712
+ * - Atmospheric effects (clouds, fog, etc.)
713
+ * - Contextual backgrounds that change with game state
714
+ */
715
+ bgImage?: string | (() => string);
716
+ /**
717
+ * Optional configuration for map behavior.
718
+ */
719
+ props?: {
720
+ /**
721
+ * Opacity of the background image (0-1).
722
+ *
723
+ * @defaultValue 1
724
+ *
725
+ * @example
726
+ * ```typescript
727
+ * bgOpacity: 0.5 // 50% transparent
728
+ * bgOpacity: 0.2 // Subtle background
729
+ * bgOpacity: 1 // Fully opaque (default)
730
+ * ```
731
+ */
732
+ bgOpacity?: number;
733
+ };
734
+ /**
735
+ * CSS class name overrides for different map regions.
736
+ */
737
+ classNames?: {
738
+ /**
739
+ * CSS class for the main map container.
740
+ * Controls overall layout and styling.
741
+ *
742
+ * @example
743
+ * ```typescript
744
+ * container: 'bg-gradient-to-b from-blue-900 to-black'
745
+ * ```
746
+ */
747
+ container?: string;
748
+ /**
749
+ * CSS class for the top hotspot area.
750
+ * Applied to the container holding hotspots with `position: 'top'`.
751
+ *
752
+ * @example
753
+ * ```typescript
754
+ * topHotspots: 'bg-muted/50 backdrop-blur-sm p-2'
755
+ * ```
756
+ */
757
+ topHotspots?: string;
758
+ /**
759
+ * CSS class for the bottom hotspot area.
760
+ * Applied to the container holding hotspots with `position: 'bottom'`.
761
+ */
762
+ bottomHotspots?: string;
763
+ /**
764
+ * CSS class for the left hotspot area.
765
+ * Applied to the container holding hotspots with `position: 'left'`.
766
+ */
767
+ leftHotspots?: string;
768
+ /**
769
+ * CSS class for the right hotspot area.
770
+ * Applied to the container holding hotspots with `position: 'right'`.
771
+ */
772
+ rightHotspots?: string;
773
+ };
774
+ };
775
+ /**
776
+ * Resolved/processed interactive map data returned by `InteractiveMap.display()`.
777
+ * All dynamic values (functions) have been resolved to their concrete values.
778
+ *
779
+ * @remarks
780
+ * This type represents the map after processing:
781
+ * - All function-based images resolved to strings
782
+ * - All conditional hotspots evaluated and filtered (undefined removed)
783
+ * - All dynamic hotspot properties resolved
784
+ *
785
+ * This is the data structure consumed by the UI rendering layer.
786
+ *
787
+ * @example
788
+ * ```typescript
789
+ * const map = newInteractiveMap('world', {
790
+ * image: () => `/maps/${season}.jpg`,
791
+ * hotspots: [
792
+ * () => hasKey ? { type: 'label', content: 'Door', ... } : undefined
793
+ * ]
794
+ * });
795
+ *
796
+ * // After calling display(), all functions are resolved:
797
+ * const displayData: InteractiveMapType = map.display();
798
+ * // displayData.image is now a string like '/maps/winter.jpg'
799
+ * // displayData.hotspots contains only concrete hotspots (undefined filtered out)
800
+ * ```
801
+ */
802
+ export type InteractiveMapType = Pick<InteractiveMapOptions, "caption" | "props" | "classNames"> & {
803
+ /**
804
+ * Resolved map image URL/path.
805
+ * If the original was a function, it has been called and resolved to a string.
806
+ */
807
+ image: string;
808
+ /**
809
+ * Resolved background image URL/path.
810
+ * If the original was a function, it has been called and resolved to a string.
811
+ * Undefined if no background image was specified.
812
+ */
813
+ bgImage?: string;
814
+ /**
815
+ * Array of resolved, concrete hotspots.
816
+ * All dynamic hotspots (functions) have been evaluated.
817
+ * Hotspots that returned `undefined` have been filtered out.
818
+ */
819
+ hotspots: Array<AnyHotspot>;
820
+ };
821
+ export {};
822
+ //# sourceMappingURL=types.d.ts.map