@datamoc/mw_games 0.7.6 → 0.7.8

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 (367) hide show
  1. package/CHANGELOG.md +359 -0
  2. package/README.md +7 -7
  3. package/dist/actors/Inventory.d.ts +7 -1
  4. package/dist/actors/Inventory.d.ts.map +1 -1
  5. package/dist/actors/Inventory.js.map +1 -1
  6. package/dist/actors/Shop.d.ts.map +1 -1
  7. package/dist/actors/Shop.js +4 -1
  8. package/dist/actors/Shop.js.map +1 -1
  9. package/dist/actors/craft.d.ts +35 -14
  10. package/dist/actors/craft.d.ts.map +1 -1
  11. package/dist/actors/craft.js +56 -24
  12. package/dist/actors/craft.js.map +1 -1
  13. package/dist/actors/index.d.ts +1 -1
  14. package/dist/actors/index.d.ts.map +1 -1
  15. package/dist/ai/Heuristics.d.ts +240 -0
  16. package/dist/ai/Heuristics.d.ts.map +1 -0
  17. package/dist/ai/Heuristics.js +298 -0
  18. package/dist/ai/Heuristics.js.map +1 -0
  19. package/dist/ai/index.d.ts +4 -0
  20. package/dist/ai/index.d.ts.map +1 -1
  21. package/dist/ai/index.js +2 -0
  22. package/dist/ai/index.js.map +1 -1
  23. package/dist/ai/lua.d.ts +21 -0
  24. package/dist/ai/lua.d.ts.map +1 -1
  25. package/dist/ai/lua.js +21 -0
  26. package/dist/ai/lua.js.map +1 -1
  27. package/dist/ai/score.d.ts +162 -0
  28. package/dist/ai/score.d.ts.map +1 -0
  29. package/dist/ai/score.js +147 -0
  30. package/dist/ai/score.js.map +1 -0
  31. package/dist/assets/loader.d.ts +18 -4
  32. package/dist/assets/loader.d.ts.map +1 -1
  33. package/dist/assets/loader.js +40 -11
  34. package/dist/assets/loader.js.map +1 -1
  35. package/dist/audio/Positional.d.ts +125 -0
  36. package/dist/audio/Positional.d.ts.map +1 -0
  37. package/dist/audio/Positional.js +135 -0
  38. package/dist/audio/Positional.js.map +1 -0
  39. package/dist/audio/Sound.d.ts +7 -1
  40. package/dist/audio/Sound.d.ts.map +1 -1
  41. package/dist/audio/Sound.js +8 -2
  42. package/dist/audio/Sound.js.map +1 -1
  43. package/dist/audio/index.d.ts +2 -0
  44. package/dist/audio/index.d.ts.map +1 -1
  45. package/dist/audio/index.js +1 -0
  46. package/dist/audio/index.js.map +1 -1
  47. package/dist/battle/AttackDialog.d.ts +75 -0
  48. package/dist/battle/AttackDialog.d.ts.map +1 -0
  49. package/dist/battle/AttackDialog.js +115 -0
  50. package/dist/battle/AttackDialog.js.map +1 -0
  51. package/dist/battle/AttackPreview.d.ts +70 -0
  52. package/dist/battle/AttackPreview.d.ts.map +1 -0
  53. package/dist/battle/AttackPreview.js +78 -0
  54. package/dist/battle/AttackPreview.js.map +1 -0
  55. package/dist/battle/BattleStats.d.ts +45 -0
  56. package/dist/battle/BattleStats.d.ts.map +1 -0
  57. package/dist/battle/BattleStats.js +63 -0
  58. package/dist/battle/BattleStats.js.map +1 -0
  59. package/dist/battle/UnitSelector.d.ts +72 -0
  60. package/dist/battle/UnitSelector.d.ts.map +1 -0
  61. package/dist/battle/UnitSelector.js +117 -0
  62. package/dist/battle/UnitSelector.js.map +1 -0
  63. package/dist/battle/Whiteboard.d.ts +50 -0
  64. package/dist/battle/Whiteboard.d.ts.map +1 -0
  65. package/dist/battle/Whiteboard.js +94 -0
  66. package/dist/battle/Whiteboard.js.map +1 -0
  67. package/dist/battle/index.d.ts +10 -0
  68. package/dist/battle/index.d.ts.map +1 -1
  69. package/dist/battle/index.js +5 -0
  70. package/dist/battle/index.js.map +1 -1
  71. package/dist/board/Classics.d.ts.map +1 -1
  72. package/dist/board/Classics.js +51 -39
  73. package/dist/board/Classics.js.map +1 -1
  74. package/dist/board/FogOfWar.d.ts +32 -2
  75. package/dist/board/FogOfWar.d.ts.map +1 -1
  76. package/dist/board/FogOfWar.js +70 -6
  77. package/dist/board/FogOfWar.js.map +1 -1
  78. package/dist/board/HexSkirmish.d.ts.map +1 -1
  79. package/dist/board/HexSkirmish.js +69 -20
  80. package/dist/board/HexSkirmish.js.map +1 -1
  81. package/dist/board/Tactics.d.ts +7 -0
  82. package/dist/board/Tactics.d.ts.map +1 -1
  83. package/dist/board/Tactics.js +2 -2
  84. package/dist/board/Tactics.js.map +1 -1
  85. package/dist/core/Achievements.d.ts +40 -7
  86. package/dist/core/Achievements.d.ts.map +1 -1
  87. package/dist/core/Achievements.js +53 -7
  88. package/dist/core/Achievements.js.map +1 -1
  89. package/dist/core/Hex.d.ts +26 -6
  90. package/dist/core/Hex.d.ts.map +1 -1
  91. package/dist/core/Hex.js +40 -13
  92. package/dist/core/Hex.js.map +1 -1
  93. package/dist/core/Input.d.ts +36 -0
  94. package/dist/core/Input.d.ts.map +1 -1
  95. package/dist/core/Input.js +63 -1
  96. package/dist/core/Input.js.map +1 -1
  97. package/dist/core/Random.d.ts +93 -0
  98. package/dist/core/Random.d.ts.map +1 -1
  99. package/dist/core/Random.js +177 -0
  100. package/dist/core/Random.js.map +1 -1
  101. package/dist/core/SyncGuard.d.ts +57 -0
  102. package/dist/core/SyncGuard.d.ts.map +1 -0
  103. package/dist/core/SyncGuard.js +116 -0
  104. package/dist/core/SyncGuard.js.map +1 -0
  105. package/dist/core/index.d.ts +3 -1
  106. package/dist/core/index.d.ts.map +1 -1
  107. package/dist/core/index.js +2 -1
  108. package/dist/core/index.js.map +1 -1
  109. package/dist/i18n/Po.d.ts +51 -0
  110. package/dist/i18n/Po.d.ts.map +1 -0
  111. package/dist/i18n/Po.js +176 -0
  112. package/dist/i18n/Po.js.map +1 -0
  113. package/dist/i18n/index.d.ts +2 -0
  114. package/dist/i18n/index.d.ts.map +1 -1
  115. package/dist/i18n/index.js +1 -0
  116. package/dist/i18n/index.js.map +1 -1
  117. package/dist/mw_games.global.js +104 -98
  118. package/dist/mwl/MapFile.d.ts +41 -0
  119. package/dist/mwl/MapFile.d.ts.map +1 -0
  120. package/dist/mwl/MapFile.js +43 -0
  121. package/dist/mwl/MapFile.js.map +1 -0
  122. package/dist/mwl/actors.d.ts +46 -2
  123. package/dist/mwl/actors.d.ts.map +1 -1
  124. package/dist/mwl/actors.js +46 -2
  125. package/dist/mwl/actors.js.map +1 -1
  126. package/dist/mwl/campaign.d.ts +51 -0
  127. package/dist/mwl/campaign.d.ts.map +1 -0
  128. package/dist/mwl/campaign.js +58 -0
  129. package/dist/mwl/campaign.js.map +1 -0
  130. package/dist/mwl/carryover.d.ts +86 -0
  131. package/dist/mwl/carryover.d.ts.map +1 -0
  132. package/dist/mwl/carryover.js +70 -0
  133. package/dist/mwl/carryover.js.map +1 -0
  134. package/dist/mwl/catalog.d.ts +10 -1
  135. package/dist/mwl/catalog.d.ts.map +1 -1
  136. package/dist/mwl/catalog.js +10 -1
  137. package/dist/mwl/catalog.js.map +1 -1
  138. package/dist/mwl/compiler.d.ts +70 -3
  139. package/dist/mwl/compiler.d.ts.map +1 -1
  140. package/dist/mwl/compiler.js +70 -3
  141. package/dist/mwl/compiler.js.map +1 -1
  142. package/dist/mwl/conditions.d.ts +10 -1
  143. package/dist/mwl/conditions.d.ts.map +1 -1
  144. package/dist/mwl/conditions.js +10 -1
  145. package/dist/mwl/conditions.js.map +1 -1
  146. package/dist/mwl/content.d.ts +21 -0
  147. package/dist/mwl/content.d.ts.map +1 -1
  148. package/dist/mwl/content.js +19 -0
  149. package/dist/mwl/content.js.map +1 -1
  150. package/dist/mwl/expression.d.ts +21 -0
  151. package/dist/mwl/expression.d.ts.map +1 -1
  152. package/dist/mwl/expression.js +21 -0
  153. package/dist/mwl/expression.js.map +1 -1
  154. package/dist/mwl/fengari.d.ts +12 -1
  155. package/dist/mwl/fengari.d.ts.map +1 -1
  156. package/dist/mwl/fengari.js +12 -1
  157. package/dist/mwl/fengari.js.map +1 -1
  158. package/dist/mwl/grammar.d.ts +45 -1
  159. package/dist/mwl/grammar.d.ts.map +1 -1
  160. package/dist/mwl/grammar.js +45 -1
  161. package/dist/mwl/grammar.js.map +1 -1
  162. package/dist/mwl/hooks.d.ts +51 -6
  163. package/dist/mwl/hooks.d.ts.map +1 -1
  164. package/dist/mwl/hooks.js +45 -1
  165. package/dist/mwl/hooks.js.map +1 -1
  166. package/dist/mwl/index.d.ts +9 -3
  167. package/dist/mwl/index.d.ts.map +1 -1
  168. package/dist/mwl/index.js +5 -2
  169. package/dist/mwl/index.js.map +1 -1
  170. package/dist/mwl/persistence.d.ts +32 -0
  171. package/dist/mwl/persistence.d.ts.map +1 -1
  172. package/dist/mwl/persistence.js +32 -0
  173. package/dist/mwl/persistence.js.map +1 -1
  174. package/dist/mwl/readers.d.ts +24 -2
  175. package/dist/mwl/readers.d.ts.map +1 -1
  176. package/dist/mwl/readers.js +27 -4
  177. package/dist/mwl/readers.js.map +1 -1
  178. package/dist/mwl/report.d.ts +21 -2
  179. package/dist/mwl/report.d.ts.map +1 -1
  180. package/dist/mwl/report.js +21 -2
  181. package/dist/mwl/report.js.map +1 -1
  182. package/dist/mwl/runtime.d.ts +212 -7
  183. package/dist/mwl/runtime.d.ts.map +1 -1
  184. package/dist/mwl/runtime.js +682 -90
  185. package/dist/mwl/runtime.js.map +1 -1
  186. package/dist/mwl/schema.d.ts +57 -1
  187. package/dist/mwl/schema.d.ts.map +1 -1
  188. package/dist/mwl/schema.js +181 -21
  189. package/dist/mwl/schema.js.map +1 -1
  190. package/dist/mwl/scripts.d.ts +12 -1
  191. package/dist/mwl/scripts.d.ts.map +1 -1
  192. package/dist/mwl/scripts.js +12 -1
  193. package/dist/mwl/scripts.js.map +1 -1
  194. package/dist/mwl/utils.d.ts +2 -0
  195. package/dist/mwl/utils.d.ts.map +1 -1
  196. package/dist/mwl/utils.js +4 -1
  197. package/dist/mwl/utils.js.map +1 -1
  198. package/dist/roguelike/MultiTurnBeam.d.ts +50 -12
  199. package/dist/roguelike/MultiTurnBeam.d.ts.map +1 -1
  200. package/dist/roguelike/MultiTurnBeam.js +100 -53
  201. package/dist/roguelike/MultiTurnBeam.js.map +1 -1
  202. package/dist/roguelike/Targeting.d.ts +94 -0
  203. package/dist/roguelike/Targeting.d.ts.map +1 -1
  204. package/dist/roguelike/Targeting.js +123 -0
  205. package/dist/roguelike/Targeting.js.map +1 -1
  206. package/dist/roguelike/index.d.ts +3 -1
  207. package/dist/roguelike/index.d.ts.map +1 -1
  208. package/dist/roguelike/index.js +1 -0
  209. package/dist/roguelike/index.js.map +1 -1
  210. package/dist/rpg/GameState.d.ts +2 -2
  211. package/dist/rpg/GameState.js +2 -2
  212. package/dist/simulation/CampaignSave.d.ts +68 -0
  213. package/dist/simulation/CampaignSave.d.ts.map +1 -0
  214. package/dist/simulation/CampaignSave.js +54 -0
  215. package/dist/simulation/CampaignSave.js.map +1 -0
  216. package/dist/simulation/EventPresentation.d.ts +111 -0
  217. package/dist/simulation/EventPresentation.d.ts.map +1 -0
  218. package/dist/simulation/EventPresentation.js +120 -0
  219. package/dist/simulation/EventPresentation.js.map +1 -0
  220. package/dist/simulation/index.d.ts +4 -0
  221. package/dist/simulation/index.d.ts.map +1 -1
  222. package/dist/simulation/index.js +2 -0
  223. package/dist/simulation/index.js.map +1 -1
  224. package/dist/two-d/pixi-interop.d.ts +18 -6
  225. package/dist/two-d/pixi-interop.d.ts.map +1 -1
  226. package/dist/two-d/pixi-interop.js +18 -6
  227. package/dist/two-d/pixi-interop.js.map +1 -1
  228. package/dist/two-d/render/AnimatedSprite.d.ts +74 -7
  229. package/dist/two-d/render/AnimatedSprite.d.ts.map +1 -1
  230. package/dist/two-d/render/AnimatedSprite.js +88 -26
  231. package/dist/two-d/render/AnimatedSprite.js.map +1 -1
  232. package/dist/two-d/render/Camera.d.ts +64 -4
  233. package/dist/two-d/render/Camera.d.ts.map +1 -1
  234. package/dist/two-d/render/Camera.js +132 -11
  235. package/dist/two-d/render/Camera.js.map +1 -1
  236. package/dist/two-d/render/ColorTransformBatcher.d.ts +7 -1
  237. package/dist/two-d/render/ColorTransformBatcher.d.ts.map +1 -1
  238. package/dist/two-d/render/ColorTransformBatcher.js +10 -1
  239. package/dist/two-d/render/ColorTransformBatcher.js.map +1 -1
  240. package/dist/two-d/render/Halo.d.ts +58 -0
  241. package/dist/two-d/render/Halo.d.ts.map +1 -0
  242. package/dist/two-d/render/Halo.js +48 -0
  243. package/dist/two-d/render/Halo.js.map +1 -0
  244. package/dist/two-d/render/ImageModifiers.d.ts +174 -0
  245. package/dist/two-d/render/ImageModifiers.d.ts.map +1 -1
  246. package/dist/two-d/render/ImageModifiers.js +454 -1
  247. package/dist/two-d/render/ImageModifiers.js.map +1 -1
  248. package/dist/two-d/render/LightningArc.d.ts +57 -0
  249. package/dist/two-d/render/LightningArc.d.ts.map +1 -0
  250. package/dist/two-d/render/LightningArc.js +91 -0
  251. package/dist/two-d/render/LightningArc.js.map +1 -0
  252. package/dist/two-d/render/PaletteRemap.d.ts +135 -0
  253. package/dist/two-d/render/PaletteRemap.d.ts.map +1 -0
  254. package/dist/two-d/render/PaletteRemap.js +181 -0
  255. package/dist/two-d/render/PaletteRemap.js.map +1 -0
  256. package/dist/two-d/render/Particles.d.ts +26 -0
  257. package/dist/two-d/render/Particles.d.ts.map +1 -1
  258. package/dist/two-d/render/Particles.js +23 -2
  259. package/dist/two-d/render/Particles.js.map +1 -1
  260. package/dist/two-d/render/SpriteAttachment.d.ts +55 -0
  261. package/dist/two-d/render/SpriteAttachment.d.ts.map +1 -0
  262. package/dist/two-d/render/SpriteAttachment.js +60 -0
  263. package/dist/two-d/render/SpriteAttachment.js.map +1 -0
  264. package/dist/two-d/render/TerrainGraphics.d.ts +142 -0
  265. package/dist/two-d/render/TerrainGraphics.d.ts.map +1 -0
  266. package/dist/two-d/render/TerrainGraphics.js +161 -0
  267. package/dist/two-d/render/TerrainGraphics.js.map +1 -0
  268. package/dist/two-d/render/TerrainGraphicsLayer.d.ts +55 -0
  269. package/dist/two-d/render/TerrainGraphicsLayer.d.ts.map +1 -0
  270. package/dist/two-d/render/TerrainGraphicsLayer.js +54 -0
  271. package/dist/two-d/render/TerrainGraphicsLayer.js.map +1 -0
  272. package/dist/two-d/render/TintedSprite.js +2 -2
  273. package/dist/two-d/render/TintedSprite.js.map +1 -1
  274. package/dist/two-d/render/index.d.ts +15 -3
  275. package/dist/two-d/render/index.d.ts.map +1 -1
  276. package/dist/two-d/render/index.js +7 -1
  277. package/dist/two-d/render/index.js.map +1 -1
  278. package/dist/two-d/stage/StoryScreen.d.ts +110 -0
  279. package/dist/two-d/stage/StoryScreen.d.ts.map +1 -0
  280. package/dist/two-d/stage/StoryScreen.js +178 -0
  281. package/dist/two-d/stage/StoryScreen.js.map +1 -0
  282. package/dist/two-d/stage/index.d.ts +2 -0
  283. package/dist/two-d/stage/index.d.ts.map +1 -1
  284. package/dist/two-d/stage/index.js +1 -0
  285. package/dist/two-d/stage/index.js.map +1 -1
  286. package/dist/two-d/ui/Checkbox.d.ts +44 -0
  287. package/dist/two-d/ui/Checkbox.d.ts.map +1 -0
  288. package/dist/two-d/ui/Checkbox.js +89 -0
  289. package/dist/two-d/ui/Checkbox.js.map +1 -0
  290. package/dist/two-d/ui/DataTable.d.ts +79 -0
  291. package/dist/two-d/ui/DataTable.d.ts.map +1 -0
  292. package/dist/two-d/ui/DataTable.js +164 -0
  293. package/dist/two-d/ui/DataTable.js.map +1 -0
  294. package/dist/two-d/ui/Dropdown.d.ts +72 -0
  295. package/dist/two-d/ui/Dropdown.d.ts.map +1 -0
  296. package/dist/two-d/ui/Dropdown.js +126 -0
  297. package/dist/two-d/ui/Dropdown.js.map +1 -0
  298. package/dist/two-d/ui/FloatingText.d.ts +43 -4
  299. package/dist/two-d/ui/FloatingText.d.ts.map +1 -1
  300. package/dist/two-d/ui/FloatingText.js +52 -5
  301. package/dist/two-d/ui/FloatingText.js.map +1 -1
  302. package/dist/two-d/ui/FloatingTextStack.d.ts +86 -13
  303. package/dist/two-d/ui/FloatingTextStack.d.ts.map +1 -1
  304. package/dist/two-d/ui/FloatingTextStack.js +89 -18
  305. package/dist/two-d/ui/FloatingTextStack.js.map +1 -1
  306. package/dist/two-d/ui/Layout.d.ts +105 -0
  307. package/dist/two-d/ui/Layout.d.ts.map +1 -0
  308. package/dist/two-d/ui/Layout.js +174 -0
  309. package/dist/two-d/ui/Layout.js.map +1 -0
  310. package/dist/two-d/ui/MarkupText.d.ts +53 -0
  311. package/dist/two-d/ui/MarkupText.d.ts.map +1 -0
  312. package/dist/two-d/ui/MarkupText.js +122 -0
  313. package/dist/two-d/ui/MarkupText.js.map +1 -0
  314. package/dist/two-d/ui/ScrollBox.d.ts +67 -0
  315. package/dist/two-d/ui/ScrollBox.d.ts.map +1 -0
  316. package/dist/two-d/ui/ScrollBox.js +127 -0
  317. package/dist/two-d/ui/ScrollBox.js.map +1 -0
  318. package/dist/two-d/ui/Skins.d.ts +59 -0
  319. package/dist/two-d/ui/Skins.d.ts.map +1 -0
  320. package/dist/two-d/ui/Skins.js +77 -0
  321. package/dist/two-d/ui/Skins.js.map +1 -0
  322. package/dist/two-d/ui/Slider.d.ts +91 -0
  323. package/dist/two-d/ui/Slider.d.ts.map +1 -0
  324. package/dist/two-d/ui/Slider.js +170 -0
  325. package/dist/two-d/ui/Slider.js.map +1 -0
  326. package/dist/two-d/ui/Spinner.d.ts +67 -0
  327. package/dist/two-d/ui/Spinner.d.ts.map +1 -0
  328. package/dist/two-d/ui/Spinner.js +137 -0
  329. package/dist/two-d/ui/Spinner.js.map +1 -0
  330. package/dist/two-d/ui/TabbedList.d.ts +118 -0
  331. package/dist/two-d/ui/TabbedList.d.ts.map +1 -0
  332. package/dist/two-d/ui/TabbedList.js +221 -0
  333. package/dist/two-d/ui/TabbedList.js.map +1 -0
  334. package/dist/two-d/ui/TextModel.d.ts +65 -0
  335. package/dist/two-d/ui/TextModel.d.ts.map +1 -0
  336. package/dist/two-d/ui/TextModel.js +126 -0
  337. package/dist/two-d/ui/TextModel.js.map +1 -0
  338. package/dist/two-d/ui/TreeView.d.ts +68 -0
  339. package/dist/two-d/ui/TreeView.d.ts.map +1 -0
  340. package/dist/two-d/ui/TreeView.js +133 -0
  341. package/dist/two-d/ui/TreeView.js.map +1 -0
  342. package/dist/two-d/ui/Window.d.ts +13 -0
  343. package/dist/two-d/ui/Window.d.ts.map +1 -1
  344. package/dist/two-d/ui/Window.js +23 -0
  345. package/dist/two-d/ui/Window.js.map +1 -1
  346. package/dist/two-d/ui/WindowStack.d.ts.map +1 -1
  347. package/dist/two-d/ui/WindowStack.js +2 -0
  348. package/dist/two-d/ui/WindowStack.js.map +1 -1
  349. package/dist/two-d/ui/index.d.ts +29 -3
  350. package/dist/two-d/ui/index.d.ts.map +1 -1
  351. package/dist/two-d/ui/index.js +15 -2
  352. package/dist/two-d/ui/index.js.map +1 -1
  353. package/dist/two-d/ui/markup.d.ts +181 -0
  354. package/dist/two-d/ui/markup.d.ts.map +1 -0
  355. package/dist/two-d/ui/markup.js +370 -0
  356. package/dist/two-d/ui/markup.js.map +1 -0
  357. package/dist/version.d.ts +1 -1
  358. package/dist/version.js +1 -1
  359. package/dist/world/SideTurns.d.ts +115 -0
  360. package/dist/world/SideTurns.d.ts.map +1 -0
  361. package/dist/world/SideTurns.js +138 -0
  362. package/dist/world/SideTurns.js.map +1 -0
  363. package/dist/world/index.d.ts +2 -0
  364. package/dist/world/index.d.ts.map +1 -1
  365. package/dist/world/index.js +1 -0
  366. package/dist/world/index.js.map +1 -1
  367. package/package.json +2 -1
@@ -1,3 +1,15 @@
1
+ function matchesIngredient(item, ingredient) {
2
+ if (ingredient.matches)
3
+ return ingredient.matches(item);
4
+ if (ingredient.category !== undefined)
5
+ return item.category === ingredient.category;
6
+ if (typeof ingredient.id === 'string')
7
+ return item.id === ingredient.id;
8
+ return (ingredient.id ?? []).includes(item.id);
9
+ }
10
+ function ingredientHasMatcher(ingredient) {
11
+ return ingredient.matches !== undefined || ingredient.category !== undefined || ingredient.id !== undefined;
12
+ }
1
13
  /**
2
14
  * Resolves a recipe against an inventory: every ingredient checked, consumed, and the
3
15
  * result added, all in one call - the same small-focused-function shape `skillCheck`
@@ -5,10 +17,15 @@
5
17
  * adds/removes what a game already hands it; nothing there turns one stack of items into a
6
18
  * different one, which is the entire gap this closes.
7
19
  *
8
- * All or nothing: if any ingredient is missing or too few, nothing is touched. If every
9
- * ingredient is present but the result would not fit (`Inventory`'s own capacity, unrelated
10
- * to crafting itself), the ingredients are put back exactly as they were rather than being
11
- * spent for nothing.
20
+ * An ingredient may name an exact `id` (or any of several), a `category` ("any herb", matched
21
+ * against the item definition's own `category`), or a `matches` predicate, and a quantity is
22
+ * taken from as many matching stacks as it spans.
23
+ *
24
+ * All or nothing: if any ingredient is missing or too few, nothing is touched. The allocation
25
+ * runs against a working copy of the stacks first, so two ingredients that could both take the
26
+ * same stack cannot each count it. If every ingredient is present but the result would not fit
27
+ * (`Inventory`'s own capacity, unrelated to crafting itself), the ingredients are put back
28
+ * exactly as they were rather than being spent for nothing.
12
29
  *
13
30
  * @returns whether the recipe resolved
14
31
  *
@@ -17,38 +34,53 @@
17
34
  * import { craft, Inventory, type Recipe } from '@datamoc/mw_games/actors';
18
35
  *
19
36
  * const bag = new Inventory();
20
- * bag.add({ id: 'wood', quantity: 3, stackable: true });
21
- * bag.add({ id: 'iron', quantity: 1, stackable: true });
37
+ * bag.add({ id: 'sage', quantity: 2, stackable: true, category: 'herb' });
38
+ * bag.add({ id: 'ember', quantity: 1, stackable: true, category: 'runestone' });
22
39
  *
23
40
  * const recipe: Recipe = {
24
- * ingredients: [{ id: 'wood', quantity: 2 }, { id: 'iron', quantity: 1 }],
25
- * result: { id: 'torch', quantity: 1 },
41
+ * ingredients: [
42
+ * { category: 'herb', quantity: 2 },
43
+ * { category: 'runestone', quantity: 1 },
44
+ * ],
45
+ * result: { id: 'elixir', quantity: 1 },
26
46
  * };
27
47
  *
28
- * const made = craft(bag, recipe); // true - both ingredients present, consumed, torch added
48
+ * const made = craft(bag, recipe); // true - any herb plus any runestone becomes an elixir
29
49
  * ```
30
50
  */
31
51
  export function craft(inventory, recipe) {
32
- const needed = new Map();
52
+ //a working copy of every stack's quantity, so an earlier ingredient consumes this allocation
53
+ //even when a later one matches the same category or predicate
54
+ const available = new Map();
55
+ for (const item of inventory.items)
56
+ available.set(item, item.quantity);
57
+ const taken = new Map();
33
58
  for (const ingredient of recipe.ingredients) {
34
- needed.set(ingredient.id, (needed.get(ingredient.id) ?? 0) + ingredient.quantity);
35
- }
36
- for (const [id, quantity] of needed) {
37
- const held = inventory.find(id);
38
- if (!held || held.quantity < quantity)
59
+ if (!ingredientHasMatcher(ingredient))
60
+ throw new Error('recipe ingredient must name an id, a category, or a matches predicate');
61
+ if (!Number.isFinite(ingredient.quantity) || ingredient.quantity <= 0)
62
+ throw new Error('recipe ingredient quantity must be a positive number');
63
+ let needed = ingredient.quantity;
64
+ for (const item of inventory.items) {
65
+ if (needed <= 0)
66
+ break;
67
+ const held = available.get(item) ?? 0;
68
+ if (held <= 0 || !matchesIngredient(item, ingredient))
69
+ continue;
70
+ const take = Math.min(held, needed);
71
+ available.set(item, held - take);
72
+ needed -= take;
73
+ taken.set(item, (taken.get(item) ?? 0) + take);
74
+ }
75
+ if (needed > 0)
39
76
  return false;
40
77
  }
41
- //captured before anything is removed, so a failed add below can restore exactly this
42
- const consumed = recipe.ingredients.map((ingredient) => ({
43
- ...inventory.find(ingredient.id),
44
- quantity: ingredient.quantity,
45
- }));
46
- for (const ingredient of recipe.ingredients)
47
- inventory.remove(ingredient.id, ingredient.quantity);
78
+ for (const [item, quantity] of taken)
79
+ inventory.remove(item.id, quantity, item.instanceId);
48
80
  if (inventory.add(recipe.result))
49
81
  return true;
50
- for (const item of consumed)
51
- inventory.add(item);
82
+ for (const [item, quantity] of taken)
83
+ inventory.add({ ...item, quantity });
52
84
  return false;
53
85
  }
54
86
  //# sourceMappingURL=craft.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"craft.js","sourceRoot":"","sources":["../../src/actors/craft.ts"],"names":[],"mappings":"AAUA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAM,UAAU,KAAK,CAAC,SAAoB,EAAE,MAAc;IACzD,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IACzC,KAAK,MAAM,UAAU,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;QAC7C,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;IACnF,CAAC;IACD,KAAK,MAAM,CAAC,EAAE,EAAE,QAAQ,CAAC,IAAI,MAAM,EAAE,CAAC;QACrC,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAChC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,QAAQ,GAAG,QAAQ;YAAE,OAAO,KAAK,CAAC;IACrD,CAAC;IAED,qFAAqF;IACrF,MAAM,QAAQ,GAAG,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;QACxD,GAAG,SAAS,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAE;QACjC,QAAQ,EAAE,UAAU,CAAC,QAAQ;KAC7B,CAAC,CAAC,CAAC;IAEJ,KAAK,MAAM,UAAU,IAAI,MAAM,CAAC,WAAW;QAAE,SAAS,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC;IAClG,IAAI,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC;QAAE,OAAO,IAAI,CAAC;IAE9C,KAAK,MAAM,IAAI,IAAI,QAAQ;QAAE,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACjD,OAAO,KAAK,CAAC;AACd,CAAC"}
1
+ {"version":3,"file":"craft.js","sourceRoot":"","sources":["../../src/actors/craft.ts"],"names":[],"mappings":"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"}
@@ -10,7 +10,7 @@ export type { EquippableItem, EquipmentOptions, SavedEquipment } from './Equipme
10
10
  export { Inventory } from './Inventory.ts';
11
11
  export type { InventoryItem, InventoryOptions, ItemDefinition, SavedInventory, SavedInventoryItem, } from './Inventory.ts';
12
12
  export { craft } from './craft.ts';
13
- export type { Recipe } from './craft.ts';
13
+ export type { Ingredient, Recipe } from './craft.ts';
14
14
  export { applyStatusEffect } from './StatusEffect.ts';
15
15
  export type { EffectClock, StatusEffectOptions, StatusEffectHandle } from './StatusEffect.ts';
16
16
  export { identify, enchant, damageItem, repairItem } from './ItemState.ts';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/actors/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/actors/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,240 @@
1
+ /** A single aspect's value: the shapes a game authors in content without inventing a type. */
2
+ export type AspectValue = number | string | boolean | readonly string[];
3
+ export interface AspectValues {
4
+ readonly [name: string]: AspectValue | undefined;
5
+ }
6
+ /**
7
+ * The named tuning knobs of a heuristic AI - Wesnoth's "aspects" - read by whatever stage is
8
+ * scoring candidates. `mwg` ships no values, because the numbers are game content: a side sets
9
+ * `aggression`, `caution`, `village_value`, `keep_away` and the rest to whatever its own balance
10
+ * wants, and a difficulty level overrides a few of them.
11
+ *
12
+ * Typed readers give a stage a usable number without every caller casting: a missing aspect reads
13
+ * as the fallback (`1` for a weight, so an unset aspect is neutral rather than zeroing a score).
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * import { Aspects } from '@datamoc/mw_games/ai';
18
+ *
19
+ * const aspects = new Aspects({ aggression: 2, keep_away: 3 });
20
+ * console.log(aspects.number('aggression')); // 2
21
+ * console.log(aspects.number('caution')); // 0 - the fallback
22
+ * ```
23
+ */
24
+ export declare class Aspects {
25
+ private values;
26
+ constructor(defaults?: AspectValues);
27
+ has(name: string): boolean;
28
+ get(name: string): AspectValue | undefined;
29
+ set(name: string, value: AspectValue): void;
30
+ /** a numeric aspect, or `fallback` when it is missing or not a number */
31
+ number(name: string, fallback?: number): number;
32
+ flag(name: string, fallback?: boolean): boolean;
33
+ /** a list aspect; a bare string reads as a one-entry list, anything else as empty */
34
+ list(name: string): readonly string[];
35
+ names(): string[];
36
+ /** a new store with `overrides` laid over this one, leaving this one untouched */
37
+ with(overrides: AspectValues): Aspects;
38
+ toJSON(): Record<string, AspectValue>;
39
+ static fromJSON(values: Readonly<Record<string, AspectValue>>): Aspects;
40
+ }
41
+ export interface DifficultyLevel {
42
+ id: string;
43
+ name?: string;
44
+ /** the aspects this level changes from the game's base set */
45
+ aspects?: AspectValues;
46
+ }
47
+ /**
48
+ * Named difficulty levels, each a set of aspect overrides - Wesnoth's native difficulty ladder,
49
+ * which is really "the same AI with different numbers". A level is applied on top of a base aspect
50
+ * set, so a game writes its normal AI once and a level only lists what it changes.
51
+ *
52
+ * @example
53
+ * ```ts
54
+ * import { Aspects, Difficulty } from '@datamoc/mw_games/ai';
55
+ *
56
+ * const difficulty = new Difficulty(
57
+ * [{ id: 'hard', aspects: { aggression: 2, keep_away: 3 } }],
58
+ * { aggression: 1 },
59
+ * );
60
+ * console.log(difficulty.aspectsFor('hard').number('aggression')); // 2
61
+ * ```
62
+ */
63
+ export declare class Difficulty {
64
+ private readonly levels;
65
+ private readonly base;
66
+ constructor(levels?: readonly DifficultyLevel[], base?: AspectValues);
67
+ get ids(): string[];
68
+ get(id: string): DifficultyLevel | undefined;
69
+ /** the base aspects with a level's overrides applied; throws for an unknown level */
70
+ aspectsFor(id: string): Aspects;
71
+ }
72
+ export type GoalKind = 'attack' | 'defend' | 'retreat' | 'capture' | 'scout';
73
+ export interface Goal {
74
+ unit: string;
75
+ kind: GoalKind;
76
+ /** what the goal is about: the enemy to attack, the village to hold */
77
+ target?: string;
78
+ /** a tiebreaker between goals, read by a stage's own `weigh`; defaults to 1 */
79
+ priority?: number;
80
+ }
81
+ /**
82
+ * Per-unit goals - "hold this village", "attack that leader" - kept as a small registry for a
83
+ * stage's `weigh` to read. `mwg` does not decide what a goal is worth, the same way it ships no
84
+ * damage formula: the registry answers *what* a unit has been told to do, and a game's weigh
85
+ * decides how much that matters.
86
+ *
87
+ * @example
88
+ * ```ts
89
+ * import { Goals } from '@datamoc/mw_games/ai';
90
+ *
91
+ * const goals = new Goals();
92
+ * goals.set({ unit: 'hero', kind: 'attack', target: 'rat', priority: 3 });
93
+ * console.log(goals.get('hero')?.kind); // 'attack'
94
+ * ```
95
+ */
96
+ export declare class Goals {
97
+ private readonly byUnit;
98
+ set(goal: Goal): void;
99
+ clear(unit: string): void;
100
+ get(unit: string): Goal | undefined;
101
+ all(): readonly Goal[];
102
+ get isEmpty(): boolean;
103
+ }
104
+ /**
105
+ * The order a side recruits in - Wesnoth's `recruitment_pattern`, e.g. `scout, fighter, archer` -
106
+ * cycled by turn. A side with nothing left to recruit falls back to its `fallback`, and a side
107
+ * with neither recruits nothing.
108
+ *
109
+ * @example
110
+ * ```ts
111
+ * import { RecruitmentPattern } from '@datamoc/mw_games/ai';
112
+ *
113
+ * const pattern = new RecruitmentPattern(['scout', 'fighter'], 'fighter');
114
+ * console.log(pattern.at(0), pattern.at(1), pattern.at(2)); // scout fighter scout
115
+ * ```
116
+ */
117
+ export declare class RecruitmentPattern {
118
+ private readonly order;
119
+ private readonly fallback;
120
+ constructor(order: readonly string[], fallback?: string | null);
121
+ get length(): number;
122
+ /** the unit type at a position in the cycle, or the fallback when the pattern is empty */
123
+ at(index: number): string | null;
124
+ /** the entry after `after` in the cycle, or the first when `after` is not in it */
125
+ next(after: string | null): string | null;
126
+ }
127
+ export interface HeuristicCandidate<A = unknown> {
128
+ /** an id for tracing which candidate won */
129
+ id: string;
130
+ action: A;
131
+ /** the unit the action belongs to, matched against `Goals` by a stage's own `weigh` */
132
+ unit?: string;
133
+ /**
134
+ * Aspect-named weights this candidate contributes, e.g. `{ aggression: 3, village_value: 2 }`.
135
+ * `distance` is special: with a `keep_away` aspect it is penalised by `keepAwayScore`.
136
+ */
137
+ factors?: Readonly<Record<string, number>>;
138
+ /** the acting unit's type, when the candidate carries one */
139
+ type?: string;
140
+ /** the acting unit's role/function, e.g. `leader` or `courier` */
141
+ role?: string;
142
+ /** whether the acting unit can recruit */
143
+ can_recruit?: boolean;
144
+ /** the acting unit's own name */
145
+ name?: string;
146
+ }
147
+ export interface HeuristicContext {
148
+ aspects: Aspects;
149
+ goals?: Goals;
150
+ turn?: number;
151
+ }
152
+ export interface HeuristicStage<A = unknown> {
153
+ id: string;
154
+ /** whether this stage runs at all; it always does when omitted */
155
+ when?: (context: HeuristicContext) => boolean;
156
+ /** score one candidate; higher wins. Defaults to `defaultWeigh` */
157
+ weigh?: (candidate: HeuristicCandidate<A>, context: HeuristicContext) => number;
158
+ }
159
+ export interface HeuristicDecision<A = unknown> {
160
+ candidate: HeuristicCandidate<A>;
161
+ stage: string;
162
+ score: number;
163
+ }
164
+ /**
165
+ * The candidate-action pipeline at the heart of a Wesnoth-style AI: stages run in order, the first
166
+ * one whose `when` passes scores every candidate with its `weigh`, and the highest score wins (the
167
+ * earlier candidate on a tie, so a decision is stable across equal scores).
168
+ *
169
+ * The default `weigh` is the aspect-weighted sum of each candidate's `factors`, which is what makes
170
+ * the aspects worth having: a game sets `aggression: 2` once and every candidate naming
171
+ * `aggression` is worth twice as much. `keep_away` is honoured through a candidate's `distance`
172
+ * factor. A stage that needs more - goal priorities, terrain, this turn's state - brings its own
173
+ * `weigh`, and `mwg` stays out of the rules.
174
+ *
175
+ * @example
176
+ * ```ts
177
+ * import { Aspects, HeuristicAI } from '@datamoc/mw_games/ai';
178
+ *
179
+ * const ai = new HeuristicAI<{ kind: string }>({ stages: [{ id: 'best' }] });
180
+ * const decision = ai.decide(
181
+ * [
182
+ * { id: 'wait', action: { kind: 'wait' }, factors: { caution: 2 } },
183
+ * { id: 'charge', action: { kind: 'attack' }, factors: { aggression: 3 } },
184
+ * ],
185
+ * { aspects: new Aspects({ aggression: 1, caution: 1 }) },
186
+ * );
187
+ * console.log(decision?.candidate.id); // 'charge'
188
+ * ```
189
+ */
190
+ export declare class HeuristicAI<A = unknown> {
191
+ private readonly stages;
192
+ constructor(options: {
193
+ stages: readonly HeuristicStage<A>[];
194
+ });
195
+ /** the first stage's best candidate, or `null` when nothing applies or no candidate is given */
196
+ decide(candidates: readonly HeuristicCandidate<A>[], context: HeuristicContext): HeuristicDecision<A> | null;
197
+ }
198
+ /**
199
+ * The default stage weight: each factor times the aspect of the same name (an unset aspect weighs
200
+ * 1, so a factor on its own is not erased), minus a `keepAwayScore` penalty for a candidate's
201
+ * `distance`.
202
+ *
203
+ * @example
204
+ * ```ts
205
+ * import { Aspects, defaultWeigh } from '@datamoc/mw_games/ai';
206
+ *
207
+ * const score = defaultWeigh(
208
+ * { id: 'charge', action: {}, factors: { aggression: 3 } },
209
+ * { aspects: new Aspects({ aggression: 2 }) },
210
+ * );
211
+ * console.log(score); // 6
212
+ * ```
213
+ */
214
+ export declare function defaultWeigh<A>(candidate: HeuristicCandidate<A>, context: HeuristicContext): number;
215
+ /**
216
+ * How much a candidate at `distance` loses for being inside a `keepAway` radius: 0 when there is no
217
+ * radius or the candidate is outside it, otherwise the shortfall as a negative score.
218
+ *
219
+ * @example
220
+ * ```ts
221
+ * import { keepAwayScore } from '@datamoc/mw_games/ai';
222
+ *
223
+ * console.log(keepAwayScore(1, 3)); // -2, two tiles inside the radius
224
+ * console.log(keepAwayScore(5, 3)); // 0, outside it
225
+ * ```
226
+ */
227
+ export declare function keepAwayScore(distance: number, keepAway: number): number;
228
+ /**
229
+ * What a stage's own weigh can add for a goal: its `priority`, or 1 when it has none.
230
+ *
231
+ * @example
232
+ * ```ts
233
+ * import { goalScore } from '@datamoc/mw_games/ai';
234
+ *
235
+ * console.log(goalScore({ unit: 'u1', kind: 'attack', priority: 3 })); // 3
236
+ * console.log(goalScore(undefined)); // 0, no goal at all
237
+ * ```
238
+ */
239
+ export declare function goalScore(goal: Goal | undefined): number;
240
+ //# sourceMappingURL=Heuristics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Heuristics.d.ts","sourceRoot":"","sources":["../../src/ai/Heuristics.ts"],"names":[],"mappings":"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"}