@datamoc/mw_games 0.7.6 → 0.7.7

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 (283) hide show
  1. package/CHANGELOG.md +270 -0
  2. package/dist/actors/Inventory.d.ts +7 -1
  3. package/dist/actors/Inventory.d.ts.map +1 -1
  4. package/dist/actors/Inventory.js.map +1 -1
  5. package/dist/actors/craft.d.ts +35 -14
  6. package/dist/actors/craft.d.ts.map +1 -1
  7. package/dist/actors/craft.js +56 -24
  8. package/dist/actors/craft.js.map +1 -1
  9. package/dist/actors/index.d.ts +1 -1
  10. package/dist/actors/index.d.ts.map +1 -1
  11. package/dist/ai/Heuristics.d.ts +232 -0
  12. package/dist/ai/Heuristics.d.ts.map +1 -0
  13. package/dist/ai/Heuristics.js +298 -0
  14. package/dist/ai/Heuristics.js.map +1 -0
  15. package/dist/ai/index.d.ts +4 -0
  16. package/dist/ai/index.d.ts.map +1 -1
  17. package/dist/ai/index.js +2 -0
  18. package/dist/ai/index.js.map +1 -1
  19. package/dist/ai/score.d.ts +126 -0
  20. package/dist/ai/score.d.ts.map +1 -0
  21. package/dist/ai/score.js +121 -0
  22. package/dist/ai/score.js.map +1 -0
  23. package/dist/assets/loader.d.ts +18 -4
  24. package/dist/assets/loader.d.ts.map +1 -1
  25. package/dist/assets/loader.js +40 -11
  26. package/dist/assets/loader.js.map +1 -1
  27. package/dist/audio/Positional.d.ts +125 -0
  28. package/dist/audio/Positional.d.ts.map +1 -0
  29. package/dist/audio/Positional.js +135 -0
  30. package/dist/audio/Positional.js.map +1 -0
  31. package/dist/audio/Sound.d.ts +7 -1
  32. package/dist/audio/Sound.d.ts.map +1 -1
  33. package/dist/audio/Sound.js +8 -2
  34. package/dist/audio/Sound.js.map +1 -1
  35. package/dist/audio/index.d.ts +2 -0
  36. package/dist/audio/index.d.ts.map +1 -1
  37. package/dist/audio/index.js +1 -0
  38. package/dist/audio/index.js.map +1 -1
  39. package/dist/battle/AttackDialog.d.ts +75 -0
  40. package/dist/battle/AttackDialog.d.ts.map +1 -0
  41. package/dist/battle/AttackDialog.js +115 -0
  42. package/dist/battle/AttackDialog.js.map +1 -0
  43. package/dist/battle/AttackPreview.d.ts +70 -0
  44. package/dist/battle/AttackPreview.d.ts.map +1 -0
  45. package/dist/battle/AttackPreview.js +78 -0
  46. package/dist/battle/AttackPreview.js.map +1 -0
  47. package/dist/battle/BattleStats.d.ts +45 -0
  48. package/dist/battle/BattleStats.d.ts.map +1 -0
  49. package/dist/battle/BattleStats.js +63 -0
  50. package/dist/battle/BattleStats.js.map +1 -0
  51. package/dist/battle/UnitSelector.d.ts +72 -0
  52. package/dist/battle/UnitSelector.d.ts.map +1 -0
  53. package/dist/battle/UnitSelector.js +117 -0
  54. package/dist/battle/UnitSelector.js.map +1 -0
  55. package/dist/battle/Whiteboard.d.ts +50 -0
  56. package/dist/battle/Whiteboard.d.ts.map +1 -0
  57. package/dist/battle/Whiteboard.js +94 -0
  58. package/dist/battle/Whiteboard.js.map +1 -0
  59. package/dist/battle/index.d.ts +10 -0
  60. package/dist/battle/index.d.ts.map +1 -1
  61. package/dist/battle/index.js +5 -0
  62. package/dist/battle/index.js.map +1 -1
  63. package/dist/board/FogOfWar.d.ts +32 -2
  64. package/dist/board/FogOfWar.d.ts.map +1 -1
  65. package/dist/board/FogOfWar.js +70 -6
  66. package/dist/board/FogOfWar.js.map +1 -1
  67. package/dist/core/Achievements.d.ts +40 -7
  68. package/dist/core/Achievements.d.ts.map +1 -1
  69. package/dist/core/Achievements.js +51 -7
  70. package/dist/core/Achievements.js.map +1 -1
  71. package/dist/core/Hex.d.ts +26 -6
  72. package/dist/core/Hex.d.ts.map +1 -1
  73. package/dist/core/Hex.js +40 -13
  74. package/dist/core/Hex.js.map +1 -1
  75. package/dist/core/Input.d.ts +36 -0
  76. package/dist/core/Input.d.ts.map +1 -1
  77. package/dist/core/Input.js +63 -1
  78. package/dist/core/Input.js.map +1 -1
  79. package/dist/core/Random.d.ts +93 -0
  80. package/dist/core/Random.d.ts.map +1 -1
  81. package/dist/core/Random.js +177 -0
  82. package/dist/core/Random.js.map +1 -1
  83. package/dist/core/SyncGuard.d.ts +57 -0
  84. package/dist/core/SyncGuard.d.ts.map +1 -0
  85. package/dist/core/SyncGuard.js +116 -0
  86. package/dist/core/SyncGuard.js.map +1 -0
  87. package/dist/core/index.d.ts +3 -1
  88. package/dist/core/index.d.ts.map +1 -1
  89. package/dist/core/index.js +2 -1
  90. package/dist/core/index.js.map +1 -1
  91. package/dist/i18n/Po.d.ts +51 -0
  92. package/dist/i18n/Po.d.ts.map +1 -0
  93. package/dist/i18n/Po.js +176 -0
  94. package/dist/i18n/Po.js.map +1 -0
  95. package/dist/i18n/index.d.ts +2 -0
  96. package/dist/i18n/index.d.ts.map +1 -1
  97. package/dist/i18n/index.js +1 -0
  98. package/dist/i18n/index.js.map +1 -1
  99. package/dist/mw_games.global.js +104 -98
  100. package/dist/mwl/MapFile.d.ts +41 -0
  101. package/dist/mwl/MapFile.d.ts.map +1 -0
  102. package/dist/mwl/MapFile.js +43 -0
  103. package/dist/mwl/MapFile.js.map +1 -0
  104. package/dist/mwl/campaign.d.ts +51 -0
  105. package/dist/mwl/campaign.d.ts.map +1 -0
  106. package/dist/mwl/campaign.js +58 -0
  107. package/dist/mwl/campaign.js.map +1 -0
  108. package/dist/mwl/carryover.d.ts +79 -0
  109. package/dist/mwl/carryover.d.ts.map +1 -0
  110. package/dist/mwl/carryover.js +63 -0
  111. package/dist/mwl/carryover.js.map +1 -0
  112. package/dist/mwl/content.d.ts +10 -0
  113. package/dist/mwl/content.d.ts.map +1 -1
  114. package/dist/mwl/content.js +8 -0
  115. package/dist/mwl/content.js.map +1 -1
  116. package/dist/mwl/hooks.d.ts +6 -5
  117. package/dist/mwl/hooks.d.ts.map +1 -1
  118. package/dist/mwl/hooks.js.map +1 -1
  119. package/dist/mwl/index.d.ts +8 -2
  120. package/dist/mwl/index.d.ts.map +1 -1
  121. package/dist/mwl/index.js +4 -1
  122. package/dist/mwl/index.js.map +1 -1
  123. package/dist/mwl/runtime.d.ts +140 -7
  124. package/dist/mwl/runtime.d.ts.map +1 -1
  125. package/dist/mwl/runtime.js +427 -33
  126. package/dist/mwl/runtime.js.map +1 -1
  127. package/dist/mwl/schema.d.ts.map +1 -1
  128. package/dist/mwl/schema.js +97 -18
  129. package/dist/mwl/schema.js.map +1 -1
  130. package/dist/roguelike/MultiTurnBeam.d.ts +50 -12
  131. package/dist/roguelike/MultiTurnBeam.d.ts.map +1 -1
  132. package/dist/roguelike/MultiTurnBeam.js +100 -53
  133. package/dist/roguelike/MultiTurnBeam.js.map +1 -1
  134. package/dist/roguelike/Targeting.d.ts +94 -0
  135. package/dist/roguelike/Targeting.d.ts.map +1 -1
  136. package/dist/roguelike/Targeting.js +123 -0
  137. package/dist/roguelike/Targeting.js.map +1 -1
  138. package/dist/roguelike/index.d.ts +3 -1
  139. package/dist/roguelike/index.d.ts.map +1 -1
  140. package/dist/roguelike/index.js +1 -0
  141. package/dist/roguelike/index.js.map +1 -1
  142. package/dist/simulation/CampaignSave.d.ts +68 -0
  143. package/dist/simulation/CampaignSave.d.ts.map +1 -0
  144. package/dist/simulation/CampaignSave.js +54 -0
  145. package/dist/simulation/CampaignSave.js.map +1 -0
  146. package/dist/simulation/EventPresentation.d.ts +111 -0
  147. package/dist/simulation/EventPresentation.d.ts.map +1 -0
  148. package/dist/simulation/EventPresentation.js +120 -0
  149. package/dist/simulation/EventPresentation.js.map +1 -0
  150. package/dist/simulation/index.d.ts +4 -0
  151. package/dist/simulation/index.d.ts.map +1 -1
  152. package/dist/simulation/index.js +2 -0
  153. package/dist/simulation/index.js.map +1 -1
  154. package/dist/two-d/pixi-interop.d.ts +18 -6
  155. package/dist/two-d/pixi-interop.d.ts.map +1 -1
  156. package/dist/two-d/pixi-interop.js +18 -6
  157. package/dist/two-d/pixi-interop.js.map +1 -1
  158. package/dist/two-d/render/AnimatedSprite.d.ts +74 -7
  159. package/dist/two-d/render/AnimatedSprite.d.ts.map +1 -1
  160. package/dist/two-d/render/AnimatedSprite.js +88 -26
  161. package/dist/two-d/render/AnimatedSprite.js.map +1 -1
  162. package/dist/two-d/render/Camera.d.ts +64 -4
  163. package/dist/two-d/render/Camera.d.ts.map +1 -1
  164. package/dist/two-d/render/Camera.js +132 -11
  165. package/dist/two-d/render/Camera.js.map +1 -1
  166. package/dist/two-d/render/Halo.d.ts +58 -0
  167. package/dist/two-d/render/Halo.d.ts.map +1 -0
  168. package/dist/two-d/render/Halo.js +48 -0
  169. package/dist/two-d/render/Halo.js.map +1 -0
  170. package/dist/two-d/render/ImageModifiers.d.ts +174 -0
  171. package/dist/two-d/render/ImageModifiers.d.ts.map +1 -1
  172. package/dist/two-d/render/ImageModifiers.js +438 -1
  173. package/dist/two-d/render/ImageModifiers.js.map +1 -1
  174. package/dist/two-d/render/LightningArc.d.ts +57 -0
  175. package/dist/two-d/render/LightningArc.d.ts.map +1 -0
  176. package/dist/two-d/render/LightningArc.js +91 -0
  177. package/dist/two-d/render/LightningArc.js.map +1 -0
  178. package/dist/two-d/render/PaletteRemap.d.ts +135 -0
  179. package/dist/two-d/render/PaletteRemap.d.ts.map +1 -0
  180. package/dist/two-d/render/PaletteRemap.js +181 -0
  181. package/dist/two-d/render/PaletteRemap.js.map +1 -0
  182. package/dist/two-d/render/Particles.d.ts +26 -0
  183. package/dist/two-d/render/Particles.d.ts.map +1 -1
  184. package/dist/two-d/render/Particles.js +23 -2
  185. package/dist/two-d/render/Particles.js.map +1 -1
  186. package/dist/two-d/render/SpriteAttachment.d.ts +55 -0
  187. package/dist/two-d/render/SpriteAttachment.d.ts.map +1 -0
  188. package/dist/two-d/render/SpriteAttachment.js +60 -0
  189. package/dist/two-d/render/SpriteAttachment.js.map +1 -0
  190. package/dist/two-d/render/TerrainGraphics.d.ts +142 -0
  191. package/dist/two-d/render/TerrainGraphics.d.ts.map +1 -0
  192. package/dist/two-d/render/TerrainGraphics.js +161 -0
  193. package/dist/two-d/render/TerrainGraphics.js.map +1 -0
  194. package/dist/two-d/render/index.d.ts +13 -3
  195. package/dist/two-d/render/index.d.ts.map +1 -1
  196. package/dist/two-d/render/index.js +6 -1
  197. package/dist/two-d/render/index.js.map +1 -1
  198. package/dist/two-d/stage/StoryScreen.d.ts +110 -0
  199. package/dist/two-d/stage/StoryScreen.d.ts.map +1 -0
  200. package/dist/two-d/stage/StoryScreen.js +178 -0
  201. package/dist/two-d/stage/StoryScreen.js.map +1 -0
  202. package/dist/two-d/stage/index.d.ts +2 -0
  203. package/dist/two-d/stage/index.d.ts.map +1 -1
  204. package/dist/two-d/stage/index.js +1 -0
  205. package/dist/two-d/stage/index.js.map +1 -1
  206. package/dist/two-d/ui/Checkbox.d.ts +44 -0
  207. package/dist/two-d/ui/Checkbox.d.ts.map +1 -0
  208. package/dist/two-d/ui/Checkbox.js +89 -0
  209. package/dist/two-d/ui/Checkbox.js.map +1 -0
  210. package/dist/two-d/ui/DataTable.d.ts +79 -0
  211. package/dist/two-d/ui/DataTable.d.ts.map +1 -0
  212. package/dist/two-d/ui/DataTable.js +164 -0
  213. package/dist/two-d/ui/DataTable.js.map +1 -0
  214. package/dist/two-d/ui/Dropdown.d.ts +72 -0
  215. package/dist/two-d/ui/Dropdown.d.ts.map +1 -0
  216. package/dist/two-d/ui/Dropdown.js +126 -0
  217. package/dist/two-d/ui/Dropdown.js.map +1 -0
  218. package/dist/two-d/ui/FloatingText.d.ts +43 -4
  219. package/dist/two-d/ui/FloatingText.d.ts.map +1 -1
  220. package/dist/two-d/ui/FloatingText.js +52 -5
  221. package/dist/two-d/ui/FloatingText.js.map +1 -1
  222. package/dist/two-d/ui/FloatingTextStack.d.ts +86 -13
  223. package/dist/two-d/ui/FloatingTextStack.d.ts.map +1 -1
  224. package/dist/two-d/ui/FloatingTextStack.js +89 -18
  225. package/dist/two-d/ui/FloatingTextStack.js.map +1 -1
  226. package/dist/two-d/ui/Layout.d.ts +105 -0
  227. package/dist/two-d/ui/Layout.d.ts.map +1 -0
  228. package/dist/two-d/ui/Layout.js +174 -0
  229. package/dist/two-d/ui/Layout.js.map +1 -0
  230. package/dist/two-d/ui/ScrollBox.d.ts +67 -0
  231. package/dist/two-d/ui/ScrollBox.d.ts.map +1 -0
  232. package/dist/two-d/ui/ScrollBox.js +127 -0
  233. package/dist/two-d/ui/ScrollBox.js.map +1 -0
  234. package/dist/two-d/ui/Skins.d.ts +59 -0
  235. package/dist/two-d/ui/Skins.d.ts.map +1 -0
  236. package/dist/two-d/ui/Skins.js +77 -0
  237. package/dist/two-d/ui/Skins.js.map +1 -0
  238. package/dist/two-d/ui/Slider.d.ts +91 -0
  239. package/dist/two-d/ui/Slider.d.ts.map +1 -0
  240. package/dist/two-d/ui/Slider.js +170 -0
  241. package/dist/two-d/ui/Slider.js.map +1 -0
  242. package/dist/two-d/ui/Spinner.d.ts +67 -0
  243. package/dist/two-d/ui/Spinner.d.ts.map +1 -0
  244. package/dist/two-d/ui/Spinner.js +137 -0
  245. package/dist/two-d/ui/Spinner.js.map +1 -0
  246. package/dist/two-d/ui/TabbedList.d.ts +118 -0
  247. package/dist/two-d/ui/TabbedList.d.ts.map +1 -0
  248. package/dist/two-d/ui/TabbedList.js +221 -0
  249. package/dist/two-d/ui/TabbedList.js.map +1 -0
  250. package/dist/two-d/ui/TextModel.d.ts +65 -0
  251. package/dist/two-d/ui/TextModel.d.ts.map +1 -0
  252. package/dist/two-d/ui/TextModel.js +126 -0
  253. package/dist/two-d/ui/TextModel.js.map +1 -0
  254. package/dist/two-d/ui/TreeView.d.ts +68 -0
  255. package/dist/two-d/ui/TreeView.d.ts.map +1 -0
  256. package/dist/two-d/ui/TreeView.js +133 -0
  257. package/dist/two-d/ui/TreeView.js.map +1 -0
  258. package/dist/two-d/ui/Window.d.ts +13 -0
  259. package/dist/two-d/ui/Window.d.ts.map +1 -1
  260. package/dist/two-d/ui/Window.js +23 -0
  261. package/dist/two-d/ui/Window.js.map +1 -1
  262. package/dist/two-d/ui/WindowStack.d.ts.map +1 -1
  263. package/dist/two-d/ui/WindowStack.js +2 -0
  264. package/dist/two-d/ui/WindowStack.js.map +1 -1
  265. package/dist/two-d/ui/index.d.ts +27 -3
  266. package/dist/two-d/ui/index.d.ts.map +1 -1
  267. package/dist/two-d/ui/index.js +14 -2
  268. package/dist/two-d/ui/index.js.map +1 -1
  269. package/dist/two-d/ui/markup.d.ts +127 -0
  270. package/dist/two-d/ui/markup.d.ts.map +1 -0
  271. package/dist/two-d/ui/markup.js +322 -0
  272. package/dist/two-d/ui/markup.js.map +1 -0
  273. package/dist/version.d.ts +1 -1
  274. package/dist/version.js +1 -1
  275. package/dist/world/SideTurns.d.ts +115 -0
  276. package/dist/world/SideTurns.d.ts.map +1 -0
  277. package/dist/world/SideTurns.js +138 -0
  278. package/dist/world/SideTurns.js.map +1 -0
  279. package/dist/world/index.d.ts +2 -0
  280. package/dist/world/index.d.ts.map +1 -1
  281. package/dist/world/index.js +1 -0
  282. package/dist/world/index.js.map +1 -1
  283. package/package.json +2 -1
@@ -3,9 +3,17 @@ import type { Step } from './Pathfinder.ts';
3
3
  export interface BeamDamageContext {
4
4
  cell: Step;
5
5
  step: number;
6
- /** the number of cells remaining after this one */
6
+ /** the number of cells remaining after this one, counting only fronts already resolved */
7
7
  remaining: number;
8
8
  }
9
+ /**
10
+ * What ends a beam cell, as one explicit choice rather than a combination of flags:
11
+ *
12
+ * - `'terrain'` (the default): outside the map or opaque terrain stops it, the ordinary beam.
13
+ * - `'none'`: it passes through terrain; only `isBlocked` (when given) can stop it.
14
+ * - a function: the game decides, terrain ignored.
15
+ */
16
+ export type BeamBlocker = 'terrain' | 'none' | ((cell: Step, context: BeamDamageContext) => boolean);
9
17
  export interface MultiTurnBeamOptions<T> {
10
18
  level: Level;
11
19
  from: Step;
@@ -15,31 +23,55 @@ export interface MultiTurnBeamOptions<T> {
15
23
  targetsAt?: (cell: Step) => readonly T[];
16
24
  /** Applies the resolved damage. The game owns health, resistances and effects. */
17
25
  applyDamage?: (target: T, amount: number, context: BeamDamageContext) => void;
18
- /** A dynamic blocker can stop a beam after it has started. */
26
+ /** An extra game rule on top of `blocker`, e.g. "a wall of fire melts the beam". */
19
27
  isBlocked?: (cell: Step, context: BeamDamageContext) => boolean;
20
- /** Whether the beam stops at opaque terrain. Defaults to true. */
28
+ /** The explicit blocker policy; defaults to `'terrain'`, matching a plain beam. */
29
+ blocker?: BeamBlocker;
30
+ /** @deprecated use `blocker: 'none'`; kept so existing content keeps its meaning */
21
31
  stopAtOpaque?: boolean;
32
+ /**
33
+ * Opt-in per-turn fronts instead of a straight line. Given the cells the previous turn
34
+ * actually reached (the origin alone at turn 0) and the turn number, it returns the cells
35
+ * this turn affects; an empty list ends the beam. A cone widens, a burst stops moving, a
36
+ * forked front splits, and a moving front is whatever the game returns each turn - none of
37
+ * which the framework has to understand, only sequence and save.
38
+ */
39
+ fronts?: (previous: readonly Step[], turn: number) => readonly Step[];
40
+ /** Names the shape this beam is, saved so a reload resumes the same one; defaults to `'line'`/`'fronts'`. */
41
+ shape?: string;
42
+ /** Called for every cell the front actually reaches, before targets are resolved. */
43
+ onCell?: (cell: Step, context: BeamDamageContext) => void;
22
44
  }
23
45
  export interface BeamStep<T> {
24
46
  status: 'idle' | 'active' | 'done' | 'blocked' | 'cancelled';
47
+ /** the first cell this turn reached, or null when the front was empty or fully blocked */
25
48
  cell: Step | null;
49
+ /** every cell this turn reached, in front order */
50
+ cells: readonly Step[];
26
51
  step: number;
27
52
  remaining: number;
28
53
  targets: readonly T[];
29
54
  damage: number;
30
55
  }
31
56
  export interface MultiTurnBeamSave {
32
- path: Step[];
33
- index: number;
34
57
  state: 'idle' | 'active' | 'done' | 'blocked' | 'cancelled';
58
+ /** every front resolved so far, oldest first; `fronts[index]` is the next to resolve */
59
+ fronts: Step[][];
60
+ index: number;
61
+ /** the shape the beam was built with, so a reload resumes the same one */
62
+ shape: string;
63
+ /** @deprecated a save written before fronts landed: one cell per turn */
64
+ path?: Step[];
35
65
  }
36
66
  /**
37
- * Advances a straight beam one cell per turn.
67
+ * Advances a beam one front per turn.
38
68
  *
39
- * The framework owns traversal and timing. A game supplies target lookup and damage
40
- * application, so this stays useful for hit points, shields, status effects and any other
41
- * combat model. The path is captured at construction time, while blockers and targets are
42
- * checked when each cell is reached. `advance` is deterministic when those callbacks are.
69
+ * The framework owns traversal and timing. A game supplies target lookup and damage application,
70
+ * so this stays useful for hit points, shields, status effects and any other combat model. The
71
+ * default shape is a straight line captured at construction time; `fronts` opts into a per-turn
72
+ * front resolver, which is what expresses a cone, a burst, a fork or a moving front without any
73
+ * of those rules living here. Blockers and targets are checked when each cell is reached, so
74
+ * `advance` is deterministic when those callbacks are.
43
75
  *
44
76
  * @example
45
77
  * ```ts
@@ -53,18 +85,24 @@ export interface MultiTurnBeamSave {
53
85
  */
54
86
  export declare class MultiTurnBeam<T = unknown> {
55
87
  private options;
56
- private path;
88
+ private fronts;
57
89
  private index;
58
90
  private state;
91
+ private shape;
59
92
  constructor(options: MultiTurnBeamOptions<T>);
60
93
  get active(): boolean;
61
94
  get done(): boolean;
95
+ /** The shape this beam was built with, for a save or a UI label. */
96
+ get beamShape(): string;
97
+ /** Every cell of every resolved front, in order - the straight path of a line beam. */
62
98
  get currentPath(): readonly Step[];
63
99
  start(): boolean;
64
- /** Resolves and applies one beam cell. Call once per game turn. */
100
+ /** Resolves and applies one beam front. Call once per game turn. */
65
101
  advance(): BeamStep<T>;
66
102
  cancel(): void;
67
103
  toJSON(): MultiTurnBeamSave;
68
104
  static fromJSON<T>(options: Omit<MultiTurnBeamOptions<T>, 'from' | 'target'>, data: MultiTurnBeamSave): MultiTurnBeam<T>;
105
+ private blocked;
106
+ private inactiveStep;
69
107
  }
70
108
  //# sourceMappingURL=MultiTurnBeam.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"MultiTurnBeam.d.ts","sourceRoot":"","sources":["../../src/roguelike/MultiTurnBeam.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAExC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAE5C,MAAM,WAAW,iBAAiB;IACjC,IAAI,EAAE,IAAI,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,mDAAmD;IACnD,SAAS,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,oBAAoB,CAAC,CAAC;IACtC,KAAK,EAAE,KAAK,CAAC;IACb,IAAI,EAAE,IAAI,CAAC;IACX,MAAM,EAAE,IAAI,CAAC;IACb,MAAM,EAAE,MAAM,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,iBAAiB,KAAK,MAAM,CAAC,CAAC;IACrE,gEAAgE;IAChE,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,SAAS,CAAC,EAAE,CAAC;IACzC,kFAAkF;IAClF,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,KAAK,IAAI,CAAC;IAC9E,8DAA8D;IAC9D,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,iBAAiB,KAAK,OAAO,CAAC;IAChE,kEAAkE;IAClE,YAAY,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,QAAQ,CAAC,CAAC;IAC1B,MAAM,EAAE,MAAM,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,GAAG,WAAW,CAAC;IAC7D,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,SAAS,CAAC,EAAE,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,iBAAiB;IACjC,IAAI,EAAE,IAAI,EAAE,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,GAAG,WAAW,CAAC;CAC5D;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,aAAa,CAAC,CAAC,GAAG,OAAO;IACrC,OAAO,CAAC,OAAO,CAA0B;IACzC,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,KAAK,CAAK;IAClB,OAAO,CAAC,KAAK,CAAsC;IAEnD,YAAY,OAAO,EAAE,oBAAoB,CAAC,CAAC,CAAC,EAS3C;IAED,IAAI,MAAM,IAAI,OAAO,CAEpB;IAED,IAAI,IAAI,IAAI,OAAO,CAElB;IAED,IAAI,WAAW,IAAI,SAAS,IAAI,EAAE,CAEjC;IAED,KAAK,IAAI,OAAO,CAIf;IAED,mEAAmE;IACnE,OAAO,IAAI,QAAQ,CAAC,CAAC,CAAC,CAuDrB;IAED,MAAM,IAAI,IAAI,CAEb;IAED,MAAM,IAAI,iBAAiB,CAE1B;IAED,MAAM,CAAC,QAAQ,CAAC,CAAC,EAChB,OAAO,EAAE,IAAI,CAAC,oBAAoB,CAAC,CAAC,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC,EACzD,IAAI,EAAE,iBAAiB,GACrB,aAAa,CAAC,CAAC,CAAC,CAOlB;CACD"}
1
+ {"version":3,"file":"MultiTurnBeam.d.ts","sourceRoot":"","sources":["../../src/roguelike/MultiTurnBeam.ts"],"names":[],"mappings":"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"}
@@ -1,11 +1,13 @@
1
1
  import { resolveAreaOnLevel } from './Targeting.js';
2
2
  /**
3
- * Advances a straight beam one cell per turn.
3
+ * Advances a beam one front per turn.
4
4
  *
5
- * The framework owns traversal and timing. A game supplies target lookup and damage
6
- * application, so this stays useful for hit points, shields, status effects and any other
7
- * combat model. The path is captured at construction time, while blockers and targets are
8
- * checked when each cell is reached. `advance` is deterministic when those callbacks are.
5
+ * The framework owns traversal and timing. A game supplies target lookup and damage application,
6
+ * so this stays useful for hit points, shields, status effects and any other combat model. The
7
+ * default shape is a straight line captured at construction time; `fronts` opts into a per-turn
8
+ * front resolver, which is what expresses a cone, a burst, a fork or a moving front without any
9
+ * of those rules living here. Blockers and targets are checked when each cell is reached, so
10
+ * `advance` is deterministic when those callbacks are.
9
11
  *
10
12
  * @example
11
13
  * ```ts
@@ -28,7 +30,16 @@ export class MultiTurnBeam {
28
30
  throw new Error('beam damage must be non-negative');
29
31
  }
30
32
  this.options = options;
31
- this.path = resolveAreaOnLevel(options.level, options.from, options.target, { kind: 'line' }).slice(1);
33
+ this.shape = options.shape ?? (options.fronts ? 'fronts' : 'line');
34
+ if (options.fronts) {
35
+ const first = options.fronts([{ ...options.from }], 0);
36
+ this.fronts = first.length ? [first.map((cell) => ({ ...cell }))] : [];
37
+ }
38
+ else {
39
+ this.fronts = resolveAreaOnLevel(options.level, options.from, options.target, { kind: 'line' })
40
+ .slice(1)
41
+ .map((cell) => [{ ...cell }]);
42
+ }
32
43
  }
33
44
  get active() {
34
45
  return this.state === 'active';
@@ -36,64 +47,65 @@ export class MultiTurnBeam {
36
47
  get done() {
37
48
  return this.state === 'done' || this.state === 'blocked' || this.state === 'cancelled';
38
49
  }
50
+ /** The shape this beam was built with, for a save or a UI label. */
51
+ get beamShape() {
52
+ return this.shape;
53
+ }
54
+ /** Every cell of every resolved front, in order - the straight path of a line beam. */
39
55
  get currentPath() {
40
- return this.path;
56
+ return this.fronts.flat().map((cell) => ({ ...cell }));
41
57
  }
42
58
  start() {
43
59
  if (this.state !== 'idle')
44
60
  return false;
45
- this.state = this.path.length === 0 ? 'done' : 'active';
61
+ this.state = this.fronts.length === 0 ? 'done' : 'active';
46
62
  return true;
47
63
  }
48
- /** Resolves and applies one beam cell. Call once per game turn. */
64
+ /** Resolves and applies one beam front. Call once per game turn. */
49
65
  advance() {
50
- if (this.state !== 'active') {
51
- return {
52
- status: this.state,
53
- cell: null,
54
- step: this.index,
55
- remaining: Math.max(0, this.path.length - this.index),
56
- targets: [],
57
- damage: 0,
58
- };
59
- }
60
- const cell = this.path[this.index];
61
- const context = {
62
- cell: { ...cell },
63
- step: this.index,
64
- remaining: this.path.length - this.index - 1,
65
- };
66
- const blocked = !this.options.level.inside(cell.x, cell.y) ||
67
- (this.options.stopAtOpaque !== false && !this.options.level.transparent(cell.x, cell.y)) ||
68
- this.options.isBlocked?.(cell, context) === true;
69
- if (blocked) {
70
- this.state = 'blocked';
71
- return {
72
- status: 'blocked',
73
- cell: { ...cell },
74
- step: this.index,
75
- remaining: context.remaining,
76
- targets: [],
77
- damage: 0,
78
- };
79
- }
80
- const targets = this.options.targetsAt?.(cell) ?? [];
66
+ if (this.state !== 'active')
67
+ return this.inactiveStep();
68
+ const front = this.fronts[this.index] ?? [];
69
+ const remaining = Math.max(0, this.fronts.length - this.index - 1);
70
+ const cells = [];
71
+ const targets = [];
81
72
  let damage = 0;
82
- for (const target of targets) {
83
- const amount = typeof this.options.damage === 'function' ? this.options.damage(target, context) : this.options.damage;
84
- if (!Number.isFinite(amount) || amount < 0)
85
- throw new Error('beam damage callback must return a non-negative finite number');
86
- damage += amount;
87
- this.options.applyDamage?.(target, amount, context);
73
+ for (const raw of front) {
74
+ const context = { cell: { ...raw }, step: this.index, remaining };
75
+ if (this.blocked(raw, context))
76
+ continue;
77
+ cells.push({ ...raw });
78
+ this.options.onCell?.({ ...raw }, context);
79
+ for (const target of this.options.targetsAt?.(raw) ?? []) {
80
+ const amount = typeof this.options.damage === 'function'
81
+ ? this.options.damage(target, context)
82
+ : this.options.damage;
83
+ if (!Number.isFinite(amount) || amount < 0)
84
+ throw new Error('beam damage callback must return a non-negative finite number');
85
+ damage += amount;
86
+ targets.push(target);
87
+ this.options.applyDamage?.(target, amount, context);
88
+ }
88
89
  }
90
+ const step = this.index;
89
91
  this.index++;
90
- if (this.index >= this.path.length)
92
+ //a per-turn resolver extends the beam from what this front actually reached; a blocked
93
+ //cell does not propagate, and no cells at all ends the show
94
+ if (this.options.fronts && cells.length > 0) {
95
+ const next = this.options.fronts(cells.map((cell) => ({ ...cell })), this.index);
96
+ if (next.length)
97
+ this.fronts.push(next.map((cell) => ({ ...cell })));
98
+ }
99
+ if (cells.length === 0)
100
+ this.state = 'blocked';
101
+ else if (this.index >= this.fronts.length)
91
102
  this.state = 'done';
92
103
  return {
93
- status: this.state === 'done' ? 'done' : 'active',
94
- cell: { ...cell },
95
- step: context.step,
96
- remaining: context.remaining,
104
+ status: cells.length === 0 ? 'blocked' : this.state,
105
+ cell: cells[0] ? { ...cells[0] } : null,
106
+ cells: cells.map((cell) => ({ ...cell })),
107
+ step,
108
+ remaining,
97
109
  targets: [...targets],
98
110
  damage,
99
111
  };
@@ -103,15 +115,50 @@ export class MultiTurnBeam {
103
115
  this.state = 'cancelled';
104
116
  }
105
117
  toJSON() {
106
- return { path: this.path.map((cell) => ({ ...cell })), index: this.index, state: this.state };
118
+ return {
119
+ state: this.state,
120
+ fronts: this.fronts.map((front) => front.map((cell) => ({ ...cell }))),
121
+ index: this.index,
122
+ shape: this.shape,
123
+ };
107
124
  }
108
125
  static fromJSON(options, data) {
126
+ const shape = data.shape ?? (options.fronts ? 'fronts' : 'line');
127
+ if (options.shape !== undefined && options.shape !== shape)
128
+ throw new Error(`beam save is shape "${shape}", not "${options.shape}"`);
109
129
  const beam = Object.create(MultiTurnBeam.prototype);
110
130
  beam.options = options;
111
- beam.path = data.path.map((cell) => ({ ...cell }));
131
+ beam.shape = shape;
132
+ //a save written before fronts landed stored one cell per turn as `path`
133
+ beam.fronts = data.fronts
134
+ ? data.fronts.map((front) => front.map((cell) => ({ ...cell })))
135
+ : (data.path ?? []).map((cell) => [{ ...cell }]);
112
136
  beam.index = data.index;
113
137
  beam.state = data.state;
114
138
  return beam;
115
139
  }
140
+ blocked(cell, context) {
141
+ const blocker = this.options.blocker ?? (this.options.stopAtOpaque === false ? 'none' : 'terrain');
142
+ let blockedByPolicy = false;
143
+ if (blocker === 'terrain') {
144
+ blockedByPolicy =
145
+ !this.options.level.inside(cell.x, cell.y) || !this.options.level.transparent(cell.x, cell.y);
146
+ }
147
+ else if (blocker !== 'none') {
148
+ blockedByPolicy = blocker({ ...cell }, context) === true;
149
+ }
150
+ return blockedByPolicy || this.options.isBlocked?.({ ...cell }, context) === true;
151
+ }
152
+ inactiveStep() {
153
+ return {
154
+ status: this.state,
155
+ cell: null,
156
+ cells: [],
157
+ step: this.index,
158
+ remaining: Math.max(0, this.fronts.length - this.index),
159
+ targets: [],
160
+ damage: 0,
161
+ };
162
+ }
116
163
  }
117
164
  //# sourceMappingURL=MultiTurnBeam.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"MultiTurnBeam.js","sourceRoot":"","sources":["../../src/roguelike/MultiTurnBeam.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"MultiTurnBeam.js","sourceRoot":"","sources":["../../src/roguelike/MultiTurnBeam.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import type { Level } from './Level.ts';
2
2
  import type { Step } from './Pathfinder.ts';
3
+ import { Signal } from '../core/Signal.ts';
3
4
  /**
4
5
  * How a target resolves into the cells it actually affects.
5
6
  *
@@ -180,4 +181,97 @@ export declare function areaFalloffMultiplier(index: number, steps: readonly num
180
181
  * very first cell already blocks, in which case the shove lands against the wall instead.
181
182
  */
182
183
  export declare function knockbackPath(level: Level, from: Step, direction: Step, distance: number): Step[];
184
+ export interface TargetingControllerOptions {
185
+ /** where the aim starts: the thrower's own cell */
186
+ origin: Step;
187
+ /** how far the aim reaches, in the owning shape's own ruler (`canTarget`'s) */
188
+ range: number;
189
+ /** whether a wall between origin and cursor blocks the aim; on by default */
190
+ requireLineOfSight?: boolean;
191
+ /** how a confirmed aim resolves into cells; a single cell by default */
192
+ shape?: AreaShape;
193
+ /** the cursor's first cell; defaults to `origin` */
194
+ cursor?: Step;
195
+ /**
196
+ * A game's own rule on top of range and sight (not through an ally, not an empty floor
197
+ * tile). The controller never guesses what is in a cell: only the game knows.
198
+ */
199
+ validate?: (target: Step) => boolean;
200
+ }
201
+ /** What `TargetingController.confirm` returns, and what its `onConfirm` carries. Cells only. */
202
+ export interface TargetResult {
203
+ readonly origin: Step;
204
+ readonly target: Step;
205
+ readonly shape: AreaShape;
206
+ /** the cells the shape affects, resolved through the level's own topology */
207
+ readonly cells: readonly Step[];
208
+ }
209
+ /**
210
+ * The input-facing half of targeting: a cursor a player moves over cells, the range and
211
+ * line-of-sight rule that decides whether the current aim is legal, an optional preview of
212
+ * the shape, and a confirm/cancel result. It carries no renderer: `moveTo` takes a cell, so
213
+ * a game converts its own pointer position through its own camera, and the controller hands
214
+ * back cells for the game to draw and resolve. Legality beyond range and sight is the
215
+ * optional `validate` hook's job, and damage is never the controller's business.
216
+ *
217
+ * @example
218
+ * ```ts
219
+ * import { Level, FLOOR, WALL, TargetingController } from '@datamoc/mw_games/roguelike';
220
+ *
221
+ * const level = new Level(12, 12, [WALL, FLOOR], 1);
222
+ * const aiming = new TargetingController(level, {
223
+ * origin: { x: 2, y: 2 },
224
+ * range: 6,
225
+ * shape: { kind: 'burst', radius: 1 },
226
+ * });
227
+ *
228
+ * aiming.move(1, 0); // keyboard: one cell east
229
+ * aiming.moveTo({ x: 5, y: 2 }); // pointer: the caller resolved the screen point first
230
+ * console.log(aiming.valid); // in range and in sight
231
+ * console.log(aiming.preview()); // the burst's cells, or [] while the aim is illegal
232
+ *
233
+ * const shot = aiming.confirm(); // null when illegal, the target cells when not
234
+ * ```
235
+ */
236
+ export declare class TargetingController {
237
+ readonly onMove: Signal<Step>;
238
+ readonly onConfirm: Signal<TargetResult>;
239
+ readonly onCancel: Signal<void>;
240
+ private readonly level;
241
+ private readonly origin;
242
+ private readonly range;
243
+ private readonly requireLineOfSight;
244
+ private readonly validate?;
245
+ private shape;
246
+ private cursor;
247
+ constructor(level: Level, options: TargetingControllerOptions);
248
+ /** the cell the cursor is on now, as a copy - mutating it does not move the aim */
249
+ get target(): Step;
250
+ /** how many cells the cursor is from the origin, in the level's own ruler */
251
+ get distance(): number;
252
+ get inRange(): boolean;
253
+ get inSight(): boolean;
254
+ /** whether the current aim is legal: range, sight unless waived, and the game's own rule */
255
+ get valid(): boolean;
256
+ /**
257
+ * Moves the cursor one cell, for keyboard and gamepad navigation. On a square level the
258
+ * offset is added directly; on a hex level it is looked up among the cursor's six
259
+ * neighbours, since only the level knows which offset means which direction there. A move
260
+ * that would leave the map is ignored rather than clamped to its edge.
261
+ */
262
+ move(dx: number, dy: number): void;
263
+ /**
264
+ * Moves the cursor to a specific cell, for pointer navigation: the caller has already
265
+ * turned a screen point into a cell through its own camera. Off-map cells are ignored.
266
+ */
267
+ moveTo(cell: Step): void;
268
+ /** Replaces the shape a confirm resolves to, keeping the cursor where it is. */
269
+ setShape(shape: AreaShape): void;
270
+ /** The cells the current aim would affect, for a preview overlay; empty while illegal. */
271
+ preview(): Step[];
272
+ /** Returns the confirmed target, or null when the current aim is illegal. */
273
+ confirm(): TargetResult | null;
274
+ /** Abandons the aim without confirming; the game closes its own overlay on `onCancel`. */
275
+ cancel(): void;
276
+ }
183
277
  //# sourceMappingURL=Targeting.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Targeting.d.ts","sourceRoot":"","sources":["../../src/roguelike/Targeting.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Targeting.d.ts","sourceRoot":"","sources":["../../src/roguelike/Targeting.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import { hexDistance, hexLine, hexRange } from '../core/Hex.js';
2
+ import { Signal } from '../core/Signal.js';
2
3
  /**
3
4
  * The straight-line ruler distance a roguelike uses: diagonals cost the same as a step.
4
5
  *
@@ -284,4 +285,126 @@ export function knockbackPath(level, from, direction, distance) {
284
285
  }
285
286
  return cells;
286
287
  }
288
+ /**
289
+ * The input-facing half of targeting: a cursor a player moves over cells, the range and
290
+ * line-of-sight rule that decides whether the current aim is legal, an optional preview of
291
+ * the shape, and a confirm/cancel result. It carries no renderer: `moveTo` takes a cell, so
292
+ * a game converts its own pointer position through its own camera, and the controller hands
293
+ * back cells for the game to draw and resolve. Legality beyond range and sight is the
294
+ * optional `validate` hook's job, and damage is never the controller's business.
295
+ *
296
+ * @example
297
+ * ```ts
298
+ * import { Level, FLOOR, WALL, TargetingController } from '@datamoc/mw_games/roguelike';
299
+ *
300
+ * const level = new Level(12, 12, [WALL, FLOOR], 1);
301
+ * const aiming = new TargetingController(level, {
302
+ * origin: { x: 2, y: 2 },
303
+ * range: 6,
304
+ * shape: { kind: 'burst', radius: 1 },
305
+ * });
306
+ *
307
+ * aiming.move(1, 0); // keyboard: one cell east
308
+ * aiming.moveTo({ x: 5, y: 2 }); // pointer: the caller resolved the screen point first
309
+ * console.log(aiming.valid); // in range and in sight
310
+ * console.log(aiming.preview()); // the burst's cells, or [] while the aim is illegal
311
+ *
312
+ * const shot = aiming.confirm(); // null when illegal, the target cells when not
313
+ * ```
314
+ */
315
+ export class TargetingController {
316
+ constructor(level, options) {
317
+ this.onMove = new Signal();
318
+ this.onConfirm = new Signal();
319
+ this.onCancel = new Signal();
320
+ if (!Number.isFinite(options.range) || options.range < 0) {
321
+ throw new Error('targeting range must be a finite non-negative number');
322
+ }
323
+ this.level = level;
324
+ this.origin = { ...options.origin };
325
+ this.range = options.range;
326
+ this.requireLineOfSight = options.requireLineOfSight ?? true;
327
+ this.validate = options.validate;
328
+ this.shape = options.shape ?? { kind: 'single' };
329
+ this.cursor = { ...(options.cursor ?? options.origin) };
330
+ }
331
+ /** the cell the cursor is on now, as a copy - mutating it does not move the aim */
332
+ get target() {
333
+ return { ...this.cursor };
334
+ }
335
+ /** how many cells the cursor is from the origin, in the level's own ruler */
336
+ get distance() {
337
+ return this.level.shape === 'hex'
338
+ ? hexDistance(this.origin, this.cursor)
339
+ : chebyshevDistance(this.origin, this.cursor);
340
+ }
341
+ get inRange() {
342
+ return this.distance <= this.range;
343
+ }
344
+ get inSight() {
345
+ return hasLineOfSight(this.level, this.origin, this.cursor);
346
+ }
347
+ /** whether the current aim is legal: range, sight unless waived, and the game's own rule */
348
+ get valid() {
349
+ if (!this.inRange)
350
+ return false;
351
+ if (this.requireLineOfSight && !this.inSight)
352
+ return false;
353
+ return this.validate ? this.validate(this.cursor) : true;
354
+ }
355
+ /**
356
+ * Moves the cursor one cell, for keyboard and gamepad navigation. On a square level the
357
+ * offset is added directly; on a hex level it is looked up among the cursor's six
358
+ * neighbours, since only the level knows which offset means which direction there. A move
359
+ * that would leave the map is ignored rather than clamped to its edge.
360
+ */
361
+ move(dx, dy) {
362
+ if (this.level.shape === 'hex') {
363
+ const next = this.level
364
+ .neighbors(this.cursor.x, this.cursor.y)
365
+ .find((neighbor) => neighbor.x - this.cursor.x === dx && neighbor.y - this.cursor.y === dy);
366
+ if (next)
367
+ this.moveTo(next);
368
+ return;
369
+ }
370
+ this.moveTo({ x: this.cursor.x + dx, y: this.cursor.y + dy });
371
+ }
372
+ /**
373
+ * Moves the cursor to a specific cell, for pointer navigation: the caller has already
374
+ * turned a screen point into a cell through its own camera. Off-map cells are ignored.
375
+ */
376
+ moveTo(cell) {
377
+ if (!this.level.inside(cell.x, cell.y))
378
+ return;
379
+ this.cursor = { x: cell.x, y: cell.y };
380
+ this.onMove.dispatch({ ...this.cursor });
381
+ }
382
+ /** Replaces the shape a confirm resolves to, keeping the cursor where it is. */
383
+ setShape(shape) {
384
+ this.shape = shape;
385
+ }
386
+ /** The cells the current aim would affect, for a preview overlay; empty while illegal. */
387
+ preview() {
388
+ if (!this.valid)
389
+ return [];
390
+ return resolveAreaOnLevel(this.level, this.origin, this.cursor, this.shape);
391
+ }
392
+ /** Returns the confirmed target, or null when the current aim is illegal. */
393
+ confirm() {
394
+ if (!this.valid)
395
+ return null;
396
+ const result = {
397
+ origin: { ...this.origin },
398
+ target: { ...this.cursor },
399
+ shape: this.shape,
400
+ cells: this.preview(),
401
+ };
402
+ this.onConfirm.dispatch(result);
403
+ return result;
404
+ }
405
+ /** Abandons the aim without confirming; the game closes its own overlay on `onCancel`. */
406
+ cancel() {
407
+ this.onCancel.dispatch();
408
+ }
409
+ }
287
410
  //# sourceMappingURL=Targeting.js.map