@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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Positional.js","sourceRoot":"","sources":["../../src/audio/Positional.ts"],"names":[],"mappings":"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"}
@@ -28,7 +28,13 @@ export declare class Sound {
28
28
  private caption?;
29
29
  volume: number;
30
30
  constructor(path: string, options?: SoundOptions);
31
- play(): void;
31
+ /**
32
+ * Plays one instance from the pool.
33
+ *
34
+ * @param gain multiplied into the sound's own volume, for a distance-attenuated one-shot
35
+ * (`Positional.SoundSource.playFor`); 1 leaves it at `volume`.
36
+ */
37
+ play(gain?: number): void;
32
38
  stopAll(): void;
33
39
  }
34
40
  //# sourceMappingURL=Sound.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Sound.d.ts","sourceRoot":"","sources":["../../src/audio/Sound.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAC;AAG3D,MAAM,WAAW,YAAY;IAC5B,4FAA4F;IAC5F,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,8FAA8F;IAC9F,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,QAAQ,CAAC;IAEpC,2FAA2F;IAC3F,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;;GAaG;AACH,qBAAa,KAAK;IACjB,OAAO,CAAC,IAAI,CAAa;IACzB,OAAO,CAAC,IAAI,CAAK;IACjB,OAAO,CAAC,OAAO,CAAC,CAAS;IACzB,MAAM,EAAE,MAAM,CAAC;IAEf,YAAY,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,EAMnD;IAED,IAAI,IAAI,IAAI,CAYX;IAED,OAAO,IAAI,IAAI,CAKd;CACD"}
1
+ {"version":3,"file":"Sound.d.ts","sourceRoot":"","sources":["../../src/audio/Sound.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAC;AAG3D,MAAM,WAAW,YAAY;IAC5B,4FAA4F;IAC5F,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,8FAA8F;IAC9F,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,QAAQ,CAAC;IAEpC,2FAA2F;IAC3F,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;;GAaG;AACH,qBAAa,KAAK;IACjB,OAAO,CAAC,IAAI,CAAa;IACzB,OAAO,CAAC,IAAI,CAAK;IACjB,OAAO,CAAC,OAAO,CAAC,CAAS;IACzB,MAAM,EAAE,MAAM,CAAC;IAEf,YAAY,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,EAMnD;IAED;;;;;OAKG;IACH,IAAI,CAAC,IAAI,SAAI,GAAG,IAAI,CAYnB;IAED,OAAO,IAAI,IAAI,CAKd;CACD"}
@@ -23,10 +23,16 @@ export class Sound {
23
23
  this.caption = options.caption;
24
24
  this.pool = Array.from({ length: size }, () => create(path));
25
25
  }
26
- play() {
26
+ /**
27
+ * Plays one instance from the pool.
28
+ *
29
+ * @param gain multiplied into the sound's own volume, for a distance-attenuated one-shot
30
+ * (`Positional.SoundSource.playFor`); 1 leaves it at `volume`.
31
+ */
32
+ play(gain = 1) {
27
33
  const audio = this.pool[this.next];
28
34
  this.next = (this.next + 1) % this.pool.length;
29
- audio.volume = this.volume;
35
+ audio.volume = this.volume * gain;
30
36
  audio.currentTime = 0;
31
37
  // play() can return a Promise that rejects if this reuse interrupts its own prior,
32
38
  // still-loading play() (a real browser, not the fakes tests supply) - swallow it the
@@ -1 +1 @@
1
- {"version":3,"file":"Sound.js","sourceRoot":"","sources":["../../src/audio/Sound.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAiB,MAAM,eAAe,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAc1C;;;;;;;;;;;;;GAaG;AACH,MAAM,OAAO,KAAK;IAMjB,YAAY,IAAY,EAAE,OAAO,GAAiB,EAAE;QAJ5C,SAAI,GAAG,CAAC,CAAC;QAKhB,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,QAAQ,IAAI,CAAC,CAAC,CAAC;QAChD,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,WAAW,CAAC;QAC7C,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,CAAC,CAAC;QAClC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;QAC/B,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;IAC9D,CAAC;IAED,IAAI;QACH,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnC,IAAI,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC;QAE/C,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC3B,KAAK,CAAC,WAAW,GAAG,CAAC,CAAC;QACtB,mFAAmF;QACnF,qFAAqF;QACrF,wFAAwF;QACxF,KAAK,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QAEnD,IAAI,IAAI,CAAC,OAAO;YAAE,SAAS,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;IAC9D,CAAC;IAED,OAAO;QACN,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YAC/B,KAAK,CAAC,KAAK,EAAE,CAAC;YACd,KAAK,CAAC,WAAW,GAAG,CAAC,CAAC;QACvB,CAAC;IACF,CAAC;CACD"}
1
+ {"version":3,"file":"Sound.js","sourceRoot":"","sources":["../../src/audio/Sound.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAiB,MAAM,eAAe,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAc1C;;;;;;;;;;;;;GAaG;AACH,MAAM,OAAO,KAAK;IAMjB,YAAY,IAAY,EAAE,OAAO,GAAiB,EAAE;QAJ5C,SAAI,GAAG,CAAC,CAAC;QAKhB,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,QAAQ,IAAI,CAAC,CAAC,CAAC;QAChD,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,WAAW,CAAC;QAC7C,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,CAAC,CAAC;QAClC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;QAC/B,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;IAC9D,CAAC;IAED;;;;;OAKG;IACH,IAAI,CAAC,IAAI,GAAG,CAAC;QACZ,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnC,IAAI,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC;QAE/C,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QAClC,KAAK,CAAC,WAAW,GAAG,CAAC,CAAC;QACtB,mFAAmF;QACnF,qFAAqF;QACrF,wFAAwF;QACxF,KAAK,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QAEnD,IAAI,IAAI,CAAC,OAAO;YAAE,SAAS,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;IAC9D,CAAC;IAED,OAAO;QACN,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YAC/B,KAAK,CAAC,KAAK,EAAE,CAAC;YACd,KAAK,CAAC,WAAW,GAAG,CAAC,CAAC;QACvB,CAAC;IACF,CAAC;CACD"}
@@ -12,4 +12,6 @@ export { synthesizeTone, playTone } from './Synth.ts';
12
12
  export type { Waveform, ToneOptions } from './Synth.ts';
13
13
  export { parseMidi, scheduleMidi, noteToFrequency, MidiPlayer } from './Midi.ts';
14
14
  export type { MidiFile, MidiEvent, MidiNoteEvent, MidiTempoEvent, ScheduledNote, MidiPlayerOptions } from './Midi.ts';
15
+ export { AudioListener, SoundSource, audioGain, audioPan } from './Positional.ts';
16
+ export type { AudioPoint, AudioFalloff, SoundSourceOptions } from './Positional.ts';
15
17
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/audio/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC1C,YAAY,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,YAAY,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,YAAY,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtD,YAAY,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACjF,YAAY,EAAE,QAAQ,EAAE,SAAS,EAAE,aAAa,EAAE,cAAc,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/audio/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC1C,YAAY,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,YAAY,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,YAAY,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtD,YAAY,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACjF,YAAY,EAAE,QAAQ,EAAE,SAAS,EAAE,aAAa,EAAE,cAAc,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AACtH,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAClF,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC"}
@@ -5,4 +5,5 @@ export { createAudio } from './Playable.js';
5
5
  export { Orchestrator } from './Orchestrator.js';
6
6
  export { synthesizeTone, playTone } from './Synth.js';
7
7
  export { parseMidi, scheduleMidi, noteToFrequency, MidiPlayer } from './Midi.js';
8
+ export { AudioListener, SoundSource, audioGain, audioPan } from './Positional.js';
8
9
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/audio/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE1C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtD,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/audio/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE1C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtD,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAEjF,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,75 @@
1
+ import { Signal } from '../core/Signal.ts';
2
+ import { AttackPreview, type AttackFrame } from './AttackPreview.ts';
3
+ import { UnitSelector, type SelectableUnit, type SelectorStage } from './UnitSelector.ts';
4
+ /** The per-strike numbers a game's own rules produce for one attacker/target pair. */
5
+ export interface StrikeNumbers {
6
+ damage: number;
7
+ strikes: number;
8
+ chanceToHit?: number;
9
+ hits?: readonly boolean[];
10
+ }
11
+ export interface AttackDialogOptions {
12
+ units: readonly SelectableUnit[];
13
+ side?: string;
14
+ disabled?: (unit: SelectableUnit) => boolean;
15
+ canTarget?: (attacker: SelectableUnit, target: SelectableUnit) => boolean;
16
+ /** the game's damage rules, called once the attacker and target are both chosen */
17
+ damageFor: (attacker: SelectableUnit, target: SelectableUnit) => StrikeNumbers;
18
+ /** seconds between strikes in the preview animation */
19
+ strikeDuration?: number;
20
+ }
21
+ /**
22
+ * The attack dialog's whole state, with no renderer: the unit selector, the damage preview the
23
+ * game's numbers build, and the animation clock the preview is sampled on.
24
+ *
25
+ * The order of business is the one a player sees - choose an attacker, choose a target, and the
26
+ * dialog shows a blow that lands over time - so the clock is here rather than in a widget: `update`
27
+ * takes the frame's `dt` and returns the preview frame to draw, and `finished` says when the
28
+ * animation has run out and the caller may apply the result. `damageFor` is where the game's own
29
+ * rules live; this model never invents damage.
30
+ *
31
+ * @example
32
+ * ```ts
33
+ * import { AttackDialog } from '@datamoc/mw_games/battle';
34
+ *
35
+ * const dialog = new AttackDialog({
36
+ * units: [{ id: 'hero', side: '1' }, { id: 'rat', side: '2' }],
37
+ * side: '1',
38
+ * damageFor: () => ({ damage: 4, strikes: 2 }),
39
+ * strikeDuration: 0.25,
40
+ * });
41
+ * dialog.select();
42
+ * dialog.select();
43
+ * console.log(dialog.update(0.3)?.defenderHp); // the defender after the second strike
44
+ * ```
45
+ */
46
+ export declare class AttackDialog {
47
+ readonly onChange: Signal<void>;
48
+ readonly selector: UnitSelector;
49
+ private readonly damageFor;
50
+ private readonly strikeDuration;
51
+ private built;
52
+ private elapsed_;
53
+ constructor(options: AttackDialogOptions);
54
+ get stage(): SelectorStage;
55
+ get attacker(): SelectableUnit | null;
56
+ get target(): SelectableUnit | null;
57
+ /** the damage preview, built once both choices are made */
58
+ get preview(): AttackPreview | null;
59
+ /** how long the animation has been playing, in seconds */
60
+ get elapsed(): number;
61
+ /** whether the animation has reached the end of the preview */
62
+ get finished(): boolean;
63
+ /** the frame to draw right now, or `null` before both choices are made */
64
+ get frame(): AttackFrame | null;
65
+ move(delta: number): void;
66
+ select(): boolean;
67
+ back(): void;
68
+ /** advances the animation, returning the frame to draw */
69
+ update(dt: number): AttackFrame | null;
70
+ /** skips the animation to its end, the click-through a player makes */
71
+ finish(): void;
72
+ reset(): void;
73
+ private readonly handleSelectorChange;
74
+ }
75
+ //# sourceMappingURL=AttackDialog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AttackDialog.d.ts","sourceRoot":"","sources":["../../src/battle/AttackDialog.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACrE,OAAO,EAAE,YAAY,EAAE,KAAK,cAAc,EAAE,KAAK,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAE1F,sFAAsF;AACtF,MAAM,WAAW,aAAa;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,SAAS,OAAO,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,mBAAmB;IACnC,KAAK,EAAE,SAAS,cAAc,EAAE,CAAC;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,OAAO,CAAC;IAC7C,SAAS,CAAC,EAAE,CAAC,QAAQ,EAAE,cAAc,EAAE,MAAM,EAAE,cAAc,KAAK,OAAO,CAAC;IAC1E,mFAAmF;IACnF,SAAS,EAAE,CAAC,QAAQ,EAAE,cAAc,EAAE,MAAM,EAAE,cAAc,KAAK,aAAa,CAAC;IAC/E,uDAAuD;IACvD,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,qBAAa,YAAY;IACxB,QAAQ,CAAC,QAAQ,eAAsB;IACvC,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC;IAEhC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAsE;IAChG,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,KAAK,CAA8B;IAC3C,OAAO,CAAC,QAAQ,CAAK;IAErB,YAAY,OAAO,EAAE,mBAAmB,EAUvC;IAED,IAAI,KAAK,IAAI,aAAa,CAEzB;IAED,IAAI,QAAQ,IAAI,cAAc,GAAG,IAAI,CAEpC;IAED,IAAI,MAAM,IAAI,cAAc,GAAG,IAAI,CAElC;IAED,2DAA2D;IAC3D,IAAI,OAAO,IAAI,aAAa,GAAG,IAAI,CAElC;IAED,0DAA0D;IAC1D,IAAI,OAAO,IAAI,MAAM,CAEpB;IAED,+DAA+D;IAC/D,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED,0EAA0E;IAC1E,IAAI,KAAK,IAAI,WAAW,GAAG,IAAI,CAE9B;IAED,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAExB;IAED,MAAM,IAAI,OAAO,CAEhB;IAED,IAAI,IAAI,IAAI,CAEX;IAED,0DAA0D;IAC1D,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAIrC;IAED,uEAAuE;IACvE,MAAM,IAAI,IAAI,CAEb;IAED,KAAK,IAAI,IAAI,CAIZ;IAED,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAiBnC;CACF"}
@@ -0,0 +1,115 @@
1
+ import { Signal } from '../core/Signal.js';
2
+ import { AttackPreview } from './AttackPreview.js';
3
+ import { UnitSelector } from './UnitSelector.js';
4
+ /**
5
+ * The attack dialog's whole state, with no renderer: the unit selector, the damage preview the
6
+ * game's numbers build, and the animation clock the preview is sampled on.
7
+ *
8
+ * The order of business is the one a player sees - choose an attacker, choose a target, and the
9
+ * dialog shows a blow that lands over time - so the clock is here rather than in a widget: `update`
10
+ * takes the frame's `dt` and returns the preview frame to draw, and `finished` says when the
11
+ * animation has run out and the caller may apply the result. `damageFor` is where the game's own
12
+ * rules live; this model never invents damage.
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * import { AttackDialog } from '@datamoc/mw_games/battle';
17
+ *
18
+ * const dialog = new AttackDialog({
19
+ * units: [{ id: 'hero', side: '1' }, { id: 'rat', side: '2' }],
20
+ * side: '1',
21
+ * damageFor: () => ({ damage: 4, strikes: 2 }),
22
+ * strikeDuration: 0.25,
23
+ * });
24
+ * dialog.select();
25
+ * dialog.select();
26
+ * console.log(dialog.update(0.3)?.defenderHp); // the defender after the second strike
27
+ * ```
28
+ */
29
+ export class AttackDialog {
30
+ constructor(options) {
31
+ this.onChange = new Signal();
32
+ this.built = null;
33
+ this.elapsed_ = 0;
34
+ this.handleSelectorChange = () => {
35
+ if (this.selector.done && this.attacker && this.target) {
36
+ const numbers = this.damageFor(this.attacker, this.target);
37
+ this.built = new AttackPreview({
38
+ attacker: { hp: this.attacker.hp ?? 0, name: this.attacker.id },
39
+ defender: { hp: this.target.hp ?? 0, name: this.target.id },
40
+ damage: numbers.damage,
41
+ strikes: numbers.strikes,
42
+ ...(numbers.chanceToHit === undefined ? {} : { chanceToHit: numbers.chanceToHit }),
43
+ ...(numbers.hits === undefined ? {} : { hits: numbers.hits }),
44
+ strikeDuration: this.strikeDuration,
45
+ });
46
+ this.elapsed_ = 0;
47
+ }
48
+ else {
49
+ this.built = null;
50
+ }
51
+ this.onChange.dispatch();
52
+ };
53
+ this.damageFor = options.damageFor;
54
+ this.strikeDuration = options.strikeDuration ?? 0;
55
+ this.selector = new UnitSelector({
56
+ units: options.units,
57
+ ...(options.side === undefined ? {} : { side: options.side }),
58
+ ...(options.disabled === undefined ? {} : { disabled: options.disabled }),
59
+ ...(options.canTarget === undefined ? {} : { canTarget: options.canTarget }),
60
+ });
61
+ this.selector.onChange.add(this.handleSelectorChange);
62
+ }
63
+ get stage() {
64
+ return this.selector.stage;
65
+ }
66
+ get attacker() {
67
+ return this.selector.attacker;
68
+ }
69
+ get target() {
70
+ return this.selector.target;
71
+ }
72
+ /** the damage preview, built once both choices are made */
73
+ get preview() {
74
+ return this.built;
75
+ }
76
+ /** how long the animation has been playing, in seconds */
77
+ get elapsed() {
78
+ return this.elapsed_;
79
+ }
80
+ /** whether the animation has reached the end of the preview */
81
+ get finished() {
82
+ return this.built !== null && this.elapsed_ >= this.built.duration;
83
+ }
84
+ /** the frame to draw right now, or `null` before both choices are made */
85
+ get frame() {
86
+ return this.built ? this.built.sampleAt(this.elapsed_) : null;
87
+ }
88
+ move(delta) {
89
+ this.selector.move(delta);
90
+ }
91
+ select() {
92
+ return this.selector.select();
93
+ }
94
+ back() {
95
+ this.selector.back();
96
+ }
97
+ /** advances the animation, returning the frame to draw */
98
+ update(dt) {
99
+ if (!this.built)
100
+ return null;
101
+ this.elapsed_ = Math.min(this.built.duration, this.elapsed_ + Math.max(0, dt));
102
+ return this.frame;
103
+ }
104
+ /** skips the animation to its end, the click-through a player makes */
105
+ finish() {
106
+ if (this.built)
107
+ this.elapsed_ = this.built.duration;
108
+ }
109
+ reset() {
110
+ this.selector.reset();
111
+ this.built = null;
112
+ this.elapsed_ = 0;
113
+ }
114
+ }
115
+ //# sourceMappingURL=AttackDialog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AttackDialog.js","sourceRoot":"","sources":["../../src/battle/AttackDialog.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,70 @@
1
+ export interface PreviewCombatant {
2
+ hp: number;
3
+ maxHp?: number;
4
+ name?: string;
5
+ }
6
+ export interface AttackPreviewOptions {
7
+ attacker: PreviewCombatant;
8
+ defender: PreviewCombatant;
9
+ /** damage per strike, from the game's own formula - `mwg` supplies none */
10
+ damage: number;
11
+ strikes: number;
12
+ /** chance to hit, 0 to 1; 1 (always hits) when omitted */
13
+ chanceToHit?: number;
14
+ /** which strikes land; every strike when omitted, for the maximum-damage preview */
15
+ hits?: readonly boolean[];
16
+ /** seconds between strikes in the animation; 0 when there is no animation */
17
+ strikeDuration?: number;
18
+ }
19
+ /** One moment of the attack animation: the board after `strike` strikes. */
20
+ export interface AttackFrame {
21
+ strike: number;
22
+ time: number;
23
+ attackerHp: number;
24
+ defenderHp: number;
25
+ /** hp taken off the defender by the strikes so far */
26
+ defenderDamage: number;
27
+ }
28
+ /**
29
+ * The numbers an attack dialog shows and animates: the attacker and defender's hp over the course
30
+ * of a blow, plus the totals the labels print.
31
+ *
32
+ * It deliberately computes no damage of its own - this framework supplies no damage formula, so a
33
+ * game hands in the per-strike `damage` exactly as its own rules worked it out. What the preview
34
+ * adds is the part a dialog needs and a formula does not: the *timeline* (one frame per strike,
35
+ * `sampleAt` the animation's own clock) and the two totals (the maximum, `damage * strikes`, and
36
+ * the expected value once `chanceToHit` is weighed). Which strikes actually land is `hits`, so a
37
+ * preview can show the maximum by default or a known outcome a replay already rolled.
38
+ *
39
+ * @example
40
+ * ```ts
41
+ * import { AttackPreview } from '@datamoc/mw_games/battle';
42
+ *
43
+ * const preview = new AttackPreview({
44
+ * attacker: { hp: 20 },
45
+ * defender: { hp: 12 },
46
+ * damage: 4,
47
+ * strikes: 3,
48
+ * chanceToHit: 0.6,
49
+ * strikeDuration: 0.2,
50
+ * });
51
+ * console.log(preview.totalDamage); // 12
52
+ * console.log(preview.sampleAt(0.25).defenderHp); // 8, after the second strike
53
+ * ```
54
+ */
55
+ export declare class AttackPreview {
56
+ readonly frames: readonly AttackFrame[];
57
+ readonly totalDamage: number;
58
+ readonly expectedDamage: number;
59
+ readonly chanceToHit: number;
60
+ private readonly strikeDuration;
61
+ private readonly defenderHp0;
62
+ constructor(options: AttackPreviewOptions);
63
+ /** how long the animation runs, in seconds */
64
+ get duration(): number;
65
+ /** whether the defender would be at 0 hp once every landing strike has been shown */
66
+ get defenderKilled(): boolean;
67
+ /** the frame the animation shows at `time`, clamped to its own start and end */
68
+ sampleAt(time: number): AttackFrame;
69
+ }
70
+ //# sourceMappingURL=AttackPreview.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AttackPreview.d.ts","sourceRoot":"","sources":["../../src/battle/AttackPreview.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,gBAAgB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,oBAAoB;IACpC,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,2EAA2E;IAC3E,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,0DAA0D;IAC1D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oFAAoF;IACpF,IAAI,CAAC,EAAE,SAAS,OAAO,EAAE,CAAC;IAC1B,6EAA6E;IAC7E,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,4EAA4E;AAC5E,MAAM,WAAW,WAAW;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,sDAAsD;IACtD,cAAc,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,qBAAa,aAAa;IACzB,QAAQ,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,CAAC;IACxC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IAErC,YAAY,OAAO,EAAE,oBAAoB,EAoBxC;IAED,8CAA8C;IAC9C,IAAI,QAAQ,IAAI,MAAM,CAErB;IAED,qFAAqF;IACrF,IAAI,cAAc,IAAI,OAAO,CAE5B;IAED,gFAAgF;IAChF,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAWlC;CACD"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * The numbers an attack dialog shows and animates: the attacker and defender's hp over the course
3
+ * of a blow, plus the totals the labels print.
4
+ *
5
+ * It deliberately computes no damage of its own - this framework supplies no damage formula, so a
6
+ * game hands in the per-strike `damage` exactly as its own rules worked it out. What the preview
7
+ * adds is the part a dialog needs and a formula does not: the *timeline* (one frame per strike,
8
+ * `sampleAt` the animation's own clock) and the two totals (the maximum, `damage * strikes`, and
9
+ * the expected value once `chanceToHit` is weighed). Which strikes actually land is `hits`, so a
10
+ * preview can show the maximum by default or a known outcome a replay already rolled.
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * import { AttackPreview } from '@datamoc/mw_games/battle';
15
+ *
16
+ * const preview = new AttackPreview({
17
+ * attacker: { hp: 20 },
18
+ * defender: { hp: 12 },
19
+ * damage: 4,
20
+ * strikes: 3,
21
+ * chanceToHit: 0.6,
22
+ * strikeDuration: 0.2,
23
+ * });
24
+ * console.log(preview.totalDamage); // 12
25
+ * console.log(preview.sampleAt(0.25).defenderHp); // 8, after the second strike
26
+ * ```
27
+ */
28
+ export class AttackPreview {
29
+ constructor(options) {
30
+ this.chanceToHit = clamp01(options.chanceToHit ?? 1);
31
+ this.strikeDuration = Math.max(0, options.strikeDuration ?? 0);
32
+ this.totalDamage = Math.max(0, options.damage) * Math.max(0, options.strikes);
33
+ this.expectedDamage = this.totalDamage * this.chanceToHit;
34
+ this.defenderHp0 = options.defender.hp;
35
+ const frames = [];
36
+ let defenderHp = options.defender.hp;
37
+ for (let strike = 0; strike <= options.strikes; strike++) {
38
+ if (strike > 0 && (options.hits?.[strike - 1] ?? true))
39
+ defenderHp -= Math.max(0, options.damage);
40
+ frames.push({
41
+ strike,
42
+ time: strike * this.strikeDuration,
43
+ attackerHp: options.attacker.hp,
44
+ defenderHp: Math.max(0, defenderHp),
45
+ defenderDamage: this.defenderHp0 - Math.max(0, defenderHp),
46
+ });
47
+ }
48
+ this.frames = frames;
49
+ }
50
+ /** how long the animation runs, in seconds */
51
+ get duration() {
52
+ return this.strikeDuration * (this.frames.length - 1);
53
+ }
54
+ /** whether the defender would be at 0 hp once every landing strike has been shown */
55
+ get defenderKilled() {
56
+ return this.frames[this.frames.length - 1].defenderHp === 0;
57
+ }
58
+ /** the frame the animation shows at `time`, clamped to its own start and end */
59
+ sampleAt(time) {
60
+ const last = this.frames.length - 1;
61
+ if (this.strikeDuration <= 0)
62
+ return this.frames[last];
63
+ //compare frame times rather than dividing, so a float strike duration does not land on the
64
+ //frame before the one its time names (0.4 / 0.2 can read just under 2)
65
+ let index = 0;
66
+ for (let frame = 0; frame <= last; frame++) {
67
+ if (this.frames[frame].time <= time + 1e-9)
68
+ index = frame;
69
+ else
70
+ break;
71
+ }
72
+ return this.frames[index];
73
+ }
74
+ }
75
+ function clamp01(value) {
76
+ return Math.max(0, Math.min(1, value));
77
+ }
78
+ //# sourceMappingURL=AttackPreview.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AttackPreview.js","sourceRoot":"","sources":["../../src/battle/AttackPreview.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * A per-run battle statistics breakdown by category and unit type - recruits, recalls,
3
+ * advances, kills, deaths, damage dealt, damage taken - the shape Wesnoth's own
4
+ * statistics dialog shows and a bare `core.PlayerStats<T>` cannot give a game for free,
5
+ * since `T` there is an opaque summary the game must already know how to combine. Here
6
+ * the categories and the per-unit-type breakdown are the primitive: a game records one
7
+ * event at a time as it happens and reads either a category's total or its breakdown by
8
+ * unit type, then folds `toJSON()` into a `core.RunHistory` entry or a `core.PlayerStats`
9
+ * total the way any other run summary would.
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * import { BattleStats } from '@datamoc/mw_games/battle';
14
+ *
15
+ * const stats = new BattleStats();
16
+ * stats.record('kills', 'orc-grunt');
17
+ * stats.record('kills', 'orc-grunt');
18
+ * stats.record('damageDealt', 'elvish-archer', 12);
19
+ *
20
+ * console.log(stats.total('kills')); // 2
21
+ * console.log(stats.breakdown('kills')); // [{ unitType: 'orc-grunt', count: 2 }]
22
+ * ```
23
+ */
24
+ export type BattleStatCategory = 'recruits' | 'recalls' | 'advances' | 'kills' | 'deaths' | 'damageDealt' | 'damageTaken';
25
+ export declare class BattleStats {
26
+ private counts;
27
+ /** adds `amount` (default 1) to a category's count for one unit type */
28
+ record(category: BattleStatCategory, unitType: string, amount?: number): void;
29
+ /** one category's count for one unit type; 0 for a combination nothing has recorded yet */
30
+ forType(category: BattleStatCategory, unitType: string): number;
31
+ /** a category's total across every unit type */
32
+ total(category: BattleStatCategory): number;
33
+ /** every unit type a category has recorded, with its count - the rows a stats screen breaks a category into */
34
+ breakdown(category: BattleStatCategory): readonly {
35
+ unitType: string;
36
+ count: number;
37
+ }[];
38
+ toJSON(): {
39
+ counts: [BattleStatCategory, [string, number][]][];
40
+ };
41
+ static fromJSON(data: {
42
+ counts: [BattleStatCategory, [string, number][]][];
43
+ }): BattleStats;
44
+ }
45
+ //# sourceMappingURL=BattleStats.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BattleStats.d.ts","sourceRoot":"","sources":["../../src/battle/BattleStats.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,MAAM,MAAM,kBAAkB,GAC3B,UAAU,GACV,SAAS,GACT,UAAU,GACV,OAAO,GACP,QAAQ,GACR,aAAa,GACb,aAAa,CAAC;AAEjB,qBAAa,WAAW;IACvB,OAAO,CAAC,MAAM,CAAsD;IAEpE,wEAAwE;IACxE,MAAM,CAAC,QAAQ,EAAE,kBAAkB,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,SAAI,GAAG,IAAI,CAIvE;IAED,2FAA2F;IAC3F,OAAO,CAAC,QAAQ,EAAE,kBAAkB,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE9D;IAED,gDAAgD;IAChD,KAAK,CAAC,QAAQ,EAAE,kBAAkB,GAAG,MAAM,CAM1C;IAED,+GAA+G;IAC/G,SAAS,CAAC,QAAQ,EAAE,kBAAkB,GAAG,SAAS;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAGtF;IAED,MAAM,IAAI;QAAE,MAAM,EAAE,CAAC,kBAAkB,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,EAAE,CAAA;KAAE,CAE/D;IAED,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,CAAC,kBAAkB,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,EAAE,CAAA;KAAE,GAAG,WAAW,CAIzF;CACD"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * A per-run battle statistics breakdown by category and unit type - recruits, recalls,
3
+ * advances, kills, deaths, damage dealt, damage taken - the shape Wesnoth's own
4
+ * statistics dialog shows and a bare `core.PlayerStats<T>` cannot give a game for free,
5
+ * since `T` there is an opaque summary the game must already know how to combine. Here
6
+ * the categories and the per-unit-type breakdown are the primitive: a game records one
7
+ * event at a time as it happens and reads either a category's total or its breakdown by
8
+ * unit type, then folds `toJSON()` into a `core.RunHistory` entry or a `core.PlayerStats`
9
+ * total the way any other run summary would.
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * import { BattleStats } from '@datamoc/mw_games/battle';
14
+ *
15
+ * const stats = new BattleStats();
16
+ * stats.record('kills', 'orc-grunt');
17
+ * stats.record('kills', 'orc-grunt');
18
+ * stats.record('damageDealt', 'elvish-archer', 12);
19
+ *
20
+ * console.log(stats.total('kills')); // 2
21
+ * console.log(stats.breakdown('kills')); // [{ unitType: 'orc-grunt', count: 2 }]
22
+ * ```
23
+ */
24
+ export class BattleStats {
25
+ constructor() {
26
+ this.counts = new Map();
27
+ }
28
+ /** adds `amount` (default 1) to a category's count for one unit type */
29
+ record(category, unitType, amount = 1) {
30
+ const byType = this.counts.get(category) ?? new Map();
31
+ byType.set(unitType, (byType.get(unitType) ?? 0) + amount);
32
+ this.counts.set(category, byType);
33
+ }
34
+ /** one category's count for one unit type; 0 for a combination nothing has recorded yet */
35
+ forType(category, unitType) {
36
+ return this.counts.get(category)?.get(unitType) ?? 0;
37
+ }
38
+ /** a category's total across every unit type */
39
+ total(category) {
40
+ const byType = this.counts.get(category);
41
+ if (!byType)
42
+ return 0;
43
+ let sum = 0;
44
+ for (const count of byType.values())
45
+ sum += count;
46
+ return sum;
47
+ }
48
+ /** every unit type a category has recorded, with its count - the rows a stats screen breaks a category into */
49
+ breakdown(category) {
50
+ const byType = this.counts.get(category);
51
+ return byType ? [...byType.entries()].map(([unitType, count]) => ({ unitType, count })) : [];
52
+ }
53
+ toJSON() {
54
+ return { counts: [...this.counts].map(([category, byType]) => [category, [...byType]]) };
55
+ }
56
+ static fromJSON(data) {
57
+ const stats = new BattleStats();
58
+ for (const [category, entries] of data.counts)
59
+ stats.counts.set(category, new Map(entries));
60
+ return stats;
61
+ }
62
+ }
63
+ //# sourceMappingURL=BattleStats.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BattleStats.js","sourceRoot":"","sources":["../../src/battle/BattleStats.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAWH,MAAM,OAAO,WAAW;IAAxB;QACS,WAAM,GAAG,IAAI,GAAG,EAA2C,CAAC;IAsCrE,CAAC;IApCA,wEAAwE;IACxE,MAAM,CAAC,QAA4B,EAAE,QAAgB,EAAE,MAAM,GAAG,CAAC;QAChE,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,IAAI,GAAG,EAAkB,CAAC;QACtE,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC;QAC3D,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACnC,CAAC;IAED,2FAA2F;IAC3F,OAAO,CAAC,QAA4B,EAAE,QAAgB;QACrD,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACtD,CAAC;IAED,gDAAgD;IAChD,KAAK,CAAC,QAA4B;QACjC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACzC,IAAI,CAAC,MAAM;YAAE,OAAO,CAAC,CAAC;QACtB,IAAI,GAAG,GAAG,CAAC,CAAC;QACZ,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE;YAAE,GAAG,IAAI,KAAK,CAAC;QAClD,OAAO,GAAG,CAAC;IACZ,CAAC;IAED,+GAA+G;IAC/G,SAAS,CAAC,QAA4B;QACrC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACzC,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9F,CAAC;IAED,MAAM;QACL,OAAO,EAAE,MAAM,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IAC1F,CAAC;IAED,MAAM,CAAC,QAAQ,CAAC,IAA4D;QAC3E,MAAM,KAAK,GAAG,IAAI,WAAW,EAAE,CAAC;QAChC,KAAK,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,IAAI,IAAI,CAAC,MAAM;YAAE,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;QAC5F,OAAO,KAAK,CAAC;IACd,CAAC;CACD"}
@@ -0,0 +1,72 @@
1
+ import { Signal } from '../core/Signal.ts';
2
+ export interface SelectableUnit {
3
+ id: string;
4
+ side?: string;
5
+ /** current hp, so an attack dialog can preview it; the selector itself never reads it */
6
+ hp?: number;
7
+ disabled?: boolean;
8
+ }
9
+ export interface UnitSelectorOptions {
10
+ units: readonly SelectableUnit[];
11
+ /** only units on this side may attack; every enabled unit when omitted */
12
+ side?: string;
13
+ /** further narrows who may be chosen at all (a unit that has already acted, say) */
14
+ disabled?: (unit: SelectableUnit) => boolean;
15
+ /** narrows the targets once an attacker is chosen (adjacency, range, fog) */
16
+ canTarget?: (attacker: SelectableUnit, target: SelectableUnit) => boolean;
17
+ }
18
+ export type SelectorStage = 'attacker' | 'target';
19
+ /**
20
+ * The two-step unit selector an attack starts with: pick who attacks, then pick what they attack.
21
+ *
22
+ * Renderer-free, so the choosing rules are tested without a board: `candidates` are the enabled
23
+ * units on the selecting side, `targets` are the enabled units that pass `canTarget` once an
24
+ * attacker is chosen, `move` walks the active list and `select` advances a stage. `back` undoes the
25
+ * attacker choice rather than only the target, so a player who picked the wrong attacker lands
26
+ * where they can pick again rather than being stuck one step in.
27
+ *
28
+ * @example
29
+ * ```ts
30
+ * import { UnitSelector } from '@datamoc/mw_games/battle';
31
+ *
32
+ * const selector = new UnitSelector({
33
+ * units: [{ id: 'hero', side: '1' }, { id: 'rat', side: '2' }],
34
+ * side: '1',
35
+ * });
36
+ * selector.select(); // hero attacks
37
+ * console.log(selector.targets.map((unit) => unit.id)); // ['rat']
38
+ * ```
39
+ */
40
+ export declare class UnitSelector {
41
+ readonly onChange: Signal<void>;
42
+ private readonly units;
43
+ private readonly side?;
44
+ private readonly disabledOf;
45
+ private readonly canTarget;
46
+ private stage_;
47
+ private charge;
48
+ private victim;
49
+ private highlight_;
50
+ constructor(options: UnitSelectorOptions);
51
+ get stage(): SelectorStage;
52
+ get attacker(): SelectableUnit | null;
53
+ get target(): SelectableUnit | null;
54
+ /** both steps are done, so the caller has an attack to make */
55
+ get done(): boolean;
56
+ /** the units that may be picked as the attacker */
57
+ get candidates(): readonly SelectableUnit[];
58
+ /** the units the chosen attacker may hit; empty until an attacker is chosen */
59
+ get targets(): readonly SelectableUnit[];
60
+ /** the index of the highlight within the active list */
61
+ get highlight(): number;
62
+ get highlighted(): SelectableUnit | null;
63
+ /** moves the highlight, wrapping at both ends, within whichever list is active */
64
+ move(delta: number): void;
65
+ /** takes the highlighted unit: on the attacker stage it advances to choosing a target */
66
+ select(): boolean;
67
+ /** undoes the attacker choice, so a wrong attacker can be picked again */
68
+ back(): void;
69
+ reset(): void;
70
+ private active;
71
+ }
72
+ //# sourceMappingURL=UnitSelector.d.ts.map