@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
@@ -0,0 +1,370 @@
1
+ /** the tags this parser knows; anything else is literal text, not a tag */
2
+ const KNOWN_TAGS = new Set(['b', 'i', 'span', 'br', 'img']);
3
+ /**
4
+ * Reads `source` as inline markup and returns one span per styled run.
5
+ *
6
+ * Known tags are `<b>`, `<i>`, `<span color='…' size='…'>`, `<br/>` (or `<br>`, which becomes a
7
+ * newline in the text) and `<img>path</img>` (or `<img src='path'/>`). Nesting works the way it
8
+ * reads: a `<b>` inside an `<i>` is both, and a closing tag restores exactly the style it opened
9
+ * over. `&lt;`, `&gt;`, `&amp;`, `&quot;` and `&apos;` are the escapes for the characters that would
10
+ * otherwise be syntax.
11
+ *
12
+ * Two rules are deliberate, because content written by hand meets both. An **unknown or malformed**
13
+ * tag - `<blink>`, a `</b>` that closes nothing, an `<img>` with no `</img>` - is kept literally in
14
+ * the text rather than dropped or guessed at, so the worst case is text that reads oddly instead of
15
+ * text that lost a piece. And a `$name` that nothing supplies is kept literally too, for the same
16
+ * reason.
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * import { escapeHtml, markupToHtml, parseMarkup, stripMarkup } from '@datamoc/mw_games/two-d/ui';
21
+ *
22
+ * const spans = parseMarkup("Hail, <b>$name</b>! <span color='#c0ffee'>Four</span> coins<br/>And an<img>gold.png</img>", {
23
+ * variables: { name: 'Kalenz' },
24
+ * });
25
+ *
26
+ * console.log(spans[1]); // { text: 'Kalenz', bold: true, italic: false }
27
+ * console.log(spans[2]); // { text: 'Four', bold: false, italic: false, color: '#c0ffee' }
28
+ * console.log(spans[3].text); // '\nAnd an'
29
+ * console.log(spans[4].image); // 'gold.png'
30
+ * console.log(stripMarkup('<b>Bold</b> and <i>italic</i>')); // 'Bold and italic'
31
+ * console.log(markupToHtml(spans)); // escaped text with <b>/<i> around the emphasis
32
+ * ```
33
+ */
34
+ export function parseMarkup(source, options = {}) {
35
+ const variables = options.variables ?? {};
36
+ const spans = [];
37
+ const stack = [];
38
+ let style = { bold: false, italic: false };
39
+ let text = '';
40
+ const flush = () => {
41
+ if (!text)
42
+ return;
43
+ const span = { text, bold: style.bold, italic: style.italic };
44
+ if (style.color !== undefined)
45
+ span.color = style.color;
46
+ if (style.size !== undefined)
47
+ span.size = style.size;
48
+ spans.push(span);
49
+ text = '';
50
+ };
51
+ for (let index = 0; index < source.length; index++) {
52
+ const char = source[index];
53
+ if (char === '&') {
54
+ const entity = readEntity(source, index);
55
+ if (entity) {
56
+ text += entity.text;
57
+ index = entity.end;
58
+ continue;
59
+ }
60
+ }
61
+ if (char === '$') {
62
+ const variable = readVariable(source, index);
63
+ const value = variable ? variables[variable.name] : undefined;
64
+ if (variable && value !== undefined) {
65
+ text += value;
66
+ index = variable.end;
67
+ continue;
68
+ }
69
+ }
70
+ if (char === '<') {
71
+ const tag = readTag(source, index);
72
+ if (tag && KNOWN_TAGS.has(tag.name)) {
73
+ if (tag.name === 'img') {
74
+ const image = readImage(source, tag);
75
+ if (image) {
76
+ flush();
77
+ spans.push({ text: '', bold: false, italic: false, image: image.src });
78
+ index = image.end;
79
+ continue;
80
+ }
81
+ }
82
+ else if (tag.name === 'br') {
83
+ text += '\n';
84
+ index = tag.end;
85
+ continue;
86
+ }
87
+ else if (!tag.closing) {
88
+ //flush before the style moves: the text so far belongs to the style it was written
89
+ //in, and accumulating across a tag would give all of it the last style seen
90
+ flush();
91
+ stack.push({ tag: tag.name, style });
92
+ style = opened(style, tag.name, tag.body);
93
+ index = tag.end;
94
+ continue;
95
+ }
96
+ else {
97
+ const top = stack[stack.length - 1];
98
+ if (top?.tag === tag.name) {
99
+ flush();
100
+ style = top.style;
101
+ stack.pop();
102
+ index = tag.end;
103
+ continue;
104
+ }
105
+ }
106
+ }
107
+ }
108
+ text += char;
109
+ }
110
+ flush();
111
+ return spans;
112
+ }
113
+ /** The same text with the markup taken out, for a place that cannot render any of it. */
114
+ export function stripMarkup(source, options = {}) {
115
+ return parseMarkup(source, options)
116
+ .map((span) => span.image ?? span.text)
117
+ .join('');
118
+ }
119
+ /**
120
+ * Spans as an HTML fragment for a renderer that speaks HTML text, which is what the ui's `RichLabel`
121
+ * is. Emphasis, colour and size all render - `HTMLText`'s dialect is real CSS, so a `color`/`size`
122
+ * span becomes a `<span style="...">` wrapper - and the text is escaped, so `a < b` can never turn
123
+ * into a tag. An image span renders as nothing here: `HTMLText` has no inline-image primitive, so a
124
+ * caller that wants the image drawn positions a sprite itself, at the position `layoutMarkupLines`
125
+ * reports for that span.
126
+ */
127
+ export function markupToHtml(spans) {
128
+ return spans
129
+ .map((span) => {
130
+ if (span.image !== undefined)
131
+ return '';
132
+ let text = escapeHtml(span.text);
133
+ if (span.italic)
134
+ text = `<i>${text}</i>`;
135
+ if (span.bold)
136
+ text = `<b>${text}</b>`;
137
+ const style = [
138
+ span.color !== undefined ? `color:${span.color}` : undefined,
139
+ span.size !== undefined ? `font-size:${span.size}px` : undefined,
140
+ ].filter((declaration) => declaration !== undefined);
141
+ if (style.length > 0)
142
+ text = `<span style="${style.join(';')}">${text}</span>`;
143
+ return text;
144
+ })
145
+ .join('');
146
+ }
147
+ /**
148
+ * The plain-text projection for a place that cannot render any markup at all - a screen reader, a
149
+ * log line, a tooltip that only takes a string. Unlike `stripMarkup` (which keeps an image span's
150
+ * raw path, useful for round-tripping but not for reading aloud), an image becomes `describeImage`'s
151
+ * result, defaulting to `[image]` so an image is at least announced as present rather than silently
152
+ * dropped or read as a file path.
153
+ *
154
+ * @example
155
+ * ```ts
156
+ * import { markupAccessibilityText, parseMarkup } from '@datamoc/mw_games/two-d/ui';
157
+ *
158
+ * const spans = parseMarkup("Pay <b>10</b> gold<img>coin.png</img>");
159
+ * console.log(markupAccessibilityText(spans)); // 'Pay 10 gold[image]'
160
+ * console.log(markupAccessibilityText(spans, { describeImage: () => ' (coin icon)' }));
161
+ * // 'Pay 10 gold (coin icon)'
162
+ * ```
163
+ */
164
+ export function markupAccessibilityText(spans, options = {}) {
165
+ const describeImage = options.describeImage ?? (() => '[image]');
166
+ return spans.map((span) => (span.image !== undefined ? describeImage(span.image) : span.text)).join('');
167
+ }
168
+ /**
169
+ * Wraps already-parsed spans into lines, word by word, each word measured under its own span's
170
+ * style - which is the acceptance this item was missing: wrapping computed *after* styling, not
171
+ * before it, so a bold or larger run wraps where its own wider glyphs actually land rather than
172
+ * where the plain text would have. An explicit line break (`<br/>`, which `parseMarkup` turns into
173
+ * a literal `\n`) always starts a new line; otherwise a word moves to the next line only once it
174
+ * would overflow `maxWidth`, so a single word wider than `maxWidth` still renders (not clipped) as
175
+ * ever a caller's original design.
176
+ *
177
+ * Backend-neutral by construction: the same spans, the same `measure` function and the same
178
+ * `maxWidth` given to `layoutMarkupLines` decide the exact same line breaks whether the caller then
179
+ * draws each line through `markupToHtml`/`HTMLText` or through a canvas `Text2D` per run - which is
180
+ * what makes the two backends render "equivalent runs" rather than each doing its own wrapping.
181
+ *
182
+ * @example
183
+ * ```ts
184
+ * import { layoutMarkupLines, parseMarkup } from '@datamoc/mw_games/two-d/ui';
185
+ *
186
+ * const spans = parseMarkup('Take <b>two small</b> coins');
187
+ * const lines = layoutMarkupLines(spans, (piece) => piece.text.length * 8, 100);
188
+ * console.log(lines.map((line) => line.spans.map((span) => span.text).join('')));
189
+ * ```
190
+ */
191
+ export function layoutMarkupLines(spans, measure, maxWidth) {
192
+ const pieces = [];
193
+ for (const span of spans) {
194
+ if (span.image !== undefined) {
195
+ pieces.push({ ...span });
196
+ continue;
197
+ }
198
+ const parts = span.text.split('\n');
199
+ parts.forEach((part, index) => {
200
+ if (index > 0)
201
+ pieces.push({ text: '', bold: span.bold, italic: span.italic, hardBreak: true });
202
+ for (const word of part.match(/\S+|\s+/g) ?? []) {
203
+ pieces.push({ text: word, bold: span.bold, italic: span.italic, color: span.color, size: span.size });
204
+ }
205
+ });
206
+ }
207
+ const lines = [];
208
+ let current = [];
209
+ let width = 0;
210
+ const flushLine = () => {
211
+ //trailing whitespace never counts toward a line's own width or survives into it
212
+ while (current.length > 0 &&
213
+ current[current.length - 1].text.trim() === '' &&
214
+ !current[current.length - 1].image) {
215
+ width -= measure(current[current.length - 1]);
216
+ current.pop();
217
+ }
218
+ lines.push({ spans: mergeMarkupSpans(current), width: Math.max(0, width) });
219
+ current = [];
220
+ width = 0;
221
+ };
222
+ for (const piece of pieces) {
223
+ if (piece.hardBreak) {
224
+ flushLine();
225
+ continue;
226
+ }
227
+ const pieceWidth = measure(piece);
228
+ const isSpace = piece.image === undefined && piece.text.trim() === '';
229
+ if (width > 0 && !isSpace && width + pieceWidth > maxWidth)
230
+ flushLine();
231
+ current.push(piece);
232
+ width += pieceWidth;
233
+ }
234
+ if (current.length > 0 || lines.length === 0)
235
+ flushLine();
236
+ return lines;
237
+ }
238
+ function mergeMarkupSpans(pieces) {
239
+ const merged = [];
240
+ for (const piece of pieces) {
241
+ const last = merged[merged.length - 1];
242
+ if (last &&
243
+ piece.image === undefined &&
244
+ last.image === undefined &&
245
+ last.bold === piece.bold &&
246
+ last.italic === piece.italic &&
247
+ last.color === piece.color &&
248
+ last.size === piece.size) {
249
+ last.text += piece.text;
250
+ }
251
+ else {
252
+ merged.push({ ...piece });
253
+ }
254
+ }
255
+ return merged;
256
+ }
257
+ /**
258
+ * Positions already-laid-out lines into a flat list of placed runs, one per span, so a canvas
259
+ * backend draws each span as its own `Text2D` (or an image as its own sprite) at the reported
260
+ * `x`/`y`. This is the half `layoutMarkupLines` deliberately leaves out: it decides *which*
261
+ * spans share a line, this decides *where* on the line each one lands.
262
+ *
263
+ * Direction is what makes the two backends equivalent rather than merely identical in text: an
264
+ * `rtl` line places its first (logical) span at the right edge and flows left from it, while an
265
+ * `ltr` line flows right from the left edge, so the same spans read in the language's own order
266
+ * on either side. Alignment then moves the whole line within `maxWidth`, defaulting to the
267
+ * direction's natural edge (`'right'` for `rtl`, `'left'` for `ltr`). A line wider than
268
+ * `maxWidth` is not clipped - the same "a single long word still renders" rule
269
+ * `layoutMarkupLines` already keeps - it simply starts left of zero.
270
+ *
271
+ * @example
272
+ * ```ts
273
+ * import { layoutMarkupLines, parseMarkup, positionMarkupLines } from '@datamoc/mw_games/two-d/ui';
274
+ *
275
+ * const spans = parseMarkup('Take <b>two</b> coins<img>coin.png</img>');
276
+ * const measure = (piece: { text: string; image?: string }) =>
277
+ * piece.image !== undefined ? 16 : piece.text.length * 8;
278
+ * const lines = layoutMarkupLines(spans, measure, 120);
279
+ * const runs = positionMarkupLines(lines, { measure, maxWidth: 120, lineHeight: 16 });
280
+ * console.log(runs.map((run) => run.span.image ?? run.span.text).join(''));
281
+ * ```
282
+ */
283
+ export function positionMarkupLines(lines, layout) {
284
+ const direction = layout.direction ?? 'ltr';
285
+ const align = layout.align ?? (direction === 'rtl' ? 'right' : 'left');
286
+ const placed = [];
287
+ for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
288
+ const line = lines[lineIndex];
289
+ const y = lineIndex * layout.lineHeight;
290
+ const widths = line.spans.map((span) => layout.measure(span));
291
+ const total = widths.reduce((sum, width) => sum + width, 0);
292
+ const startX = align === 'left' ? 0 : align === 'right' ? layout.maxWidth - total : (layout.maxWidth - total) / 2;
293
+ let cursor = direction === 'rtl' ? startX + total : startX;
294
+ for (let index = 0; index < line.spans.length; index++) {
295
+ const width = widths[index];
296
+ const x = direction === 'rtl' ? cursor - width : cursor;
297
+ cursor = direction === 'rtl' ? cursor - width : cursor + width;
298
+ placed.push({ span: line.spans[index], x, y, width });
299
+ }
300
+ }
301
+ return placed;
302
+ }
303
+ /** escapes the five characters that would otherwise read as syntax */
304
+ export function escapeHtml(text) {
305
+ return text
306
+ .replaceAll('&', '&amp;')
307
+ .replaceAll('<', '&lt;')
308
+ .replaceAll('>', '&gt;')
309
+ .replaceAll('"', '&quot;')
310
+ .replaceAll("'", '&#39;');
311
+ }
312
+ function opened(style, name, body) {
313
+ if (name === 'b')
314
+ return { ...style, bold: true };
315
+ if (name === 'i')
316
+ return { ...style, italic: true };
317
+ const color = attribute(body, 'color');
318
+ const size = Number(attribute(body, 'size') ?? '');
319
+ const next = { ...style };
320
+ if (color)
321
+ next.color = color;
322
+ if (Number.isFinite(size) && size > 0)
323
+ next.size = size;
324
+ return next;
325
+ }
326
+ /** the value of one attribute, quoted or bare; `undefined` when it is not there */
327
+ function attribute(body, name) {
328
+ const match = new RegExp(`(?:^|\\s)${name}\\s*=\\s*(?:"([^"]*)"|'([^']*)'|([^\\s"'>]+))`).exec(body);
329
+ return match ? (match[1] ?? match[2] ?? match[3]) : undefined;
330
+ }
331
+ function readTag(source, at) {
332
+ const match = /^<(\/?)([a-zA-Z]+)([^>]*)>/.exec(source.slice(at));
333
+ if (!match)
334
+ return null;
335
+ const body = match[3].trimEnd();
336
+ return {
337
+ name: match[2].toLowerCase(),
338
+ closing: match[1] === '/',
339
+ selfClosing: body.endsWith('/') && match[1] === '',
340
+ body: body.replace(/\/$/, '').trim(),
341
+ end: at + match[0].length - 1,
342
+ };
343
+ }
344
+ /** `<img>path</img>` or `<img src='path'/>`, whichever shape was written; null when neither is */
345
+ function readImage(source, tag) {
346
+ if (tag.selfClosing) {
347
+ const src = attribute(tag.body, 'src');
348
+ return src ? { src, end: tag.end } : null;
349
+ }
350
+ const close = source.indexOf('</img>', tag.end + 1);
351
+ if (close < 0)
352
+ return null;
353
+ const src = source.slice(tag.end + 1, close).trim();
354
+ return src ? { src, end: close + '</img>'.length - 1 } : null;
355
+ }
356
+ function readEntity(source, at) {
357
+ const named = { lt: '<', gt: '>', amp: '&', quot: '"', apos: "'" };
358
+ const match = /^&(lt|gt|amp|quot|apos);/.exec(source.slice(at));
359
+ if (match)
360
+ return { text: named[match[1]], end: at + match[0].length - 1 };
361
+ const numeric = /^&#(\d+);/.exec(source.slice(at));
362
+ if (numeric)
363
+ return { text: String.fromCodePoint(Number(numeric[1])), end: at + numeric[0].length - 1 };
364
+ return null;
365
+ }
366
+ function readVariable(source, at) {
367
+ const match = /^\$([A-Za-z_][A-Za-z0-9_]*)/.exec(source.slice(at));
368
+ return match ? { name: match[1], end: at + match[0].length - 1 } : null;
369
+ }
370
+ //# sourceMappingURL=markup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markup.js","sourceRoot":"","sources":["../../../src/two-d/ui/markup.ts"],"names":[],"mappings":"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"}
package/dist/version.d.ts CHANGED
@@ -9,5 +9,5 @@
9
9
  * this from `package.json` automatically, chosen because this project's own build already
10
10
  * has no step before `tsc`/`vite` that this would need to run ahead of.
11
11
  */
12
- export declare const version = "0.7.6";
12
+ export declare const version = "0.7.8";
13
13
  //# sourceMappingURL=version.d.ts.map
package/dist/version.js CHANGED
@@ -9,5 +9,5 @@
9
9
  * this from `package.json` automatically, chosen because this project's own build already
10
10
  * has no step before `tsc`/`vite` that this would need to run ahead of.
11
11
  */
12
- export const version = '0.7.6';
12
+ export const version = '0.7.8';
13
13
  //# sourceMappingURL=version.js.map
@@ -0,0 +1,115 @@
1
+ /**
2
+ * The turn model a strategy game counts by: sides take turns one after another, a round is
3
+ * every side taking one, and the time of day steps on once per round. This is deliberately
4
+ * distinct from `TurnClock` (timed effects on a single actor) and `simulation.Scheduler`
5
+ * (energy-cost turn order within a side): it is the outer loop a scenario runs on.
6
+ *
7
+ * The time schedule carries a `lawfulBonus` per entry, in percent, the number the reference
8
+ * game's `[time] lawful_bonus=` writes (day `25`, night `-25`, dawn/dusk `0`). A unit's
9
+ * alignment turns that into its own bonus, and a `TimeArea` can override the schedule for
10
+ * part of the map, which is what makes the bonus per-hex rather than global.
11
+ */
12
+ /** how a unit's damage answers the time of day, the reference's `align=` values */
13
+ export type Alignment = 'lawful' | 'neutral' | 'chaotic' | 'liminal';
14
+ export interface TimeOfDay {
15
+ /** the time's own name: a game's content (`'morning'`, `'first_watch'`) */
16
+ id: string;
17
+ /** the schedule's own percentage a lawful unit gains and a chaotic one loses */
18
+ lawfulBonus: number;
19
+ }
20
+ /** A part of the map on its own schedule, indexed in parallel with the global one. */
21
+ export interface TimeArea {
22
+ /** the schedule this area's cells use instead of the global one */
23
+ times: readonly TimeOfDay[];
24
+ contains(x: number, y: number): boolean;
25
+ }
26
+ export interface SideTurnsOptions {
27
+ /** the sides in the order they take turns within a round */
28
+ sides: readonly string[];
29
+ /** the time-of-day schedule, one entry per round, cycling */
30
+ schedule: readonly TimeOfDay[];
31
+ /** which side takes the first turn; 0 by default */
32
+ sideIndex?: number;
33
+ /** the first round, 1 by default (rounds are 1-based, as the reference's `turn` is) */
34
+ round?: number;
35
+ /** the schedule entry the first round uses; 0 by default */
36
+ timeIndex?: number;
37
+ /** per-cell schedule overrides; the first area containing a cell wins */
38
+ areas?: readonly TimeArea[];
39
+ }
40
+ export interface SideTurn {
41
+ side: string;
42
+ round: number;
43
+ /** true on the turn that opened a new round and advanced the time of day */
44
+ newRound: boolean;
45
+ }
46
+ /** what `SideTurns.toJSON` writes and `fromJSON` resumes from */
47
+ export interface SideTurnState {
48
+ round: number;
49
+ sideIndex: number;
50
+ timeIndex: number;
51
+ }
52
+ /**
53
+ * Which side is taking its turn, which round it is, and what time of day applies - the
54
+ * outer scenario loop. `advance()` moves to the next side; wrapping past the last side opens
55
+ * a new round and steps the schedule on one entry, cycling. `timeOfDayAt(x, y)` and
56
+ * `lawfulBonusAt(...)` read the schedule for a cell, so a time area can give one region a
57
+ * different bonus without a second turn counter.
58
+ *
59
+ * @example
60
+ * ```ts
61
+ * import { SideTurns } from '@datamoc/mw_games/world';
62
+ *
63
+ * const turns = new SideTurns({
64
+ * sides: ['rebels', 'undead'],
65
+ * schedule: [
66
+ * { id: 'day', lawfulBonus: 25 },
67
+ * { id: 'night', lawfulBonus: -25 },
68
+ * ],
69
+ * });
70
+ *
71
+ * console.log(turns.side, turns.round, turns.timeOfDay.id); // 'rebels' 1 'day'
72
+ * console.log(turns.lawfulBonusAt('lawful', 4, 4)); // 25 - a lawful unit in the day
73
+ * turns.advance(); // 'undead', still round 1
74
+ * turns.advance(); // 'rebels', round 2, timeOfDay is now 'night'
75
+ * ```
76
+ */
77
+ export declare class SideTurns {
78
+ readonly sides: readonly string[];
79
+ readonly schedule: readonly TimeOfDay[];
80
+ private readonly areas;
81
+ private state;
82
+ constructor(options: SideTurnsOptions);
83
+ get round(): number;
84
+ /** the side whose turn it is */
85
+ get side(): string;
86
+ get timeIndex(): number;
87
+ /** the global time of day, before any time area overrides it */
88
+ get timeOfDay(): TimeOfDay;
89
+ get snapshot(): SideTurnState;
90
+ /** The time of day at a cell: the first containing time area's entry, else the global one. */
91
+ timeOfDayAt(x: number, y: number): TimeOfDay;
92
+ /** The percentage bonus `alignment` gets at a cell, through that cell's own time of day. */
93
+ lawfulBonusAt(alignment: Alignment, x: number, y: number): number;
94
+ /** Moves to the next side, opening a new round and stepping the schedule on a wrap. */
95
+ advance(): SideTurn;
96
+ toJSON(): SideTurnState;
97
+ /** Resumes from `toJSON`, with the same sides/schedule/areas the game was created with. */
98
+ static fromJSON(options: SideTurnsOptions, state: SideTurnState): SideTurns;
99
+ }
100
+ /**
101
+ * What one alignment gets out of the time of day's own `lawfulBonus`, in percent: a lawful
102
+ * unit takes it, a chaotic one takes its negation, and neutral and liminal units ignore the
103
+ * time entirely (the reference's own four `align=` values).
104
+ *
105
+ * @example
106
+ * ```ts
107
+ * import { alignmentBonus } from '@datamoc/mw_games/world';
108
+ *
109
+ * alignmentBonus('lawful', 25); // 25 - day favours a lawful unit
110
+ * alignmentBonus('chaotic', 25); // -25 - the same day punishes a chaotic one
111
+ * alignmentBonus('neutral', 25); // 0 - neutral ignores the time of day
112
+ * ```
113
+ */
114
+ export declare function alignmentBonus(alignment: Alignment, lawfulBonus: number): number;
115
+ //# sourceMappingURL=SideTurns.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SideTurns.d.ts","sourceRoot":"","sources":["../../src/world/SideTurns.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,mFAAmF;AACnF,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;AAErE,MAAM,WAAW,SAAS;IACzB,2EAA2E;IAC3E,EAAE,EAAE,MAAM,CAAC;IAEX,gFAAgF;IAChF,WAAW,EAAE,MAAM,CAAC;CACpB;AAED,sFAAsF;AACtF,MAAM,WAAW,QAAQ;IACxB,mEAAmE;IACnE,KAAK,EAAE,SAAS,SAAS,EAAE,CAAC;IAE5B,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CACxC;AAED,MAAM,WAAW,gBAAgB;IAChC,4DAA4D;IAC5D,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAEzB,6DAA6D;IAC7D,QAAQ,EAAE,SAAS,SAAS,EAAE,CAAC;IAE/B,oDAAoD;IACpD,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,uFAAuF;IACvF,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,4DAA4D;IAC5D,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,yEAAyE;IACzE,KAAK,CAAC,EAAE,SAAS,QAAQ,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,QAAQ;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IAEd,4EAA4E;IAC5E,QAAQ,EAAE,OAAO,CAAC;CAClB;AAED,iEAAiE;AACjE,MAAM,WAAW,aAAa;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,qBAAa,SAAS;IACrB,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,QAAQ,EAAE,SAAS,SAAS,EAAE,CAAC;IAExC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAsB;IAC5C,OAAO,CAAC,KAAK,CAAgB;IAE7B,YAAY,OAAO,EAAE,gBAAgB,EAmBpC;IAED,IAAI,KAAK,IAAI,MAAM,CAElB;IAED,gCAAgC;IAChC,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED,gEAAgE;IAChE,IAAI,SAAS,IAAI,SAAS,CAEzB;IAED,IAAI,QAAQ,IAAI,aAAa,CAE5B;IAED,8FAA8F;IAC9F,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,SAAS,CAM3C;IAED,4FAA4F;IAC5F,aAAa,CAAC,SAAS,EAAE,SAAS,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAEhE;IAED,uFAAuF;IACvF,OAAO,IAAI,QAAQ,CAUlB;IAED,MAAM,IAAI,aAAa,CAEtB;IAED,2FAA2F;IAC3F,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,gBAAgB,EAAE,KAAK,EAAE,aAAa,GAAG,SAAS,CAO1E;CACD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAShF"}
@@ -0,0 +1,138 @@
1
+ /**
2
+ * The turn model a strategy game counts by: sides take turns one after another, a round is
3
+ * every side taking one, and the time of day steps on once per round. This is deliberately
4
+ * distinct from `TurnClock` (timed effects on a single actor) and `simulation.Scheduler`
5
+ * (energy-cost turn order within a side): it is the outer loop a scenario runs on.
6
+ *
7
+ * The time schedule carries a `lawfulBonus` per entry, in percent, the number the reference
8
+ * game's `[time] lawful_bonus=` writes (day `25`, night `-25`, dawn/dusk `0`). A unit's
9
+ * alignment turns that into its own bonus, and a `TimeArea` can override the schedule for
10
+ * part of the map, which is what makes the bonus per-hex rather than global.
11
+ */
12
+ /**
13
+ * Which side is taking its turn, which round it is, and what time of day applies - the
14
+ * outer scenario loop. `advance()` moves to the next side; wrapping past the last side opens
15
+ * a new round and steps the schedule on one entry, cycling. `timeOfDayAt(x, y)` and
16
+ * `lawfulBonusAt(...)` read the schedule for a cell, so a time area can give one region a
17
+ * different bonus without a second turn counter.
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * import { SideTurns } from '@datamoc/mw_games/world';
22
+ *
23
+ * const turns = new SideTurns({
24
+ * sides: ['rebels', 'undead'],
25
+ * schedule: [
26
+ * { id: 'day', lawfulBonus: 25 },
27
+ * { id: 'night', lawfulBonus: -25 },
28
+ * ],
29
+ * });
30
+ *
31
+ * console.log(turns.side, turns.round, turns.timeOfDay.id); // 'rebels' 1 'day'
32
+ * console.log(turns.lawfulBonusAt('lawful', 4, 4)); // 25 - a lawful unit in the day
33
+ * turns.advance(); // 'undead', still round 1
34
+ * turns.advance(); // 'rebels', round 2, timeOfDay is now 'night'
35
+ * ```
36
+ */
37
+ export class SideTurns {
38
+ constructor(options) {
39
+ if (options.sides.length === 0)
40
+ throw new Error('SideTurns needs at least one side');
41
+ if (options.schedule.length === 0)
42
+ throw new Error('SideTurns needs a non-empty schedule');
43
+ const round = options.round ?? 1;
44
+ const sideIndex = options.sideIndex ?? 0;
45
+ const timeIndex = options.timeIndex ?? 0;
46
+ if (!Number.isInteger(round) || round < 1)
47
+ throw new Error('SideTurns round must be a positive integer');
48
+ if (sideIndex < 0 || sideIndex >= options.sides.length) {
49
+ throw new Error(`SideTurns side index ${sideIndex} is outside ${options.sides.length} sides`);
50
+ }
51
+ if (timeIndex < 0 || timeIndex >= options.schedule.length) {
52
+ throw new Error(`SideTurns time index ${timeIndex} is outside ${options.schedule.length} times`);
53
+ }
54
+ this.sides = [...options.sides];
55
+ this.schedule = [...options.schedule];
56
+ this.areas = options.areas ?? [];
57
+ this.state = { round, sideIndex, timeIndex };
58
+ }
59
+ get round() {
60
+ return this.state.round;
61
+ }
62
+ /** the side whose turn it is */
63
+ get side() {
64
+ return this.sides[this.state.sideIndex];
65
+ }
66
+ get timeIndex() {
67
+ return this.state.timeIndex;
68
+ }
69
+ /** the global time of day, before any time area overrides it */
70
+ get timeOfDay() {
71
+ return this.schedule[this.state.timeIndex];
72
+ }
73
+ get snapshot() {
74
+ return { ...this.state };
75
+ }
76
+ /** The time of day at a cell: the first containing time area's entry, else the global one. */
77
+ timeOfDayAt(x, y) {
78
+ for (const area of this.areas) {
79
+ if (!area.contains(x, y))
80
+ continue;
81
+ return area.times[this.state.timeIndex] ?? this.timeOfDay;
82
+ }
83
+ return this.timeOfDay;
84
+ }
85
+ /** The percentage bonus `alignment` gets at a cell, through that cell's own time of day. */
86
+ lawfulBonusAt(alignment, x, y) {
87
+ return alignmentBonus(alignment, this.timeOfDayAt(x, y).lawfulBonus);
88
+ }
89
+ /** Moves to the next side, opening a new round and stepping the schedule on a wrap. */
90
+ advance() {
91
+ let newRound = false;
92
+ this.state.sideIndex++;
93
+ if (this.state.sideIndex >= this.sides.length) {
94
+ this.state.sideIndex = 0;
95
+ this.state.round++;
96
+ this.state.timeIndex = (this.state.timeIndex + 1) % this.schedule.length;
97
+ newRound = true;
98
+ }
99
+ return { side: this.side, round: this.state.round, newRound };
100
+ }
101
+ toJSON() {
102
+ return this.snapshot;
103
+ }
104
+ /** Resumes from `toJSON`, with the same sides/schedule/areas the game was created with. */
105
+ static fromJSON(options, state) {
106
+ return new SideTurns({
107
+ ...options,
108
+ round: state.round,
109
+ sideIndex: state.sideIndex,
110
+ timeIndex: state.timeIndex,
111
+ });
112
+ }
113
+ }
114
+ /**
115
+ * What one alignment gets out of the time of day's own `lawfulBonus`, in percent: a lawful
116
+ * unit takes it, a chaotic one takes its negation, and neutral and liminal units ignore the
117
+ * time entirely (the reference's own four `align=` values).
118
+ *
119
+ * @example
120
+ * ```ts
121
+ * import { alignmentBonus } from '@datamoc/mw_games/world';
122
+ *
123
+ * alignmentBonus('lawful', 25); // 25 - day favours a lawful unit
124
+ * alignmentBonus('chaotic', 25); // -25 - the same day punishes a chaotic one
125
+ * alignmentBonus('neutral', 25); // 0 - neutral ignores the time of day
126
+ * ```
127
+ */
128
+ export function alignmentBonus(alignment, lawfulBonus) {
129
+ switch (alignment) {
130
+ case 'lawful':
131
+ return lawfulBonus;
132
+ case 'chaotic':
133
+ return -lawfulBonus;
134
+ default:
135
+ return 0;
136
+ }
137
+ }
138
+ //# sourceMappingURL=SideTurns.js.map