@v12-engine/engine 0.1.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (951) hide show
  1. package/dist/abilities/amounts.d.ts +28 -0
  2. package/dist/abilities/amounts.js +74 -0
  3. package/dist/abilities/amounts.js.map +1 -0
  4. package/dist/abilities/buffs.d.ts +106 -6
  5. package/dist/abilities/buffs.js +443 -0
  6. package/dist/abilities/buffs.js.map +1 -0
  7. package/dist/abilities/castTold.d.ts +2 -0
  8. package/dist/abilities/castTold.js +66 -0
  9. package/dist/abilities/castTold.js.map +1 -0
  10. package/dist/abilities/chance.d.ts +38 -0
  11. package/dist/abilities/chance.js +77 -0
  12. package/dist/abilities/chance.js.map +1 -0
  13. package/dist/abilities/charges.d.ts +18 -0
  14. package/dist/abilities/charges.js +101 -0
  15. package/dist/abilities/charges.js.map +1 -0
  16. package/dist/abilities/control.d.ts +28 -0
  17. package/dist/abilities/control.js +113 -0
  18. package/dist/abilities/control.js.map +1 -0
  19. package/dist/abilities/dispel.js +51 -0
  20. package/dist/abilities/dispel.js.map +1 -0
  21. package/dist/abilities/effectFields.d.ts +1 -1
  22. package/dist/abilities/effectFields.js +676 -0
  23. package/dist/abilities/effectFields.js.map +1 -0
  24. package/dist/abilities/effectIndex.d.ts +27 -0
  25. package/dist/abilities/effectIndex.js +64 -0
  26. package/dist/abilities/effectIndex.js.map +1 -0
  27. package/dist/abilities/effects/buff.d.ts +9 -3
  28. package/dist/abilities/effects/buff.js +84 -0
  29. package/dist/abilities/effects/buff.js.map +1 -0
  30. package/dist/abilities/effects/conditions.d.ts +2 -0
  31. package/dist/abilities/effects/conditions.js +41 -0
  32. package/dist/abilities/effects/conditions.js.map +1 -0
  33. package/dist/abilities/effects/core.js +54 -0
  34. package/dist/abilities/effects/core.js.map +1 -0
  35. package/dist/abilities/effects/damage.d.ts +1 -1
  36. package/dist/abilities/effects/damage.js +43 -0
  37. package/dist/abilities/effects/damage.js.map +1 -0
  38. package/dist/abilities/effects/heal.d.ts +2 -2
  39. package/dist/abilities/effects/heal.js +51 -0
  40. package/dist/abilities/effects/heal.js.map +1 -0
  41. package/dist/abilities/effects/missile.d.ts +13 -1
  42. package/dist/abilities/effects/missile.js +104 -0
  43. package/dist/abilities/effects/missile.js.map +1 -0
  44. package/dist/abilities/effects/morph.d.ts +5 -0
  45. package/dist/abilities/effects/morph.js +43 -0
  46. package/dist/abilities/effects/morph.js.map +1 -0
  47. package/dist/abilities/effects/moves.d.ts +16 -4
  48. package/dist/abilities/effects/moves.js +212 -0
  49. package/dist/abilities/effects/moves.js.map +1 -0
  50. package/dist/abilities/effects/passives.d.ts +19 -9
  51. package/dist/abilities/effects/passives.js +131 -0
  52. package/dist/abilities/effects/passives.js.map +1 -0
  53. package/dist/abilities/effects/proxy.d.ts +5 -0
  54. package/dist/abilities/effects/proxy.js +60 -0
  55. package/dist/abilities/effects/proxy.js.map +1 -0
  56. package/dist/abilities/effects/sight.d.ts +2 -2
  57. package/dist/abilities/effects/sight.js +39 -0
  58. package/dist/abilities/effects/sight.js.map +1 -0
  59. package/dist/abilities/effects/spawn.d.ts +5 -2
  60. package/dist/abilities/effects/spawn.js +40 -0
  61. package/dist/abilities/effects/spawn.js.map +1 -0
  62. package/dist/abilities/effects/timed.d.ts +2 -2
  63. package/dist/abilities/effects/timed.js +96 -0
  64. package/dist/abilities/effects/timed.js.map +1 -0
  65. package/dist/abilities/effects/trade.d.ts +1 -1
  66. package/dist/abilities/effects/trade.js +25 -0
  67. package/dist/abilities/effects/trade.js.map +1 -0
  68. package/dist/abilities/effects.d.ts +97 -23
  69. package/dist/abilities/effects.js +860 -0
  70. package/dist/abilities/effects.js.map +1 -0
  71. package/dist/abilities/land.d.ts +38 -4
  72. package/dist/abilities/land.js +189 -0
  73. package/dist/abilities/land.js.map +1 -0
  74. package/dist/abilities/recast.d.ts +45 -0
  75. package/dist/abilities/recast.js +118 -0
  76. package/dist/abilities/recast.js.map +1 -0
  77. package/dist/abilities/scaled.js +40 -0
  78. package/dist/abilities/scaled.js.map +1 -0
  79. package/dist/abilities/screen.d.ts +6 -0
  80. package/dist/abilities/screen.js +83 -0
  81. package/dist/abilities/screen.js.map +1 -0
  82. package/dist/abilities/shapes.d.ts +66 -0
  83. package/dist/abilities/shapes.js +122 -0
  84. package/dist/abilities/shapes.js.map +1 -0
  85. package/dist/abilities/text.d.ts +14 -2
  86. package/dist/abilities/text.js +204 -0
  87. package/dist/abilities/text.js.map +1 -0
  88. package/dist/abilities/triggers.d.ts +98 -0
  89. package/dist/abilities/triggers.js +351 -0
  90. package/dist/abilities/triggers.js.map +1 -0
  91. package/dist/abilityChanges.d.ts +131 -0
  92. package/dist/abilityChanges.js +345 -0
  93. package/dist/abilityChanges.js.map +1 -0
  94. package/dist/ai/area.d.ts +4 -1
  95. package/dist/ai/brain.d.ts +4 -1
  96. package/dist/ai/brain.js +844 -0
  97. package/dist/ai/brain.js.map +1 -0
  98. package/dist/ai/budget.js +74 -0
  99. package/dist/ai/budget.js.map +1 -0
  100. package/dist/ai/checks.d.ts +9 -1
  101. package/dist/ai/checks.js +242 -0
  102. package/dist/ai/checks.js.map +1 -0
  103. package/dist/ai/computer.d.ts +11 -4
  104. package/dist/ai/computer.js +251 -0
  105. package/dist/ai/computer.js.map +1 -0
  106. package/dist/ai/defence.d.ts +4 -0
  107. package/dist/ai/defence.js +430 -0
  108. package/dist/ai/defence.js.map +1 -0
  109. package/dist/ai/define.d.ts +3 -2
  110. package/dist/ai/define.js +31 -0
  111. package/dist/ai/define.js.map +1 -0
  112. package/dist/ai/economy.d.ts +10 -7
  113. package/dist/ai/economy.js +780 -0
  114. package/dist/ai/economy.js.map +1 -0
  115. package/dist/ai/geometry.js +38 -0
  116. package/dist/ai/geometry.js.map +1 -0
  117. package/dist/ai/goals.d.ts +1 -0
  118. package/dist/ai/goals.js +560 -0
  119. package/dist/ai/goals.js.map +1 -0
  120. package/dist/ai/knowledge.d.ts +72 -7
  121. package/dist/ai/knowledge.js +804 -0
  122. package/dist/ai/knowledge.js.map +1 -0
  123. package/dist/ai/lanes.d.ts +10 -2
  124. package/dist/ai/lanes.js +715 -0
  125. package/dist/ai/lanes.js.map +1 -0
  126. package/dist/ai/melee/build.js +264 -0
  127. package/dist/ai/melee/build.js.map +1 -0
  128. package/dist/ai/melee/goals.d.ts +7 -0
  129. package/dist/ai/melee/goals.js +720 -0
  130. package/dist/ai/melee/goals.js.map +1 -0
  131. package/dist/ai/melee/heroes.js +68 -0
  132. package/dist/ai/melee/heroes.js.map +1 -0
  133. package/dist/ai/melee/index.d.ts +1 -1
  134. package/dist/ai/melee/index.js +51 -0
  135. package/dist/ai/melee/index.js.map +1 -0
  136. package/dist/ai/melee/production.js +29 -0
  137. package/dist/ai/melee/production.js.map +1 -0
  138. package/dist/ai/melee/surrender.js +64 -0
  139. package/dist/ai/melee/surrender.js.map +1 -0
  140. package/dist/ai/micro.d.ts +9 -0
  141. package/dist/ai/micro.js +1316 -0
  142. package/dist/ai/micro.js.map +1 -0
  143. package/dist/ai/mission.d.ts +9 -2
  144. package/dist/ai/mission.js +538 -0
  145. package/dist/ai/mission.js.map +1 -0
  146. package/dist/ai/missionRules.d.ts +1 -1
  147. package/dist/ai/missionRules.js +26 -0
  148. package/dist/ai/missionRules.js.map +1 -0
  149. package/dist/ai/module.d.ts +6 -2
  150. package/dist/ai/module.js +37 -0
  151. package/dist/ai/module.js.map +1 -0
  152. package/dist/ai/options.d.ts +3 -3
  153. package/dist/ai/plans/build.d.ts +20 -1
  154. package/dist/ai/plans/build.js +608 -0
  155. package/dist/ai/plans/build.js.map +1 -0
  156. package/dist/ai/plans/placement.d.ts +4 -1
  157. package/dist/ai/plans/placement.js +295 -0
  158. package/dist/ai/plans/placement.js.map +1 -0
  159. package/dist/ai/plans/plans.d.ts +9 -0
  160. package/dist/ai/plans/plans.js +110 -0
  161. package/dist/ai/plans/plans.js.map +1 -0
  162. package/dist/ai/plans/production.d.ts +14 -2
  163. package/dist/ai/plans/production.js +434 -0
  164. package/dist/ai/plans/production.js.map +1 -0
  165. package/dist/ai/plans/purse.d.ts +6 -3
  166. package/dist/ai/plans/purse.js +139 -0
  167. package/dist/ai/plans/purse.js.map +1 -0
  168. package/dist/ai/plans/tech.d.ts +6 -0
  169. package/dist/ai/plans/tech.js +122 -0
  170. package/dist/ai/plans/tech.js.map +1 -0
  171. package/dist/ai/profiles.d.ts +4 -4
  172. package/dist/{profiles-B3NS9jwk.js → ai/profiles.js} +3 -98
  173. package/dist/ai/profiles.js.map +1 -0
  174. package/dist/ai/reach.js +54 -0
  175. package/dist/ai/reach.js.map +1 -0
  176. package/dist/ai/report.d.ts +8 -0
  177. package/dist/ai/report.js +87 -0
  178. package/dist/ai/report.js.map +1 -0
  179. package/dist/ai/roles.d.ts +4 -0
  180. package/dist/ai/roles.js +207 -0
  181. package/dist/ai/roles.js.map +1 -0
  182. package/dist/ai/rules.d.ts +8 -2
  183. package/dist/ai/rules.js +416 -0
  184. package/dist/ai/rules.js.map +1 -0
  185. package/dist/ai/scouting.d.ts +6 -0
  186. package/dist/ai/scouting.js +324 -0
  187. package/dist/ai/scouting.js.map +1 -0
  188. package/dist/ai/settings.js +99 -0
  189. package/dist/ai/settings.js.map +1 -0
  190. package/dist/ai/squads.d.ts +21 -2
  191. package/dist/ai/squads.js +916 -0
  192. package/dist/ai/squads.js.map +1 -0
  193. package/dist/ai/strength.js +320 -0
  194. package/dist/ai/strength.js.map +1 -0
  195. package/dist/ai/task.d.ts +3 -1
  196. package/dist/ai/task.js +17 -0
  197. package/dist/ai/task.js.map +1 -0
  198. package/dist/ai/tasks/attack.js +24 -0
  199. package/dist/ai/tasks/attack.js.map +1 -0
  200. package/dist/ai/tasks/capture.d.ts +3 -3
  201. package/dist/ai/tasks/capture.js +37 -0
  202. package/dist/ai/tasks/capture.js.map +1 -0
  203. package/dist/ai/tasks/creep.js +29 -0
  204. package/dist/ai/tasks/creep.js.map +1 -0
  205. package/dist/ai/tasks/defend.js +61 -0
  206. package/dist/ai/tasks/defend.js.map +1 -0
  207. package/dist/ai/tasks/drop.js +81 -0
  208. package/dist/ai/tasks/drop.js.map +1 -0
  209. package/dist/ai/tasks/escort.js +59 -0
  210. package/dist/ai/tasks/escort.js.map +1 -0
  211. package/dist/ai/tasks/gather.js +48 -0
  212. package/dist/ai/tasks/gather.js.map +1 -0
  213. package/dist/ai/tasks/harass.js +39 -0
  214. package/dist/ai/tasks/harass.js.map +1 -0
  215. package/dist/ai/tasks/hold.js +32 -0
  216. package/dist/ai/tasks/hold.js.map +1 -0
  217. package/dist/ai/tasks/index.js +34 -0
  218. package/dist/ai/tasks/index.js.map +1 -0
  219. package/dist/ai/tasks/move.js +26 -0
  220. package/dist/ai/tasks/move.js.map +1 -0
  221. package/dist/ai/tasks/patrol.js +59 -0
  222. package/dist/ai/tasks/patrol.js.map +1 -0
  223. package/dist/ai/tasks/retreat.js +28 -0
  224. package/dist/ai/tasks/retreat.js.map +1 -0
  225. package/dist/ai/tasks/scout.js +73 -0
  226. package/dist/ai/tasks/scout.js.map +1 -0
  227. package/dist/ai/tasks/shared.d.ts +1 -1
  228. package/dist/ai/tasks/shared.js +137 -0
  229. package/dist/ai/tasks/shared.js.map +1 -0
  230. package/dist/ai/team.d.ts +18 -1
  231. package/dist/ai/team.js +492 -0
  232. package/dist/ai/team.js.map +1 -0
  233. package/dist/ai/threat.d.ts +6 -0
  234. package/dist/ai/threat.js +206 -0
  235. package/dist/ai/threat.js.map +1 -0
  236. package/dist/ai/view.d.ts +21 -7
  237. package/dist/ai/view.js +677 -0
  238. package/dist/ai/view.js.map +1 -0
  239. package/dist/ai/waves.d.ts +6 -0
  240. package/dist/ai/waves.js +410 -0
  241. package/dist/ai/waves.js.map +1 -0
  242. package/dist/ai.js +54 -6239
  243. package/dist/aiTesting.d.ts +6 -3
  244. package/dist/aiTesting.js +229 -0
  245. package/dist/aiTesting.js.map +1 -0
  246. package/dist/areas.d.ts +4 -1
  247. package/dist/areas.js +295 -0
  248. package/dist/areas.js.map +1 -0
  249. package/dist/attacks.d.ts +3 -3
  250. package/dist/attacks.js +127 -0
  251. package/dist/attacks.js.map +1 -0
  252. package/dist/burning.d.ts +267 -0
  253. package/dist/burning.js +795 -0
  254. package/dist/burning.js.map +1 -0
  255. package/dist/bytes.js +21 -0
  256. package/dist/bytes.js.map +1 -0
  257. package/dist/callings.d.ts +71 -0
  258. package/dist/callings.js +157 -0
  259. package/dist/callings.js.map +1 -0
  260. package/dist/cameraOrders.d.ts +78 -0
  261. package/dist/cameraOrders.js +114 -0
  262. package/dist/cameraOrders.js.map +1 -0
  263. package/dist/camps.d.ts +70 -4
  264. package/dist/camps.js +501 -0
  265. package/dist/camps.js.map +1 -0
  266. package/dist/capture.d.ts +15 -3
  267. package/dist/capture.js +304 -0
  268. package/dist/capture.js.map +1 -0
  269. package/dist/cellLayers.d.ts +78 -13
  270. package/dist/cellLayers.js +275 -0
  271. package/dist/cellLayers.js.map +1 -0
  272. package/dist/cellPatches.d.ts +24 -0
  273. package/dist/cellPatches.js +73 -0
  274. package/dist/cellPatches.js.map +1 -0
  275. package/dist/cinematics.d.ts +10 -5
  276. package/dist/cinematics.js +161 -0
  277. package/dist/cinematics.js.map +1 -0
  278. package/dist/clock.d.ts +2 -2
  279. package/dist/clock.js +78 -0
  280. package/dist/clock.js.map +1 -0
  281. package/dist/commands.d.ts +36 -4
  282. package/dist/commands.js +636 -0
  283. package/dist/commands.js.map +1 -0
  284. package/dist/components.d.ts +31 -10
  285. package/dist/components.js +74 -0
  286. package/dist/components.js.map +1 -0
  287. package/dist/computers.d.ts +4 -4
  288. package/dist/computers.js +194 -0
  289. package/dist/computers.js.map +1 -0
  290. package/dist/concealment.d.ts +25 -2
  291. package/dist/concealment.js +409 -0
  292. package/dist/concealment.js.map +1 -0
  293. package/dist/coreComponents.js +130 -0
  294. package/dist/coreComponents.js.map +1 -0
  295. package/dist/createGame.d.ts +306 -0
  296. package/dist/createGame.js +405 -0
  297. package/dist/createGame.js.map +1 -0
  298. package/dist/dashing.d.ts +18 -2
  299. package/dist/dashing.js +41 -0
  300. package/dist/dashing.js.map +1 -0
  301. package/dist/debug.d.ts +8 -0
  302. package/dist/debug.js +5 -3
  303. package/dist/debug2.js +76 -0
  304. package/dist/debug2.js.map +1 -0
  305. package/dist/debugCommands.d.ts +6 -6
  306. package/dist/debugCommands.js +537 -0
  307. package/dist/debugCommands.js.map +1 -0
  308. package/dist/debugging.d.ts +1 -0
  309. package/dist/defaultSets.d.ts +183 -0
  310. package/dist/defaultSets.js +216 -0
  311. package/dist/defaultSets.js.map +1 -0
  312. package/dist/definitions.d.ts +6 -0
  313. package/dist/definitions.js +78 -0
  314. package/dist/definitions.js.map +1 -0
  315. package/dist/defs.d.ts +1895 -165
  316. package/dist/defs.js +380 -0
  317. package/dist/defs.js.map +1 -0
  318. package/dist/denying.d.ts +4 -4
  319. package/dist/denying.js +106 -0
  320. package/dist/denying.js.map +1 -0
  321. package/dist/destructibles.d.ts +2 -2
  322. package/dist/destructibles.js +36 -0
  323. package/dist/destructibles.js.map +1 -0
  324. package/dist/determinism.d.ts +4 -4
  325. package/dist/determinism.js +247 -0
  326. package/dist/determinism.js.map +1 -0
  327. package/dist/dialog.d.ts +17 -2
  328. package/dist/dialog.js +108 -0
  329. package/dist/dialog.js.map +1 -0
  330. package/dist/effectRefs.js +28 -0
  331. package/dist/effectRefs.js.map +1 -0
  332. package/dist/effects.d.ts +27 -11
  333. package/dist/effects.js +110 -0
  334. package/dist/effects.js.map +1 -0
  335. package/dist/endings.d.ts +1 -1
  336. package/dist/endings.js +78 -0
  337. package/dist/endings.js.map +1 -0
  338. package/dist/engagement.d.ts +40 -6
  339. package/dist/engagement.js +530 -0
  340. package/dist/engagement.js.map +1 -0
  341. package/dist/engineEvents.d.ts +72 -9
  342. package/dist/engineEvents.js +520 -0
  343. package/dist/engineEvents.js.map +1 -0
  344. package/dist/entity.d.ts +275 -26
  345. package/dist/entity.js +441 -0
  346. package/dist/entity.js.map +1 -0
  347. package/dist/entityIndex.d.ts +10 -3
  348. package/dist/entityIndex.js +149 -0
  349. package/dist/entityIndex.js.map +1 -0
  350. package/dist/errors.d.ts +6 -2
  351. package/dist/errors.js +59 -0
  352. package/dist/errors.js.map +1 -0
  353. package/dist/events.d.ts +11 -5
  354. package/dist/events.js +158 -0
  355. package/dist/events.js.map +1 -0
  356. package/dist/exactMath.js +273 -0
  357. package/dist/exactMath.js.map +1 -0
  358. package/dist/features.d.ts +1 -1
  359. package/dist/features.js +127 -0
  360. package/dist/features.js.map +1 -0
  361. package/dist/fields.d.ts +25 -5
  362. package/dist/fields.js +140 -0
  363. package/dist/fields.js.map +1 -0
  364. package/dist/fixture.d.ts +3 -3
  365. package/dist/fixture.js +512 -0
  366. package/dist/fixture.js.map +1 -0
  367. package/dist/fog.d.ts +26 -3
  368. package/dist/fog.js +333 -0
  369. package/dist/fog.js.map +1 -0
  370. package/dist/foliage.d.ts +476 -0
  371. package/dist/foliage.js +1478 -0
  372. package/dist/foliage.js.map +1 -0
  373. package/dist/foliageDebug.d.ts +57 -0
  374. package/dist/foliageDebug.js +172 -0
  375. package/dist/foliageDebug.js.map +1 -0
  376. package/dist/foliageRegrowth.d.ts +20 -0
  377. package/dist/foliageRegrowth.js +332 -0
  378. package/dist/foliageRegrowth.js.map +1 -0
  379. package/dist/formState.d.ts +67 -0
  380. package/dist/formState.js +65 -0
  381. package/dist/formState.js.map +1 -0
  382. package/dist/formation.d.ts +46 -8
  383. package/dist/formation.js +297 -0
  384. package/dist/formation.js.map +1 -0
  385. package/dist/forms.d.ts +58 -0
  386. package/dist/forms.js +146 -0
  387. package/dist/forms.js.map +1 -0
  388. package/dist/friendlyFire.d.ts +2 -2
  389. package/dist/friendlyFire.js +37 -0
  390. package/dist/friendlyFire.js.map +1 -0
  391. package/dist/game.d.ts +141 -48
  392. package/dist/game.js +834 -0
  393. package/dist/game.js.map +1 -0
  394. package/dist/gameData.d.ts +96 -22
  395. package/dist/gameData.js +1690 -0
  396. package/dist/gameData.js.map +1 -0
  397. package/dist/gameFiles.d.ts +3 -3
  398. package/dist/gameFiles.js +14 -0
  399. package/dist/gameFiles.js.map +1 -0
  400. package/dist/gates.js +92 -0
  401. package/dist/gates.js.map +1 -0
  402. package/dist/generate.d.ts +28 -1
  403. package/dist/generate.js +1062 -0
  404. package/dist/generate.js.map +1 -0
  405. package/dist/generateWorker.d.ts +7 -3
  406. package/dist/generateWorker.js +86 -0
  407. package/dist/generateWorker.js.map +1 -0
  408. package/dist/grid.d.ts +9 -0
  409. package/dist/grid.js +102 -0
  410. package/dist/grid.js.map +1 -0
  411. package/dist/ground.d.ts +17 -1
  412. package/dist/ground.js +375 -0
  413. package/dist/ground.js.map +1 -0
  414. package/dist/groundObjects.d.ts +70 -0
  415. package/dist/groundObjects.js +371 -0
  416. package/dist/groundObjects.js.map +1 -0
  417. package/dist/groundSpeed.d.ts +20 -3
  418. package/dist/groundSpeed.js +117 -0
  419. package/dist/groundSpeed.js.map +1 -0
  420. package/dist/hero.d.ts +126 -10
  421. package/dist/hero.js +432 -0
  422. package/dist/hero.js.map +1 -0
  423. package/dist/holds.d.ts +94 -0
  424. package/dist/holds.js +305 -0
  425. package/dist/holds.js.map +1 -0
  426. package/dist/home.d.ts +1 -1
  427. package/dist/home.js +30 -0
  428. package/dist/home.js.map +1 -0
  429. package/dist/hooks.d.ts +339 -57
  430. package/dist/hooks.js +105 -0
  431. package/dist/hooks.js.map +1 -0
  432. package/dist/hotReload.d.ts +6 -5
  433. package/dist/hotReload.js +145 -0
  434. package/dist/hotReload.js.map +1 -0
  435. package/dist/ids.d.ts +3 -3
  436. package/dist/ids.js +30 -0
  437. package/dist/ids.js.map +1 -0
  438. package/dist/idsFile.d.ts +36 -0
  439. package/dist/idsFile.js +134 -0
  440. package/dist/idsFile.js.map +1 -0
  441. package/dist/index.d.ts +105 -68
  442. package/dist/index.js +194 -4701
  443. package/dist/inspect.d.ts +29 -2
  444. package/dist/{inspect-D9dWHmZ0.js → inspect.js} +21 -12
  445. package/dist/inspect.js.map +1 -0
  446. package/dist/inventory.d.ts +14 -1
  447. package/dist/inventory.js +84 -0
  448. package/dist/inventory.js.map +1 -0
  449. package/dist/items.d.ts +136 -8
  450. package/dist/items.js +585 -0
  451. package/dist/items.js.map +1 -0
  452. package/dist/lanes.d.ts +12 -3
  453. package/dist/lanes.js +496 -0
  454. package/dist/lanes.js.map +1 -0
  455. package/dist/life.d.ts +6 -2
  456. package/dist/life.js +287 -0
  457. package/dist/life.js.map +1 -0
  458. package/dist/log.d.ts +26 -3
  459. package/dist/log.js +192 -0
  460. package/dist/log.js.map +1 -0
  461. package/dist/manaBurn.d.ts +2 -2
  462. package/dist/manaBurn.js +75 -0
  463. package/dist/manaBurn.js.map +1 -0
  464. package/dist/map/file.d.ts +56 -3
  465. package/dist/map/file.js +264 -0
  466. package/dist/map/file.js.map +1 -0
  467. package/dist/map/images.js +61 -0
  468. package/dist/map/images.js.map +1 -0
  469. package/dist/map/look.d.ts +1 -1
  470. package/dist/map/look.js +30 -0
  471. package/dist/map/look.js.map +1 -0
  472. package/dist/map/pathProfile.js +183 -0
  473. package/dist/map/pathProfile.js.map +1 -0
  474. package/dist/map/resize.d.ts +4 -0
  475. package/dist/map/resize.js +136 -0
  476. package/dist/map/resize.js.map +1 -0
  477. package/dist/map/schema.js +257 -0
  478. package/dist/map/schema.js.map +1 -0
  479. package/dist/map/terrain.js +214 -0
  480. package/dist/map/terrain.js.map +1 -0
  481. package/dist/map/types.d.ts +92 -13
  482. package/dist/map/types.js +251 -0
  483. package/dist/map/types.js.map +1 -0
  484. package/dist/mapCheck/cells.js +94 -0
  485. package/dist/mapCheck/cells.js.map +1 -0
  486. package/dist/mapCheck/rules/foliage.d.ts +9 -0
  487. package/dist/mapCheck/rules/foliage.js +83 -0
  488. package/dist/mapCheck/rules/foliage.js.map +1 -0
  489. package/dist/mapCheck/rules/names.js +39 -0
  490. package/dist/mapCheck/rules/names.js.map +1 -0
  491. package/dist/mapCheck/rules/objects.d.ts +1 -1
  492. package/dist/mapCheck/rules/objects.js +56 -0
  493. package/dist/mapCheck/rules/objects.js.map +1 -0
  494. package/dist/mapCheck/rules/paths.js +32 -0
  495. package/dist/mapCheck/rules/paths.js.map +1 -0
  496. package/dist/mapCheck/rules/players.js +20 -0
  497. package/dist/mapCheck/rules/players.js.map +1 -0
  498. package/dist/mapCheck/rules/races.js +56 -0
  499. package/dist/mapCheck/rules/races.js.map +1 -0
  500. package/dist/mapCheck/rules/random.js +47 -0
  501. package/dist/mapCheck/rules/random.js.map +1 -0
  502. package/dist/mapCheck/rules/regions.js +30 -0
  503. package/dist/mapCheck/rules/regions.js.map +1 -0
  504. package/dist/mapCheck/rules/targets.js +31 -0
  505. package/dist/mapCheck/rules/targets.js.map +1 -0
  506. package/dist/mapCheck/rules/walking.js +45 -0
  507. package/dist/mapCheck/rules/walking.js.map +1 -0
  508. package/dist/mapCheck.d.ts +2 -2
  509. package/dist/mapCheck.js +63 -0
  510. package/dist/mapCheck.js.map +1 -0
  511. package/dist/mapFields.d.ts +25 -2
  512. package/dist/mapFields.js +134 -0
  513. package/dist/mapFields.js.map +1 -0
  514. package/dist/mapFile.js +176 -0
  515. package/dist/mapFile.js.map +1 -0
  516. package/dist/mapMeasure.d.ts +8 -0
  517. package/dist/mapMeasure.js +283 -0
  518. package/dist/mapMeasure.js.map +1 -0
  519. package/dist/mapMerge.d.ts +1 -0
  520. package/dist/mapMerge.js +172 -0
  521. package/dist/mapMerge.js.map +1 -0
  522. package/dist/mapObjectives.js +138 -0
  523. package/dist/mapObjectives.js.map +1 -0
  524. package/dist/mapObjects.js +80 -0
  525. package/dist/mapObjects.js.map +1 -0
  526. package/dist/math.d.ts +37 -7
  527. package/dist/math.js +155 -0
  528. package/dist/math.js.map +1 -0
  529. package/dist/messages.d.ts +71 -4
  530. package/dist/messages.js +29 -0
  531. package/dist/messages.js.map +1 -0
  532. package/dist/missionRules.d.ts +10 -2
  533. package/dist/{missionRules-C-bGxm0Q.js → missionRules.js} +30 -150
  534. package/dist/missionRules.js.map +1 -0
  535. package/dist/mods.d.ts +39 -20
  536. package/dist/mods.js +271 -0
  537. package/dist/mods.js.map +1 -0
  538. package/dist/moves.d.ts +87 -0
  539. package/dist/moves.js +539 -0
  540. package/dist/moves.js.map +1 -0
  541. package/dist/names.d.ts +7 -0
  542. package/dist/names.js +122 -0
  543. package/dist/names.js.map +1 -0
  544. package/dist/navigation/agentParams.js +14 -0
  545. package/dist/navigation/agentParams.js.map +1 -0
  546. package/dist/navigation/air.d.ts +7 -3
  547. package/dist/navigation/air.js +401 -0
  548. package/dist/navigation/air.js.map +1 -0
  549. package/dist/navigation/builds.d.ts +34 -2
  550. package/dist/navigation/builds.js +287 -0
  551. package/dist/navigation/builds.js.map +1 -0
  552. package/dist/navigation/groundAreas.d.ts +59 -1
  553. package/dist/navigation/groundAreas.js +407 -0
  554. package/dist/navigation/groundAreas.js.map +1 -0
  555. package/dist/navigation/navigation.d.ts +98 -9
  556. package/dist/navigation/recast.d.ts +194 -18
  557. package/dist/navigation/recast.js +1379 -0
  558. package/dist/navigation/recast.js.map +1 -0
  559. package/dist/navigation/sidePaths.d.ts +8 -3
  560. package/dist/navigation/sidePaths.js +241 -0
  561. package/dist/navigation/sidePaths.js.map +1 -0
  562. package/dist/navigation/sized.js +243 -0
  563. package/dist/navigation/sized.js.map +1 -0
  564. package/dist/nearest.js +32 -0
  565. package/dist/nearest.js.map +1 -0
  566. package/dist/noise.js +59 -0
  567. package/dist/noise.js.map +1 -0
  568. package/dist/objectives.d.ts +12 -3
  569. package/dist/objectives.js +144 -0
  570. package/dist/objectives.js.map +1 -0
  571. package/dist/orders/basic.d.ts +5 -5
  572. package/dist/orders/basic.js +417 -0
  573. package/dist/orders/basic.js.map +1 -0
  574. package/dist/orders/build.js +278 -0
  575. package/dist/orders/build.js.map +1 -0
  576. package/dist/orders/cast.d.ts +13 -3
  577. package/dist/orders/cast.js +558 -0
  578. package/dist/orders/cast.js.map +1 -0
  579. package/dist/orders/facts.js +16 -0
  580. package/dist/orders/facts.js.map +1 -0
  581. package/dist/orders/harvest.d.ts +21 -5
  582. package/dist/orders/harvest.js +540 -0
  583. package/dist/orders/harvest.js.map +1 -0
  584. package/dist/orders/idle.d.ts +3 -1
  585. package/dist/orders/idle.js +234 -0
  586. package/dist/orders/idle.js.map +1 -0
  587. package/dist/orders/items.js +92 -0
  588. package/dist/orders/items.js.map +1 -0
  589. package/dist/orders/queue.js +108 -0
  590. package/dist/orders/queue.js.map +1 -0
  591. package/dist/orders/waiting.d.ts +39 -0
  592. package/dist/orders/waiting.js +149 -0
  593. package/dist/orders/waiting.js.map +1 -0
  594. package/dist/pack.schema.js +13 -0
  595. package/dist/pack.schema.js.map +1 -0
  596. package/dist/pack.schema.json +7622 -841
  597. package/dist/packs/director/index.d.ts +13 -5
  598. package/dist/packs/director/index.js +204 -0
  599. package/dist/packs/director/index.js.map +1 -0
  600. package/dist/packs/heroes/campaign.d.ts +3 -2
  601. package/dist/packs/heroes/campaign.js +241 -0
  602. package/dist/packs/heroes/campaign.js.map +1 -0
  603. package/dist/packs/heroes/carry.d.ts +6 -0
  604. package/dist/packs/heroes/carry.js +112 -0
  605. package/dist/packs/heroes/carry.js.map +1 -0
  606. package/dist/packs/heroes/commands.js +115 -0
  607. package/dist/packs/heroes/commands.js.map +1 -0
  608. package/dist/packs/heroes/fallen.d.ts +43 -0
  609. package/dist/packs/heroes/fallen.js +61 -0
  610. package/dist/packs/heroes/fallen.js.map +1 -0
  611. package/dist/packs/heroes/hire.js +60 -0
  612. package/dist/packs/heroes/hire.js.map +1 -0
  613. package/dist/packs/heroes/index.d.ts +7 -6
  614. package/dist/packs/heroes/index.js +130 -0
  615. package/dist/packs/heroes/index.js.map +1 -0
  616. package/dist/packs/heroes/loop.d.ts +95 -0
  617. package/dist/packs/heroes/loop.js +250 -0
  618. package/dist/packs/heroes/loop.js.map +1 -0
  619. package/dist/packs/heroes/making.d.ts +6 -3
  620. package/dist/packs/heroes/making.js +55 -0
  621. package/dist/packs/heroes/making.js.map +1 -0
  622. package/dist/packs/heroes/respawn.d.ts +3 -3
  623. package/dist/packs/heroes/respawn.js +87 -0
  624. package/dist/packs/heroes/respawn.js.map +1 -0
  625. package/dist/packs/heroes/sets.d.ts +32 -0
  626. package/dist/packs/heroes/sets.js +51 -0
  627. package/dist/packs/heroes/sets.js.map +1 -0
  628. package/dist/packs/heroes/spells.d.ts +16 -0
  629. package/dist/packs/heroes/spells.js +269 -0
  630. package/dist/packs/heroes/spells.js.map +1 -0
  631. package/dist/packs/items/defaults.d.ts +43 -0
  632. package/dist/packs/items/defaults.js +1504 -0
  633. package/dist/packs/items/defaults.js.map +1 -0
  634. package/dist/packs/items/index.d.ts +27 -0
  635. package/dist/packs/items/index.js +214 -0
  636. package/dist/packs/items/index.js.map +1 -0
  637. package/dist/packs/moba/index.d.ts +3 -3
  638. package/dist/packs/moba/index.js +174 -0
  639. package/dist/packs/moba/index.js.map +1 -0
  640. package/dist/packs/pack.d.ts +15 -15
  641. package/dist/packs/pack.js +14 -0
  642. package/dist/packs/pack.js.map +1 -0
  643. package/dist/packs.d.ts +15 -7
  644. package/dist/packs.js +222 -0
  645. package/dist/packs.js.map +1 -0
  646. package/dist/paths.d.ts +2 -0
  647. package/dist/paths.js +109 -0
  648. package/dist/paths.js.map +1 -0
  649. package/dist/plain.js +57 -0
  650. package/dist/plain.js.map +1 -0
  651. package/dist/player.d.ts +33 -10
  652. package/dist/player.js +159 -0
  653. package/dist/player.js.map +1 -0
  654. package/dist/portals.d.ts +1 -1
  655. package/dist/portals.js +194 -0
  656. package/dist/portals.js.map +1 -0
  657. package/dist/prefabs.d.ts +25 -2
  658. package/dist/prefabs.js +239 -0
  659. package/dist/prefabs.js.map +1 -0
  660. package/dist/presetBuildings.js +58 -0
  661. package/dist/presetBuildings.js.map +1 -0
  662. package/dist/presetFoliage.d.ts +27 -0
  663. package/dist/presetFoliage.js +98 -0
  664. package/dist/presetFoliage.js.map +1 -0
  665. package/dist/presetGround.js +65 -0
  666. package/dist/presetGround.js.map +1 -0
  667. package/dist/proxies.d.ts +12 -0
  668. package/dist/proxies.js +79 -0
  669. package/dist/proxies.js.map +1 -0
  670. package/dist/query.d.ts +56 -0
  671. package/dist/query.js +88 -0
  672. package/dist/query.js.map +1 -0
  673. package/dist/races.d.ts +15 -14
  674. package/dist/races.js +172 -0
  675. package/dist/races.js.map +1 -0
  676. package/dist/randomGroups.js +91 -0
  677. package/dist/randomGroups.js.map +1 -0
  678. package/dist/registry.d.ts +117 -53
  679. package/dist/registry.js +323 -0
  680. package/dist/registry.js.map +1 -0
  681. package/dist/remains.d.ts +11 -9
  682. package/dist/remains.js +188 -0
  683. package/dist/remains.js.map +1 -0
  684. package/dist/replication.d.ts +12 -7
  685. package/dist/replication.js +100 -0
  686. package/dist/replication.js.map +1 -0
  687. package/dist/rng.d.ts +2 -0
  688. package/dist/rng.js +40 -0
  689. package/dist/rng.js.map +1 -0
  690. package/dist/rules.d.ts +186 -99
  691. package/dist/rules.js +179 -0
  692. package/dist/rules.js.map +1 -0
  693. package/dist/rulesCheck.d.ts +5 -4
  694. package/dist/rulesCheck.js +693 -0
  695. package/dist/rulesCheck.js.map +1 -0
  696. package/dist/runes.d.ts +2 -2
  697. package/dist/runes.js +80 -0
  698. package/dist/runes.js.map +1 -0
  699. package/dist/save.d.ts +37 -12
  700. package/dist/save.js +532 -0
  701. package/dist/save.js.map +1 -0
  702. package/dist/scenario.d.ts +2 -2
  703. package/dist/scenario.js +99 -0
  704. package/dist/scenario.js.map +1 -0
  705. package/dist/scheduler.d.ts +3 -0
  706. package/dist/scheduler.js +156 -0
  707. package/dist/scheduler.js.map +1 -0
  708. package/dist/score.d.ts +19 -19
  709. package/dist/score.js +709 -0
  710. package/dist/score.js.map +1 -0
  711. package/dist/sequences.d.ts +10 -6
  712. package/dist/sequences.js +206 -0
  713. package/dist/sequences.js.map +1 -0
  714. package/dist/server/doorman.js +154 -0
  715. package/dist/server/doorman.js.map +1 -0
  716. package/dist/server/gameServer.d.ts +16 -1
  717. package/dist/server/gameServer.js +643 -0
  718. package/dist/server/gameServer.js.map +1 -0
  719. package/dist/server/index.d.ts +1 -1
  720. package/dist/server/lobby.js +199 -0
  721. package/dist/server/lobby.js.map +1 -0
  722. package/dist/server/log.js +18 -0
  723. package/dist/server/log.js.map +1 -0
  724. package/dist/server/match.js +267 -0
  725. package/dist/server/match.js.map +1 -0
  726. package/dist/server/mode.d.ts +18 -9
  727. package/dist/server/mode.js +220 -0
  728. package/dist/server/mode.js.map +1 -0
  729. package/dist/{navigation-worker.js → server/navigation-worker.js} +1 -1
  730. package/dist/server/navigation-worker.js.map +1 -0
  731. package/dist/server/navigation.js +39 -0
  732. package/dist/server/navigation.js.map +1 -0
  733. package/dist/server/ops.d.ts +4 -0
  734. package/dist/server/ops.js +137 -0
  735. package/dist/server/ops.js.map +1 -0
  736. package/dist/server/packs.d.ts +3 -3
  737. package/dist/server/packs.js +32 -0
  738. package/dist/server/packs.js.map +1 -0
  739. package/dist/server/ratings.d.ts +5 -0
  740. package/dist/server/ratings.js +104 -0
  741. package/dist/server/ratings.js.map +1 -0
  742. package/dist/server/relay.d.ts +12 -2
  743. package/dist/server/relay.js +144 -0
  744. package/dist/server/relay.js.map +1 -0
  745. package/dist/server/rooms.d.ts +9 -2
  746. package/dist/server/rooms.js +442 -0
  747. package/dist/server/rooms.js.map +1 -0
  748. package/dist/server/seat.js +116 -0
  749. package/dist/server/seat.js.map +1 -0
  750. package/dist/server/seatSettings.d.ts +3 -3
  751. package/dist/server/seatSettings.js +63 -0
  752. package/dist/server/seatSettings.js.map +1 -0
  753. package/dist/server/secret.js +13 -0
  754. package/dist/server/secret.js.map +1 -0
  755. package/dist/server/start.d.ts +13 -6
  756. package/dist/server/start.js +76 -0
  757. package/dist/server/start.js.map +1 -0
  758. package/dist/server/websocket.d.ts +9 -1
  759. package/dist/server/websocket.js +306 -0
  760. package/dist/server/websocket.js.map +1 -0
  761. package/dist/server.js +15 -2861
  762. package/dist/session/can.js +26 -0
  763. package/dist/session/can.js.map +1 -0
  764. package/dist/session/commandReplay.d.ts +8 -2
  765. package/dist/session/commandReplay.js +293 -0
  766. package/dist/session/commandReplay.js.map +1 -0
  767. package/dist/session/debug.js +151 -0
  768. package/dist/session/debug.js.map +1 -0
  769. package/dist/session/delay.js +62 -0
  770. package/dist/session/delay.js.map +1 -0
  771. package/dist/session/delta.d.ts +14 -1
  772. package/dist/session/delta.js +407 -0
  773. package/dist/session/delta.js.map +1 -0
  774. package/dist/session/entityState.js +415 -0
  775. package/dist/session/entityState.js.map +1 -0
  776. package/dist/session/local.d.ts +47 -5
  777. package/dist/session/local.js +670 -0
  778. package/dist/session/local.js.map +1 -0
  779. package/dist/session/memory.d.ts +20 -2
  780. package/dist/session/memory.js +131 -0
  781. package/dist/session/memory.js.map +1 -0
  782. package/dist/session/protocol.d.ts +325 -31
  783. package/dist/session/protocol.js +55 -0
  784. package/dist/session/protocol.js.map +1 -0
  785. package/dist/session/remote.d.ts +74 -12
  786. package/dist/session/remote.js +1394 -0
  787. package/dist/session/remote.js.map +1 -0
  788. package/dist/session/replay.d.ts +11 -2
  789. package/dist/session/replay.js +160 -0
  790. package/dist/session/replay.js.map +1 -0
  791. package/dist/session/seed.js +12 -0
  792. package/dist/session/seed.js.map +1 -0
  793. package/dist/session/share.d.ts +5 -1
  794. package/dist/session/share.js +94 -0
  795. package/dist/session/share.js.map +1 -0
  796. package/dist/session/socket.d.ts +1 -1
  797. package/dist/session/socket.js +29 -0
  798. package/dist/session/socket.js.map +1 -0
  799. package/dist/session/wire.d.ts +5 -0
  800. package/dist/session/wire.js +84 -0
  801. package/dist/session/wire.js.map +1 -0
  802. package/dist/session/worker.d.ts +77 -12
  803. package/dist/session/worker.js +499 -0
  804. package/dist/session/worker.js.map +1 -0
  805. package/dist/shared.d.ts +31 -5
  806. package/dist/shared.js +166 -0
  807. package/dist/shared.js.map +1 -0
  808. package/dist/sides.d.ts +9 -3
  809. package/dist/sides.js +149 -0
  810. package/dist/sides.js.map +1 -0
  811. package/dist/sight.js +85 -0
  812. package/dist/sight.js.map +1 -0
  813. package/dist/simulate-worker.js +1 -1
  814. package/dist/simulate.d.ts +12 -8
  815. package/dist/{simulate-oXBiw1Ff.js → simulate.js} +10 -11
  816. package/dist/simulate.js.map +1 -0
  817. package/dist/smart.js +77 -0
  818. package/dist/smart.js.map +1 -0
  819. package/dist/spatial.d.ts +32 -16
  820. package/dist/spatial.js +234 -0
  821. package/dist/spatial.js.map +1 -0
  822. package/dist/spawning.d.ts +1 -0
  823. package/dist/spawning.js +224 -0
  824. package/dist/spawning.js.map +1 -0
  825. package/dist/stances.d.ts +6 -3
  826. package/dist/stances.js +169 -0
  827. package/dist/stances.js.map +1 -0
  828. package/dist/start.d.ts +10 -10
  829. package/dist/start.js +75 -0
  830. package/dist/start.js.map +1 -0
  831. package/dist/stats.d.ts +42 -16
  832. package/dist/stats.js +290 -0
  833. package/dist/stats.js.map +1 -0
  834. package/dist/summoned.d.ts +52 -0
  835. package/dist/summoned.js +38 -0
  836. package/dist/summoned.js.map +1 -0
  837. package/dist/summons.d.ts +10 -13
  838. package/dist/summons.js +271 -0
  839. package/dist/summons.js.map +1 -0
  840. package/dist/systems/abilities.js +224 -0
  841. package/dist/systems/abilities.js.map +1 -0
  842. package/dist/systems/combat.d.ts +35 -1
  843. package/dist/systems/combat.js +677 -0
  844. package/dist/systems/combat.js.map +1 -0
  845. package/dist/systems/combatTold.d.ts +3 -1
  846. package/dist/systems/combatTold.js +85 -0
  847. package/dist/systems/combatTold.js.map +1 -0
  848. package/dist/systems/construction.js +80 -0
  849. package/dist/systems/construction.js.map +1 -0
  850. package/dist/systems/economy.d.ts +0 -6
  851. package/dist/systems/economy.js +128 -0
  852. package/dist/systems/economy.js.map +1 -0
  853. package/dist/systems/fog.d.ts +1 -1
  854. package/dist/systems/fog.js +120 -0
  855. package/dist/systems/fog.js.map +1 -0
  856. package/dist/systems/life.js +32 -0
  857. package/dist/systems/life.js.map +1 -0
  858. package/dist/systems/movement.d.ts +24 -12
  859. package/dist/systems/movement.js +1247 -0
  860. package/dist/systems/movement.js.map +1 -0
  861. package/dist/systems/orders.js +64 -0
  862. package/dist/systems/orders.js.map +1 -0
  863. package/dist/systems/production.js +322 -0
  864. package/dist/systems/production.js.map +1 -0
  865. package/dist/systems/productionQueue.js +120 -0
  866. package/dist/systems/productionQueue.js.map +1 -0
  867. package/dist/systems/victory.d.ts +1 -1
  868. package/dist/systems/victory.js +56 -0
  869. package/dist/systems/victory.js.map +1 -0
  870. package/dist/talk.d.ts +6 -6
  871. package/dist/talk.js +153 -0
  872. package/dist/talk.js.map +1 -0
  873. package/dist/targeting.d.ts +142 -0
  874. package/dist/targeting.js +49 -0
  875. package/dist/targeting.js.map +1 -0
  876. package/dist/tech.d.ts +20 -3
  877. package/dist/tech.js +239 -0
  878. package/dist/tech.js.map +1 -0
  879. package/dist/techTree.d.ts +7 -0
  880. package/dist/techTree.js +90 -0
  881. package/dist/techTree.js.map +1 -0
  882. package/dist/terrain.d.ts +59 -7
  883. package/dist/terrain.js +389 -0
  884. package/dist/terrain.js.map +1 -0
  885. package/dist/terrainSave.d.ts +6 -0
  886. package/dist/terrainSave.js +74 -0
  887. package/dist/terrainSave.js.map +1 -0
  888. package/dist/testing.d.ts +8 -7
  889. package/dist/testing.js +13 -739
  890. package/dist/testing.js.map +1 -1
  891. package/dist/tiles.d.ts +1 -1
  892. package/dist/tiles.js +43 -0
  893. package/dist/tiles.js.map +1 -0
  894. package/dist/timers.d.ts +29 -4
  895. package/dist/timers.js +242 -0
  896. package/dist/timers.js.map +1 -0
  897. package/dist/tomes.d.ts +4 -4
  898. package/dist/tomes.js +94 -0
  899. package/dist/tomes.js.map +1 -0
  900. package/dist/trade.js +103 -0
  901. package/dist/trade.js.map +1 -0
  902. package/dist/transport.d.ts +6 -1
  903. package/dist/transport.js +281 -0
  904. package/dist/transport.js.map +1 -0
  905. package/dist/tunables.d.ts +10 -3
  906. package/dist/tunables.js +56 -0
  907. package/dist/tunables.js.map +1 -0
  908. package/dist/unitResources.d.ts +67 -0
  909. package/dist/unitResources.js +266 -0
  910. package/dist/unitResources.js.map +1 -0
  911. package/dist/upkeep.d.ts +6 -1
  912. package/dist/upkeep.js +26 -0
  913. package/dist/upkeep.js.map +1 -0
  914. package/dist/veterancy.d.ts +8 -3
  915. package/dist/veterancy.js +131 -0
  916. package/dist/veterancy.js.map +1 -0
  917. package/dist/vision.d.ts +36 -0
  918. package/dist/vision.js +133 -0
  919. package/dist/vision.js.map +1 -0
  920. package/dist/walls.d.ts +1 -1
  921. package/dist/walls.js +278 -0
  922. package/dist/walls.js.map +1 -0
  923. package/dist/world.d.ts +606 -88
  924. package/dist/world.js +1263 -0
  925. package/dist/world.js.map +1 -0
  926. package/package.json +2 -2
  927. package/dist/ai.js.map +0 -1
  928. package/dist/attributes.d.ts +0 -66
  929. package/dist/brain-BvVdUhX8.js +0 -7659
  930. package/dist/brain-BvVdUhX8.js.map +0 -1
  931. package/dist/builds-C6LfgvEU.js +0 -460
  932. package/dist/builds-C6LfgvEU.js.map +0 -1
  933. package/dist/coreComponents-syuh2fPG.js +0 -5149
  934. package/dist/coreComponents-syuh2fPG.js.map +0 -1
  935. package/dist/debugCommands-CzjrSm66.js +0 -14739
  936. package/dist/debugCommands-CzjrSm66.js.map +0 -1
  937. package/dist/determinism-BCymQwiE.js +0 -518
  938. package/dist/determinism-BCymQwiE.js.map +0 -1
  939. package/dist/game-DiJEN-Ks.js +0 -5800
  940. package/dist/game-DiJEN-Ks.js.map +0 -1
  941. package/dist/index.js.map +0 -1
  942. package/dist/inspect-D9dWHmZ0.js.map +0 -1
  943. package/dist/local-Dkovq42k.js +0 -1379
  944. package/dist/local-Dkovq42k.js.map +0 -1
  945. package/dist/missionRules-C-bGxm0Q.js.map +0 -1
  946. package/dist/navigation-worker.js.map +0 -1
  947. package/dist/packs-g5thvmGn.js +0 -3199
  948. package/dist/packs-g5thvmGn.js.map +0 -1
  949. package/dist/profiles-B3NS9jwk.js.map +0 -1
  950. package/dist/server.js.map +0 -1
  951. package/dist/simulate-oXBiw1Ff.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"server.js","names":["commandRejectedEvent","packSchema"],"sources":["../src/server/navigation.ts","../src/server/log.ts","../src/server/ratings.ts","../src/server/secret.ts","../src/server/doorman.ts","../src/server/seat.ts","../src/server/seatSettings.ts","../src/server/lobby.ts","../src/server/ops.ts","../src/server/match.ts","../src/server/websocket.ts","../src/server/gameServer.ts","../src/server/mode.ts","../src/server/start.ts","../src/server/rooms.ts","../src/server/relay.ts","../src/pack.schema.json","../src/server/packs.ts"],"sourcesContent":["import { existsSync } from 'node:fs'\nimport { fileURLToPath } from 'node:url'\nimport { type NavigationBuilder, navigationWorker } from '#engine/navigation/builds'\n\n/**\n * A builder of navigations in a worker thread of this process (S37): what a server builds with, so that the world of\n * one game being made does not hold up the ticks of the others (two seconds for a map of 512 cells a side). One\n * thread, made the first time and kept, which keeps the process alive only while it builds. Nothing where the\n * worker's file is not there: in this repository, which runs the engine as it is written, it builds on the thread\n * that asks\n *\n * @beta\n */\nexport function nodeNavigationBuilder(): NavigationBuilder | undefined {\n\t// the worker's file sits next to this one in the build: named so, a bundler leaves the address alone\n\tconst file = 'navigation-worker.js'\n\tconst script = new URL(file, import.meta.url)\n\tif (script.protocol !== 'file:' || !existsSync(fileURLToPath(script))) return undefined\n\tlet made: Promise<{ worker: { ref(): void; unref(): void }; builder: NavigationBuilder }> | undefined\n\tlet pending = 0\n\treturn async (build) => {\n\t\tmade ??= import('node:worker_threads').then(({ Worker }) => {\n\t\t\tconst worker = new Worker(script)\n\t\t\treturn { worker, builder: navigationWorker(worker as never) }\n\t\t})\n\t\tconst { worker, builder } = await made\n\t\t// it keeps the process alive while it builds, and not after\n\t\tif (pending++ === 0) worker.ref()\n\t\ttry {\n\t\t\treturn await builder(build)\n\t\t} finally {\n\t\t\tif (--pending === 0) worker.unref()\n\t\t}\n\t}\n}\n","import { type LogEntry, Logger, logLine, rootLogger } from '#engine/log'\n\n/**\n * What a server may be given to log with: a logger, or a function that takes each line as text\n *\n * @beta\n */\nexport type ServerLog = Logger | ((line: string) => void)\n\n/**\n * The logger of a server, from what it was started with. A logger: one under it. A function of lines: a logger of its\n * own that gives it the server's own entries as their words, and everything else (a game's, a warning) as a whole\n * line. Nothing: one under the root, which says warnings and errors only, as a server said nothing else before\n */\nexport function serverLogger(given: ServerLog | undefined): Logger {\n\tif (given instanceof Logger) return given.child()\n\tif (!given) return rootLogger.child({ level: 'warn' })\n\tconst logger = new Logger()\n\tlogger.to((entry: LogEntry) => given(entry.category === 'server' && entry.level === 'info' ? entry.message : logLine(entry)))\n\treturn logger\n}\n","import { createHash } from 'node:crypto'\nimport { mkdir, readFile, writeFile } from 'node:fs/promises'\nimport { dirname } from 'node:path'\nimport type { GameResult } from '#engine/game'\n\n/*\n * Ranks (features-plan H4): a rating for each player in each mode, Elo, kept by the server from the results of its\n * games. There are no accounts: a player is who brings the same key (`join`'s `who`, kept in their browser), or who a\n * login says they are (`SeatAnswer.id`); what is kept and shown is a hash of it (`ratedId`), never the key\n */\n\n/**\n * A player's standing in a mode\n *\n * @beta\n */\nexport interface Rating {\n\trating: number\n\tgames: number\n\twins: number\n\t/** The name they last played by */\n\tname: string\n}\n\n/**\n * Where a server keeps ratings (`GameServerOptions.ratings`): what it reads to show in the lobby and the list, and\n * what it tells of every game that ended. `createRatings` keeps them in a file, or in memory; a game's own keeps them\n * where it likes (a database)\n *\n * @beta\n */\nexport interface Ratings {\n\t/** A player's standing in a mode, by their rated id (`ratedId`): none for one who never played it */\n\tget(mode: string, player: string): Rating | undefined\n\t/** Takes in a game that ended: the rated seats' ratings move by how it went, against what was expected */\n\trecord(result: GameResult): void | Promise<void>\n\t/**\n\t * The best of a mode, best first, with their rated ids and places (from 1): what `/ratings` lists. A store with none\n\t * lists nothing @beta\n\t */\n\ttop?(mode: string, count: number): RatedPlayer[]\n\t/** A player's place in a mode (from 1), with their standing: none for one who never played it @beta */\n\tplaceOf?(mode: string, player: string): RatedPlayer | undefined\n}\n\n/**\n * A player in a list of the best: their standing, their rated id (`ratedId`), and their place, from 1\n *\n * @beta\n */\nexport interface RatedPlayer extends Rating {\n\tid: string\n\tplace: number\n}\n\n/**\n * The id a player is rated by: a hash of their key or login id, safe to show and keep. The same key, the same id\n *\n * @beta\n */\nexport const ratedId = (key: string): string => createHash('sha256').update(key).digest('hex').slice(0, 16)\n\n/**\n * Ratings kept in a file (`file`, JSON, read once and written after every game) or in memory: Elo, each side's\n * players against the average of the other's, `k` 32, starting at 1500. The computer counts as `start`, and is not\n * rated; a game with no winner, or with players of one side only, changes nothing\n *\n * @beta\n */\nexport function createRatings(options: { file?: string; k?: number; start?: number } = {}): Ratings & { ready: Promise<void> } {\n\tconst k = options.k ?? 32\n\tconst start = options.start ?? 1500\n\tlet byMode: Record<string, Record<string, Rating>> = {}\n\tconst ready = (async () => {\n\t\tif (!options.file) return\n\t\t// none yet: empty. One that cannot be read is not written over, nor started from empty: it says so\n\t\tlet text: string\n\t\ttry {\n\t\t\ttext = await readFile(options.file, 'utf8')\n\t\t} catch (error) {\n\t\t\tif ((error as NodeJS.ErrnoException).code === 'ENOENT') return\n\t\t\tthrow error\n\t\t}\n\t\tbyMode = JSON.parse(text)\n\t})()\n\t// told to whoever waits for it (`ready`, `record`), not to the process as a rejection nobody heard\n\tready.catch(() => undefined)\n\tlet writing = Promise.resolve()\n\tconst keep = () => {\n\t\tif (!options.file) return writing\n\t\tconst file = options.file\n\t\twriting = writing.then(async () => {\n\t\t\tawait mkdir(dirname(file), { recursive: true })\n\t\t\tawait writeFile(file, JSON.stringify(byMode, null, 1))\n\t\t})\n\t\treturn writing\n\t}\n\t/** A mode's players, best first: by rating, then by more games, then by id, so the order is always the same */\n\tconst ranked = (mode: string): RatedPlayer[] =>\n\t\tObject.entries(byMode[mode] ?? {})\n\t\t\t.sort(([a, x], [b, y]) => y.rating - x.rating || y.games - x.games || (a < b ? -1 : 1))\n\t\t\t.map(([id, rating], index) => ({ id, place: index + 1, ...rating }))\n\treturn {\n\t\tready,\n\t\tget: (mode, player) => byMode[mode]?.[player],\n\t\ttop: (mode, count) => ranked(mode).slice(0, Math.max(0, count)),\n\t\tplaceOf: (mode, player) => ranked(mode).find((each) => each.id === player),\n\t\tasync record(result) {\n\t\t\tawait ready\n\t\t\tif (result.winner === null || result.failed) return\n\t\t\tconst seats = result.seats.filter((seat) => seat.outcome !== 'none')\n\t\t\tconst rated = seats.filter((seat) => !seat.ai && seat.rated)\n\t\t\tif (!rated.length) return\n\t\t\tconst table = (byMode[result.mode] ??= {})\n\t\t\tconst ratingOf = (seat: (typeof seats)[number]) => (!seat.ai && seat.rated ? (table[seat.rated]?.rating ?? start) : start)\n\t\t\tconst sides = new Map<string, (typeof seats)[number][]>()\n\t\t\tfor (const seat of seats) {\n\t\t\t\tconst side = seat.outcome\n\t\t\t\tsides.set(side, [...(sides.get(side) ?? []), seat])\n\t\t\t}\n\t\t\tconst winners = sides.get('victory') ?? []\n\t\t\tconst losers = sides.get('defeat') ?? []\n\t\t\tif (!winners.length || !losers.length) return\n\t\t\tconst average = (list: typeof seats) => list.reduce((sum, seat) => sum + ratingOf(seat), 0) / list.length\n\t\t\tconst changes = rated.map((seat) => {\n\t\t\t\tconst won = seat.outcome === 'victory'\n\t\t\t\tconst theirs = average(won ? losers : winners)\n\t\t\t\tconst mine = ratingOf(seat)\n\t\t\t\tconst expected = 1 / (1 + 10 ** ((theirs - mine) / 400))\n\t\t\t\treturn { seat, won, next: mine + k * ((won ? 1 : 0) - expected) }\n\t\t\t})\n\t\t\tfor (const { seat, won, next } of changes) {\n\t\t\t\tconst before = table[seat.rated!] ?? { rating: start, games: 0, wins: 0, name: seat.name }\n\t\t\t\ttable[seat.rated!] = { rating: Math.round(next), games: before.games + 1, wins: before.wins + (won ? 1 : 0), name: seat.name }\n\t\t\t}\n\t\t\tawait keep()\n\t\t}\n\t}\n}\n","import { createHash, timingSafeEqual } from 'node:crypto'\n\n/** A secret as bytes of one length, whatever its own: what is compared takes as long whatever it is */\nconst digest = (text: string) => createHash('sha256').update(text).digest()\n\n/**\n * Whether what was brought is the secret (an admin's key, a key to watch, to the numbers), compared in a time that\n * says nothing of how much of it is right (RV37)\n */\nexport const sameSecret = (brought: string, secret: string): boolean => timingSafeEqual(digest(brought), digest(secret))\n","/**\n * Where one who joins a server's game goes (`Doorman`): their seat again, a free seat, a seat the game decides\n * (`GameServerOptions.seat`), watching, or away with why (design review E12). It decides and says so; the server seats,\n * tells and closes\n */\nimport { errorFields, type Logger } from '#engine/log'\nimport type { ServerSeat } from '#engine/game'\nimport { PROTOCOL_VERSION } from '#engine/session/protocol'\nimport type { LobbyPlayerMessage } from '../session/wire'\nimport type { GameServerOptions, SeatAnswer } from './gameServer'\nimport { ratedId } from './ratings'\nimport type { Seat } from './seat'\nimport { sameSecret } from './secret'\n\n/** A join, its fields checked */\nexport type Join = Extract<LobbyPlayerMessage, { type: 'join' }>\n\n/**\n * Where one who joins goes\n *\n * @internal\n */\nexport type Door =\n\t/**\n\t * A seat: theirs again, a free one, or the game's choice. `id` and `rated`, where there (even as nothing), are who\n\t * holds it and what they are rated as, set as they sit; left out, the seat keeps its own (theirs again)\n\t */\n\t| { to: 'seat'; seat: Seat; name?: string; named?: boolean; id?: string; rated?: string }\n\t/** Watching: every seat is taken, and they may */\n\t| { to: 'watch' }\n\t/** Every seat is taken in the lobby, and they may not watch: they stay connected, and are told */\n\t| { to: 'full' }\n\t/** Away, told why: their connection closed */\n\t| { to: 'refuse'; reason: string }\n\n/** What the doorman reads of the server it keeps the door of */\nexport interface DoorContext {\n\treadonly phase: 'lobby' | 'game' | 'ended'\n\treadonly seats: readonly Seat[]\n\treadonly draining: boolean\n\treadonly options: Pick<GameServerOptions, 'seat' | 'watch'>\n\treadonly logger: Logger\n\tpublicSeat(seat: Seat): ServerSeat\n}\n\n/**\n * Decides where each who joins a server goes\n *\n * @internal\n */\nexport class Doorman {\n\tconstructor(private readonly server: DoorContext) {}\n\n\t/** Why one who joins speaks another version of the wire, or nothing: told before any seat is taken or welcome sent that it would misread */\n\twrongWire(join: Join): string | undefined {\n\t\tif (join.protocol === undefined || join.protocol === PROTOCOL_VERSION) return undefined\n\t\treturn `this server runs another version of the engine (protocol ${PROTOCOL_VERSION}, yours is ${join.protocol}): update the game`\n\t}\n\n\t/**\n\t * Where one who joins from `address` goes: decided now, or once the game said (`GameServerOptions.seat`), a promise\n\t * then. Their wire is the server's (`wrongWire`)\n\t */\n\tplace(join: Join, address: string): Door | Promise<Door> {\n\t\tconst { server } = this\n\t\t// back to the seat one had\n\t\tconst theirs = join.token ? server.seats.find((each) => each.token === join.token) : undefined\n\t\tif (theirs) return { to: 'seat', seat: theirs, name: join.name }\n\t\t// stopping: those of the game may come back to it, nobody new comes\n\t\tif (server.draining) return { to: 'refuse', reason: 'the server is stopping' }\n\t\t// coming back to a game that is no more (it ended and everyone left, or they were let go): not a new seat\n\t\tif (join.back) return { to: 'refuse', reason: 'the game you were in is over' }\n\t\tif (server.options.seat && !join.watch) return this.asked(join, address)\n\t\tconst free = !join.watch && server.phase === 'lobby' ? server.seats.find((each) => !each.ai && !each.connected) : undefined\n\t\tif (!free) return this.noSeat(join, address)\n\t\treturn { to: 'seat', seat: free, name: join.name, rated: join.who ? ratedId(join.who) : undefined }\n\t}\n\n\t/** Where the game says one who joins sits (`seat`), checked as it answered: a seat may have been taken meanwhile */\n\tprivate async asked(join: Join, address: string): Promise<Door> {\n\t\tconst { server } = this\n\t\tlet answer: SeatAnswer\n\t\ttry {\n\t\t\tanswer = await server.options.seat!({\n\t\t\t\taddress,\n\t\t\t\tname: join.name,\n\t\t\t\tticket: join.ticket,\n\t\t\t\tfree: server.seats.filter((seat) => seat.isFree).map((seat) => server.publicSeat(seat)),\n\t\t\t\tphase: server.phase\n\t\t\t})\n\t\t} catch (error) {\n\t\t\tserver.logger.error('server', 'deciding a seat threw: they are refused', { error: errorFields(error), name: join.name })\n\t\t\treturn { to: 'refuse', reason: 'the server could not seat you' }\n\t\t}\n\t\tif (!answer || typeof answer !== 'object') return { to: 'refuse', reason: 'the server could not seat you' }\n\t\tif ('refuse' in answer) return { to: 'refuse', reason: String(answer.refuse) }\n\t\treturn this.answered(join, answer, address)\n\t}\n\n\t/** The game's answer, checked against the seats as they are now */\n\tanswered(join: Join, answer: Exclude<SeatAnswer, { refuse: string }>, address: string): Door {\n\t\tconst { server } = this\n\t\tconst { player, id } = answer\n\t\tlet seat: Seat | undefined\n\t\tif (player === undefined) seat = server.phase === 'lobby' ? server.seats.find((each) => each.isFree) : undefined\n\t\telse {\n\t\t\tseat = server.seats.find((each) => each.player === player)\n\t\t\tif (!seat) return { to: 'refuse', reason: `there is no seat ${player}` }\n\t\t\tif (seat.ai) return { to: 'refuse', reason: \"that seat is the computer's\" }\n\t\t\t// held by another, or by nobody the game knows: not theirs to take\n\t\t\tif (!seat.isFree && (id === undefined || seat.id !== id)) return { to: 'refuse', reason: 'that seat is taken' }\n\t\t\tif (seat.isFree && server.phase !== 'lobby') return { to: 'refuse', reason: 'the game has started' }\n\t\t}\n\t\tif (!seat) return this.noSeat(join, address)\n\t\tconst rate = answer.rate || join.who || id\n\t\treturn {\n\t\t\tto: 'seat',\n\t\t\tseat,\n\t\t\tname: answer.name ?? join.name,\n\t\t\tnamed: answer.name !== undefined,\n\t\t\tid,\n\t\t\trated: rate !== undefined ? ratedId(rate) : undefined\n\t\t}\n\t}\n\n\t/** No seat for who joined: they watch, if they may, or are told so */\n\tprivate noSeat(join: Join, address: string): Door {\n\t\tconst { phase } = this.server\n\t\tif (phase === 'lobby' && !join.watch) return { to: 'full' }\n\t\tif (this.mayWatch(join, address)) return { to: 'watch' }\n\t\tif (phase === 'lobby') return { to: 'full' }\n\t\t// a game on, with no seat for them and nothing to watch\n\t\treturn { to: 'refuse', reason: 'watching is not open' }\n\t}\n\n\t/** Whether who asks may watch, as the server was told (`watch`) */\n\tprivate mayWatch(join: Join, address: string): boolean {\n\t\tconst { watch } = this.server.options\n\t\tif (!watch) return false\n\t\tif (watch === true) return true\n\t\tif (typeof watch === 'object') return join.key !== undefined && sameSecret(join.key, watch.key)\n\t\tconst seated = this.server.seats.some((seat) => seat.connected && seat.connection?.address === address)\n\t\treturn watch({ address, name: join.name, key: join.key, seated, phase: this.server.phase })\n\t}\n}\n","/**\n * A seat of a server's game (`Seat`): who holds it, and what each step of its life clears, said in one place\n * (design review E12). Taken by a connection (`take`), let go of by one that may come back to it (`drop`), given up\n * (`free`), made new for a new game (`reset`), held no more by whoever it was (`revoke`: a kick)\n */\nimport type { PlayerId } from '#engine/entity'\nimport type { ServerSeat } from '#engine/game'\nimport type { ModeSeat } from './mode'\nimport type { Connection } from './websocket'\n\n/** A token to come back to a seat with: what only who has it knows */\nexport function randomToken(): string {\n\tconst bytes = new Uint8Array(16)\n\tcrypto.getRandomValues(bytes)\n\treturn [...bytes].map((byte) => byte.toString(16).padStart(2, '0')).join('')\n}\n\n/**\n * A seat of a server's game and who holds it\n *\n * @internal\n */\nexport class Seat implements ServerSeat {\n\treadonly player: PlayerId\n\tname: string\n\t/** A computer plays it */\n\tai: boolean\n\t/** Someone is in it now */\n\tconnected = false\n\tready = false\n\tsettings?: Record<string, string>\n\t/** Who it is rated as (`ratedId`), where the server rates */\n\trated?: string\n\trating?: number\n\t/** What lets who held it back in: theirs while they hold it, in the lobby and in the game */\n\ttoken?: string\n\t/** Who holds it, as the `seat` hook said (`SeatAnswer.id`) */\n\tid?: string\n\t/** When what an honest client never sends came from it, in this game: the last `cheating.seconds`' worth, and how many in all */\n\tcheats: { at: number[]; total: number } = { at: [], total: 0 }\n\t/** The connection of who is in it */\n\tconnection?: Connection\n\n\tconstructor(\n\t\t/** What the mode says of it */\n\t\treadonly mode: ModeSeat,\n\t\t/** Whether a computer plays it when nobody holds it */\n\t\tprivate readonly computer: boolean,\n\t\tsettings: Record<string, string>\n\t) {\n\t\tthis.player = mode.player\n\t\tthis.name = mode.name\n\t\tthis.ai = computer\n\t\tthis.settings = settings\n\t}\n\n\t/** Nobody in it, nor holding it in a game to come back to, nor a computer */\n\tget isFree(): boolean {\n\t\treturn !this.ai && !this.connected && this.token === undefined\n\t}\n\n\t/**\n\t * Taken by a connection: whoever was in it before is closed out (a player back from another page), and its token is\n\t * kept, or made. A name it is given is taken. Gives the token\n\t */\n\ttake(connection: Connection, name?: string): string {\n\t\tif (this.connection && this.connection !== connection) this.connection.close()\n\t\tthis.connected = true\n\t\tthis.connection = connection\n\t\tthis.token ??= randomToken()\n\t\tif (name) this.name = name.slice(0, 24)\n\t\treturn this.token\n\t}\n\n\t/** Whoever holds another seat moves here, in the lobby: their connection, token, who they are and their name; not ready */\n\ttakeFrom(other: Seat): void {\n\t\tthis.connected = true\n\t\tthis.connection = other.connection\n\t\tthis.token = other.token\n\t\tthis.id = other.id\n\t\tthis.rated = other.rated\n\t\tthis.name = other.name\n\t\tthis.ready = false\n\t\tother.free()\n\t}\n\n\t/** Its connection gone, held for who had it to come back to (a game on): their token, who they are and their name stay */\n\tdrop(): void {\n\t\tthis.connected = false\n\t\tthis.connection = undefined\n\t}\n\n\t/** Given up: free for anyone, with the mode's name, nobody's token, not ready */\n\tfree(): void {\n\t\tthis.drop()\n\t\tthis.token = undefined\n\t\tthis.id = undefined\n\t\tthis.rated = undefined\n\t\tthis.ready = false\n\t\tthis.name = this.mode.name\n\t}\n\n\t/** Held no more by who it was: their token and who they are no good (a kick). Their connection is closed by the caller */\n\trevoke(): void {\n\t\tthis.token = undefined\n\t\tthis.id = undefined\n\t\tthis.rated = undefined\n\t}\n\n\t/** What a new game starts it with: nobody's cheats, not ready, the computer's where the server says */\n\tnewGame(): void {\n\t\tthis.cheats = { at: [], total: 0 }\n\t\tthis.ready = false\n\t\tthis.ai = this.computer\n\t}\n\n\t/** New, for a new game with nobody: free, and as a new game starts it */\n\treset(): void {\n\t\tthis.free()\n\t\tthis.newGame()\n\t}\n}\n","/**\n * What a lobby offers to choose for each seat (`seatSettingsOf`): a race where the game has races, and for a game that\n * says how its computers play (`GameModule.ai`) a difficulty and a personality, the engine's and the game's own. On the\n * server's side: it reads the computer player's profiles (`@v12-engine/engine/ai`), which the engine's core does not load\n */\nimport type { GameModule, ServerHooks } from '#engine/game'\nimport { RANDOM_RACE } from '#engine/races'\nimport { difficultiesOf, personalitiesOf } from '#engine/ai/profiles'\n\n/** A name as a lobby shows it: `insane` as `Insane` */\nconst titled = (id: string) => id.charAt(0).toUpperCase() + id.slice(1)\n\n/**\n * What a lobby offers a computer's seat of a game that says how its computers play (`GameModule.ai`, AI plan D1): a\n * difficulty and a personality, the engine's and the game's own. Nothing for a game that says nothing, or `lobby: false`\n */\nfunction computerSettingsOf(game: GameModule): NonNullable<ServerHooks['seatSettings']> {\n\tif (!game.ai || game.ai.lobby === false) return {}\n\tconst registry = { module: game }\n\tconst difficulties = Object.keys(difficultiesOf(registry))\n\tconst personalities = Object.keys(personalitiesOf(registry))\n\treturn {\n\t\tdifficulty: {\n\t\t\tlabel: 'Difficulty',\n\t\t\toptions: difficulties,\n\t\t\tlabels: Object.fromEntries(difficulties.map((id) => [id, titled(id)])),\n\t\t\tdefault: game.ai.defaults?.difficulty ?? 'normal'\n\t\t},\n\t\tpersonality: {\n\t\t\tlabel: 'Personality',\n\t\t\toptions: personalities,\n\t\t\tlabels: Object.fromEntries(personalities.map((id) => [id, titled(id)])),\n\t\t\tdefault: game.ai.defaults?.personality ?? 'balanced'\n\t\t}\n\t}\n}\n\n/**\n * What a game offers to choose for each seat in a lobby (`ServerHooks.seatSettings`), with a race among them where the\n * game has races and does not offer its own choice of one: each race, by its name, and `random` where there are two\n * or more; only those of\n * `races` where given (the races a map offers, `GameMap.races`). The first is chosen until someone does\n *\n * @beta\n */\nexport function seatSettingsOf(game: GameModule, races?: readonly string[]): NonNullable<ServerHooks['seatSettings']> {\n\tconst own = { ...computerSettingsOf(game), ...game.server?.seatSettings }\n\tconst all = game.races ?? []\n\tif (!all.length || own.race) return own\n\tconst offered = races?.length ? all.filter((race) => races.includes(race.id)) : all\n\t// random where there is more than one to draw from\n\tconst options = [...offered.map((race) => race.id), ...(offered.length > 1 ? [RANDOM_RACE] : [])]\n\treturn {\n\t\trace: {\n\t\t\tlabel: 'Race',\n\t\t\toptions: options.length ? options : [all[0].id],\n\t\t\t// every race's name, those a map fixes for a seat among them\n\t\t\tlabels: { ...Object.fromEntries(all.map((race) => [race.id, race.name])), [RANDOM_RACE]: 'Random' },\n\t\t\tdefault: options[0] ?? all[0].id\n\t\t},\n\t\t...own\n\t}\n}\n","/**\n * A server's lobby (`Lobby`): its seats, the game's settings, who is ready, when a game starts, and what everyone is\n * told of it (design review E12). The server hands it what a seated player asks for in the lobby; the connections,\n * the game and its phase stay the server's\n */\nimport type { PlayerId } from '#engine/entity'\nimport type { Logger } from '#engine/log'\nimport type { ServerHooks, ServerSeat } from '#engine/game'\nimport type { LobbyServerMessage } from '../session/wire'\nimport type { GameServerOptions } from './gameServer'\nimport { Seat } from './seat'\nimport { seatSettingsOf } from './seatSettings'\n\n/**\n * What the lobby reads of the server it is the lobby of\n *\n * @internal\n */\nexport interface LobbyContext {\n\treadonly phase: 'lobby' | 'game' | 'ended'\n\treadonly options: Pick<GameServerOptions, 'mode' | 'ai' | 'settings' | 'ratings' | 'autoStart'>\n\treadonly logger: Logger\n\t/** Tells everyone connected */\n\ttellAll(message: LobbyServerMessage): void\n\t/** Starts the game: every seat filled, or everyone ready */\n\tstartGame(): void\n}\n\n/**\n * A server's seats and settings, and when its game starts\n *\n * @internal\n */\nexport class Lobby {\n\t/** Its seats, in the mode's order */\n\treadonly seats: Seat[]\n\t/** The game's settings (`ServerHooks.settings`), as chosen */\n\treadonly settings: Record<string, string> = {}\n\t/** What each seat can have chosen for it: the game's (`ServerHooks.seatSettings`), and a race where it has races (`seatSettingsOf`) */\n\treadonly seatOffered: NonNullable<ServerHooks['seatSettings']>\n\t/** Back in the lobby after a game, with its players: it starts once they are ready, not as the seats fill */\n\tagain = false\n\t/** The votes to let a seated player go (`voteKick`), by who it is about: who voted */\n\tprivate readonly kickVotes = new Map<PlayerId, Set<PlayerId>>()\n\tprivate readonly hooks: ServerHooks\n\n\tconstructor(private readonly server: LobbyContext) {\n\t\tconst { mode, ai, settings } = server.options\n\t\tthis.hooks = mode.game.server ?? {}\n\t\tthis.seatOffered = mode.seatSettings ?? seatSettingsOf(mode.game)\n\t\tfor (const [name, setting] of Object.entries(this.hooks.settings ?? {})) {\n\t\t\tconst wanted = settings?.[name]\n\t\t\tthis.settings[name] = wanted !== undefined && setting.options.includes(wanted) ? wanted : setting.default\n\t\t}\n\t\tthis.seats = mode.seats.map(\n\t\t\t(seat) =>\n\t\t\t\tnew Seat(\n\t\t\t\t\tseat,\n\t\t\t\t\t!!ai?.includes(seat.player),\n\t\t\t\t\t// what the mode fixes for the seat over the defaults: a race a map gives its player\n\t\t\t\t\t{\n\t\t\t\t\t\t...Object.fromEntries(Object.entries(this.seatOffered).map(([name, setting]) => [name, setting.default])),\n\t\t\t\t\t\t// what the mode chose for it until someone chooses otherwise: its team and colour on the map\n\t\t\t\t\t\t...Object.fromEntries(Object.entries(seat.preset ?? {}).filter(([name]) => Object.hasOwn(this.seatOffered, name))),\n\t\t\t\t\t\t...seat.fixed\n\t\t\t\t\t}\n\t\t\t\t)\n\t\t)\n\t}\n\n\t/** A seat by its player */\n\tseat(player: PlayerId): Seat | undefined {\n\t\treturn this.seats.find((each) => each.player === player)\n\t}\n\n\t/** A seat as everyone is told it (the lobby, the hooks) */\n\tpublicSeat(seat: Seat): ServerSeat {\n\t\tconst { ratings, mode } = this.server.options\n\t\t// the team chosen for it, where the lobby offers one, else the mode's\n\t\tconst team = seat.settings?.team !== undefined ? Number(seat.settings.team) : seat.mode.team\n\t\treturn {\n\t\t\tplayer: seat.player,\n\t\t\tname: seat.name,\n\t\t\tai: seat.ai,\n\t\t\tconnected: seat.connected,\n\t\t\t...(team !== undefined && { team }),\n\t\t\t...(this.server.phase === 'lobby' && !seat.ai && { ready: !!seat.ready }),\n\t\t\t...(seat.settings && Object.keys(seat.settings).length > 0 && { settings: { ...seat.settings } }),\n\t\t\t...(seat.mode.fixed && Object.keys(seat.mode.fixed).length > 0 && { fixed: Object.keys(seat.mode.fixed) }),\n\t\t\t...(seat.rated && { rated: seat.rated }),\n\t\t\t...(seat.rated && ratings && { rating: ratings.get(mode.id, seat.rated)?.rating ?? undefined }),\n\t\t\t...(this.kickVotes.get(seat.player)?.size && { kick: { votes: this.kickVotes.get(seat.player)!.size, needed: this.kickNeeded(seat) } })\n\t\t}\n\t}\n\n\t/** The players who may vote to let one go: the others seated and there */\n\tprivate voters(about: Seat): Seat[] {\n\t\treturn this.seats.filter((seat) => seat !== about && seat.connected && !seat.ai)\n\t}\n\n\t/** How many votes let a seated player go: most of the others seated and there */\n\tprivate kickNeeded(about: Seat): number {\n\t\treturn Math.floor(this.voters(about).length / 2) + 1\n\t}\n\n\t/**\n\t * A seated player votes to let another go, in the lobby: once most of the others seated and there have, the seat\n\t * voted on is given back (its player is the server's to let go, `GameServer.kick`). Nothing for a seat nobody is in,\n\t * a computer's, or one's own\n\t */\n\tvoteKick(by: Seat, player: PlayerId): Seat | undefined {\n\t\tconst about = this.seat(player)\n\t\tif (this.server.phase !== 'lobby' || !about || about === by || !about.connected || about.ai || !by.connected) return undefined\n\t\tconst votes = this.kickVotes.get(player) ?? new Set<PlayerId>()\n\t\tvotes.add(by.player)\n\t\tthis.kickVotes.set(player, votes)\n\t\t// only those still there count\n\t\tconst counted = this.voters(about).filter((seat) => votes.has(seat.player)).length\n\t\tif (counted >= this.kickNeeded(about)) {\n\t\t\tthis.kickVotes.delete(player)\n\t\t\treturn about\n\t\t}\n\t\tthis.broadcast()\n\t\treturn undefined\n\t}\n\n\t/** A player left their seat, or moved: the votes about them and theirs go */\n\tprivate forgetVotes(player: PlayerId): void {\n\t\tthis.kickVotes.delete(player)\n\t\tfor (const votes of this.kickVotes.values()) votes.delete(player)\n\t}\n\n\t/** The rating of the players in its seats, on average: none where nobody rated sits or there are no ratings */\n\trating(): number | undefined {\n\t\tconst { ratings, mode } = this.server.options\n\t\tif (!ratings) return undefined\n\t\tconst known = this.seats.filter((seat) => seat.connected && seat.rated).map((seat) => ratings.get(mode.id, seat.rated!)?.rating ?? undefined)\n\t\tconst rated = known.filter((rating): rating is number => rating !== undefined)\n\t\treturn rated.length ? Math.round(rated.reduce((sum, rating) => sum + rating, 0) / rated.length) : undefined\n\t}\n\n\t/** Tells everyone the lobby as it is now */\n\tbroadcast(): void {\n\t\tconst { mode } = this.server.options\n\t\tthis.server.tellAll({\n\t\t\ttype: 'lobby',\n\t\t\tmode: mode.id,\n\t\t\tgame: mode.game.id,\n\t\t\tphase: this.server.phase,\n\t\t\tseats: this.seats.map((seat) => this.publicSeat(seat)),\n\t\t\tsettings: { ...this.settings },\n\t\t\toffered: this.hooks.settings ?? {},\n\t\t\tseatOffered: this.seatOffered\n\t\t})\n\t}\n\n\t/** A player took a seat in the lobby: the game starts once every seat is filled, unless they are back from one (\"Again\") */\n\tseated(): void {\n\t\tthis.broadcast()\n\t\tif ((this.server.options.autoStart ?? true) && !this.again && this.seats.every((each) => each.ai || each.connected)) this.server.startGame()\n\t}\n\n\t/** A player left the lobby: their seat is free for anyone */\n\tleft(seat: Seat): void {\n\t\tthis.forgetVotes(seat.player)\n\t\tseat.free()\n\t\tthis.broadcast()\n\t}\n\n\t/** A seated player moves to another seat, free, in the lobby: their own is free for anyone then. The seat moved to, or none */\n\tmove(from: Seat, player: PlayerId): Seat | undefined {\n\t\tconst to = this.seat(player)\n\t\tif (!to || to === from || this.server.phase !== 'lobby' || !to.isFree) return undefined\n\t\tthis.forgetVotes(from.player)\n\t\tto.takeFrom(from)\n\t\tthis.server.logger.info('server', `${to.name} moved from seat ${from.player} to ${to.player}`, {\n\t\t\tname: to.name,\n\t\t\tfrom: from.player,\n\t\t\tplayer: to.player\n\t\t})\n\t\tthis.broadcast()\n\t\treturn to\n\t}\n\n\t/** A seated player says they are ready, or not: once every player seated is, the game starts */\n\tready(seat: Seat, ready: boolean): void {\n\t\tif (this.server.phase !== 'lobby' || seat.ready === ready) return\n\t\tseat.ready = ready\n\t\tthis.broadcast()\n\t\tif (this.seats.every((each) => !each.connected || each.ready)) this.server.startGame()\n\t}\n\n\t/** A seated player's new name */\n\trename(seat: Seat, name: string): void {\n\t\tif (this.server.phase !== 'lobby') return\n\t\tconst trimmed = name.trim().slice(0, 24)\n\t\tif (!trimmed || trimmed === seat.name) return\n\t\tseat.name = trimmed\n\t\tthis.broadcast()\n\t}\n\n\t/** A setting of the game a seated player chose: one it offers, with a value it offers */\n\tset(name: string, value: string): void {\n\t\tif (this.server.phase !== 'lobby' || !Object.hasOwn(this.hooks.settings ?? {}, name)) return\n\t\tif (!this.hooks.settings![name].options.includes(value)) return\n\t\tthis.settings[name] = value\n\t\tthis.broadcast()\n\t}\n\n\t/**\n\t * A setting of a seat chosen by a seated player (`by`): their own seat, or one no other player holds (a computer's,\n\t * or one nobody took yet). Not what the mode fixes\n\t */\n\tsetSeat(by: Seat, player: PlayerId, name: string, value: string): void {\n\t\tif (this.server.phase !== 'lobby' || !Object.hasOwn(this.seatOffered, name)) return\n\t\tif (!this.seatOffered[name].options.includes(value)) return\n\t\tconst seat = this.seat(player)\n\t\tif (!seat || (seat !== by && seat.connected && !seat.ai)) return\n\t\tif (seat.mode.fixed && Object.hasOwn(seat.mode.fixed, name)) return\n\t\t// one no two seats hold (a colour): not while another holds it\n\t\tif (this.seatOffered[name].unique && this.seats.some((other) => other !== seat && other.settings?.[name] === value)) return\n\t\tseat.settings = { ...seat.settings, [name]: value }\n\t\tthis.broadcast()\n\t}\n\n\t/** A game starts: what nobody joined is the computer's */\n\tfill(): void {\n\t\tthis.kickVotes.clear()\n\t\tfor (const seat of this.seats) if (!seat.connected) seat.ai = true\n\t}\n\n\t/** Back after a game, with who is still there in their seats (and who comes again with `keepToken`): the seats of those gone are free */\n\tbackFromGame(keepToken: string): void {\n\t\t// those still on the last game's screen are seated, not there: the next starts when everyone says ready\n\t\tthis.again = true\n\t\tfor (const seat of this.seats) {\n\t\t\tseat.newGame()\n\t\t\tif (!seat.connected && seat.token !== keepToken) seat.free()\n\t\t}\n\t}\n\n\t/** New, for a new game with nobody: every seat free */\n\treset(): void {\n\t\tthis.again = false\n\t\tfor (const seat of this.seats) seat.reset()\n\t}\n}\n","import type { Ratings } from './ratings'\nimport { mkdir, writeFile } from 'node:fs/promises'\nimport { join } from 'node:path'\nimport type { GameResult } from '#engine/game'\nimport { type GameKindListing, type GameListing, PROTOCOL_VERSION } from '#engine/session/protocol'\nimport type { IncomingMessage, ServerResponse } from 'node:http'\nimport { sameSecret } from './secret'\n\n/*\n * A server to run in production (S58): what the HTTP server the sockets are on answers besides them. An address that\n * says whether it is well (`/health`: 200 while it takes games, 503 once it is stopping, for a load balancer to send\n * no one new) and one with its numbers (`/metrics`: as Prometheus reads them, or as JSON with `?json`)\n */\n\n/**\n * What a game server says of itself: the numbers of `/metrics` (`GameServer.metrics`)\n *\n * @beta\n */\nexport interface ServerMetrics {\n\t/** In its lobby, playing, or showing a game that ended */\n\tphase: 'lobby' | 'game' | 'ended'\n\t/** It takes no new games: it is stopping, once the running one is over (`close({ drain })`) */\n\tdraining: boolean\n\t/** Seconds since it started */\n\tuptime: number\n\t/** Games running now (0 or 1 for one server), and played to their end since it started */\n\tgames: { running: number; played: number }\n\t/** Seats held by someone connected, and those watching */\n\tplayers: number\n\twatching: number\n\t/** Connections open, seated or not */\n\tconnections: number\n\t/** Ticks of the simulation stepped since it started, and the seconds they took; how long the last one took */\n\tticks: number\n\ttickSeconds: number\n\tlastTickSeconds: number\n\t/** Rounds of sending everyone what changed, and the seconds they took */\n\tsends: number\n\tsendSeconds: number\n\t/** Bytes sent to those connected now, over the wire */\n\tbytesSent: number\n}\n\n/**\n * Where the addresses are, and who may read the numbers (`GameServerOptions.http`)\n *\n * @beta\n */\nexport interface ServerHttpOptions {\n\t/** The address that says whether it is well. `/health` when left out; false for none */\n\thealth?: string | false\n\t/** The address of its numbers. `/metrics` when left out; false for none */\n\tmetrics?: string | false\n\t/**\n\t * The address of the list of its games, for a page to find one (`GameListing`, JSON, readable from any page). `/games`\n\t * when left out; false for none\n\t */\n\tgames?: string | false\n\t/**\n\t * The address of the best of each mode (`/ratings?mode=melee&n=10&player=<rated id>`: the top and the player's own\n\t * place, JSON, readable from any page), where the server keeps ratings. `/ratings` when left out; false for none @beta\n\t */\n\tratings?: string | false\n\t/**\n\t * What reading the numbers takes (`Authorization: Bearer <key>`): they say how many play, and when. Anyone who can\n\t * reach the address when left out: keep it behind the proxy then\n\t */\n\tmetricsKey?: string\n}\n\n/** The numbers of a process: its memory */\nconst memory = () => {\n\tconst { rss, heapUsed } = process.memoryUsage()\n\treturn { rss, heapUsed }\n}\n\n/**\n * The numbers of the rooms of a process, in Prometheus's text: each by its room, where there is more than one\n *\n * @internal\n */\nexport function metricsText(rooms: ReadonlyMap<string, ServerMetrics>): string {\n\tconst lines: string[] = []\n\tconst each = (name: string, help: string, type: 'gauge' | 'counter', value: (metrics: ServerMetrics) => number) => {\n\t\tlines.push(`# HELP vg_${name} ${help}`, `# TYPE vg_${name} ${type}`)\n\t\tfor (const [room, metrics] of rooms) lines.push(`vg_${name}${room ? `{room=${JSON.stringify(room)}}` : ''} ${value(metrics)}`)\n\t}\n\teach('up', 'Whether it takes new games: 0 once it is stopping', 'gauge', (m) => (m.draining ? 0 : 1))\n\teach('games_running', 'Games running now', 'gauge', (m) => m.games.running)\n\teach('games_played_total', 'Games played to their end', 'counter', (m) => m.games.played)\n\teach('players', 'Seats held by someone connected', 'gauge', (m) => m.players)\n\teach('watching', 'Connections watching a game', 'gauge', (m) => m.watching)\n\teach('connections', 'Connections open', 'gauge', (m) => m.connections)\n\teach('ticks_total', 'Ticks of the simulation stepped', 'counter', (m) => m.ticks)\n\teach('tick_seconds_total', 'Seconds the ticks took', 'counter', (m) => round(m.tickSeconds))\n\teach('last_tick_seconds', 'How long the last tick took', 'gauge', (m) => round(m.lastTickSeconds))\n\teach('sends_total', 'Rounds of sending what changed', 'counter', (m) => m.sends)\n\teach('send_seconds_total', 'Seconds the sending took', 'counter', (m) => round(m.sendSeconds))\n\teach('bytes_sent', 'Bytes sent to those connected now', 'gauge', (m) => m.bytesSent)\n\teach('uptime_seconds', 'Seconds since it started', 'gauge', (m) => round(m.uptime))\n\tconst { rss, heapUsed } = memory()\n\tlines.push(\n\t\t// which version of the wire it speaks: a deploy of mixed versions shows as two values\n\t\t'# HELP vg_protocol_version The version of the wire its players have to speak',\n\t\t'# TYPE vg_protocol_version gauge',\n\t\t`vg_protocol_version ${PROTOCOL_VERSION}`,\n\t\t'# HELP vg_memory_bytes Memory of the process',\n\t\t'# TYPE vg_memory_bytes gauge',\n\t\t`vg_memory_bytes{kind=\"rss\"} ${rss}`,\n\t\t`vg_memory_bytes{kind=\"heap\"} ${heapUsed}`\n\t)\n\treturn `${lines.join('\\n')}\\n`\n}\n\nconst round = (value: number) => Math.round(value * 1e6) / 1e6\n\n/**\n * Answers the health and numbers addresses for the rooms of a process; false for any other request, which goes to\n * what answered it before. `draining` is whether the process takes no new games\n */\nexport function answerOps(\n\trequest: IncomingMessage,\n\tresponse: ServerResponse,\n\toptions: ServerHttpOptions | undefined,\n\tstate: () => { draining: boolean; rooms: ReadonlyMap<string, ServerMetrics> },\n\tgames?: () => { games: GameListing[]; kinds?: GameKindListing[] },\n\tratings?: { ratings?: Ratings; modes: readonly string[] }\n): boolean {\n\tconst { pathname, searchParams } = new URL(request.url ?? '/', 'http://server')\n\tconst best = options?.ratings ?? '/ratings'\n\tif (best && ratings?.ratings && pathname === best && (request.method === 'GET' || request.method === 'HEAD')) {\n\t\tconst mode = searchParams.get('mode') ?? ratings.modes[0] ?? ''\n\t\tconst count = Math.min(Math.max(Number(searchParams.get('n')) || 10, 1), 100)\n\t\tconst player = searchParams.get('player')\n\t\tconst top = ratings.ratings.top?.(mode, count) ?? []\n\t\tconst own = player ? ratings.ratings.placeOf?.(mode, player) : undefined\n\t\t// read by the pages of a game, wherever they are served from; names are what players chose to be seen by\n\t\tresponse.writeHead(200, { 'content-type': 'application/json', 'cache-control': 'no-store', 'access-control-allow-origin': '*' })\n\t\tresponse.end(JSON.stringify({ mode, modes: ratings.modes, top, ...(own && { player: own }) }))\n\t\treturn true\n\t}\n\tconst health = options?.health ?? '/health'\n\tconst metrics = options?.metrics ?? '/metrics'\n\tconst list = options?.games ?? '/games'\n\tif (request.method !== 'GET' && request.method !== 'HEAD') return false\n\tif (list && games && pathname === list) {\n\t\t// read by the pages of a game, wherever they are served from\n\t\tresponse.writeHead(200, { 'content-type': 'application/json', 'cache-control': 'no-store', 'access-control-allow-origin': '*' })\n\t\tresponse.end(JSON.stringify({ draining: state().draining, ...games() }))\n\t\treturn true\n\t}\n\tif (health && pathname === health) {\n\t\tconst { draining } = state()\n\t\tresponse.writeHead(draining ? 503 : 200, { 'content-type': 'application/json', 'cache-control': 'no-store' })\n\t\tresponse.end(JSON.stringify({ ok: !draining, draining }))\n\t\treturn true\n\t}\n\tif (metrics && pathname === metrics) {\n\t\tif (options?.metricsKey && !sameSecret(request.headers.authorization ?? '', `Bearer ${options.metricsKey}`)) {\n\t\t\tresponse.writeHead(401, { 'content-type': 'text/plain', 'www-authenticate': 'Bearer' })\n\t\t\tresponse.end('The numbers take a key\\n')\n\t\t\treturn true\n\t\t}\n\t\tconst { rooms, draining } = state()\n\t\tif (searchParams.has('json')) {\n\t\t\tresponse.writeHead(200, { 'content-type': 'application/json', 'cache-control': 'no-store' })\n\t\t\tresponse.end(JSON.stringify({ draining, protocol: PROTOCOL_VERSION, memory: memory(), rooms: Object.fromEntries(rooms) }))\n\t\t} else {\n\t\t\tresponse.writeHead(200, { 'content-type': 'text/plain; version=0.0.4', 'cache-control': 'no-store' })\n\t\t\tresponse.end(metricsText(rooms))\n\t\t}\n\t\treturn true\n\t}\n\treturn false\n}\n\n/** A game's result as a file of its own in `dir` (`GameServerOptions.resultsDir`): by when it ended and its mode */\nexport async function writeResult(dir: string, result: GameResult): Promise<void> {\n\tawait mkdir(dir, { recursive: true })\n\tawait writeFile(join(dir, `${Date.now()}-${result.mode.replace(/[^\\w-]/g, '_')}.json`), JSON.stringify(result, null, 2))\n}\n","/**\n * A server's game (`Match`): its world and the host that sends it, a pause, what a cheating client gets, the end of a\n * game everyone left, and its result told and kept (design review E12). The server makes one when its lobby starts a\n * game, seats the connections at it, and lets go of it back in the lobby\n */\nimport { commandRejected as commandRejectedEvent, pauseAsked } from '#engine/engineEvents'\nimport type { PlayerId } from '#engine/entity'\nimport type { RejectedKind } from '#engine/errors'\nimport type { GameResult, ServerHooks, ServerSeat } from '#engine/game'\nimport { errorFields, type Logger } from '#engine/log'\nimport type { NavigationBuilds } from '#engine/navigation/builds'\nimport { SessionHost } from '#engine/session/remote'\nimport type { World } from '#engine/world'\nimport type { LobbyServerMessage } from '../session/wire'\nimport type { CheatAnswer, CheatReport, GameServerOptions } from './gameServer'\nimport { writeResult } from './ops'\nimport type { Seat } from './seat'\n\n/** What a game reads of the server it is played on */\nexport interface MatchContext {\n\treadonly options: GameServerOptions\n\treadonly logger: Logger\n\treadonly seats: readonly Seat[]\n\t/** Who builds the navigation of its world, where the server says */\n\treadonly navigation?: NavigationBuilds\n\t/** How many watch now: for the line of where the host's time goes */\n\treadonly watching: number\n\tpublicSeat(seat: Seat): ServerSeat\n\t/** What nobody joined is the computer's: once the world is made, before the computers sit */\n\tfill(): void\n\t/** Tells everyone connected */\n\ttellAll(message: LobbyServerMessage): void\n\tkick(player: PlayerId, reason: string): void\n\t/** The game is over: the server ends it (`GameServer.endGame`) */\n\tend(): void\n}\n\n/**\n * A game a server runs, from its world made to its result kept\n *\n * @internal\n */\nexport class Match {\n\t/** Who paused the game: an admin, or a player (with a pause of theirs, for so long at most). Nobody while it runs */\n\tpausedBy?: PlayerId | 'admin'\n\t/** What the players are told of a game that did not end by its rules: an error thrown out of a tick, a cheat, the server stopping */\n\tfailed?: string\n\tprivate readonly hooks: ServerHooks\n\tprivate resumeTimer?: ReturnType<typeof setTimeout>\n\t/** Counts down to the end of a game every player has left */\n\tprivate abandoned?: ReturnType<typeof setTimeout>\n\tprivate watcher?: ReturnType<typeof setInterval>\n\tprivate statsTimer?: ReturnType<typeof setInterval>\n\n\tprivate constructor(\n\t\tprivate readonly server: MatchContext,\n\t\treadonly world: World,\n\t\treadonly host: SessionHost\n\t) {\n\t\tthis.hooks = server.options.mode.game.server ?? {}\n\t}\n\n\t/**\n\t * Makes the world of a game with these settings, the computer in the seats that are its, and its host: the game's\n\t * `onGameStart` told. Nobody is connected to it yet, and it does not run until `run`\n\t */\n\tstatic async make(server: MatchContext, settings: Record<string, string>): Promise<Match> {\n\t\tconst { mode } = server.options\n\t\tconst { seats } = server\n\t\tconst world = await mode.makeWorld(\n\t\t\t{ ...settings },\n\t\t\t{\n\t\t\t\tlog: server.logger,\n\t\t\t\tseats: Object.fromEntries(seats.map((seat) => [seat.player, { ...seat.settings }])),\n\t\t\t\t...(server.navigation && { navigation: server.navigation })\n\t\t\t}\n\t\t)\n\t\t// a part of the game that throws ends this game, and no other, unless the game says otherwise (`GameModule.onError`)\n\t\tworld.errorPolicy ??= 'end'\n\t\tserver.fill()\n\t\tconst modeSeats = seats.map((seat) => seat.mode)\n\t\tfor (const seat of seats) {\n\t\t\tif (!seat.ai || !mode.ai) continue\n\t\t\tconst seatSettings = { ...seat.settings }\n\t\t\t// the brain the mode says, else the first the game lists; remembered by the world, so a\n\t\t\t// save of the game has its computers back (`core.computers`)\n\t\t\tconst brain = typeof mode.brain === 'function' ? mode.brain(seat.mode, modeSeats, seatSettings) : mode.brain\n\t\t\tworld.computers.seat(seat.player, { ...mode.ai(seat.mode, modeSeats, seatSettings), brain })\n\t\t}\n\t\tlet match: Match | undefined\n\t\tworld.events.on(commandRejectedEvent, ({ player, type, kind, reason }) => match!.cheated(player, kind, reason, type))\n\t\t// a player asked for a pause, and the rules let them: the host stops until it is over\n\t\tworld.events.on(pauseAsked, ({ player, seconds }) => match!.pause(true, { player, seconds }))\n\t\tconst host = new SessionHost(world, {\n\t\t\tsendRate: server.options.sendRate,\n\t\t\t// the game's world goes with it: back to the lobby, or the server closed\n\t\t\townsWorld: true,\n\t\t\tdropSilentAfter: server.options.dropSilentAfter,\n\t\t\tkeptBytes: server.options.keptBytes,\n\t\t\t// whatever is thrown out of a tick all the same (a game that asks for `throw`, a fault of the engine's)\n\t\t\tonError: (error) => {\n\t\t\t\tserver.logger.error('server', 'stepping the game threw: the game ends', { error: errorFields(error), game: mode.game.id })\n\t\t\t\tif (match) match.failed = 'The game stopped: an error on the server'\n\t\t\t\tserver.end()\n\t\t\t},\n\t\t\tonSlowTick: (ms, tick) => {\n\t\t\t\tif (tick % 30 === 0) server.logger.warn('server', `tick ${tick} took ${ms.toFixed(1)} ms`, { ms, tick })\n\t\t\t}\n\t\t})\n\t\tmatch = new Match(server, world, host)\n\t\tmatch.hooks.onGameStart?.(\n\t\t\tworld,\n\t\t\tseats.map((seat) => server.publicSeat(seat)),\n\t\t\t{ ...settings }\n\t\t)\n\t\treturn match\n\t}\n\n\t/** Runs it, those seated connected to it: its ticks, the line of its time every `stats` seconds, and the look out for its end */\n\trun(): void {\n\t\tconst { server, host, world } = this\n\t\thost.start()\n\t\tserver.logger.info('server', `the game started: ${server.seats.map((seat) => `${seat.name}${seat.ai ? ' (computer)' : ''}`).join(', ')}`)\n\t\tif (server.options.stats) {\n\t\t\thost.takeTiming()\n\t\t\tthis.statsTimer = setInterval(() => this.printStats(), server.options.stats * 1000)\n\t\t}\n\t\tthis.watcher = setInterval(() => {\n\t\t\tif (world.winner !== null || world.failure) server.end()\n\t\t}, 250)\n\t}\n\n\t/** A seat's client sent what an honest one never does: counted, said, and answered as the game says (`onCheat`) */\n\tprivate cheated(player: PlayerId | undefined, kind: RejectedKind, reason: string, type?: string): void {\n\t\tconst { server } = this\n\t\tconst seat = server.seats.find((each) => each.player === player)\n\t\tif (!seat || seat.ai || !seat.connection) return\n\t\tconst { kickAfter = 20, seconds = 10 } = server.options.cheating ?? {}\n\t\tconst now = performance.now()\n\t\tconst { cheats } = seat\n\t\tcheats.at = cheats.at.filter((at) => now - at < seconds * 1000)\n\t\tcheats.at.push(now)\n\t\tcheats.total++\n\t\tconst report: CheatReport = {\n\t\t\tseat: server.publicSeat(seat),\n\t\t\taddress: seat.connection.address,\n\t\t\tkind,\n\t\t\treason,\n\t\t\t...(type !== undefined && { type }),\n\t\t\trecent: cheats.at.length,\n\t\t\ttotal: cheats.total\n\t\t}\n\t\tlet answer: CheatAnswer | void\n\t\ttry {\n\t\t\tanswer = server.options.onCheat ? server.options.onCheat(report) : cheats.at.length >= kickAfter ? 'kick' : 'warn'\n\t\t} catch (error) {\n\t\t\tserver.logger.error('server', 'onCheat threw: a warning, as without it', { error: errorFields(error) })\n\t\t\tanswer = 'warn'\n\t\t}\n\t\tconst fields = { name: seat.name, player: seat.player, address: report.address, reason, type, recent: report.recent, total: report.total }\n\t\tconst said = `${seat.name} sent what their client never does: ${reason} (${report.recent} in ${seconds} s, ${report.total} in all)`\n\t\tif (answer === 'ignore' || answer === undefined) return server.logger.debug('server', said, fields)\n\t\tserver.logger.warn('server', said, fields)\n\t\tif (answer === 'kick') server.kick(seat.player, 'sent what the game does not let a player send')\n\t\telse if (answer === 'end') {\n\t\t\tthis.failed = `The game ended: ${seat.name} sent what the game does not let a player send`\n\t\t\tserver.end()\n\t\t}\n\t}\n\n\t/**\n\t * Pauses the game for everyone, or lets it go on. A player's pause (`by`, from the `pause` command) ends by itself\n\t * after its seconds, or when a seated player says `resume`; an admin's, when the admin says so. A player's pause is\n\t * refused while an admin's holds: its timer would end the admin's\n\t */\n\tpause(paused: boolean, by?: { player: PlayerId; seconds: number }): void {\n\t\tconst { server, world } = this\n\t\tif (paused && by && this.pausedBy === 'admin') return\n\t\tthis.unpause()\n\t\tthis.host.speed = paused ? 0 : 1\n\t\tif (paused) {\n\t\t\tthis.pausedBy = by?.player ?? 'admin'\n\t\t\tif (by) this.resumeTimer = setTimeout(() => this.pause(false), by.seconds * 1000)\n\t\t}\n\t\tfor (const seat of server.seats) if (!seat.ai) world.message(seat.player, { key: paused ? 'gamePaused' : 'gameResumed' }, 'info')\n\t\t// paused, no tick goes by that would carry the message: it is told on the side too\n\t\tserver.tellAll({ type: 'paused', paused, ...(paused && by && { player: by.player, seconds: by.seconds }) })\n\t\tconst who = by ? server.seats.find((seat) => seat.player === by.player)?.name : undefined\n\t\tserver.logger.info('server', paused ? `paused${who ? ` by ${who}, ${by!.seconds} s at most` : ''}` : 'resumed')\n\t}\n\n\t/** No pause on: its timer stopped */\n\tprivate unpause(): void {\n\t\tclearTimeout(this.resumeTimer)\n\t\tthis.resumeTimer = undefined\n\t\tthis.pausedBy = undefined\n\t}\n\n\t/**\n\t * Every player left the game: it ends after a while (`abandonAfter`), unless one comes back. Nobody else would end it:\n\t * the host stops with nobody to send to, the world with it, and no winner is ever decided\n\t */\n\tabandon(): void {\n\t\tconst after = this.server.options.abandonAfter ?? 60\n\t\tif (!Number.isFinite(after) || this.abandoned !== undefined) return\n\t\tthis.server.logger.info('server', `everyone left: the game ends in ${after} seconds unless someone comes back`)\n\t\tthis.abandoned = setTimeout(() => {\n\t\t\tthis.abandoned = undefined\n\t\t\tif (!this.server.seats.some((each) => each.connected)) this.server.end()\n\t\t}, after * 1000)\n\t}\n\n\t/** Someone came back to the game, or it ended: it is not abandoned */\n\tkeep(): void {\n\t\tclearTimeout(this.abandoned)\n\t\tthis.abandoned = undefined\n\t}\n\n\t/**\n\t * A line of where the host's time went since the last one: ticks a second against the game's rate, how long a tick\n\t * of the simulation takes on average and at most, against the time a tick lasts, and the same for sending\n\t */\n\tprivate printStats(): void {\n\t\tconst { host, world, server } = this\n\t\tconst timing = host.takeTiming()\n\t\tconst budget = 1000 / world.tickRate\n\t\tconst average = (total: number, count: number) => (count ? total / count : 0)\n\t\tconst tick = average(timing.tickTime, timing.ticks)\n\t\tconst send = average(timing.sendTime, timing.sends)\n\t\tconst busy = ((timing.tickTime + timing.sendTime) / (timing.seconds * 1000)) * 100\n\t\tserver.logger.info(\n\t\t\t'server',\n\t\t\t[\n\t\t\t\t`ticks ${(timing.ticks / timing.seconds).toFixed(1)}/s of ${world.tickRate}`,\n\t\t\t\t`step avg ${tick.toFixed(2)} ms, max ${timing.tickMax.toFixed(2)} ms (${((tick / budget) * 100).toFixed(1)}% of ${budget.toFixed(1)} ms)`,\n\t\t\t\t`send avg ${send.toFixed(2)} ms, max ${timing.sendMax.toFixed(2)} ms, ${(timing.sends / timing.seconds).toFixed(1)}/s`,\n\t\t\t\t`busy ${busy.toFixed(1)}%`,\n\t\t\t\t`${world.entities.size} entities, ${server.seats.filter((seat) => seat.connected).length} players and ${server.watching} watching`\n\t\t\t].join(' | ')\n\t\t)\n\t}\n\n\t/** Its timers stopped: no pause, no end looked out for or counted down to, no line of its time */\n\tstop(): void {\n\t\tthis.keep()\n\t\tthis.unpause()\n\t\tclearInterval(this.watcher)\n\t\tclearInterval(this.statsTimer)\n\t}\n\n\t/** The game is over: its timers stopped, the result told to everyone and kept. The game goes on being shown */\n\tasync end(): Promise<GameResult> {\n\t\tconst { server, world } = this\n\t\tconst { options } = server\n\t\tthis.stop()\n\t\tconst result: GameResult = {\n\t\t\tgame: options.mode.game.id,\n\t\t\tmode: options.mode.id,\n\t\t\twinner: world.winner,\n\t\t\tduration: world.time,\n\t\t\t...((world.failure || this.failed) && { failed: world.failure?.message ?? this.failed }),\n\t\t\tseats: server.seats.map((seat) => {\n\t\t\t\tconst team = world.player(seat.player).team\n\t\t\t\treturn {\n\t\t\t\t\t...server.publicSeat(seat),\n\t\t\t\t\toutcome: world.winner === null ? 'none' : world.winner === team ? 'victory' : 'defeat'\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t\tthis.hooks.onGameEnd?.(world, result)\n\t\tserver.tellAll({ type: 'result', result })\n\t\tserver.logger.info('server', `the game ended: ${result.seats.map((seat) => `${seat.name} ${seat.outcome}`).join(', ')}`)\n\t\ttry {\n\t\t\tif (this.hooks.saveResult) await this.hooks.saveResult(result)\n\t\t\telse if (options.resultsDir) await writeResult(options.resultsDir, result)\n\t\t} catch (error) {\n\t\t\tserver.logger.error('server', 'the result could not be kept', { error: errorFields(error) })\n\t\t}\n\t\ttry {\n\t\t\tawait options.ratings?.record(result)\n\t\t} catch (error) {\n\t\t\tserver.logger.error('server', 'the ratings could not be kept', { error: errorFields(error) })\n\t\t}\n\t\treturn result\n\t}\n}\n","import { errorFields, type Logger, rootLogger } from '#engine/log'\nimport { createHash } from 'node:crypto'\nimport { createServer, type IncomingMessage, type Server } from 'node:http'\nimport type { Duplex } from 'node:stream'\nimport { constants, createDeflateRaw, type DeflateRaw, inflateRawSync } from 'node:zlib'\nimport type { Transport } from '#engine/session/remote'\n\n/*\n * WebSockets on Node's own `http`, as much of RFC 6455 as a game needs: the handshake, text messages (in one frame\n * or several), ping and pong, and closing; and compression (`permessage-deflate`, RFC 7692), which browsers ask for\n * on their own. No binary. Each connection is a `Transport`, the same a `loopback` gives: the game server does not\n * know which it talks through\n */\n\nconst GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'\nconst TEXT = 0x1\nconst CONTINUATION = 0x0\nconst CLOSE = 0x8\nconst PING = 0x9\nconst PONG = 0xa\n\n/**\n * A connection, with what it came with: the address it was asked at, and who from\n *\n * @beta\n */\nexport interface Connection extends Transport {\n\treadonly url: string\n\treadonly address: string\n\t/** Bytes written to it so far, as they go over the wire: compressed, where it is */\n\treadonly bytesSent: number\n\t/** Whether what goes over it is compressed (`permessage-deflate`) */\n\treadonly compressed: boolean\n}\n\n/** @beta */\nexport interface SocketServerOptions {\n\tport: number\n\t/** Where it says what goes wrong with a connection. The root logger when left out */\n\tlog?: Logger\n\t/** Characters a message may have at most: a longer one closes the connection. 64 KB when left out */\n\tmaxMessage?: number\n\t/** Whether to compress what goes over connections whose other end can take it. Yes when left out */\n\tcompress?: boolean\n\t/**\n\t * Seconds a connection may say nothing before it is closed: it is pinged meanwhile, and a browser answers a ping by\n\t * itself, so only one that is gone (a machine that vanished without closing) goes. 60 when left out\n\t */\n\tidleTimeout?: number\n\t/**\n\t * Bytes waiting to go out to a connection, at most: one that takes what it is sent too slowly is closed, where it\n\t * once queued in the server's memory without end. 16 MB when left out\n\t */\n\tmaxBuffered?: number\n\t/**\n\t * Connections open at once, at most: one more is answered `503` and never upgraded (RV38). 1024 when left out\n\t *\n\t * @beta\n\t */\n\tmaxConnections?: number\n\t/** What answers plain HTTP requests: a page saying what this is, by default */\n\tonRequest?: Parameters<typeof createServer>[1]\n}\n\n/**\n * Where a game server's connections come from (`GameServer.attach`): `onConnection` is told of each one made, `close`\n * closes them all. A socket server, or a room of a port that several share (`GameRooms`)\n *\n * @beta\n */\nexport interface ConnectionSource {\n\t/** The port they come in at */\n\treadonly port: number\n\t/** Who is told of each connection made: one handler, the last given */\n\tonConnection(handler: (connection: Connection) => void): void\n\t/** Closes every connection it made */\n\tclose(): Promise<void>\n}\n\n/**\n * A WebSocket server: `onConnection` is told of each connection made. `close` stops it, and closes them all\n *\n * @beta\n */\nexport interface SocketServer extends ConnectionSource {\n\treadonly server: Server\n\t/** The port it listens on: the one given, or the one it was given for 0 */\n\treadonly port: number\n}\n\n/**\n * Starts a WebSocket server, and resolves once it listens\n *\n * @beta\n */\nexport function startSocketServer(options: SocketServerOptions): Promise<SocketServer> {\n\tconst maxMessage = options.maxMessage ?? 64 * 1024\n\tconst maxConnections = options.maxConnections ?? 1024\n\tconst limits = { idle: (options.idleTimeout ?? 60) * 1000, buffered: options.maxBuffered ?? 16 * 1024 * 1024 }\n\tlet handler: (connection: Connection) => void = () => {}\n\tconst open = new Set<Duplex>()\n\t/** Each connection's own check: pinged when quiet, closed when silent too long */\n\tconst sweeps = new Set<() => void>()\n\tconst sweeper = setInterval(\n\t\t() => {\n\t\t\tfor (const sweep of sweeps) sweep()\n\t\t},\n\t\tMath.max(Math.min(limits.idle / 3, 20_000), 50)\n\t)\n\tsweeper.unref?.()\n\tconst server = createServer(\n\t\toptions.onRequest ??\n\t\t\t((_request, response) => {\n\t\t\t\tresponse.writeHead(426, { 'content-type': 'text/plain' })\n\t\t\t\tresponse.end('A game server: connect with a WebSocket\\n')\n\t\t\t})\n\t)\n\tserver.on('upgrade', (request: IncomingMessage, socket: Duplex) => {\n\t\tconst key = request.headers['sec-websocket-key']\n\t\tif (request.headers.upgrade?.toLowerCase() !== 'websocket' || typeof key !== 'string') {\n\t\t\tsocket.end('HTTP/1.1 400 Bad Request\\r\\n\\r\\n')\n\t\t\treturn\n\t\t}\n\t\tif (open.size >= maxConnections) {\n\t\t\tsocket.end('HTTP/1.1 503 Service Unavailable\\r\\nContent-Length: 0\\r\\n\\r\\n')\n\t\t\treturn\n\t\t}\n\t\tconst accept = createHash('sha1')\n\t\t\t.update(key + GUID)\n\t\t\t.digest('base64')\n\t\t// compressed where the other end asks for it: what it sends us each message on its own, so that taking it in\n\t\t// keeps nothing between messages (`client_no_context_takeover`); what we send, against all that went before\n\t\tconst offered = String(request.headers['sec-websocket-extensions'] ?? '')\n\t\tconst compressed = (options.compress ?? true) && /(^|,)\\s*permessage-deflate\\b/.test(offered)\n\t\tconst extension = compressed ? 'Sec-WebSocket-Extensions: permessage-deflate; client_no_context_takeover\\r\\n' : ''\n\t\tsocket.write(\n\t\t\t`HTTP/1.1 101 Switching Protocols\\r\\nUpgrade: websocket\\r\\nConnection: Upgrade\\r\\nSec-WebSocket-Accept: ${accept}\\r\\n${extension}\\r\\n`\n\t\t)\n\t\topen.add(socket)\n\t\tconst { connection, sweep } = connectionOf(socket, request, maxMessage, compressed, limits, options.log ?? rootLogger)\n\t\tsweeps.add(sweep)\n\t\tsocket.on('close', () => {\n\t\t\topen.delete(socket)\n\t\t\tsweeps.delete(sweep)\n\t\t})\n\t\thandler(connection)\n\t})\n\treturn new Promise((resolve) =>\n\t\tserver.listen(options.port, () => {\n\t\t\tconst address = server.address()\n\t\t\tresolve({\n\t\t\t\tserver,\n\t\t\t\tport: typeof address === 'object' && address ? address.port : options.port,\n\t\t\t\tonConnection: (next) => (handler = next),\n\t\t\t\tclose: () =>\n\t\t\t\t\tnew Promise((done) => {\n\t\t\t\t\t\tclearInterval(sweeper)\n\t\t\t\t\t\tfor (const socket of open) socket.destroy()\n\t\t\t\t\t\tserver.close(() => done())\n\t\t\t\t\t})\n\t\t\t})\n\t\t})\n\t)\n}\n\n/** The end every compressed message has, which the wire leaves off (RFC 7692): an empty block of a sync flush */\nconst SYNC_TAIL = Buffer.from([0x00, 0x00, 0xff, 0xff])\n/** Messages shorter than this go as they are: compressing them would save next to nothing */\nconst COMPRESS_FROM = 64\n/** Milliseconds a connection closed from here stays open for the other end's own close, which says it has read everything */\nconst CLOSE_WAIT = 1000\n\n/** A frame to send: a server's are never masked. `deflated` marks a compressed message (RSV1) */\nfunction frame(opcode: number, payload: Buffer, deflated = false): Buffer {\n\tconst length = payload.length\n\tconst head = length < 126 ? Buffer.alloc(2) : length < 65536 ? Buffer.alloc(4) : Buffer.alloc(10)\n\thead[0] = 0x80 | (deflated ? 0x40 : 0) | opcode\n\tif (length < 126) head[1] = length\n\telse if (length < 65536) {\n\t\thead[1] = 126\n\t\thead.writeUInt16BE(length, 2)\n\t} else {\n\t\thead[1] = 127\n\t\thead.writeBigUInt64BE(BigInt(length), 2)\n\t}\n\treturn Buffer.concat([head, payload])\n}\n\n/**\n * Compresses messages one after another against all the ones before (`permessage-deflate` with context): a delta\n * says much of what the one before it said, which the compressor remembers. In order, as zlib works in the background\n */\nclass Compressor {\n\tprivate readonly deflate: DeflateRaw = createDeflateRaw({ windowBits: 15, memLevel: 8 })\n\tprivate readonly chunks: Buffer[] = []\n\t/** The last message being compressed: the next waits for it, so that they go out in order */\n\tprivate queue: Promise<void> = Promise.resolve()\n\t/** Closed: what is still queued is let go, not written into a stream that is gone */\n\tprivate closed = false\n\n\tconstructor() {\n\t\tthis.deflate.on('data', (chunk: Buffer) => this.chunks.push(chunk))\n\t}\n\n\t/** Compresses a message, and hands it on when done: in the order they were given */\n\tcompress(message: Buffer, done: (compressed: Buffer) => void): void {\n\t\tthis.queue = this.queue.then(\n\t\t\t() =>\n\t\t\t\tnew Promise<void>((resolve) => {\n\t\t\t\t\tif (this.closed) return resolve()\n\t\t\t\t\tthis.deflate.write(message)\n\t\t\t\t\tthis.deflate.flush(constants.Z_SYNC_FLUSH, () => {\n\t\t\t\t\t\tconst all = Buffer.concat(this.chunks.splice(0))\n\t\t\t\t\t\t// the flush's empty block is left off the wire, and added back by who takes it in\n\t\t\t\t\t\tdone(all.subarray(0, all.length - SYNC_TAIL.length))\n\t\t\t\t\t\tresolve()\n\t\t\t\t\t})\n\t\t\t\t})\n\t\t)\n\t}\n\n\t/** Runs once what is being compressed has gone: for what goes as it is, in its turn */\n\tafter(run: () => void): void {\n\t\tthis.queue = this.queue.then(run)\n\t}\n\n\tclose(): void {\n\t\tif (this.closed) return\n\t\tthis.closed = true\n\t\tthis.deflate.close()\n\t}\n}\n\nfunction connectionOf(\n\tsocket: Duplex,\n\trequest: IncomingMessage,\n\tmaxMessage: number,\n\tcompressed: boolean,\n\tlimits: { idle: number; buffered: number },\n\tlog: Logger\n): { connection: Connection; sweep: () => void } {\n\tlet onMessage: (message: string) => void = () => {}\n\tlet onClose: () => void = () => {}\n\tlet closed = false\n\tlet buffer = Buffer.alloc(0)\n\t/** The parts of a text message sent in several frames, until its last, and whether it came compressed */\n\tlet parts: Buffer[] = []\n\tlet deflated = false\n\tlet bytesSent = 0\n\tconst compressor = compressed ? new Compressor() : undefined\n\t/** Whether the close frame has gone: nothing more is written after it */\n\tlet ended = false\n\t/** When something last came in */\n\tlet heardAt = Date.now()\n\tconst write = (data: Buffer) => {\n\t\tif (ended || socket.destroyed) return\n\t\tbytesSent += data.length\n\t\tsocket.write(data)\n\t\t// what the other end does not take in piles up here: past the limit, it is let go\n\t\tif (socket.writableLength > limits.buffered) {\n\t\t\tlog.warn('server', 'a connection took in too slowly, it is closed', { waiting: socket.writableLength, address: request.socket.remoteAddress })\n\t\t\tended = true\n\t\t\tsocket.destroy()\n\t\t}\n\t}\n\n\tconst shut = () => {\n\t\tif (closed) return\n\t\tclosed = true\n\t\tonClose()\n\t}\n\t/** Our close has gone, and the other end's is waited for, a moment: what comes in is read for that alone */\n\tlet lingering: ReturnType<typeof setTimeout> | undefined\n\tconst end = () => {\n\t\tclearTimeout(lingering)\n\t\tlingering = undefined\n\t\tsocket.end()\n\t}\n\t/** `answering`: the other end closed first, and has nothing more to say or to read */\n\tconst close = (code = 1000, answering = false) => {\n\t\tif (closed) return\n\t\tconst payload = Buffer.alloc(2)\n\t\tpayload.writeUInt16BE(code, 0)\n\t\t// what was sent before still goes, before the close: a message being compressed is written once it is done,\n\t\t// and a last word (\"refused\") went nowhere when the close overtook it\n\t\tconst finish = () => {\n\t\t\twrite(frame(CLOSE, payload))\n\t\t\tended = true\n\t\t\tcompressor?.close()\n\t\t\tif (answering || socket.destroyed) return end()\n\t\t\t// the socket is ended once the other end answers with its own close, having read all that came before\n\t\t\t// ours, or after a moment when it does not. Ended at once, what it had not read yet was lost with the\n\t\t\t// connection: a player refused was never told why\n\t\t\tlingering = setTimeout(end, CLOSE_WAIT)\n\t\t\tlingering.unref?.()\n\t\t}\n\t\tif (compressor) compressor.after(finish)\n\t\telse finish()\n\t\tshut()\n\t}\n\n\tsocket.on('data', (data: Buffer) => {\n\t\t// closed, by either side: what still comes in is not read, but for the close that answers ours. Ending the\n\t\t// socket only ends what goes out, and a client could otherwise close, and then join and take a seat that\n\t\t// nothing would ever free again\n\t\tif (closed && !lingering) return\n\t\theardAt = Date.now()\n\t\tbuffer = Buffer.concat([buffer, data])\n\t\t// as many whole frames as have come in\n\t\tfor (;;) {\n\t\t\t// a frame before may have closed it: what came after it in the same chunk is not read either\n\t\t\tif ((closed && !lingering) || buffer.length < 2) return\n\t\t\tconst fin = (buffer[0] & 0x80) !== 0\n\t\t\t// a compressed message says so in its first frame\n\t\t\tconst rsv1 = (buffer[0] & 0x40) !== 0\n\t\t\tconst opcode = buffer[0] & 0x0f\n\t\t\tconst masked = (buffer[1] & 0x80) !== 0\n\t\t\tlet length = buffer[1] & 0x7f\n\t\t\tlet at = 2\n\t\t\tif (length === 126) {\n\t\t\t\tif (buffer.length < 4) return\n\t\t\t\tlength = buffer.readUInt16BE(2)\n\t\t\t\tat = 4\n\t\t\t} else if (length === 127) {\n\t\t\t\tif (buffer.length < 10) return\n\t\t\t\tconst long = buffer.readBigUInt64BE(2)\n\t\t\t\tif (long > BigInt(maxMessage)) return closed ? end() : close(1009)\n\t\t\t\tlength = Number(long)\n\t\t\t\tat = 10\n\t\t\t}\n\t\t\t// a client's frames are always masked\n\t\t\t// (waiting for its close, a frame that is not right ends the wait)\n\t\t\tif (!masked) return closed ? end() : close(1002)\n\t\t\tif (length > maxMessage) return closed ? end() : close(1009)\n\t\t\tif (buffer.length < at + 4 + length) return\n\t\t\tconst mask = buffer.subarray(at, at + 4)\n\t\t\tconst payload = Buffer.from(buffer.subarray(at + 4, at + 4 + length))\n\t\t\tfor (let i = 0; i < payload.length; i++) payload[i] ^= mask[i & 3]\n\t\t\tbuffer = buffer.subarray(at + 4 + length)\n\n\t\t\t// closed from here, and waiting: only the other end's close is looked for\n\t\t\tif (closed) {\n\t\t\t\tif (opcode === CLOSE) return end()\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif (opcode === TEXT || opcode === CONTINUATION) {\n\t\t\t\tif (opcode === TEXT) deflated = rsv1 && compressed\n\t\t\t\tparts.push(payload)\n\t\t\t\tif (parts.reduce((sum, part) => sum + part.length, 0) > maxMessage) return close(1009)\n\t\t\t\tif (!fin) continue\n\t\t\t\tlet data = Buffer.concat(parts)\n\t\t\t\tparts = []\n\t\t\t\tif (deflated) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\t// each on its own, as agreed: and no bigger than a message may be, whatever it says it is\n\t\t\t\t\t\t// it ends at a sync flush, not at a last block: that is where inflating stops, not an error\n\t\t\t\t\t\tdata = inflateRawSync(Buffer.concat([data, SYNC_TAIL]), { maxOutputLength: maxMessage, finishFlush: constants.Z_SYNC_FLUSH })\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn close(1009)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// what the server does with a message is its own business, but it is never a reason for the process to\n\t\t\t\t// go: whatever is thrown closes this connection alone (1011, the server could not go on with it)\n\t\t\t\ttry {\n\t\t\t\t\tonMessage(data.toString('utf8'))\n\t\t\t\t} catch (error) {\n\t\t\t\t\tlog.error('server', 'a message could not be handled, the connection is closed', {\n\t\t\t\t\t\terror: errorFields(error),\n\t\t\t\t\t\taddress: request.socket.remoteAddress\n\t\t\t\t\t})\n\t\t\t\t\treturn close(1011)\n\t\t\t\t}\n\t\t\t} else if (opcode === PING) write(frame(PONG, payload))\n\t\t\telse if (opcode === CLOSE) return close(1000, true)\n\t\t\t// pongs, and binary, which a game here never sends, are let be\n\t\t}\n\t})\n\t// gone from the other side: nothing can be written any more\n\tconst gone = () => {\n\t\tclearTimeout(lingering)\n\t\tlingering = undefined\n\t\tended = true\n\t\tcompressor?.close()\n\t\tshut()\n\t}\n\tsocket.on('close', gone)\n\tsocket.on('error', gone)\n\n\tconst connection: Connection = {\n\t\turl: request.url ?? '/',\n\t\taddress: request.socket.remoteAddress ?? '',\n\t\tcompressed,\n\t\tget bytesSent() {\n\t\t\treturn bytesSent\n\t\t},\n\t\tsend(message) {\n\t\t\tif (closed) return\n\t\t\tconst data = Buffer.from(message, 'utf8')\n\t\t\tif (!compressor || data.length < COMPRESS_FROM) {\n\t\t\t\t// what is short goes as it is, though it waits for what is being compressed before it\n\t\t\t\tif (compressor) compressor.after(() => write(frame(TEXT, data)))\n\t\t\t\telse write(frame(TEXT, data))\n\t\t\t\treturn\n\t\t\t}\n\t\t\tcompressor.compress(data, (deflatedData) => write(frame(TEXT, deflatedData, true)))\n\t\t},\n\t\tonMessage(next) {\n\t\t\tonMessage = next\n\t\t},\n\t\tonClose(next) {\n\t\t\tonClose = next\n\t\t},\n\t\tclose: () => close()\n\t}\n\t/** Pings a connection that has been quiet, and closes one that has said nothing for longer than it may */\n\tconst sweep = () => {\n\t\tif (closed) return\n\t\tconst quiet = Date.now() - heardAt\n\t\tif (quiet > limits.idle) return close(1001)\n\t\tif (quiet > limits.idle / 3) write(frame(PING, Buffer.alloc(0)))\n\t}\n\treturn { connection, sweep }\n}\n","import type { Ratings } from './ratings'\nimport { type LobbyPlayerMessage, type LobbyServerMessage, readPlayerMessage } from '../session/wire'\nimport { delayed } from '../session/delay'\nimport type { GameListing } from '#engine/session/protocol'\nimport type { Logger } from '#engine/log'\nimport type { NavigationBuilds } from '#engine/navigation/builds'\nimport { nodeNavigationBuilder } from './navigation'\nimport { type ServerLog, serverLogger } from './log'\nimport type { PlayerId } from '#engine/entity'\nimport type { RejectedKind } from '#engine/errors'\nimport type { GameResult, ServerHooks, ServerSeat } from '#engine/game'\nimport { isFiniteNumber, PROTOCOL_VERSION } from '#engine/session/protocol'\nimport type { SessionHost, Transport } from '#engine/session/remote'\nimport type { World } from '#engine/world'\nimport { engineMessage, type Message } from '#engine/messages'\nimport { type Door, Doorman, type Join } from './doorman'\nimport type { Seat } from './seat'\nimport { Lobby } from './lobby'\nimport { Match } from './match'\nimport type { GameMode } from './mode'\nimport { answerOps, type ServerHttpOptions, type ServerMetrics } from './ops'\nimport { sameSecret } from './secret'\nimport { type Connection, type ConnectionSource, startSocketServer } from './websocket'\n\n/*\n * A game server: a lobby, then a game, then its result, and the lobby again. Players connect over WebSockets and\n * join (`join`); they take the mode's seats in order, or the seat they had when they come back with its token. Once\n * every seat for a player is taken (or a seated player says `start`), the world is made, the computer takes the seats\n * it was given and those left empty, and everyone gets the game as a `SessionHost` sends it. Those who come after\n * the seats are taken watch, from the first player's side. In the lobby a player moves to another free seat (`sit`),\n * changes their name (`name`) and says they are ready (`ready`): once every player seated is, the game starts\n */\n\n/** @beta */\nexport interface GameServerOptions {\n\tmode: GameMode\n\t/** Port to listen on: 0 for any free one */\n\tport: number\n\t/** Seats the computer plays, whoever joins */\n\tai?: PlayerId[]\n\t/**\n\t * Where ratings are kept (`Ratings`, `createRatings`): the players' shown in the lobby and the list of games, and\n\t * moved by every game that ends. A player is who brings the same key (`join`'s `who`), or the `seat` hook's `id`\n\t */\n\tratings?: Ratings\n\t/**\n\t * The game's settings it starts with (`ServerHooks.settings`), in place of their defaults: the map a room was made\n\t * for. Only those the game offers, with a value it offers\n\t */\n\tsettings?: Record<string, string>\n\t/** Starts once every seat for a player is taken, without anyone saying so. Yes when left out */\n\tautoStart?: boolean\n\t/** What `admin` messages must carry: no admin without */\n\tadminKey?: string\n\t/** Where results are kept as files, when the game has no `saveResult` of its own. Kept nowhere without */\n\tresultsDir?: string\n\t/** Commands a second a connection may send, and how many at once: what is over the limit is dropped, and answered refused. 30 and 60 */\n\trate?: { perSecond: number; burst: number }\n\t/**\n\t * Messages a second a connection may send besides its commands (`rate`), and how many at once: what is over is\n\t * dropped, unanswered. A player's screen says it got what it was sent `sendRate` times a second, pings every two\n\t * seconds and asks for the ground a part at a time. 100 and 200 when left out\n\t *\n\t * @beta\n\t */\n\tmessageRate?: { perSecond: number; burst: number }\n\t/**\n\t * The lobby's messages a second a connection may send, and how many at once: a join, a seat, a name, a setting,\n\t * ready, start, an admin's word. Each is told to everyone (and an admin's key tried): what is over is dropped. 4 and\n\t * 20 when left out\n\t *\n\t * @beta\n\t */\n\tlobbyRate?: { perSecond: number; burst: number }\n\tsendRate?: number\n\t/** Whether the seated players may use the debug tools (`debug.*` commands): for a game to try things out in. No when left out */\n\tdebug?: boolean\n\t/** Whether to compress what goes to players whose browsers can take it (`permessage-deflate`). Yes when left out */\n\tcompress?: boolean\n\t/**\n\t * Whether those who come once the seats are taken, or who ask to, may watch: they see everything, the whole map\n\t * without fog and every player's resources, so a player who could watch on the side would know all the other\n\t * side does. No when left out. `{ key }` lets in who brings the key (`join`'s `key`); a function decides for each\n\t * who asks\n\t */\n\twatch?: boolean | { key: string } | ((request: WatchRequest) => boolean)\n\t/**\n\t * Seconds who watches a game being played sees it behind (`delayed`): so that a watcher cannot tell a side where the\n\t * other is now. None when left out\n\t */\n\twatchDelay?: number\n\t/**\n\t * Who sits where, decided for each who joins: for a login, a matchmaker's ticket, a seat a lobby gave, a name\n\t * checked. It gets who asks (`SeatRequest`: their address, name, the ticket they brought and the seats free) and\n\t * answers a seat, the name to use and who they are, or a refusal with a reason; it may be asynchronous, to ask a\n\t * login service. Without, the first free seat, by the name sent. One who comes back with their seat's token takes it\n\t * again without being asked about. Watching is still `watch`'s\n\t */\n\tseat?: (request: SeatRequest) => SeatAnswer | Promise<SeatAnswer>\n\t/**\n\t * What becomes of a player whose client sends what an honest one never does (`commandRejected`: a command not what\n\t * it says it is, another player's unit, a unit there never was): each is counted by seat and said in the log, and\n\t * this decides, from who it is, why and how many: `ignore`, `warn` (said in the log as a warning), `kick` (let go,\n\t * their token and id no good any more) or `end` (the game ends, with no winner). Without, a warning each time, and a\n\t * kick once there are `cheating.kickAfter` within `cheating.seconds`\n\t */\n\tonCheat?: (report: CheatReport) => CheatAnswer | void\n\t/** How many refusals in how many seconds the server kicks after when there is no `onCheat`, and `CheatReport.recent` counts over. 20 in 10 */\n\tcheating?: { kickAfter?: number; seconds?: number }\n\t/** Seconds between two lines of where the host's time goes, while a game runs: none without */\n\tstats?: number\n\t/**\n\t * Whether the navigations of its worlds are built in a worker thread (S37), so that a world being made does not\n\t * hold up the ticks of the games already running: yes when left out, where the engine is built (in this\n\t * repository, which runs it as it is written, they are built on the server's thread). Handed to the mode's worlds\n\t * (`GameMode.makeWorld`'s `context.navigation`): a mode of its own passes it on\n\t */\n\tnavigationWorker?: boolean\n\t/**\n\t * Seconds a game goes on once every player has left it, for one of them to come back with their token: then it ends,\n\t * with no winner, and the server goes back to its lobby. 60 when left out; 0 ends it at once, `Infinity` never. A game\n\t * may end itself sooner or later its own way (`ServerHooks.onPlayerLeave`, `world.winner`)\n\t */\n\tabandonAfter?: number\n\t/** Seconds of the game a player may go without saying it got what it was sent, before it is let go (`SessionHostOptions.dropSilentAfter`) */\n\tdropSilentAfter?: number\n\t/** Bytes of what is kept for each player to make a delta against, at most (`SessionHostOptions.keptBytes`): 8 MB when left out */\n\tkeptBytes?: number\n\t/** Seconds a connection may say nothing, pings unanswered, before it is closed (`SocketServerOptions.idleTimeout`) */\n\tidleTimeout?: number\n\t/** Bytes waiting to go out to a connection, at most, before it is closed (`SocketServerOptions.maxBuffered`) */\n\tmaxBuffered?: number\n\t/**\n\t * Connections open at once, at most (`SocketServerOptions.maxConnections`): one more is turned away before it is a\n\t * WebSocket. 1024 when left out\n\t *\n\t * @beta\n\t */\n\tmaxConnections?: number\n\t/**\n\t * Seconds a connection has to join (`join`) before it is closed: one that never does is told nothing of the lobby\n\t * meanwhile, and would be kept for good, as a browser answers the pings that let go of those gone (RV38). 10 when\n\t * left out; `Infinity` waits for ever\n\t *\n\t * @beta\n\t */\n\tjoinWithin?: number\n\t/**\n\t * Where the server says what it does (seats, games, slow ticks, what went wrong), and its games with it: a logger\n\t * (`Logger`), whose entries say what they are about by name, or a function that takes each as a line of text.\n\t * Warnings and errors go to the root logger when left out\n\t */\n\tlog?: ServerLog\n\t/**\n\t * The addresses on its HTTP server besides the sockets: one that says whether it is well (`/health`) and one with\n\t * its numbers (`/metrics`), each to move or leave out, and a key the numbers take. See `ServerHttpOptions`\n\t */\n\thttp?: ServerHttpOptions\n}\n\n/**\n * Who asks to watch a game (`GameServerOptions.watch`)\n *\n * @beta\n */\nexport interface WatchRequest {\n\t/** The address they connect from */\n\taddress: string\n\t/** The name they gave */\n\tname?: string\n\t/** The key they brought */\n\tkey?: string\n\t/** Whether a seat is held from the same address: a player who would watch their own game from the side */\n\tseated: boolean\n\tphase: GameServer['phase']\n}\n\n/**\n * A player whose client sent what an honest one never does (`GameServerOptions.onCheat`)\n *\n * @beta\n */\nexport interface CheatReport {\n\tseat: ServerSeat\n\t/** The address they connect from */\n\taddress?: string\n\t/** Why it was turned away (`commandRejected`'s kind), in words (its reason), and the command's type when it is known */\n\tkind: RejectedKind\n\treason: string\n\ttype?: string\n\t/** How many there were within `cheating.seconds`, this one with them, and in the whole game */\n\trecent: number\n\ttotal: number\n}\n\n/**\n * What the server does about it (`GameServerOptions.onCheat`): nothing, a warning in the log, let them go, or end the game\n *\n * @beta\n */\nexport type CheatAnswer = 'ignore' | 'warn' | 'kick' | 'end'\n\n/**\n * Who asks for a seat (`GameServerOptions.seat`)\n *\n * @beta\n */\nexport interface SeatRequest {\n\t/** The address they connect from */\n\taddress: string\n\t/** The name they gave */\n\tname?: string\n\t/** What they brought to be let in by (`join`'s `ticket`): a login's token, a matchmaker's ticket. Checked by the game, never by the server */\n\tticket?: string\n\t/** The seats nobody holds now, in the mode's order: none once a game is on, where only coming back is left */\n\tfree: ServerSeat[]\n\tphase: GameServer['phase']\n}\n\n/**\n * Where who asked sits (`GameServerOptions.seat`): a seat by its player (the first free one when left out), the name to\n * use there (theirs, or the seat's, when left out), and who they are (`id`): one who comes again as the same id gets the\n * seat they hold, from another page or after a drop, and the connection there before is let go. A seat another holds is\n * refused. `{ refuse }` sends them away with the reason, which they are told\n *\n * @beta\n */\nexport type SeatAnswer =\n\t| {\n\t\t\tplayer?: PlayerId\n\t\t\tname?: string\n\t\t\tid?: string\n\t\t\t/**\n\t\t\t * Who to rate them as (`ratings`): a login's account. The key they brought (`join`'s `who`) when left out, and\n\t\t\t * `id` without one; a matchmaker's ticket, a new id every game, is no one to rate\n\t\t\t */\n\t\t\trate?: string\n\t }\n\t| { refuse: string }\n\n/** A connection, and what it gives the game to talk through */\ninterface Client {\n\tconnection: Connection\n\t/** The end the game's host holds: what comes in that is not the server's goes to it */\n\trelay: Transport\n\ttoGame: (text: string) => void\n\tfromGameClosed: () => void\n\tseat?: Seat\n\twatching: boolean\n\t/** Asked about, by the `seat` hook: a second join waits for nothing and is dropped */\n\tjoining?: boolean\n\t/** Joined, and answered: seated, watching, or told every seat is taken. Only those are told of the lobby */\n\tjoined?: boolean\n\t/** The key the page joined with (`join`'s `who`): what keeps one voted out of the lobby out */\n\twho?: string\n\t/** The name it joined by: what one who watches is called in their lines */\n\tname?: string\n\t/** What closes it if it has not joined in time (`joinWithin`) */\n\tdeadline?: ReturnType<typeof setTimeout>\n\t/** The rate limits: of its commands (`rate`), of its other messages (`messageRate`), of the lobby's (`lobbyRate`) */\n\tcommands: Bucket\n\tmessages: Bucket\n\tlobby: Bucket\n}\n\n/** The messages of the lobby: each told to everyone, or a key tried (`lobbyRate`) */\nconst LOBBY_MESSAGES = new Set<unknown>(['join', 'sit', 'ready', 'resume', 'name', 'start', 'setting', 'seatSetting', 'admin', 'lobbyChat', 'kick'])\n\n/** How long a line said in the lobby may be */\nconst LOBBY_LINE = 200\n\n/** A token bucket: so many at once, filled again at so many a second */\nclass Bucket {\n\tprivate tokens: number\n\tprivate toppedUp = performance.now()\n\n\tconstructor(private readonly rate: { perSecond: number; burst: number }) {\n\t\tthis.tokens = rate.burst\n\t}\n\n\t/** Whether one more goes now: taken from the bucket, when it does */\n\ttake(): boolean {\n\t\tconst now = performance.now()\n\t\tthis.tokens = Math.min(this.rate.burst, this.tokens + ((now - this.toppedUp) / 1000) * this.rate.perSecond)\n\t\tthis.toppedUp = now\n\t\tif (this.tokens < 1) return false\n\t\tthis.tokens--\n\t\treturn true\n\t}\n}\n\n/**\n * A server of a game: its connections, who joins and where they go (`Doorman`), its lobby (`Lobby`: the seats, the\n * settings, when a game starts) and its game while one is on (`Match`)\n *\n * @beta\n */\nexport class GameServer {\n\tphase: 'lobby' | 'game' | 'ended' = 'lobby'\n\tprivate readonly clients = new Set<Client>()\n\t/** The keys of those the lobby voted out (`kick`): kept out until the server starts afresh (`reset`) */\n\tprivate readonly kickedOut = new Set<string>()\n\t/** Where each who joins goes */\n\tprivate readonly doorman: Doorman = new Doorman(this)\n\t/** The seats, the settings, and when a game starts */\n\tprivate readonly lobby: Lobby\n\t/** The game on, or the one that ended while its players are still on its screen */\n\tprivate match?: Match\n\n\tprivate constructor(\n\t\tprivate readonly source: ConnectionSource,\n\t\treadonly options: GameServerOptions,\n\t\tlogger: Logger,\n\t\t/** Who builds the navigations of its worlds: its worker thread (`navigationWorker`), or the world's own way */\n\t\tprivate readonly navigation?: NavigationBuilds\n\t) {\n\t\tconst server = this\n\t\tthis.logger = logger\n\t\tthis.lobby = new Lobby({\n\t\t\toptions,\n\t\t\tlogger,\n\t\t\tget phase() {\n\t\t\t\treturn server.phase\n\t\t\t},\n\t\t\ttellAll: (message) => this.tellAll(message),\n\t\t\tstartGame: () => void this.startGame()\n\t\t})\n\t\tsource.onConnection((connection) => this.accept(connection))\n\t}\n\n\t/** Starts a server, and resolves once it listens */\n\tstatic async start(options: GameServerOptions): Promise<GameServer> {\n\t\tconst logger = serverLogger(options.log)\n\t\tconst builder = options.navigationWorker !== false ? nodeNavigationBuilder() : undefined\n\t\tlet server: GameServer | undefined\n\t\tconst socket = await startSocketServer({\n\t\t\tport: options.port,\n\t\t\tcompress: options.compress,\n\t\t\tidleTimeout: options.idleTimeout,\n\t\t\tmaxBuffered: options.maxBuffered,\n\t\t\tmaxConnections: options.maxConnections,\n\t\t\tlog: logger,\n\t\t\t// its health and its numbers, on the same port as its sockets\n\t\t\tonRequest: (request, response) => {\n\t\t\t\tconst answered =\n\t\t\t\t\tserver &&\n\t\t\t\t\tanswerOps(\n\t\t\t\t\t\trequest,\n\t\t\t\t\t\tresponse,\n\t\t\t\t\t\toptions.http,\n\t\t\t\t\t\t() => ({ draining: server!.draining, rooms: new Map([['', server!.metrics()]]) }),\n\t\t\t\t\t\t() => ({ games: [server!.listing('')] }),\n\t\t\t\t\t\t{ ratings: options.ratings, modes: [options.mode.id] }\n\t\t\t\t\t)\n\t\t\t\tif (answered) return\n\t\t\t\tresponse.writeHead(426, { 'content-type': 'text/plain' })\n\t\t\t\tresponse.end('A game server: connect with a WebSocket\\n')\n\t\t\t}\n\t\t})\n\t\tserver = new GameServer(socket, options, logger, builder && { builder })\n\t\treturn server.begin()\n\t}\n\n\t/**\n\t * A server whose connections come from elsewhere (`ConnectionSource`): a room of `GameRooms`, those a port it shares\n\t * hands it. Closing it closes those, and leaves where they come from to whoever owns it\n\t */\n\tstatic async attach(source: ConnectionSource, options: Omit<GameServerOptions, 'port'>): Promise<GameServer> {\n\t\treturn new GameServer(source, { ...options, port: source.port }, serverLogger(options.log)).begin()\n\t}\n\n\t/** Says it is there, and starts a game at once where the computer has every seat */\n\tprivate async begin(): Promise<GameServer> {\n\t\tconst { options, source } = this\n\t\tthis.log(`${options.mode.name} (${options.mode.game.id}) on port ${source.port}, ${this.seats.length} seats`)\n\t\t// the computer in every seat: nobody to wait for (a game to watch, or to measure)\n\t\tif ((options.autoStart ?? true) && this.seats.every((seat) => seat.ai)) await this.startGame()\n\t\treturn this\n\t}\n\n\t/**\n\t * Its seats, in the mode's order: the server's own (its doorman's), a seat of a player is `publicSeat`'s\n\t *\n\t * @internal\n\t */\n\tget seats(): Seat[] {\n\t\treturn this.lobby.seats\n\t}\n\n\t/** The game's settings (`ServerHooks.settings`), as chosen in the lobby */\n\tget settings(): Record<string, string> {\n\t\treturn this.lobby.settings\n\t}\n\n\t/** The world of the game on, or of the one that ended while its players are still on its screen */\n\tget world(): World | undefined {\n\t\treturn this.match?.world\n\t}\n\n\t/** What sends the game to its players, while there is one */\n\tget host(): SessionHost | undefined {\n\t\treturn this.match?.host\n\t}\n\n\t/** Who paused the game: an admin, or a player (with a pause of theirs, for so long at most). Nobody while it runs */\n\tget pausedBy(): PlayerId | 'admin' | undefined {\n\t\treturn this.match?.pausedBy\n\t}\n\n\t/** When it started, for its uptime */\n\tprivate readonly startedAt = performance.now()\n\t/** Games played to their end */\n\tprivate played = 0\n\t/** What the hosts of the games before this one counted: the numbers go on from there */\n\tprivate readonly past = { ticks: 0, tickTime: 0, sends: 0, sendTime: 0 }\n\t/** It takes no new games: it is stopping once the running one is over (`close({ drain })`) */\n\tdraining = false\n\t/** Tells a stop that waits that the running game is over */\n\tprivate drained?: () => void\n\n\t/** What a list of games says of it (`GET /games`, `GameListing`), as the room it is reached at */\n\tlisting(room: string): GameListing {\n\t\tconst { mode } = this.options\n\t\tconst rating = this.lobby.rating()\n\t\treturn {\n\t\t\troom,\n\t\t\tgame: mode.game.id,\n\t\t\tmode: mode.name,\n\t\t\tphase: this.phase,\n\t\t\tplayers: this.seats.filter((seat) => !seat.ai && seat.connected).length,\n\t\t\tfree: this.seats.filter((seat) => !seat.ai && seat.isFree).length,\n\t\t\tcomputer: this.seats.filter((seat) => seat.ai).length,\n\t\t\twatchers: this.watching,\n\t\t\tsettings: { ...this.settings },\n\t\t\tdraining: this.draining,\n\t\t\t...(rating !== undefined && { rating })\n\t\t}\n\t}\n\n\t/** How many are connected to it, playing or watching: none, for a room that may be closed */\n\tget connections(): number {\n\t\treturn this.clients.size\n\t}\n\n\t/** How many watch */\n\tprivate get watching(): number {\n\t\treturn [...this.clients].filter((client) => client.watching).length\n\t}\n\n\t/** Its numbers now: what `/metrics` says (see `ServerMetrics`) */\n\tmetrics(): ServerMetrics {\n\t\tconst totals = this.host?.totals\n\t\treturn {\n\t\t\tphase: this.phase,\n\t\t\tdraining: this.draining,\n\t\t\tuptime: (performance.now() - this.startedAt) / 1000,\n\t\t\tgames: { running: this.phase === 'game' ? 1 : 0, played: this.played },\n\t\t\tplayers: this.seats.filter((seat) => seat.connected).length,\n\t\t\twatching: this.watching,\n\t\t\tconnections: this.clients.size,\n\t\t\tticks: this.past.ticks + (totals?.ticks ?? 0),\n\t\t\ttickSeconds: (this.past.tickTime + (totals?.tickTime ?? 0)) / 1000,\n\t\t\tlastTickSeconds: (totals?.lastTick ?? 0) / 1000,\n\t\t\tsends: this.past.sends + (totals?.sends ?? 0),\n\t\t\tsendSeconds: (this.past.sendTime + (totals?.sendTime ?? 0)) / 1000,\n\t\t\tbytesSent: this.bytesSent\n\t\t}\n\t}\n\n\t/** The game let go of, what its host counted kept for the numbers */\n\tprivate dropMatch(options?: { closeTransports: boolean }): void {\n\t\tconst { match } = this\n\t\tif (!match) return\n\t\tconst { host } = match\n\t\tmatch.stop()\n\t\tthis.past.ticks += host.totals.ticks\n\t\tthis.past.tickTime += host.totals.tickTime\n\t\tthis.past.sends += host.totals.sends\n\t\tthis.past.sendTime += host.totals.sendTime\n\t\thost.dispose(options)\n\t\tthis.match = undefined\n\t}\n\n\tget port(): number {\n\t\treturn this.source.port\n\t}\n\n\t/** Bytes sent to those connected now, as they went over the wire: compressed, where they are */\n\tget bytesSent(): number {\n\t\tlet total = 0\n\t\tfor (const client of this.clients) total += client.connection.bytesSent\n\t\treturn total\n\t}\n\n\t/** What the server and its games say: see `GameServerOptions.log` */\n\treadonly logger: Logger\n\n\tprivate log(line: string, fields?: Record<string, unknown>): void {\n\t\tthis.logger.info('server', line, fields)\n\t}\n\n\tprivate accept(connection: Connection): void {\n\t\tconst { options } = this\n\t\tconst client: Client = {\n\t\t\tconnection,\n\t\t\twatching: false,\n\t\t\tcommands: new Bucket(options.rate ?? { perSecond: 30, burst: 60 }),\n\t\t\tmessages: new Bucket(options.messageRate ?? { perSecond: 100, burst: 200 }),\n\t\t\tlobby: new Bucket(options.lobbyRate ?? { perSecond: 4, burst: 20 }),\n\t\t\ttoGame: () => {},\n\t\t\tfromGameClosed: () => {},\n\t\t\trelay: undefined as unknown as Transport\n\t\t}\n\t\tclient.relay = {\n\t\t\tsend: (message) => connection.send(message),\n\t\t\tonMessage: (handler) => (client.toGame = handler),\n\t\t\tonClose: (handler) => (client.fromGameClosed = handler),\n\t\t\tclose: () => connection.close()\n\t\t}\n\t\tthis.clients.add(client)\n\t\tconnection.onMessage((text) => this.receive(client, text))\n\t\tconnection.onClose(() => this.leave(client))\n\t\t// one that never joins is let go: nothing else would (RV38)\n\t\tconst within = options.joinWithin ?? 10\n\t\tif (Number.isFinite(within)) {\n\t\t\tclient.deadline = setTimeout(\n\t\t\t\t() => {\n\t\t\t\t\tif (client.joined || client.joining || !this.clients.has(client)) return\n\t\t\t\t\tthis.log('a connection that did not join in time was closed', { address: connection.address })\n\t\t\t\t\tconnection.close()\n\t\t\t\t},\n\t\t\t\tMath.max(0, within) * 1000\n\t\t\t)\n\t\t\tclient.deadline.unref?.()\n\t\t}\n\t}\n\n\tprivate tell(client: Client, message: LobbyServerMessage): void {\n\t\tclient.connection.send(JSON.stringify(message))\n\t}\n\n\t/** Tells everyone who joined: never a connection that has not (RV38) */\n\tprivate tellAll(message: LobbyServerMessage): void {\n\t\tfor (const client of this.clients) if (client.joined) this.tell(client, message)\n\t}\n\n\tprivate receive(client: Client, text: string): void {\n\t\t// one that has left says nothing more\n\t\tif (!this.clients.has(client)) return\n\t\t// anyone can connect and send anything: each field is checked before it is used, and what is not right is dropped\n\t\tconst message = readPlayerMessage(text)\n\t\tconst { seat } = client\n\t\t// every message through a limit: a seated player's commands through theirs (`allow`, which answers each it\n\t\t// drops), everything else through the connection's, and the lobby's, told to everyone, through one of its own (RV37)\n\t\tif (!(message?.type === 'command' && seat && this.phase === 'game') && !client.messages.take()) return\n\t\tif (!message) return\n\t\tif (LOBBY_MESSAGES.has(message.type) && !client.lobby.take()) return\n\t\tswitch (message.type) {\n\t\t\tcase 'join':\n\t\t\t\treturn this.join(client, {\n\t\t\t\t\ttype: 'join',\n\t\t\t\t\tname: typeof message.name === 'string' ? message.name : undefined,\n\t\t\t\t\ttoken: typeof message.token === 'string' ? message.token : undefined,\n\t\t\t\t\tticket: typeof message.ticket === 'string' ? message.ticket : undefined,\n\t\t\t\t\twatch: message.watch === true,\n\t\t\t\t\tkey: typeof message.key === 'string' ? message.key : undefined,\n\t\t\t\t\t// who they are to the ratings: a key of their own, of a sensible length\n\t\t\t\t\twho: typeof message.who === 'string' && message.who.length <= 128 ? message.who : undefined,\n\t\t\t\t\tback: message.back === true,\n\t\t\t\t\tprotocol: isFiniteNumber(message.protocol) ? message.protocol : undefined\n\t\t\t\t})\n\t\t\tcase 'sit':\n\t\t\t\tif (typeof message.player === 'number') this.move(client, message.player)\n\t\t\t\treturn\n\t\t\tcase 'ready':\n\t\t\t\tif (seat && typeof message.ready === 'boolean') this.lobby.ready(seat, message.ready)\n\t\t\t\treturn\n\t\t\tcase 'resume':\n\t\t\t\t// a pause a player asked for, which any seated player may end; an admin's is the admin's\n\t\t\t\tif (seat && this.phase === 'game' && this.pausedBy !== undefined && this.pausedBy !== 'admin') this.pause(false)\n\t\t\t\treturn\n\t\t\tcase 'name':\n\t\t\t\tif (seat && typeof message.name === 'string') this.lobby.rename(seat, message.name)\n\t\t\t\treturn\n\t\t\tcase 'start':\n\t\t\t\tif (seat && this.phase === 'lobby') void this.startGame()\n\t\t\t\treturn\n\t\t\tcase 'setting': {\n\t\t\t\tconst { name, value } = message\n\t\t\t\tif (seat && typeof name === 'string' && typeof value === 'string') this.lobby.set(name, value)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tcase 'seatSetting': {\n\t\t\t\tconst { player, name, value } = message\n\t\t\t\tif (seat && typeof name === 'string' && typeof value === 'string' && typeof player === 'number') this.lobby.setSeat(seat, player, name, value)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tcase 'lobbyChat': {\n\t\t\t\t// before the game, from who joined: the game has a chat of its own\n\t\t\t\tconst text = typeof message.text === 'string' ? message.text.trim().slice(0, LOBBY_LINE) : ''\n\t\t\t\tif (!client.joined || this.phase !== 'lobby' || !text) return\n\t\t\t\treturn this.tellAll({ type: 'lobbyChat', ...(seat && { player: seat.player }), name: seat?.name ?? 'A watcher', text })\n\t\t\t}\n\t\t\tcase 'kick': {\n\t\t\t\tif (!seat || typeof message.player !== 'number') return\n\t\t\t\tconst out = this.lobby.voteKick(seat, message.player)\n\t\t\t\tif (!out) return\n\t\t\t\t// kept out of this lobby by the key their page keeps (`join`'s `who`), until the server starts afresh\n\t\t\t\tconst theirs = [...this.clients].find((each) => each.seat === out)\n\t\t\t\tif (theirs?.who) this.kickedOut.add(theirs.who)\n\t\t\t\treturn this.kick(out.player, 'let go by a vote of the lobby')\n\t\t\t}\n\t\t\tcase 'admin': {\n\t\t\t\tconst { key, action } = message\n\t\t\t\tif (typeof key !== 'string' || (action !== 'pause' && action !== 'resume' && action !== 'stop')) return\n\t\t\t\treturn this.admin(client, { type: 'admin', key, action })\n\t\t\t}\n\t\t\tdefault:\n\t\t\t\t// the game's: acknowledgements, and commands within the limit\n\t\t\t\tclient.toGame(text)\n\t\t}\n\t}\n\n\t/** Whether a command of this client's goes in: a bucket that fills at the rate allowed. Why not, when it does not */\n\tprivate allow(client: Client): true | Message {\n\t\treturn client.commands.take() || engineMessage('tooManyCommands')\n\t}\n\n\tprivate join(client: Client, message: Join): void {\n\t\tif (client.seat || client.watching || client.joining) return\n\t\tclient.who = message.who\n\t\tclient.name = message.name?.trim().slice(0, 24) || undefined\n\t\tif (message.who && this.kickedOut.has(message.who)) return this.enter(client, message, { to: 'refuse', reason: 'let go by a vote of the lobby' })\n\t\tconst wrong = this.doorman.wrongWire(message)\n\t\tif (wrong) {\n\t\t\tthis.log(`${message.name ?? 'someone'} was refused: protocol ${message.protocol}, this server's is ${PROTOCOL_VERSION}`, {\n\t\t\t\tname: message.name,\n\t\t\t\tprotocol: message.protocol\n\t\t\t})\n\t\t\treturn this.enter(client, message, { to: 'refuse', reason: wrong })\n\t\t}\n\t\t// a player of the game that ended, again: the lobby, with those still there in their seats (\"Again\")\n\t\tif (this.phase === 'ended' && !message.back && message.token && this.seats.some((each) => each.token === message.token)) {\n\t\t\tthis.backToLobby(message.token)\n\t\t}\n\t\tconst door = this.doorman.place(message, client.connection.address)\n\t\tif (!(door instanceof Promise)) return this.enter(client, message, door)\n\t\t// the game is asked: checked again once it has answered, as who asked may be gone or seated meanwhile\n\t\tclient.joining = true\n\t\tvoid door.then((placed) => {\n\t\t\tclient.joining = false\n\t\t\tif (this.clients.has(client) && !client.seat && !client.watching) this.enter(client, message, placed)\n\t\t})\n\t}\n\n\t/** Takes who joined where the doorman said: a seat, watching, told every seat is taken, or away with why */\n\tprivate enter(client: Client, message: Join, door: Door): void {\n\t\tif (door.to !== 'refuse') {\n\t\t\tclient.joined = true\n\t\t\tclearTimeout(client.deadline)\n\t\t}\n\t\tswitch (door.to) {\n\t\t\tcase 'seat':\n\t\t\t\tif ('id' in door) door.seat.id = door.id\n\t\t\t\tif ('rated' in door) door.seat.rated = door.rated\n\t\t\t\treturn this.sit(client, door.seat, door.name, door.named)\n\t\t\tcase 'watch':\n\t\t\t\treturn this.watch(client)\n\t\t\tcase 'full':\n\t\t\t\treturn this.tell(client, { type: 'full' })\n\t\t\tcase 'refuse':\n\t\t\t\tthis.log(`${message.name ?? 'someone'} was refused: ${door.reason}`, { name: message.name, reason: door.reason })\n\t\t\t\tthis.tell(client, { type: 'refused', reason: door.reason })\n\t\t\t\treturn client.connection.close()\n\t\t}\n\t}\n\n\t/** A seated player moves to another seat, free, in the lobby */\n\tprivate move(client: Client, player: PlayerId): void {\n\t\tif (!client.seat) return\n\t\tconst to = this.lobby.move(client.seat, player)\n\t\tif (!to) return\n\t\tclient.seat = to\n\t\tthis.tell(client, { type: 'seated', player: to.player, token: to.token! })\n\t}\n\n\t/** Seats a client, and lets go whoever had the seat before: a player back from another page. A name the game gave goes in any phase */\n\tprivate sit(client: Client, seat: Seat, name: string | undefined, named = false): void {\n\t\tconst token = seat.take(client.connection, this.phase === 'lobby' || named ? name : undefined)\n\t\tclient.seat = seat\n\t\tthis.tell(client, { type: 'seated', player: seat.player, token })\n\t\tthis.log(`${seat.name} took seat ${seat.player}`, { name: seat.name, player: seat.player })\n\t\tthis.hooks.onPlayerJoin?.(this.publicSeat(seat), this.world)\n\t\tif (this.phase === 'lobby') return this.lobby.seated()\n\t\t// back in a game: everything they may know, then what changes\n\t\tthis.match?.keep()\n\t\tthis.seatInGame(client, seat)\n\t}\n\n\t/** The game's hooks */\n\tprivate get hooks(): ServerHooks {\n\t\treturn this.options.mode.game.server ?? {}\n\t}\n\n\t/**\n\t * Who comes once the seats are taken watches, when the server lets them (`watch`): everything, the whole map without fog, every player's resources. From\n\t * the side of the world's neutral player, so that nobody's units are theirs; from the first seat's, in a world without one\n\t */\n\tprivate watch(client: Client): void {\n\t\tclient.watching = true\n\t\tconst { world, host } = this\n\t\tif (this.phase === 'lobby' || !host || !world) {\n\t\t\tthis.tell(client, { type: 'watching', player: this.seats[0].player })\n\t\t\treturn this.lobby.broadcast()\n\t\t}\n\t\tconst side = [...world.players.values()].find((player) => player.neutral)?.id ?? this.seats[0].player\n\t\tthis.tell(client, { type: 'watching', player: side })\n\t\tconst delay = this.options.watchDelay ?? 0\n\t\thost.connect(side, delay > 0 ? delayed(client.relay, delay) : client.relay, { readOnly: true, observer: true, name: client.name })\n\t\t// a host with nobody to send to stops: it goes on for who comes to watch\n\t\tif (!host.running && this.phase === 'game') host.start()\n\t}\n\n\tprivate seatInGame(client: Client, seat: Seat): void {\n\t\tconst { host } = this\n\t\tif (!host) return\n\t\tif (this.options.debug) this.world?.debugPlayers.add(seat.player)\n\t\thost.connect(seat.player, client.relay, { allow: () => this.allow(client) })\n\t\tif (!host.running && this.phase === 'game') host.start()\n\t}\n\n\tprivate leave(client: Client): void {\n\t\tif (!this.clients.delete(client)) return\n\t\tclearTimeout(client.deadline)\n\t\tclient.fromGameClosed()\n\t\tconst { seat } = client\n\t\tif (!seat || seat.connection !== client.connection) return\n\t\tseat.drop()\n\t\tthis.log(`${seat.name} left seat ${seat.player}`, { name: seat.name, player: seat.player })\n\t\tthis.hooks.onPlayerLeave?.(this.publicSeat(seat), this.world)\n\t\tconst anyone = this.seats.some((each) => each.connected)\n\t\tif (this.phase === 'lobby') this.lobby.left(seat)\n\t\telse if (this.phase === 'ended' && !anyone) this.reset()\n\t\telse if (this.phase === 'game' && !anyone) this.match?.abandon()\n\t}\n\n\t/**\n\t * A seat as everyone is told it (the lobby, the hooks)\n\t *\n\t * @internal\n\t */\n\tpublicSeat(seat: Seat): ServerSeat {\n\t\treturn this.lobby.publicSeat(seat)\n\t}\n\n\t/** Makes the world, seats everyone at it, and runs it */\n\tasync startGame(): Promise<void> {\n\t\t// a server that is stopping starts nothing more\n\t\tif (this.phase !== 'lobby' || this.draining) return\n\t\tthis.phase = 'game'\n\t\tthis.lobby.again = false\n\t\tconst server = this\n\t\tconst match = await Match.make(\n\t\t\t{\n\t\t\t\toptions: this.options,\n\t\t\t\tlogger: this.logger,\n\t\t\t\tseats: this.seats,\n\t\t\t\tnavigation: this.navigation,\n\t\t\t\tfill: () => this.lobby.fill(),\n\t\t\t\tget watching() {\n\t\t\t\t\treturn server.watching\n\t\t\t\t},\n\t\t\t\tpublicSeat: (seat) => this.publicSeat(seat),\n\t\t\t\ttellAll: (message) => this.tellAll(message),\n\t\t\t\tkick: (player, reason) => this.kick(player, reason),\n\t\t\t\tend: () => void this.endGame()\n\t\t\t},\n\t\t\tthis.settings\n\t\t)\n\t\tthis.match = match\n\t\tthis.lobby.broadcast()\n\t\tfor (const client of this.clients) {\n\t\t\tif (client.seat) this.seatInGame(client, client.seat)\n\t\t\telse if (client.watching) this.watch(client)\n\t\t}\n\t\tmatch.run()\n\t}\n\n\t/** Lets a player go for good: told why, their connection closed, and their token and id no good any more */\n\tkick(player: PlayerId, reason: string): void {\n\t\tconst held = this.lobby.seat(player)\n\t\tif (!held) return\n\t\tconst client = [...this.clients].find((each) => each.seat === held)\n\t\theld.revoke()\n\t\tthis.log(`${held.name} was let go from seat ${held.player}: ${reason}`, { name: held.name, player: held.player, reason })\n\t\tif (!client) return\n\t\tthis.tell(client, { type: 'refused', reason })\n\t\tclient.connection.close()\n\t}\n\n\t/** The game is over: the result told to everyone and kept. The game goes on being shown until everyone has left */\n\tasync endGame(): Promise<GameResult | undefined> {\n\t\tconst { match } = this\n\t\tif (!match || this.phase !== 'game') return undefined\n\t\tthis.phase = 'ended'\n\t\tthis.played++\n\t\tconst result = await match.end()\n\t\tif (!this.seats.some((seat) => seat.connected)) this.reset()\n\t\tthis.drained?.()\n\t\treturn result\n\t}\n\n\t/**\n\t * Back to the lobby after a game, with who is still there in their seats: a player of it who joins again (\"Again\",\n\t * with their seat's token) takes theirs, the seats of those gone are free\n\t */\n\tprivate backToLobby(keepToken: string): void {\n\t\t// the players' connections stay: the lobby is theirs now\n\t\tthis.dropMatch({ closeTransports: false })\n\t\tfor (const client of this.clients) {\n\t\t\tclient.toGame = () => {}\n\t\t\tclient.fromGameClosed = () => {}\n\t\t}\n\t\tthis.phase = 'lobby'\n\t\tthis.lobby.backFromGame(keepToken)\n\t\tthis.log('back to the lobby, for another game')\n\t\tthis.lobby.broadcast()\n\t}\n\n\t/** Back to the lobby, for a new game: the old one gone, every seat free */\n\tprivate reset(): void {\n\t\tthis.dropMatch()\n\t\tthis.phase = 'lobby'\n\t\tthis.lobby.reset()\n\t\tthis.kickedOut.clear()\n\t\tthis.log('back to the lobby')\n\t}\n\n\tprivate admin(client: Client, message: Extract<LobbyPlayerMessage, { type: 'admin' }>): void {\n\t\tif (!this.options.adminKey || !sameSecret(message.key, this.options.adminKey))\n\t\t\treturn this.tell(client, { type: 'refused', reason: 'not an admin' })\n\t\tif (message.action === 'stop') {\n\t\t\tvoid this.endGame()\n\t\t\treturn\n\t\t}\n\t\tthis.pause(message.action === 'pause')\n\t}\n\n\t/**\n\t * Pauses the game for everyone, or lets it go on. A player's pause (`by`, from the `pause` command) ends by itself\n\t * after its seconds, or when a seated player says `resume`; an admin's, when the admin says so\n\t */\n\tpause(paused: boolean, by?: { player: PlayerId; seconds: number }): void {\n\t\tthis.match?.pause(paused, by)\n\t}\n\n\t/**\n\t * Stops the server: every connection closed. With `drain`, it first takes no new games (its health says so, for a\n\t * load balancer to send nobody new) and waits for the running one to end, up to so many seconds: then it is ended\n\t * (no winner, the players told the server stopped) and the server closes\n\t */\n\tasync close(options: { drain?: number } = {}): Promise<void> {\n\t\tif (options.drain && !this.draining) {\n\t\t\tthis.draining = true\n\t\t\tthis.log(`stopping: no new games, ${this.phase === 'game' ? `the running one has ${options.drain} s to end` : 'none running'}`)\n\t\t\tif (this.phase === 'game') {\n\t\t\t\tlet limit: ReturnType<typeof setTimeout> | undefined\n\t\t\t\tconst over = await new Promise<boolean>((resolve) => {\n\t\t\t\t\tthis.drained = () => resolve(true)\n\t\t\t\t\tlimit = setTimeout(() => resolve(false), options.drain! * 1000)\n\t\t\t\t})\n\t\t\t\tclearTimeout(limit)\n\t\t\t\tthis.drained = undefined\n\t\t\t\tif (!over) {\n\t\t\t\t\tif (this.match) this.match.failed = 'The server stopped'\n\t\t\t\t\tawait this.endGame()\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tthis.dropMatch()\n\t\tawait this.source.close()\n\t}\n}\n","import type { Logger } from '#engine/log'\nimport type { NavigationBuilds } from '#engine/navigation/builds'\nimport { randomSeed } from '#engine/session/seed'\nimport type { BrainDef } from '#engine/ai/brain'\nimport type { AiOptions } from '#engine/ai/options'\nimport type { PlayerId } from '#engine/entity'\nimport { createWorldFromMap, type GameModule, type ServerHooks, createRegistry } from '#engine/game'\nimport { generateMap, type GeneratorOptions, type MapGenerator } from '#engine/generate'\nimport { type GameMap, MAP_CREEPS, PLAYER_COLORS } from '#engine/map'\nimport type { Vec2 } from '#engine/math'\nimport type { World } from '#engine/world'\nimport { seatSettingsOf } from './seatSettings'\n\n/**\n * A seat a mode has: a player of the world, by default for whoever joins, or for the computer\n *\n * @beta\n */\nexport interface ModeSeat {\n\tplayer: PlayerId\n\tname: string\n\t/** Where it starts, for a computer player to know where its opponents are */\n\tbase?: Vec2\n\t/** The team it plays for, shown in the lobby: the map's, for a mode made from a map */\n\tteam?: number\n\t/**\n\t * Settings of the seat that are not the lobby's to choose, by name (`ServerHooks.seatSettings`): the race a map gives\n\t * its player (`MapPlayer.race`)\n\t */\n\tfixed?: Record<string, string>\n\t/**\n\t * Settings of the seat chosen for it until someone chooses otherwise, by name: its team and its colour on the map\n\t * (`seatLooksOf`)\n\t */\n\tpreset?: Record<string, string>\n}\n\n/**\n * What a server runs: a game module, the seats of a game of it, and how its world is made. Each game starts from a\n * world made anew, with the lobby's settings\n *\n * @beta\n */\nexport interface GameMode {\n\tid: string\n\tname: string\n\tgame: GameModule\n\tseats: ModeSeat[]\n\t/**\n\t * What can be chosen for each seat in the lobby, in place of what the game offers (`seatSettingsOf(game)`): the races\n\t * a map offers (`GameMap.races`)\n\t */\n\tseatSettings?: NonNullable<ServerHooks['seatSettings']>\n\t/**\n\t * Makes the world of a game. `context.log` is the server's logger, for the world's own to be made under;\n\t * `context.seats` what was chosen for each seat in the lobby, by player (`ServerHooks.seatSettings`): a race among\n\t * them, in a game with races (`seatSettingsOf`), for the world to give each player (`MapWorldOptions.races`);\n\t * `context.navigation` who builds its navigation (the server's worker thread: `GameServerOptions.navigationWorker`)\n\t */\n\tmakeWorld(\n\t\tsettings: Record<string, string>,\n\t\tcontext?: { log?: Logger; seats?: Readonly<Record<PlayerId, Readonly<Record<string, string>>>>; navigation?: NavigationBuilds }\n\t): Promise<World>\n\t/**\n\t * How a computer player in a seat plays: with what was chosen for that seat in the lobby (`settings`, from\n\t * `ServerHooks.seatSettings`: a difficulty, a race). No computer players without\n\t */\n\tai?(seat: ModeSeat, seats: readonly ModeSeat[], settings: Readonly<Record<string, string>>): AiOptions\n\t/**\n\t * Which brain plays a seat of the computer (`defineBrain`), or its id (one the game lists in `GameModule.brains`, or\n\t * the engine's `core.melee`): one for every seat, or one by seat, with what was chosen for it in the lobby. The first\n\t * brain the game lists when left out\n\t */\n\tbrain?: ModeBrain\n}\n\n/**\n * Which brain plays a seat of the computer on a server (`GameMode.brain`): a brain, its id, or one chosen by seat\n *\n * @beta\n */\nexport type ModeBrain =\n\tBrainDef | string | ((seat: ModeSeat, seats: readonly ModeSeat[], settings: Readonly<Record<string, string>>) => BrainDef | string | undefined)\n\n/**\n * A computer player that goes for everyone else's base: what a seat of the computer does when the server says nothing\n * else. As hard as its seat's `difficulty` says and of the `personality` it says, where the game offers them to choose\n * (`ServerHooks.seatSettings`, `seatSettingsOf`); a name the game does not have is left to the defaults\n *\n * @beta\n */\nexport const againstTheRest = (seat: ModeSeat, seats: readonly ModeSeat[], settings: Readonly<Record<string, string>> = {}): AiOptions => ({\n\tenemyBases: seats.filter((other) => other !== seat && other.base).map((other) => other.base!),\n\t...(settings.difficulty && { difficulty: settings.difficulty }),\n\t...(settings.personality && { personality: settings.personality })\n})\n\n/**\n * The race chosen for each seat in the lobby, by player: none where nobody chose one, or the game has no races\n *\n * @beta\n */\nexport function racesOfSeats(seats: Readonly<Record<PlayerId, Readonly<Record<string, string>>>> | undefined): Record<PlayerId, string> | undefined {\n\tif (!seats) return undefined\n\tconst races: Record<PlayerId, string> = {}\n\tfor (const [player, settings] of Object.entries(seats)) if (settings.race !== undefined) races[Number(player)] = settings.race\n\treturn Object.keys(races).length ? races : undefined\n}\n\n/**\n * The seats a map has room for: a seat for each of its players with a start, the creeps apart\n *\n * @beta\n */\nexport const seatsOf = (map: GameMap): ModeSeat[] =>\n\tmap.players\n\t\t.filter((player) => player.start && player.id !== MAP_CREEPS)\n\t\t.map((player) => ({\n\t\t\tplayer: player.id,\n\t\t\tname: player.name,\n\t\t\tbase: player.start,\n\t\t\tteam: player.team,\n\t\t\t// its team and colour as the map has them, until the lobby chooses others\n\t\t\tpreset: { team: String(player.team), color: String(nearestColor(player.color) + 1) },\n\t\t\t...(player.race !== undefined && { fixed: { race: player.race } })\n\t\t}))\n\n/**\n * The names of the players' colours (`PLAYER_COLORS`), in their order, as a lobby shows them\n *\n * @beta\n */\nexport const PLAYER_COLOR_NAMES = [\n\t'Blue',\n\t'Red',\n\t'Teal',\n\t'Purple',\n\t'Yellow',\n\t'Orange',\n\t'Green',\n\t'Pink',\n\t'Grey',\n\t'Light blue',\n\t'Dark green',\n\t'Brown',\n\t'Maroon',\n\t'Navy',\n\t'Turquoise',\n\t'Violet',\n\t'Wheat',\n\t'Peach',\n\t'Mint',\n\t'Lavender',\n\t'Coal',\n\t'Snow',\n\t'Emerald',\n\t'Peanut'\n] as const\n\n/** The colour of the players' (`PLAYER_COLORS`) nearest one, by its index */\nfunction nearestColor(color: readonly [number, number, number]): number {\n\tlet best = 0\n\tlet least = Infinity\n\tPLAYER_COLORS.forEach((each, index) => {\n\t\tconst [r, g, b] = [each[0] - color[0], each[1] - color[1], each[2] - color[2]]\n\t\tconst apart = r * r + g * g + b * b\n\t\tif (apart < least) {\n\t\t\tleast = apart\n\t\t\tbest = index\n\t\t}\n\t})\n\treturn best\n}\n\n/**\n * What a lobby offers to choose for each seat of a map's game beside the game's own: a team, of as many as there are\n * seats, and a colour of the players' (`PLAYER_COLORS`, by `PLAYER_COLOR_NAMES`), no two seats the same. Each seat has\n * the map's until chosen otherwise (`ModeSeat.preset`)\n *\n * @beta\n */\nexport function seatLooksOf(seats: readonly ModeSeat[]): NonNullable<ServerHooks['seatSettings']> {\n\tconst teams = Math.max(seats.length, ...seats.map((seat) => seat.team ?? 0))\n\tconst numbers = (count: number) => Array.from({ length: count }, (_, i) => String(i + 1))\n\treturn {\n\t\tteam: {\n\t\t\tlabel: 'Team',\n\t\t\toptions: numbers(teams),\n\t\t\tlabels: Object.fromEntries(numbers(teams).map((id) => [id, `Team ${id}`])),\n\t\t\tdefault: '1'\n\t\t},\n\t\tcolor: {\n\t\t\tlabel: 'Colour',\n\t\t\toptions: numbers(PLAYER_COLORS.length),\n\t\t\tlabels: Object.fromEntries(PLAYER_COLOR_NAMES.map((name, i) => [String(i + 1), name])),\n\t\t\tdefault: '1',\n\t\t\tunique: true\n\t\t}\n\t}\n}\n\n/**\n * The teams and colours chosen in the lobby (`seatLooksOf`), by player, where they are not the map's: what\n * `MapWorldOptions.teams` and `colors` take\n *\n * @beta\n */\nexport function looksOfSeats(\n\tseats: Readonly<Record<PlayerId, Readonly<Record<string, string>>>> | undefined,\n\tmodeSeats: readonly ModeSeat[]\n): { teams?: Record<PlayerId, number>; colors?: Record<PlayerId, [number, number, number]> } {\n\tconst teams: Record<PlayerId, number> = {}\n\tconst colors: Record<PlayerId, [number, number, number]> = {}\n\tfor (const seat of modeSeats) {\n\t\tconst chosen = seats?.[seat.player]\n\t\tif (!chosen) continue\n\t\tconst team = Number(chosen.team)\n\t\tif (chosen.team !== undefined && chosen.team !== seat.preset?.team && Number.isInteger(team) && team > 0) teams[seat.player] = team\n\t\tconst color = PLAYER_COLORS[Number(chosen.color) - 1]\n\t\tif (chosen.color !== undefined && chosen.color !== seat.preset?.color && color) colors[seat.player] = [...color]\n\t}\n\treturn { ...(Object.keys(teams).length && { teams }), ...(Object.keys(colors).length && { colors }) }\n}\n\n/**\n * How a mode is made from a map (`mapMode`)\n *\n * @beta\n */\nexport interface MapModeOptions {\n\t/** The seats a player may take. One for each player of the map with a start when left out */\n\tseats?: ModeSeat[]\n\t/** How a computer player in a seat plays: against everyone else's base when left out, `null` for none */\n\tai?: GameMode['ai'] | null\n\t/** Which brain plays a seat of the computer: the first the game lists when left out */\n\tbrain?: ModeBrain\n\t/**\n\t * What the game does once a world is made from the map, before anyone plays (the game's `start`, as a page or\n\t * `startServer` runs it): the units at each start, the first wave sent. Nothing when left out\n\t */\n\tstart?: (world: World, context: { map: GameMap; settings: Record<string, string> }) => void | Promise<void>\n}\n\n/**\n * A game on a map of it: a seat for each player the map has a start for, and a world made from the map for each game\n *\n * @beta\n */\nexport function mapMode(game: GameModule, map: GameMap, options: MapModeOptions = {}): GameMode {\n\tconst { start } = options\n\tconst seats = options.seats ?? seatsOf(map)\n\treturn {\n\t\tid: `map:${map.name}`,\n\t\tname: map.name,\n\t\tgame,\n\t\tseats,\n\t\t// the game's (a race among the map's), and a team and a colour\n\t\tseatSettings: { ...seatSettingsOf(game, map.races), ...seatLooksOf(seats) },\n\t\tasync makeWorld(settings, context) {\n\t\t\tconst made = structuredClone(map)\n\t\t\tconst world = await createWorldFromMap({\n\t\t\t\tmap: made,\n\t\t\t\tgame,\n\t\t\t\tseed: randomSeed(),\n\t\t\t\tlog: context?.log,\n\t\t\t\traces: racesOfSeats(context?.seats),\n\t\t\t\t...looksOfSeats(context?.seats, seats),\n\t\t\t\t...(context?.navigation && { navigation: { builds: context.navigation } })\n\t\t\t})\n\t\t\ttry {\n\t\t\t\tawait start?.(world, { map: made, settings })\n\t\t\t} catch (error) {\n\t\t\t\tworld.dispose()\n\t\t\t\tthrow error\n\t\t\t}\n\t\t\treturn world\n\t\t},\n\t\t...(options.ai !== null && { ai: options.ai ?? againstTheRest }),\n\t\t...(options.brain !== undefined && { brain: options.brain })\n\t}\n}\n\n/**\n * How a mode on a generated map is made (`generatedMode`): the generator's options (the seed a new one each time\n * when left out), and what a mode on a map takes\n *\n * @beta\n */\nexport interface GeneratedModeOptions extends Omit<GeneratorOptions, 'seed'>, MapModeOptions {\n\tseed?: number\n}\n\n/**\n * A game on a map made by a generator (`generateMap`): the map made once, with the seed given or a new one, and a\n * game of it as on any map (`mapMode`): a seat for each start, the world made from it for each game. For a room of a\n * random map (`RoomKind.room` as a function: a new map for each room made)\n *\n * @beta\n */\nexport function generatedMode(game: GameModule, generator: MapGenerator, options: GeneratedModeOptions): GameMode & { map: GameMap } {\n\tconst { start, ai, brain, seats, ...generating } = options\n\t// a new map each time a seed is not said: the server's own, not the simulation's\n\tconst seed = options.seed ?? 1 + Math.floor(Math.random() * 999999)\n\tconst { map } = generateMap(generator, { ...generating, seed, name: options.name ?? `A random map (${seed})` }, createRegistry(game))\n\tconst mode = mapMode(game, map, { start, ai, brain, seats })\n\treturn { ...mode, id: `generated:${map.generated?.seed ?? seed}`, map }\n}\n","import type { Logger } from '#engine/log'\nimport type { BrainDef } from '#engine/ai/brain'\nimport type { GameModule } from '#engine/game'\nimport type { GameMap } from '#engine/map'\nimport type { World } from '#engine/world'\nimport { GameServer, type GameServerOptions } from './gameServer'\nimport { againstTheRest, type GameMode, mapMode, type ModeSeat, seatsOf } from './mode'\n\n/**\n * What a game server is started with: the game, where its worlds come from, and the server's own options\n *\n * @beta\n */\nexport interface StartServerOptions extends Omit<GameServerOptions, 'mode' | 'port'> {\n\t/** The rules of the game (`GameModule`): of a game defined whole, its `rules`. A server never sees a game's view */\n\tgame: GameModule\n\t/** The port to listen on: 8787 when left out, 0 for any free one */\n\tport?: number\n\t/** A map of the game: each game is played on a world made from it, with a seat for each of its players with a start */\n\tmap?: GameMap\n\t/** A world made in code for each game, with the lobby's settings: in the place of a map. Says its `seats` too */\n\tmakeWorld?: (settings: Record<string, string>, context?: { log?: Logger }) => World | Promise<World>\n\t/** The seats a player may take. Those of the map when left out */\n\tseats?: ModeSeat[]\n\t/** What the game does once a world is made, before anyone plays: a mission given, the first wave sent */\n\tstart?: (world: World, context: { map?: GameMap; settings: Record<string, string> }) => void | Promise<void>\n\t/**\n\t * How a computer player in a seat plays (`ai` says which seats, and those nobody took are its too): its options by\n\t * seat (`GameMode.ai`), or the brain that plays every seat of the computer (`defineBrain`), against everyone else's\n\t * base; the first brain the game lists, against everyone else's base when left out; `null` for\n\t * a game the computer does not play. `mode` for a brain by seat (`GameMode.brain`)\n\t */\n\tcomputer?: GameMode['ai'] | BrainDef | null\n\t/** A mode put together elsewhere, in the place of all of the above but the game */\n\tmode?: GameMode\n\t/** What the mode is called. The map's name, or the game's */\n\tname?: string\n}\n\n/**\n * Starts a server of a game, and resolves once it listens: a lobby that seats who joins, then games of it, one after\n * the other, each on a world made anew.\n *\n * const server = await startServer({ game: rules, map, start: (world) => startMission(world) })\n * // server.port, server.close()\n *\n * The parts are there on their own: `GameServer.start` with a `GameMode`, `mapMode`, `SessionHost`\n *\n * @beta\n */\nexport async function startServer(options: StartServerOptions): Promise<GameServer> {\n\tconst { game, port = 8787, map, makeWorld, seats, start, computer, mode: givenMode, name, ...server } = options\n\treturn GameServer.start({ ...server, port, mode: givenMode ?? modeOf(game, { map, makeWorld, seats, start, computer, name }) })\n}\n\n/** The mode a server runs, from what it was started with */\nfunction modeOf(game: GameModule, options: Pick<StartServerOptions, 'map' | 'makeWorld' | 'seats' | 'start' | 'computer' | 'name'>): GameMode {\n\tconst { map, makeWorld, start } = options\n\tif (!map && !makeWorld) throw new Error('startServer: a server needs a map of the game, or a makeWorld, to make the world of each game from')\n\tconst seats = options.seats ?? (map ? seatsOf(map) : undefined)\n\tif (!seats?.length) {\n\t\tthrow new Error(\n\t\t\tmap\n\t\t\t\t? `startServer: the map \"${map.name}\" has no player with a start. Give its players starts, or say the seats`\n\t\t\t\t: 'startServer: say the seats players may take (seats), with a world made in code'\n\t\t)\n\t}\n\tconst { computer } = options\n\t// a brain, or the options of each seat\n\tconst brain = computer && typeof computer === 'object' ? computer : undefined\n\tconst ai = brain ? againstTheRest : (computer as GameMode['ai'] | null | undefined)\n\tconst base = map && !makeWorld ? mapMode(game, map, { seats, ai, brain }) : undefined\n\treturn {\n\t\tid: base?.id ?? game.id,\n\t\tname: options.name ?? base?.name ?? game.name,\n\t\tgame,\n\t\tseats,\n\t\tasync makeWorld(settings, context) {\n\t\t\tconst world = base ? await base.makeWorld(settings, context) : await makeWorld!(settings, context)\n\t\t\ttry {\n\t\t\t\tawait start?.(world, { map, settings })\n\t\t\t} catch (error) {\n\t\t\t\tworld.dispose()\n\t\t\t\tthrow error\n\t\t\t}\n\t\t\treturn world\n\t\t},\n\t\t...(ai !== null && { ai: ai ?? againstTheRest }),\n\t\t...(brain && { brain })\n\t}\n}\n","import type { GameKindListing, GameListing } from '#engine/session/protocol'\nimport { GameServer, type GameServerOptions } from './gameServer'\nimport type { Logger } from '#engine/log'\nimport { type ServerLog, serverLogger } from './log'\nimport { answerOps, type ServerHttpOptions, type ServerMetrics } from './ops'\nimport { type Connection, type ConnectionSource, type SocketServer, startSocketServer } from './websocket'\n\n/**\n * A room's game server: all of `GameServerOptions` but what the port they share says once for all\n *\n * @beta\n */\nexport type RoomOptions = Omit<GameServerOptions, 'port' | 'compress' | 'idleTimeout' | 'maxBuffered' | 'maxConnections' | 'http' | 'log'>\n\n/**\n * A kind of game a player may make a room of (features-plan H2): what a \"New game\" makes (`POST /games`), with the\n * settings asked for (the map) where the game offers them\n *\n * @beta\n */\nexport interface RoomKind {\n\t/** What a list calls it */\n\tname: string\n\t/**\n\t * The room it makes: the same options for each, or a function that makes a room's options each time one is made,\n\t * with the settings asked for (a random map for each: `generatedMode`). A kind of a function is listed and matched\n\t * by a room it makes once, at first\n\t */\n\troom: RoomOptions | ((settings: Record<string, string>) => RoomOptions)\n}\n\n/** @beta */\nexport interface GameRoomsOptions {\n\t/** The port they share: 8787 when left out, 0 for any free one */\n\tport?: number\n\t/**\n\t * The rooms, by name: each a game server of its own (a lobby and its games), reached at its name\n\t * (`ws://host:8787/arena`). A name is letters, digits, `-` and `_`. They are there for as long as the process is\n\t */\n\trooms: Record<string, RoomOptions>\n\t/**\n\t * What players may make rooms of, by id (`POST /games` with `{ kind, settings }`, a \"New game\"): each room made is\n\t * named after its kind and closed once nobody has been in it for `emptyFor`. None when left out\n\t */\n\tkinds?: Record<string, RoomKind>\n\t/** How many rooms players may have made at once, those matchmaking made with them: 16 when left out */\n\tmaxMade?: number\n\t/** Seconds a made room may stay empty before it is closed: 60 when left out. A room nobody came to counts from when it was made */\n\temptyFor?: number\n\t/**\n\t * Matchmaking (features-plan H3): players wait in a queue for a kind (`kinds`), at the address `queue`\n\t * (`ws://host:8787/queue`, `{ type: 'queue', kind, name }`); once as many wait as its seats for players, it makes a\n\t * room of the kind, which lets in only them, and hands each a ticket to it (`{ type: 'matched', room, ticket }`). On\n\t * where there are kinds, unless `false`; `path` for another address\n\t */\n\tmatchmaking?: boolean | { path?: string }\n\tcompress?: boolean\n\tidleTimeout?: number\n\tmaxBuffered?: number\n\t/**\n\t * Connections open at once to all the rooms and the queue, at most (`SocketServerOptions.maxConnections`). 1024 when\n\t * left out\n\t *\n\t * @beta\n\t */\n\tmaxConnections?: number\n\t/**\n\t * Seconds a connection to the queue has to say what it waits for (`queue`) before it is closed. 10 when left out;\n\t * a room's own connections have their room's (`GameServerOptions.joinWithin`)\n\t *\n\t * @beta\n\t */\n\tjoinWithin?: number\n\t/** Where they all say what they do, each line with its room */\n\tlog?: ServerLog\n\t/** The health and numbers addresses, for all of them: the numbers of each by its room */\n\thttp?: ServerHttpOptions\n\t/** Where ratings are kept, for every room's players (`GameServerOptions.ratings`), where a room says none of its own */\n\tratings?: GameServerOptions['ratings']\n}\n\n/** What a room is reached at: the first part of the address a connection asked for */\nconst roomOf = (url: string) => new URL(url, 'ws://server').pathname.split('/')[1] ?? ''\n\n/** A name for a room: letters, digits, `-` and `_` */\nconst isRoomName = (name: string) => /^[\\w-]+$/.test(name)\n\n/** What an address that asked for games too often is told */\nconst TOO_OFTEN = 'too many games asked for: wait a minute'\n\n/** The most a body of a request to make a room may be: a kind and a few settings */\nconst MAX_BODY = 4096\n\n/**\n * Where a room's game server gets its connections (`ConnectionSource`): those the shared port hands it for the room's\n * name. Its server is told of a connection closing after the room has let go of it, and its `close` closes those in\n * the room, never the port\n */\nclass RoomConnections implements ConnectionSource {\n\tprivate handler: (connection: Connection) => void = () => {}\n\tprivate readonly open = new Set<Connection>()\n\n\tconstructor(readonly port: number) {}\n\n\tonConnection(handler: (connection: Connection) => void): void {\n\t\tthis.handler = handler\n\t}\n\n\t/** A connection to the room, handed to its server */\n\thand(connection: Connection): void {\n\t\t// what the room's server is told of its closing, after the room has let go of it\n\t\tlet closed = () => {}\n\t\tconst theirs: Connection = {\n\t\t\tget url() {\n\t\t\t\treturn connection.url\n\t\t\t},\n\t\t\tget address() {\n\t\t\t\treturn connection.address\n\t\t\t},\n\t\t\tget bytesSent() {\n\t\t\t\treturn connection.bytesSent\n\t\t\t},\n\t\t\tget compressed() {\n\t\t\t\treturn connection.compressed\n\t\t\t},\n\t\t\tsend: (message) => connection.send(message),\n\t\t\tonMessage: (next) => connection.onMessage(next),\n\t\t\tonClose: (next) => void (closed = next),\n\t\t\tclose: () => connection.close()\n\t\t}\n\t\tthis.open.add(theirs)\n\t\tconnection.onClose(() => {\n\t\t\tthis.open.delete(theirs)\n\t\t\tclosed()\n\t\t})\n\t\tthis.handler(theirs)\n\t}\n\n\tasync close(): Promise<void> {\n\t\tfor (const connection of [...this.open]) connection.close()\n\t}\n}\n\n/**\n * Many games in a process (S58): game servers that share a port, each a room reached at its name, as a server of its\n * own would be at its port. One process, one thread: each room's games step in turn, so the rooms of a process are as\n * many as its ticks have time for (`/metrics` says how long they take). A connection to a room there is not is told so\n * and closed. Rooms are added in code (`add`), or made by players of the kinds it offers (`kinds`, `POST /games`) and\n * closed once empty\n *\n * @beta\n */\nexport class GameRooms {\n\tprivate readonly servers = new Map<string, GameServer>()\n\t/** Where each room's connections come from, by its name */\n\tprivate readonly doors = new Map<string, RoomConnections>()\n\t/** The rooms players made, and since when each has been empty (nobody connected): what closes them */\n\tprivate readonly made = new Map<string, { kind: string; emptySince: number; joined: boolean; matched: boolean }>()\n\t/** When each address last asked for rooms (`POST /games`): a few a minute each */\n\tprivate readonly askedBy = new Map<string, number[]>()\n\tprivate sweeper?: ReturnType<typeof setInterval>\n\tprivate nextMade = 1\n\t/** Who waits to be matched, by kind, in the order they came */\n\tprivate readonly queues = new Map<string, { connection: Connection; name?: string }[]>()\n\n\tprivate constructor(\n\t\tprivate readonly socket: SocketServer,\n\t\tprivate readonly options: GameRoomsOptions,\n\t\tprivate readonly logger: Logger\n\t) {}\n\n\t/** The rooms there are now, by name */\n\tget rooms(): ReadonlyMap<string, GameServer> {\n\t\treturn this.servers\n\t}\n\n\t/** Starts the rooms, and resolves once they listen */\n\tstatic async start(options: GameRoomsOptions): Promise<GameRooms> {\n\t\tfor (const name of Object.keys(options.rooms)) {\n\t\t\tif (!isRoomName(name)) throw new Error(`GameRooms: \"${name}\" is no name for a room: letters, digits, - and _`)\n\t\t}\n\t\tconst logger = serverLogger(options.log)\n\t\tlet rooms: GameRooms | undefined\n\t\tconst socket = await startSocketServer({\n\t\t\tport: options.port ?? 8787,\n\t\t\tcompress: options.compress,\n\t\t\tidleTimeout: options.idleTimeout,\n\t\t\tmaxBuffered: options.maxBuffered,\n\t\t\tmaxConnections: options.maxConnections,\n\t\t\tlog: logger,\n\t\t\tonRequest: (request, response) => {\n\t\t\t\tif (!rooms) return\n\t\t\t\tif (rooms.answerMake(request, response)) return\n\t\t\t\tconst answered = answerOps(\n\t\t\t\t\trequest,\n\t\t\t\t\tresponse,\n\t\t\t\t\toptions.http,\n\t\t\t\t\t() => ({ draining: rooms!.draining, rooms: rooms!.metrics() }),\n\t\t\t\t\t() => ({ games: rooms!.listing(), kinds: rooms!.kinds() }),\n\t\t\t\t\t{ ratings: options.ratings, modes: rooms.modes() }\n\t\t\t\t)\n\t\t\t\tif (answered) return\n\t\t\t\tresponse.writeHead(426, { 'content-type': 'text/plain' })\n\t\t\t\tresponse.end(`Game rooms: connect with a WebSocket to one of ${[...rooms.servers.keys()].join(', ')}\\n`)\n\t\t\t}\n\t\t})\n\t\trooms = new GameRooms(socket, options, logger)\n\t\tsocket.onConnection((connection) => rooms!.connect(connection))\n\t\ttry {\n\t\t\tfor (const [name, room] of Object.entries(options.rooms)) await rooms.add(name, room)\n\t\t} catch (error) {\n\t\t\tawait rooms.close()\n\t\t\tthrow error\n\t\t}\n\t\t// the rooms players made, closed once empty a while\n\t\tif (options.kinds && Object.keys(options.kinds).length) rooms.sweeper = setInterval(() => void rooms!.sweep(), 1000)\n\t\treturn rooms\n\t}\n\n\t/** A kind it offers, by id: its own, never what every object has (`constructor`), whatever a page sends */\n\tprivate kind(id: string): RoomKind | undefined {\n\t\tconst { kinds } = this.options\n\t\treturn kinds && Object.hasOwn(kinds, id) ? kinds[id] : undefined\n\t}\n\n\t/** The address of the queue, or none where there is no matchmaking */\n\tprivate get queuePath(): string | undefined {\n\t\tconst { matchmaking, kinds } = this.options\n\t\tif (matchmaking === false || !kinds || !Object.keys(kinds).length) return undefined\n\t\treturn (typeof matchmaking === 'object' && matchmaking.path) || 'queue'\n\t}\n\n\t/** A connection to a room: handed to its game server, or told there is no such room. The queue's, to the queue */\n\tprivate connect(connection: Connection): void {\n\t\tconst name = roomOf(connection.url)\n\t\tif (name === this.queuePath) return this.waitInQueue(connection)\n\t\tconst door = this.doors.get(name)\n\t\tif (!door) {\n\t\t\tconnection.send(JSON.stringify({ type: 'refused', reason: `there is no room \"${name}\"` }))\n\t\t\treturn connection.close()\n\t\t}\n\t\tdoor.hand(connection)\n\t}\n\n\t/** Opens a room at a name, as `rooms` does: in code, while the rooms run */\n\tasync add(name: string, room: RoomOptions): Promise<GameServer> {\n\t\tif (!isRoomName(name)) throw new Error(`GameRooms: \"${name}\" is no name for a room: letters, digits, - and _`)\n\t\tif (this.servers.has(name)) throw new Error(`GameRooms: there is a room \"${name}\" already`)\n\t\t// what comes to its name\n\t\tconst door = new RoomConnections(this.socket.port)\n\t\tthis.doors.set(name, door)\n\t\tconst server = await GameServer.attach(door, {\n\t\t\t...room,\n\t\t\tratings: room.ratings ?? this.options.ratings,\n\t\t\tlog: this.logger.child({ context: () => ({ fields: { room: name } }) })\n\t\t})\n\t\tthis.servers.set(name, server)\n\t\treturn server\n\t}\n\n\t/** Closes a room and lets go of its name: those in it are let go */\n\tasync remove(name: string): Promise<void> {\n\t\tconst server = this.servers.get(name)\n\t\tif (!server) return\n\t\tthis.servers.delete(name)\n\t\tthis.made.delete(name)\n\t\tawait server.close()\n\t\tthis.doors.delete(name)\n\t}\n\n\t/**\n\t * Makes a room of a kind it offers (`kinds`), with the settings asked for: what `POST /games` does. Its name is the\n\t * kind's and a number. Refused (with why) for a kind it does not offer, too many made, or while it stops\n\t */\n\tasync make(kind: string, settings: Record<string, string> = {}, seat?: RoomOptions['seat']): Promise<{ room: string } | { refused: string }> {\n\t\tconst offered = this.kind(kind)\n\t\tif (!offered) return { refused: `there is no kind of game \"${kind}\"` }\n\t\tif (this.draining) return { refused: 'the server is stopping: it takes no new games' }\n\t\t// what matchmaking makes counts as well: waiting in the queue is no way past how many there may be (RV38)\n\t\tif (this.made.size >= (this.options.maxMade ?? 16)) return { refused: 'there are as many games as there may be: join one' }\n\t\tlet name = `${kind}-${this.nextMade++}`\n\t\twhile (this.servers.has(name)) name = `${kind}-${this.nextMade++}`\n\t\tthis.made.set(name, { kind, emptySince: Date.now(), joined: false, matched: !!seat })\n\t\ttry {\n\t\t\tconst room = typeof offered.room === 'function' ? offered.room(settings) : offered.room\n\t\t\tawait this.add(name, { ...room, settings: { ...room.settings, ...settings }, ...(seat && { seat }) })\n\t\t} catch (error) {\n\t\t\tthis.made.delete(name)\n\t\t\tthrow error\n\t\t}\n\t\tthis.logger.info('server', `made the room ${name} (${offered.name})`)\n\t\treturn { room: name }\n\t}\n\n\t/** A room a kind makes, as its listing and matchmaking read it: the one it makes, or one it made once (a kind of a function) */\n\tprivate readonly samples = new Map<string, RoomOptions>()\n\tprivate sampleOf(kind: string): RoomOptions {\n\t\tconst { room } = this.kind(kind)!\n\t\tif (typeof room !== 'function') return room\n\t\tlet sample = this.samples.get(kind)\n\t\tif (!sample) this.samples.set(kind, (sample = room({})))\n\t\treturn sample\n\t}\n\n\t/** How many of a kind's seats are for players: those the computer does not play */\n\tprivate seatsFor(kind: string): number {\n\t\tconst room = this.sampleOf(kind)\n\t\treturn room.mode.seats.filter((seat) => !room.ai?.includes(seat.player)).length\n\t}\n\n\t/** A connection to the queue: it says the kind it waits for, is told how many wait, and its ticket once matched */\n\tprivate waitInQueue(connection: Connection): void {\n\t\tlet waitingFor: string | undefined\n\t\tconst leave = () => {\n\t\t\tif (!waitingFor) return\n\t\t\tconst queue = this.queues.get(waitingFor) ?? []\n\t\t\tconst at = queue.findIndex((each) => each.connection === connection)\n\t\t\tif (at >= 0) queue.splice(at, 1)\n\t\t\tthis.tellQueue(waitingFor)\n\t\t\twaitingFor = undefined\n\t\t}\n\t\t// one that never says what it waits for is let go (RV38)\n\t\tconst within = this.options.joinWithin ?? 10\n\t\tconst deadline = Number.isFinite(within) ? setTimeout(() => !waitingFor && connection.close(), Math.max(0, within) * 1000) : undefined\n\t\tdeadline?.unref?.()\n\t\tconnection.onClose(() => {\n\t\t\tclearTimeout(deadline)\n\t\t\tleave()\n\t\t})\n\t\tconnection.onMessage((text) => {\n\t\t\tlet message: { type?: unknown; kind?: unknown; name?: unknown }\n\t\t\ttry {\n\t\t\t\tmessage = JSON.parse(text)\n\t\t\t} catch {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (message.type === 'leave') return leave()\n\t\t\tif (message.type !== 'queue') return\n\t\t\tconst kind = String(message.kind ?? '')\n\t\t\tif (!this.kind(kind)) return connection.send(JSON.stringify({ type: 'refused', reason: `there is no kind of game \"${kind}\"` }))\n\t\t\t// waiting to be matched asks for a game as making one does, from the same few an address has (RV38)\n\t\t\tif (this.askedTooOften(connection.address)) return connection.send(JSON.stringify({ type: 'refused', reason: TOO_OFTEN }))\n\t\t\tclearTimeout(deadline)\n\t\t\tleave()\n\t\t\twaitingFor = kind\n\t\t\tconst queue = this.queues.get(kind) ?? []\n\t\t\tthis.queues.set(kind, queue)\n\t\t\tqueue.push({ connection, ...(typeof message.name === 'string' && { name: message.name.slice(0, 24) }) })\n\t\t\t// whatever goes wrong making the room is told to who waits, and never stops the process\n\t\t\tthis.match(kind).catch((error: unknown) => this.logger.error('server', 'matchmaking failed', { error: String(error) }))\n\t\t})\n\t}\n\n\t/**\n\t * Whether an address has asked for games too often to ask again now: a few a minute each, made (`POST /games`) or\n\t * waited for in the queue, so that nobody fills every place there is by asking over and over. Counted when it has not\n\t */\n\tprivate askedTooOften(address: string): boolean {\n\t\tconst now = Date.now()\n\t\tconst recent = (this.askedBy.get(address) ?? []).filter((at) => now - at < 60_000)\n\t\tif (recent.length >= 3) return true\n\t\trecent.push(now)\n\t\tthis.askedBy.set(address, recent)\n\t\tif (this.askedBy.size > 1024) for (const [each, times] of this.askedBy) if (times.every((at) => now - at >= 60_000)) this.askedBy.delete(each)\n\t\treturn false\n\t}\n\n\t/** Tells everyone waiting for a kind how many wait, of how many it takes */\n\tprivate tellQueue(kind: string): void {\n\t\tconst queue = this.queues.get(kind) ?? []\n\t\tconst needed = this.seatsFor(kind)\n\t\tfor (const each of queue) each.connection.send(JSON.stringify({ type: 'queued', kind, waiting: queue.length, needed }))\n\t}\n\n\t/** As many wait for a kind as its seats: a room of it for them, which lets in only them, and a ticket each */\n\tprivate async match(kind: string): Promise<void> {\n\t\tconst queue = this.queues.get(kind)!\n\t\tconst needed = this.seatsFor(kind)\n\t\tif (queue.length < needed) return this.tellQueue(kind)\n\t\tconst players = queue.splice(0, needed)\n\t\tconst room = this.sampleOf(kind)\n\t\tconst seats = room.mode.seats.filter((seat) => !room.ai?.includes(seat.player))\n\t\t// a ticket each, for the seat it is to: nobody else gets in\n\t\tconst tickets = new Map<string, { player: number; name?: string }>(\n\t\t\tplayers.map((player, i) => [crypto.randomUUID(), { player: seats[i].player, name: player.name }])\n\t\t)\n\t\tconst made = await this.make(kind, {}, (request) => {\n\t\t\tconst ticket = request.ticket ? tickets.get(request.ticket) : undefined\n\t\t\treturn ticket ? { player: ticket.player, name: ticket.name, id: request.ticket } : { refuse: 'this game is for the players matched to it' }\n\t\t}).catch((error: unknown) => ({ refused: error instanceof Error ? error.message : String(error) }))\n\t\tconst given = [...tickets.keys()]\n\t\tplayers.forEach(({ connection }, i) =>\n\t\t\tconnection.send(\n\t\t\t\tJSON.stringify('room' in made ? { type: 'matched', kind, room: made.room, ticket: given[i] } : { type: 'refused', reason: made.refused })\n\t\t\t)\n\t\t)\n\t\tthis.tellQueue(kind)\n\t}\n\n\t/** Closes the rooms players made that have been empty long enough */\n\tprivate async sweep(): Promise<void> {\n\t\tconst now = Date.now()\n\t\tconst emptyFor = (this.options.emptyFor ?? 60) * 1000\n\t\tfor (const [name, made] of [...this.made]) {\n\t\t\tconst server = this.servers.get(name)\n\t\t\tif (!server) continue\n\t\t\tif (server.connections > 0) {\n\t\t\t\tmade.emptySince = now\n\t\t\t\tmade.joined = true\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\t// nobody ever came: sooner. A game on: not before those who dropped out may come back to it\n\t\t\tconst wait = !made.joined\n\t\t\t\t? Math.min(emptyFor, 20_000)\n\t\t\t\t: server.phase === 'game'\n\t\t\t\t\t? Math.max(emptyFor, (server.options.abandonAfter ?? 60) * 1000)\n\t\t\t\t\t: emptyFor\n\t\t\tif (now - made.emptySince >= wait) await this.remove(name)\n\t\t}\n\t}\n\n\t/** `POST /games`: a room made of a kind it offers, `{ kind, settings }`, answered with its name. And what a page asks first */\n\tprivate answerMake(request: import('node:http').IncomingMessage, response: import('node:http').ServerResponse): boolean {\n\t\tconst list = this.options.http?.games ?? '/games'\n\t\tconst { pathname } = new URL(request.url ?? '/', 'http://server')\n\t\tif (!list || pathname !== list || (request.method !== 'POST' && request.method !== 'OPTIONS')) return false\n\t\tconst cors = { 'access-control-allow-origin': '*', 'access-control-allow-methods': 'GET, POST', 'access-control-allow-headers': 'content-type' }\n\t\tif (request.method === 'OPTIONS') {\n\t\t\tresponse.writeHead(204, cors)\n\t\t\tresponse.end()\n\t\t\treturn true\n\t\t}\n\t\tconst answer = (status: number, body: unknown) => {\n\t\t\tresponse.writeHead(status, { 'content-type': 'application/json', 'cache-control': 'no-store', ...cors })\n\t\t\tresponse.end(JSON.stringify(body))\n\t\t}\n\t\tif (this.askedTooOften(request.socket.remoteAddress ?? '')) {\n\t\t\tanswer(429, { refused: TOO_OFTEN })\n\t\t\treturn true\n\t\t}\n\t\tlet body = ''\n\t\trequest.on('data', (chunk: Buffer) => {\n\t\t\tbody += chunk.toString()\n\t\t\tif (body.length > MAX_BODY) request.destroy()\n\t\t})\n\t\trequest.on('end', () => {\n\t\t\tlet asked: { kind?: unknown; settings?: unknown }\n\t\t\ttry {\n\t\t\t\tasked = JSON.parse(body || '{}')\n\t\t\t} catch {\n\t\t\t\treturn answer(400, { refused: 'what was asked is not JSON' })\n\t\t\t}\n\t\t\tconst settings =\n\t\t\t\tasked.settings && typeof asked.settings === 'object'\n\t\t\t\t\t? Object.fromEntries(Object.entries(asked.settings as Record<string, unknown>).filter(([, value]) => typeof value === 'string'))\n\t\t\t\t\t: {}\n\t\t\tvoid this.make(String(asked.kind ?? ''), settings as Record<string, string>).then(\n\t\t\t\t(made) => answer('room' in made ? 201 : 409, made),\n\t\t\t\t(error: unknown) => answer(500, { refused: error instanceof Error ? error.message : String(error) })\n\t\t\t)\n\t\t})\n\t\treturn true\n\t}\n\n\t/** The port they share */\n\tget port(): number {\n\t\treturn this.socket.port\n\t}\n\n\t/** Whether they take no new games: stopping */\n\tget draining(): boolean {\n\t\treturn [...this.servers.values()].some((room) => room.draining)\n\t}\n\n\t/** The modes the rooms play, each once, in their order: what the ratings are kept by (`/ratings`) */\n\tmodes(): string[] {\n\t\treturn [...new Set([...this.servers.values()].map((room) => room.options.mode.id))]\n\t}\n\n\t/** Every room's game, as a list of games says it (`GET /games`) */\n\tlisting(): GameListing[] {\n\t\treturn [...this.servers].map(([name, room]) => room.listing(name))\n\t}\n\n\t/** The kinds of game players may make a room of (`kinds`), by id, with their names and the settings they offer */\n\tkinds(): GameKindListing[] {\n\t\tconst queue = this.queuePath\n\t\treturn Object.entries(this.options.kinds ?? {}).map(([id, kind]) => ({\n\t\t\tid,\n\t\t\tname: kind.name,\n\t\t\tsettings: { ...this.sampleOf(id).mode.game.server?.settings },\n\t\t\t...(queue && { queue, waiting: this.queues.get(id)?.length ?? 0, needed: this.seatsFor(id) })\n\t\t}))\n\t}\n\n\t/** The numbers of each room, by its name */\n\tmetrics(): Map<string, ServerMetrics> {\n\t\treturn new Map([...this.servers].map(([name, room]) => [name, room.metrics()]))\n\t}\n\n\t/** Stops every room (see `GameServer.close`: with `drain`, each waits for its game, all at once), then the port */\n\tasync close(options: { drain?: number } = {}): Promise<void> {\n\t\tif (this.sweeper) clearInterval(this.sweeper)\n\t\tfor (const queue of this.queues.values()) for (const { connection } of queue) connection.close()\n\t\tthis.queues.clear()\n\t\tawait Promise.all([...this.servers.values()].map((room) => room.close(options)))\n\t\tawait this.socket.close()\n\t}\n}\n","import type { Logger } from '#engine/log'\nimport { type ServerLog, serverLogger } from './log'\nimport { readMessage } from '#engine/session/protocol'\nimport type { RelayNotice, RelayRequest } from '#engine/session/share'\nimport { type Connection, type SocketServer, startSocketServer } from './websocket'\n\n/*\n * A relay for games played in a page and watched from others. A page cannot take connections, so the one that plays\n * shares its game here (`share`, under a key like the campaign's id), and those who watch ask for it by that key\n * (`watch`). The relay only passes text along (`RelayRequest`, `RelayNotice`): the playing page's host makes each\n * watcher's game, as a server does for its observers (`shareGame`), and the relay hands it to the watcher as it came,\n * and the watcher's answers back. A watcher who comes before the game is shared, or stays after it ends (a mission\n * over, the next one loading), waits for it under its key, and is let in again when it is shared anew\n */\n\ninterface Share {\n\tkey: string\n\tname: string\n\thost: Connection\n}\n\ninterface Watcher {\n\tid: number\n\tconnection: Connection\n\tkey: string\n}\n\n/** @beta */\nexport interface RelayOptions {\n\t/** Port to listen on: 0 for any free one */\n\tport: number\n\tcompress?: boolean\n\t/** Where it says what it does, as `GameServerOptions.log` */\n\tlog?: ServerLog\n}\n\n/** @beta */\nexport class WatchRelay {\n\tprivate readonly shares = new Map<string, Share>()\n\tprivate readonly watchers = new Map<number, Watcher>()\n\tprivate next = 1\n\n\t/** What the relay says: see `RelayOptions.log` */\n\treadonly logger: Logger\n\n\tprivate constructor(\n\t\tprivate readonly socket: SocketServer,\n\t\toptions: RelayOptions\n\t) {\n\t\tthis.logger = serverLogger(options.log)\n\t\tsocket.onConnection((connection) => this.accept(connection))\n\t}\n\n\t/** Starts a relay, and resolves once it listens */\n\tstatic async start(options: RelayOptions): Promise<WatchRelay> {\n\t\t// a whole map goes through as one message, to each watcher who comes\n\t\tconst socket = await startSocketServer({ port: options.port, compress: options.compress, maxMessage: 16 * 1024 * 1024 })\n\t\tconst relay = new WatchRelay(socket, options)\n\t\trelay.logger.info('server', `a relay for games watched, on port ${socket.port}`, { port: socket.port })\n\t\treturn relay\n\t}\n\n\tget port(): number {\n\t\treturn this.socket.port\n\t}\n\n\t/** How many watch what is shared under a key */\n\twatching(key: string): number {\n\t\treturn [...this.watchers.values()].filter((watcher) => watcher.key === key).length\n\t}\n\n\tprivate accept(connection: Connection): void {\n\t\tlet role: { share: Share } | { watcher: Watcher } | undefined\n\t\tconnection.onMessage((text) => {\n\t\t\t// what a watcher says goes to the game it watches, as it is\n\t\t\tif (role && 'watcher' in role) return this.fromWatcher(role.watcher, text)\n\t\t\t// anyone can connect and send anything: what is not as it should be is dropped\n\t\t\tconst message = readMessage(text)\n\t\t\tif (!message) return\n\t\t\tif (!role && message.type === 'share') {\n\t\t\t\tif (typeof message.key === 'string' && typeof message.name === 'string')\n\t\t\t\t\trole = this.share(connection, { type: 'share', key: message.key, name: message.name })\n\t\t\t} else if (!role && message.type === 'watch') {\n\t\t\t\tif (typeof message.key === 'string') role = { watcher: this.watch(connection, message.key) }\n\t\t\t} else if (role && 'share' in role && message.type === 'send' && typeof message.to === 'number' && typeof message.text === 'string') {\n\t\t\t\t// only to who watches this share: a game shared under one key says nothing to the watchers of another\n\t\t\t\tconst watcher = this.watchers.get(message.to)\n\t\t\t\tif (watcher?.key === role.share.key) watcher.connection.send(message.text)\n\t\t\t}\n\t\t})\n\t\tconnection.onClose(() => {\n\t\t\tif (role && 'share' in role) this.unshare(role.share)\n\t\t\telse if (role && 'watcher' in role) this.leave(role.watcher)\n\t\t})\n\t}\n\n\tprivate tell(connection: Connection, notice: RelayNotice): void {\n\t\tconnection.send(JSON.stringify(notice))\n\t}\n\n\tprivate share(host: Connection, message: Extract<RelayRequest, { type: 'share' }>): { share: Share } | undefined {\n\t\tif (this.shares.has(message.key)) {\n\t\t\tthis.tell(host, { type: 'taken', key: message.key })\n\t\t\treturn undefined\n\t\t}\n\t\tconst share: Share = { key: message.key, name: message.name.slice(0, 80), host }\n\t\tthis.shares.set(share.key, share)\n\t\tthis.tell(host, { type: 'shared', key: share.key })\n\t\tthis.logger.info('server', `${share.name} is shared under ${share.key}`, { name: share.name })\n\t\t// those who waited for it\n\t\tfor (const watcher of this.watchers.values()) if (watcher.key === share.key) this.tell(host, { type: 'watcher', id: watcher.id })\n\t\treturn { share }\n\t}\n\n\tprivate unshare(share: Share): void {\n\t\tif (this.shares.get(share.key) !== share) return\n\t\tthis.shares.delete(share.key)\n\t\tthis.logger.info('server', `${share.name} is no longer shared`, { name: share.name })\n\t\tfor (const watcher of this.watchers.values()) if (watcher.key === share.key) this.tell(watcher.connection, { type: 'ended', key: share.key })\n\t}\n\n\tprivate watch(connection: Connection, key: string): Watcher {\n\t\tconst watcher: Watcher = { id: this.next++, connection, key }\n\t\tthis.watchers.set(watcher.id, watcher)\n\t\tconst share = this.shares.get(key)\n\t\tif (share) this.tell(share.host, { type: 'watcher', id: watcher.id })\n\t\telse this.tell(connection, { type: 'waiting', key })\n\t\treturn watcher\n\t}\n\n\tprivate fromWatcher(watcher: Watcher, text: string): void {\n\t\tconst share = this.shares.get(watcher.key)\n\t\tif (share) this.tell(share.host, { type: 'from', watcher: watcher.id, text })\n\t}\n\n\tprivate leave(watcher: Watcher): void {\n\t\tif (!this.watchers.delete(watcher.id)) return\n\t\tconst share = this.shares.get(watcher.key)\n\t\tif (share) this.tell(share.host, { type: 'gone', watcher: watcher.id })\n\t}\n\n\t/** Stops the relay: every connection closed */\n\tasync close(): Promise<void> {\n\t\tawait this.socket.close()\n\t}\n}\n","{\n\t\"$schema\": \"http://json-schema.org/draft-07/schema#\",\n\t\"$ref\": \"#/definitions/PackFile\",\n\t\"definitions\": {\n\t\t\"PackFile\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"$schema\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"description\": \"Letters, digits, dots and dashes: what the game composed of it is called by (`melee+<id>`)\"\n\t\t\t\t},\n\t\t\t\t\"version\": {\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"description\": \"`major.minor.patch`\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"requires\": {\n\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"description\": \"The game and the packs or mods it is made for, as a mod's (`ModDef.requires`)\"\n\t\t\t\t},\n\t\t\t\t\"units\": {\n\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/UnitTypeDef\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"abilities\": {\n\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/AbilityDef\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"buffs\": {\n\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/BuffDef\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"items\": {\n\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/ItemDef\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"upgrades\": {\n\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/UpgradeDef\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"races\": {\n\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/RaceDef\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"rules\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/RulesInit\",\n\t\t\t\t\t\"description\": \"The rules it changes, as data (`GameBuilder.rules`)\"\n\t\t\t\t},\n\t\t\t\t\"data\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/GameData\",\n\t\t\t\t\t\"description\": \"Values over the definitions, as a `data.json` has them (`GameData`): the editor's Data panel writes here for a pack\"\n\t\t\t\t},\n\t\t\t\t\"looks\": {\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"additionalProperties\": {},\n\t\t\t\t\t\"description\": \"What its units look like, as a `looks.json` has them: the client's to lay over the game's view\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\",\n\t\t\t\t\"version\"\n\t\t\t],\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"A pack of content as its file has it (`packs/<id>/pack.json`): plain data, checked against the pack's schema (`@v12-engine/engine/schemas/pack.schema.json`) as it is read. Its definitions are added to the game's, or put in the place of those of the same id; then its `data` is laid over the whole as a `data.json` is (`withData`), and its `rules` over the game's\"\n\t\t},\n\t\t\"UnitTypeDef\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"basedOn\": {\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"description\": \"The definition this one is a copy of, made from the data (`copyOf` in a `data.json`, the editor's Copy as new): what it is drawn, heard and pictured as where the game gives it no look of its own\"\n\t\t\t\t},\n\t\t\t\t\"kind\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/EntityKind\"\n\t\t\t\t},\n\t\t\t\t\"radius\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Collision radius\"\n\t\t\t\t},\n\t\t\t\t\"footprint\": {\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"w\": {\n\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"d\": {\n\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"w\",\n\t\t\t\t\t\t\"d\"\n\t\t\t\t\t],\n\t\t\t\t\t\"additionalProperties\": false,\n\t\t\t\t\t\"description\": \"Size in grid cells. Required for buildings and resources\"\n\t\t\t\t},\n\t\t\t\t\"stats\": {\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"additionalProperties\": false,\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"health\": {\n\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"attackDamage\": {\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"description\": \"What a basic attack deals, before the armor of the target\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"attackSpeed\": {\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"description\": \"Attacks per second\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"attackRange\": {\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"description\": \"Measured between the edges of attacker and target\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"armor\": {\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"description\": \"Against physical damage: 100 of it halve what arrives\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"magicResist\": {\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"description\": \"The same against magic damage\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"healthRegen\": {\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"description\": \"Per second\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"mana\": {\n\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"manaRegen\": {\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"description\": \"Per second\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"moveSpeed\": {\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"description\": \"Units per second. Who has none cannot move\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"sight\": {\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"description\": \"How far it sees. At night the unit type says how far that is\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"health\"\n\t\t\t\t\t],\n\t\t\t\t\t\"description\": \"What it starts with. What is left out is 0, a critical strike is worth 175%\"\n\t\t\t\t},\n\t\t\t\t\"initialMana\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Mana it comes into the world with. All of it when left out\"\n\t\t\t\t},\n\t\t\t\t\"sight\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Vision radius by day\"\n\t\t\t\t},\n\t\t\t\t\"sightNight\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Vision radius at night. A part of the one by day when left out\"\n\t\t\t\t},\n\t\t\t\t\"flying\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Flies: over cliffs, trees and buildings, out of reach of whatever cannot hit the air\"\n\t\t\t\t},\n\t\t\t\t\"naval\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Goes on deep water and nowhere else: a boat. Sent onto land, it goes to the water nearest where it was sent. On the ground, as far as what can hit it goes\"\n\t\t\t\t},\n\t\t\t\t\"waygate\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"A waygate, in a game with the portals pack (`withPortals`): a unit told to it walks in and is at once where it leads (its `waygate` field, a point of the map; `setWaygate` in code)\"\n\t\t\t\t},\n\t\t\t\t\"destructible\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Rocks, a barricade, a gate to break down, in a game with the destructibles pack (`withDestructibles`): it stands in the way as any building does until it is brought down, and any unit whose attack reaches it attacks it when told to, never of its own accord\"\n\t\t\t\t},\n\t\t\t\t\"transport\": {\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"capacity\": {\n\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"armed\": {\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"description\": \"Attack damage each one inside adds; or in code, all of what they add, by who is inside (none for nobody)\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"capacity\"\n\t\t\t\t\t],\n\t\t\t\t\t\"additionalProperties\": false,\n\t\t\t\t\t\"description\": \"Carries others: a boat, or a building they go into (a garrison). `takes` says who may get in; `armed`, for a building with an attack, is the attack damage each one inside adds, and it attacks only while someone is inside, as an orc burrow does. What a building holds comes out when it falls; what a boat holds goes down with it\"\n\t\t\t\t},\n\t\t\t\t\"turnRate\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Radians per second\"\n\t\t\t\t},\n\t\t\t\t\"attacks\": {\n\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/AttackDef\"\n\t\t\t\t\t},\n\t\t\t\t\t\"description\": \"How it attacks, as the classic RTS two weapons: against each target the first that reaches it (its `targets`), or the one the game picks (`GameRules.combat.pickAttack`). It swings one at a time: a hit's cooldown is that attack's, whichever swings next. None for what does not attack\"\n\t\t\t\t},\n\t\t\t\t\"hero\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/HeroDef\"\n\t\t\t\t},\n\t\t\t\t\"level\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"How dangerous it is, for creeps. Shown to the player, and added up per camp\"\n\t\t\t\t},\n\t\t\t\t\"bounty\": {\n\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\"minItems\": 2,\n\t\t\t\t\t\"items\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"title\": \"min\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"title\": \"max\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"maxItems\": 2,\n\t\t\t\t\t\"description\": \"Gold for whoever kills it, rolled between the two\"\n\t\t\t\t},\n\t\t\t\t\"corpse\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Whether it leaves a corpse when it dies, for spells to use (see `raiseDead`). Units do, unless this says no: machines, spirits. Decided for each death in code: false for none, true or seconds for one (how long it lies)\"\n\t\t\t\t},\n\t\t\t\t\"spellImmune\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Immune to spells: abilities do not touch it, of friends or enemies, unless they say they get through (`piercesImmunity`). Decided in code whenever it is asked: while it stands on holy ground. The player's screen only knows the plain `true` of it, to grey out a target; the simulation goes by the code\"\n\t\t\t\t},\n\t\t\t\t\"xp\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Experience heroes get for killing it. Follows from its cost when left out\"\n\t\t\t\t},\n\t\t\t\t\"requires\": {\n\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"description\": \"Building types the player has to have before this can be built or trained\"\n\t\t\t\t},\n\t\t\t\t\"race\": {\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"description\": \"The race it is of (`GameModule.races`): only its players may build or train it. Everyone's when left out: creeps, what is shared. What a player may have is the game's to change (the `available` hook)\"\n\t\t\t\t},\n\t\t\t\t\"provides\": {\n\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"description\": \"Building types this one counts as, for requirements. A keep is a town hall too\"\n\t\t\t\t},\n\t\t\t\t\"upgradesTo\": {\n\t\t\t\t\t\"anyOf\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"description\": \"Type this building can be upgraded into, where it stands; or the types, for one that may become one of several (a keep that turns into a castle or a citadel, `upgradesOf`). The first is what an upgrade is into where none is named\"\n\t\t\t\t},\n\t\t\t\t\"inventory\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Slots for items. Heroes have six\"\n\t\t\t\t},\n\t\t\t\t\"sells\": {\n\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"description\": \"Items this building sells\"\n\t\t\t\t},\n\t\t\t\t\"hires\": {\n\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"description\": \"Units it hires out to whoever has one of theirs there, by type: a tavern's heroes, a dock's boats. Their cost, and a hero only once (see `hire`). Neutral, it hires to everyone\"\n\t\t\t\t},\n\t\t\t\t\"drops\": {\n\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\t\"item\": {\n\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"chance\": {\n\t\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\t\"item\",\n\t\t\t\t\t\t\t\"chance\"\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"additionalProperties\": false\n\t\t\t\t\t},\n\t\t\t\t\t\"description\": \"What it may leave behind when it dies, each with its chance from 0 to 1. Or code: the items it leaves, decided as it dies (one random level-3 item; more for a stronger killer)\"\n\t\t\t\t},\n\t\t\t\t\"revives\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Brings fallen heroes back\"\n\t\t\t\t},\n\t\t\t\t\"abilities\": {\n\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"description\": \"Ids of the abilities the unit has\"\n\t\t\t\t},\n\t\t\t\t\"cost\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/Cost\"\n\t\t\t\t},\n\t\t\t\t\"supply\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Supply (food) it takes up for as long as it lives, and while it is being trained\"\n\t\t\t\t},\n\t\t\t\t\"supplies\": {\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"additionalProperties\": {\n\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t},\n\t\t\t\t\t\"description\": \"What it takes of the game's other pools of supply, by name (`GameRules.supply.pools`): `{ command: 1 }`\"\n\t\t\t\t},\n\t\t\t\t\"buildTime\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Seconds to build or train\"\n\t\t\t\t},\n\t\t\t\t\"supplyProvided\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"suppliesProvided\": {\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"additionalProperties\": {\n\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t},\n\t\t\t\t\t\"description\": \"What it provides of the game's other pools of supply, by name (`GameRules.supply.pools`), once it stands finished\"\n\t\t\t\t},\n\t\t\t\t\"harvester\": {\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"capacity\": {\n\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"gatherTime\": {\n\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"kinds\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/ResourceKind\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"The kinds of resource it gathers: all of them when left out. The undead's acolytes gold and nothing else, ghouls lumber. The `canHarvest` hook has the last word (`canHarvest`)\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"capacity\",\n\t\t\t\t\t\t\"gatherTime\"\n\t\t\t\t\t],\n\t\t\t\t\t\"additionalProperties\": false\n\t\t\t\t},\n\t\t\t\t\"builds\": {\n\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"description\": \"Building types this unit can construct\"\n\t\t\t\t},\n\t\t\t\t\"trains\": {\n\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"description\": \"Unit types this building can train\"\n\t\t\t\t},\n\t\t\t\t\"researches\": {\n\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"description\": \"Upgrades that can be researched here\"\n\t\t\t\t},\n\t\t\t\t\"dropOff\": {\n\t\t\t\t\t\"anyOf\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/ResourceKind\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"description\": \"Accepts harvested resources: all of them, or only the ones listed\"\n\t\t\t\t},\n\t\t\t\t\"builtOn\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/ResourceKind\",\n\t\t\t\t\t\"description\": \"Goes up on a resource of this kind, over it, of the same footprint: the undead's cursed gold mine. The resource is its owner's alone from then on (`canHarvest`); one that takes what is gathered there (`dropOff`) has its owner's workers gather at it from around it, handing in where they stand, as many at once as `gatherers` says. It goes down with the resource, which stands again where it is destroyed\"\n\t\t\t\t},\n\t\t\t\t\"gatherers\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Workers that gather at once at it, when it stands on a resource (`builtOn`): 5 when left out\"\n\t\t\t\t},\n\t\t\t\t\"buildsItself\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Goes up on its own once it is put down, as the undead summon theirs: the builder is free at once, and nobody helps (`GameRules.production.buildRate` of one builder, all the way). The same as `construction: 'self'`\"\n\t\t\t\t},\n\t\t\t\t\"construction\": {\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"builder\",\n\t\t\t\t\t\t\"self\",\n\t\t\t\t\t\t\"instant\"\n\t\t\t\t\t],\n\t\t\t\t\t\"description\": \"How it goes up once it is put down: with builders at it (`builder`, the more the faster, as `buildRate` says), on its own with the builder free at once (`self`, the undead's), or whole at once (`instant`: a wall piece, a ward). `builder` when left out (`self` with `buildsItself`)\"\n\t\t\t\t},\n\t\t\t\t\"resource\": {\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"kind\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/ResourceKind\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"amount\": {\n\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"slots\": {\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"description\": \"Workers that fit inside at once, for resources that are entered\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"entered\": {\n\t\t\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\t\t\"description\": \"Workers go inside to gather, like into a gold mine, instead of working on it from next to it\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"gatherTime\": {\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"description\": \"Seconds a load takes, when that is not what the worker usually needs\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"kind\",\n\t\t\t\t\t\t\"amount\",\n\t\t\t\t\t\t\"slots\"\n\t\t\t\t\t],\n\t\t\t\t\t\"additionalProperties\": false\n\t\t\t\t},\n\t\t\t\t\"hotkey\": {\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"description\": \"Key used in the command card when building or training this type\"\n\t\t\t\t},\n\t\t\t\t\"threat\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Higher values are picked first when acquiring targets\"\n\t\t\t\t},\n\t\t\t\t\"invulnerable\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"undying\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Takes damage like anything else but never dies of it: it is left with a little health, like something to practise on\"\n\t\t\t\t},\n\t\t\t\t\"recovers\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Seconds after it was last hurt that it is whole again, all at once\"\n\t\t\t\t},\n\t\t\t\t\"ignored\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Nobody picks a fight with it on their own: it is only attacked when someone is told to\"\n\t\t\t\t},\n\t\t\t\t\"fellable\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Destroyed by attacks that fell, like a tree by a catapult, whatever else it withstands\"\n\t\t\t\t},\n\t\t\t\t\"wall\": {\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"family\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"gate\": {\n\t\t\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"family\"\n\t\t\t\t\t],\n\t\t\t\t\t\"additionalProperties\": false,\n\t\t\t\t\t\"description\": \"A piece of wall (`withWalls`): it joins with the pieces of its family beside it, and its shape (`single`, `end`, `straight`, `corner`, `tee`, `cross`) is its look. A gate (`gate`) joins as a piece does, turned along its line, and its owner opens and shuts it for everyone (`wallGateCommand`): its look `open` or `shut`. Pieces join by their footprints, so a gate wider than a cell sits in a line of pieces of one: one of a cell lets nobody through, the ground about a wall being kept off by the radius of what walks (a gatehouse of 3 by 3 lets through what is 1 wide)\"\n\t\t\t\t},\n\t\t\t\t\"lifetime\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Seconds it lasts before it is gone by itself, like a ward\"\n\t\t\t\t},\n\t\t\t\t\"invisible\": {\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"after\": {\n\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"additionalProperties\": false,\n\t\t\t\t\t\"description\": \"Enemies cannot see it, `after` seconds after it came into the world, unless something reveals it to them\"\n\t\t\t\t},\n\t\t\t\t\"hits\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Goes down after this many attacks and spells, whatever they deal: its health counts hits\"\n\t\t\t\t},\n\t\t\t\t\"ai\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/AiHints\",\n\t\t\t\t\t\"description\": \"What the computer player takes it for (`AiHints`): its roles (`worker`, `hall`, `soldier`...), the game's words about it, what it answers. Declared where what the kit infers from the rest of the definition is wrong or cannot be known; inferred otherwise (`inferRoles` in `@v12-engine/engine/ai`)\"\n\t\t\t\t},\n\t\t\t\t\"armourType\": {\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"description\": \"Its armour type, the column of the game's damage table (`GameRules.combat.damageTable`): `light`, `heavy`, `fortified`…\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\",\n\t\t\t\t\"name\",\n\t\t\t\t\"kind\",\n\t\t\t\t\"radius\",\n\t\t\t\t\"stats\",\n\t\t\t\t\"sight\"\n\t\t\t],\n\t\t\t\"additionalProperties\": false\n\t\t},\n\t\t\"EntityKind\": {\n\t\t\t\"type\": \"string\",\n\t\t\t\"enum\": [\n\t\t\t\t\"unit\",\n\t\t\t\t\"building\",\n\t\t\t\t\"resource\"\n\t\t\t]\n\t\t},\n\t\t\"AttackDef\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"stats\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/AttackStats\",\n\t\t\t\t\t\"description\": \"The stats its numbers are, by id: a stat of the game's (`GameModule.stats`) for a weapon whose damage, speed or range is not the unit's first's. What changes those stats (research, items, buffs) changes this attack alone\"\n\t\t\t\t},\n\t\t\t\t\"damageType\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/DamageType\",\n\t\t\t\t\t\"description\": \"Physical when left out\"\n\t\t\t\t},\n\t\t\t\t\"damagePoint\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Seconds from the start of the swing until damage lands (or the projectile leaves), at the attack speed the unit starts with\"\n\t\t\t\t},\n\t\t\t\t\"acquisitionRange\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Enemies closer than this are attacked automatically\"\n\t\t\t\t},\n\t\t\t\t\"minRange\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"How near is too near, edge to edge: what is closer than this the attack does not hit (a siege weapon's dead zone). Another of the unit's attacks takes over there where one reaches (`GameRules.combat.pickAttack`); with none, a unit told to attack steps back out of it first, and one left to itself does not pick what is that near. None when left out\"\n\t\t\t\t},\n\t\t\t\t\"onTheMove\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Fires as the unit walks (a horse archer, a tank's turret): told to move, it hits what it passes within the attack's range without stopping or turning to it, at the attack's speed. Every other order fights as any attack does (it stops to). Not when left out\"\n\t\t\t\t},\n\t\t\t\t\"projectileSpeed\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Set for ranged attacks\"\n\t\t\t\t},\n\t\t\t\t\"targets\": {\n\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"anyOf\": [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/EntityKind\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Layer\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t]\n\t\t\t\t\t},\n\t\t\t\t\t\"description\": \"What this attack can hit: kinds of things, and where they are. Everything on the ground when left out. What flies is only hit by attacks that name `air`, and an attack that names `air` alone does not hit the ground\"\n\t\t\t\t},\n\t\t\t\t\"splash\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Radius around the point of impact in which everyone is hit, friends too: in full in the inner half, by half further out. Shots of such an attack land where they were aimed, they do not follow their target\"\n\t\t\t\t},\n\t\t\t\t\"ground\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Can be told to attack a spot on the ground\"\n\t\t\t\t},\n\t\t\t\t\"fells\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Brings down what is `fellable` in the area it hits\"\n\t\t\t\t},\n\t\t\t\t\"how\": {\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"description\": \"How what this attack kills dies, for the screen (`'shot'`, `'crushed'`, `'burnt'`): the death's `how`, which the view shows its way (`UnitLook.death`). A word, or one decided for each blow. The `death` hook has the last say\"\n\t\t\t\t},\n\t\t\t\t\"attackType\": {\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"description\": \"The attack's type against the target's armour type, by the game's damage table (`GameRules.combat.damageTable`): `pierce`, `siege`… Left out, the attack is multiplied by nothing\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"damagePoint\",\n\t\t\t\t\"acquisitionRange\"\n\t\t\t],\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"One of the ways a unit attacks (`UnitTypeDef.attacks`): what it hits, how its blow lands. How hard, how fast and how far are stats: the unit's `attackDamage`, `attackSpeed` and `attackRange`, or stats of the game's own that the attack names (`stats`: a second weapon's)\"\n\t\t},\n\t\t\"AttackStats\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"damage\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/StatKey\",\n\t\t\t\t\t\"description\": \"What a hit deals, before the armour of the target. `attackDamage` when left out\"\n\t\t\t\t},\n\t\t\t\t\"speed\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/StatKey\",\n\t\t\t\t\t\"description\": \"Hits a second. `attackSpeed` when left out\"\n\t\t\t\t},\n\t\t\t\t\"range\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/StatKey\",\n\t\t\t\t\t\"description\": \"How far it reaches, between the edges. `attackRange` when left out\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"The stats an attack reads how hard, how fast and how far it hits from (`AttackDef.stats`): the unit's `attackDamage`, `attackSpeed` and `attackRange` for what is left out\"\n\t\t},\n\t\t\"StatKey\": {\n\t\t\t\"type\": \"string\",\n\t\t\t\"description\": \"A stat: one of the engine's (`CoreStatKey`), or one a game defined. Checked when the game is built\"\n\t\t},\n\t\t\"DamageType\": {\n\t\t\t\"type\": \"string\",\n\t\t\t\"enum\": [\n\t\t\t\t\"physical\",\n\t\t\t\t\"magic\",\n\t\t\t\t\"true\"\n\t\t\t],\n\t\t\t\"description\": \"Physical damage is held off by armor, magic damage by magic resist, true damage by nothing\"\n\t\t},\n\t\t\"Layer\": {\n\t\t\t\"type\": \"string\",\n\t\t\t\"enum\": [\n\t\t\t\t\"ground\",\n\t\t\t\t\"air\"\n\t\t\t],\n\t\t\t\"description\": \"Where something is: on the ground, or above it\"\n\t\t},\n\t\t\"HeroDef\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"growth\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/StatChange\",\n\t\t\t\t\t\"description\": \"What every level after the first adds\"\n\t\t\t\t},\n\t\t\t\t\"learns\": {\n\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"description\": \"Abilities the hero can put points into\"\n\t\t\t\t},\n\t\t\t\t\"spells\": {\n\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"description\": \"Abilities it has from the start and does not learn: the two spells every hero brings along. No mana, and nothing shortens their cooldown\"\n\t\t\t\t},\n\t\t\t\t\"maxLevel\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"The level it stops at. 10 when left out\"\n\t\t\t\t},\n\t\t\t\t\"meter\": {\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"description\": \"A buff whose stacks are shown next to its health and mana, as so many points out of as many as it can have\"\n\t\t\t\t},\n\t\t\t\t\"primary\": {\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"strength\",\n\t\t\t\t\t\t\"agility\",\n\t\t\t\t\t\t\"intelligence\"\n\t\t\t\t\t],\n\t\t\t\t\t\"description\": \"Its primary attribute, in a game with the attributes pack (`withAttributes`): each point of it adds to its attack as well. `AttributesOptions.primaryOf` says it in code\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"growth\",\n\t\t\t\t\"learns\"\n\t\t\t],\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"What makes a unit a hero: it gains levels, grows with them and learns abilities\"\n\t\t},\n\t\t\"StatChange\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"stats\": {\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"attackDamage\": {\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"description\": \"What a basic attack deals, before the armor of the target\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"attackSpeed\": {\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"description\": \"Attacks per second\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"attackRange\": {\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"description\": \"Measured between the edges of attacker and target\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"health\": {\n\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"armor\": {\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"description\": \"Against physical damage: 100 of it halve what arrives\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"magicResist\": {\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"description\": \"The same against magic damage\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"healthRegen\": {\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"description\": \"Per second\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"mana\": {\n\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"manaRegen\": {\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"description\": \"Per second\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"moveSpeed\": {\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"description\": \"Units per second. Who has none cannot move\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"sight\": {\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"description\": \"How far it sees. At night the unit type says how far that is\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"additionalProperties\": false\n\t\t\t\t},\n\t\t\t\t\"percent\": {\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"attackDamage\": {\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"description\": \"What a basic attack deals, before the armor of the target\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"attackSpeed\": {\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"description\": \"Attacks per second\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"attackRange\": {\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"description\": \"Measured between the edges of attacker and target\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"health\": {\n\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"armor\": {\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"description\": \"Against physical damage: 100 of it halve what arrives\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"magicResist\": {\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"description\": \"The same against magic damage\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"healthRegen\": {\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"description\": \"Per second\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"mana\": {\n\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"manaRegen\": {\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"description\": \"Per second\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"moveSpeed\": {\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"description\": \"Units per second. Who has none cannot move\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"sight\": {\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"description\": \"How far it sees. At night the unit type says how far that is\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"additionalProperties\": false\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"Something that changes stats: `stats` is added, `percent` makes them bigger or smaller by a part of what they are with everything added. Parts from different places add up: two times +10% are +20%.\"\n\t\t},\n\t\t\"Cost\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"additionalProperties\": {\n\t\t\t\t\"type\": \"number\"\n\t\t\t},\n\t\t\t\"propertyNames\": {\n\t\t\t\t\"description\": \"The id of a resource, as its game module defines it (`ResourceDef`)\"\n\t\t\t},\n\t\t\t\"description\": \"What something costs: an amount of each resource it takes, by resource id. What is left out costs nothing\"\n\t\t},\n\t\t\"ResourceKind\": {\n\t\t\t\"type\": \"string\",\n\t\t\t\"description\": \"The id of a resource, as its game module defines it (`ResourceDef`)\"\n\t\t},\n\t\t\"AiHints\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"role\": {\n\t\t\t\t\t\"anyOf\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/AiRole\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/AiRole\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"description\": \"What it is for (`AiRole`), one or several: in place of what the kit would infer\"\n\t\t\t\t},\n\t\t\t\t\"tags\": {\n\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"description\": \"Words of the game's own about it (`'ranged'`, `'cavalry'`), for a brain's queries (`UnitSet.withTag`). Beside the inferred ones\"\n\t\t\t\t},\n\t\t\t\t\"counters\": {\n\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/CounterHint\"\n\t\t\t\t\t},\n\t\t\t\t\t\"description\": \"What it is good against: tags, roles or type ids (`['air', 'siege']`), each with a weight where a word is not enough (`{ target: 'air', weight: 1 }`). Read by the matchups (`Strength.matchup`, `odds`, `counterFor`)\"\n\t\t\t\t},\n\t\t\t\t\"strength\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"How strong it is at full health, in place of the kit's formula (`defaultStrength`) and of any formula the brain was given: for a type whose numbers mislead (a unit that is all spells). Scaled by the health a unit has left\"\n\t\t\t\t},\n\t\t\t\t\"priority\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"How much it is worth hitting first, in a fight: 1 when left out. Read by the micro of C2\"\n\t\t\t\t},\n\t\t\t\t\"placement\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/PlacementName\",\n\t\t\t\t\t\"description\": \"Where the build plan puts it (`PlacementName`): `atEdge` for a tower, `behind` for supply, `cluster` beside its own kind. Over the brain's table; the role's default when left out\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"What a game declares of a unit type for the computer player (`UnitTypeDef.ai`), where what the kit infers from the definition is wrong or cannot be known: its roles, words of the game's own about it, what it answers, how much it is worth hitting. A declared role is in place of the inferred ones, not beside them\"\n\t\t},\n\t\t\"AiRole\": {\n\t\t\t\"anyOf\": [\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"worker\",\n\t\t\t\t\t\t\"gatherer\",\n\t\t\t\t\t\t\"topper\",\n\t\t\t\t\t\t\"hall\",\n\t\t\t\t\t\t\"supply\",\n\t\t\t\t\t\t\"barracks\",\n\t\t\t\t\t\t\"altar\",\n\t\t\t\t\t\t\"workshop\",\n\t\t\t\t\t\t\"tower\",\n\t\t\t\t\t\t\"shop\",\n\t\t\t\t\t\t\"soldier\",\n\t\t\t\t\t\t\"hero\",\n\t\t\t\t\t\t\"caster\",\n\t\t\t\t\t\t\"healer\",\n\t\t\t\t\t\t\"siege\",\n\t\t\t\t\t\t\"air\",\n\t\t\t\t\t\t\"scout\",\n\t\t\t\t\t\t\"detector\"\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t],\n\t\t\t\"description\": \"What a unit type is for, to a computer player: the engine's roles, or a word of the game's own. A type can have several (a ghoul is a `gatherer` and a `soldier`)\\n\\n- `worker`: gathers and builds; `gatherer`: gathers (a worker is one too); `topper`: what is put up on a deposit of the money to gather it there (`builtOn`)\\n- `hall`: takes deposits and trains workers; `supply`: provides supply and trains nothing; `barracks`: trains soldiers; `altar`: trains or revives heroes; `workshop`: researches; `tower`: a building that attacks; `shop`: sells or hires\\n- `soldier`: a unit that fights and is no hero; `hero`; `caster`: fights with mana; `healer`: has an ability that heals; `siege`: attacks that splash, fell or are aimed at the ground; `air`: flies; `scout`: the fastest of the soldiers; `detector`: finds the invisible\"\n\t\t},\n\t\t\"CounterHint\": {\n\t\t\t\"anyOf\": [\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"target\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"weight\": {\n\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"target\"\n\t\t\t\t\t],\n\t\t\t\t\t\"additionalProperties\": false\n\t\t\t\t}\n\t\t\t],\n\t\t\t\"description\": \"What a game declares a type good against (`UnitTypeDef.ai.counters`): a tag (`'air'`), a role (`'siege'`) or a type id, as a word, or with how much it counts (`weight`, 0.5 when left out: a unit whose whole opponent it counters is worth half again)\"\n\t\t},\n\t\t\"PlacementName\": {\n\t\t\t\"type\": \"string\",\n\t\t\t\"description\": \"The kit's placement strategies, by name: `nearHome` (rings around the place, keeping the way to the mine clear), `byResource` (on the nearest deposit it is built on: a mine), `atEdge` (at the edge of the base towards the enemy, for a tower), `behind` (away from the enemy, for supply), `cluster` (beside another of its type, barracks by barracks; `cluster:<type>` beside another type), `onLayer:<layer>` (where the whole footprint is on a layer of cells the game paints: `onLayer:blight` for a race that builds on blight)\"\n\t\t},\n\t\t\"AbilityDef\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"how\": {\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"description\": \"How what its damage kills dies, for the screen (`'burnt'`, `'frozen'`, `'blasted'`): the death's `how`, which the view shows its way. A word, or one decided for each blow: by its level, its caster, what it hit. An effect's own (`EffectDef.how`) goes over it; the `death` hook has the last say\"\n\t\t\t\t},\n\t\t\t\t\"basedOn\": {\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"description\": \"The definition this one is a copy of, made from the data (`copyOf` in a `data.json`, the editor's Copy as new): what it is drawn, heard and pictured as where the game gives it no look of its own\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"description\": \"What it does, in words: a template with values in braces, worked out for the caster as it is now (\\\"Deals {damage} magic damage\\\" shows \\\"Deals 80 (50 + 60% ability power) magic damage\\\"). The values are its own (`values`), or found in its effects: `damage`, `heal`, `shield`, `amount`, `duration`, `radius`, `range`, `cooldown`, `charges`, `refill`, `mana`, `count`, `distance` (see `abilityValues`)\"\n\t\t\t\t},\n\t\t\t\t\"values\": {\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"additionalProperties\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/AbilityValueDef\"\n\t\t\t\t\t},\n\t\t\t\t\t\"description\": \"Values of its own to put in its description, by name: an amount and what of the caster scales it, or worked out in code\"\n\t\t\t\t},\n\t\t\t\t\"hotkey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"target\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/AbilityTarget\",\n\t\t\t\t\t\"description\": \"none: happens at once. unit, point: needs a target, and the caster walks into range of it. direction: goes off from where the caster stands, towards where it was aimed. passive: always on, cannot be cast\"\n\t\t\t\t},\n\t\t\t\t\"targets\": {\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"allies\": {\n\t\t\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"enemies\": {\n\t\t\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"self\": {\n\t\t\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"kinds\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/EntityKind\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"layers\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Layer\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"heroes\": {\n\t\t\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"additionalProperties\": false,\n\t\t\t\t\t\"description\": \"What may be targeted, for abilities cast on a unit\"\n\t\t\t\t},\n\t\t\t\t\"manaCost\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\"\n\t\t\t\t},\n\t\t\t\t\"healthCost\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\",\n\t\t\t\t\t\"description\": \"Health it costs, as a part of all the caster can have. It cannot be cast with less than that left\"\n\t\t\t\t},\n\t\t\t\t\"cooldown\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\",\n\t\t\t\t\t\"description\": \"Seconds before it can be used again\"\n\t\t\t\t},\n\t\t\t\t\"charges\": {\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"count\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"refill\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"count\",\n\t\t\t\t\t\t\"refill\"\n\t\t\t\t\t],\n\t\t\t\t\t\"additionalProperties\": false,\n\t\t\t\t\t\"description\": \"Uses it holds, for one used so many times before it waits: `count` of them at the most, one more back every `refill` seconds while it holds fewer (shortened as the cooldown is). Each use spends one; with none left it is not ready. Its `cooldown`, where it has one, is then the time between two uses. Full when it is learned\"\n\t\t\t\t},\n\t\t\t\t\"range\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\",\n\t\t\t\t\t\"description\": \"How far away the target may be\"\n\t\t\t\t},\n\t\t\t\t\"attackRange\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"The target may be as far away as the caster's attacks reach, and `range` farther\"\n\t\t\t\t},\n\t\t\t\t\"tags\": {\n\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"description\": \"What kind of thing it is, for those who can keep out of its way: a buff that `avoids` one of these lets nothing of the ability happen to who carries it. `ground` is for what comes out of the ground or along it\"\n\t\t\t\t},\n\t\t\t\t\"maxLevel\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Levels a hero can put into it. 3 when left out\"\n\t\t\t\t},\n\t\t\t\t\"requiresLevel\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Hero level needed for the first level of the ability. Ultimates ask for 6\"\n\t\t\t\t},\n\t\t\t\t\"levelStep\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Hero levels between two levels of the ability. 2 when left out\"\n\t\t\t\t},\n\t\t\t\t\"castTime\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Seconds between starting to cast and the effect. The caster stands still for as long and cannot be called away: what it is told to do in the meantime, it does right after. None when left out\"\n\t\t\t\t},\n\t\t\t\t\"piercesImmunity\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Gets through a unit's immunity to spells (see `UnitTypeDef.spellImmune`), as an ultimate may. Or for the targets its function says\"\n\t\t\t\t},\n\t\t\t\t\"aim\": {\n\t\t\t\t\t\"anyOf\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\t\t\"shape\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\t\t\"const\": \"circle\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"radius\": {\n\t\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\"\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\t\t\"shape\",\n\t\t\t\t\t\t\t\t\"radius\"\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"additionalProperties\": false\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\t\t\"shape\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\t\t\"const\": \"line\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"length\": {\n\t\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"width\": {\n\t\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\"\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\t\t\"shape\",\n\t\t\t\t\t\t\t\t\"length\",\n\t\t\t\t\t\t\t\t\"width\"\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"additionalProperties\": false\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\t\t\"shape\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\t\t\"const\": \"cone\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"length\": {\n\t\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"angle\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\t\t\"shape\",\n\t\t\t\t\t\t\t\t\"length\",\n\t\t\t\t\t\t\t\t\"angle\"\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"additionalProperties\": false\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"description\": \"The ground it covers, when that is not what its effects say: see `aimOf`\"\n\t\t\t\t},\n\t\t\t\t\"autocast\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Units use it on their own when they have nothing else to do. The player can switch this off\"\n\t\t\t\t},\n\t\t\t\t\"charge\": {\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"description\": \"A buff of the caster that charges the ability when it has as many stacks as it can: the ability shows it, its `charged` effects do their other thing, and casting it takes the buff away\"\n\t\t\t\t},\n\t\t\t\t\"effects\": {\n\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/EffectDef\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"passive\": {\n\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/EffectDef\"\n\t\t\t\t\t},\n\t\t\t\t\t\"description\": \"What an ability that is cast does all the time besides, once it is learned: buffs, auras, `onHit`\"\n\t\t\t\t},\n\t\t\t\t\"channel\": {\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"duration\": {\n\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"every\": {\n\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"effects\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/EffectDef\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"finished\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/EffectDef\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"What it does once kept up to the end, and only then: a recall's teleport home\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"breaksOnDamage\": {\n\t\t\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\t\t\"description\": \"Broken off when the caster is hurt, as a recall is: any damage, from anyone\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"duration\"\n\t\t\t\t\t],\n\t\t\t\t\t\"additionalProperties\": false,\n\t\t\t\t\t\"description\": \"Kept up once it went off: the caster stands, for `duration` seconds, doing it every `every` seconds (effects, or code), until it is done or interrupted (another order, a stun), when `interrupted` runs. An ice storm\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\",\n\t\t\t\t\"name\",\n\t\t\t\t\"target\",\n\t\t\t\t\"effects\"\n\t\t\t],\n\t\t\t\"additionalProperties\": false\n\t\t},\n\t\t\"AbilityValueDef\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"amount\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\"\n\t\t\t\t},\n\t\t\t\t\"scaling\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/Scaling\"\n\t\t\t\t},\n\t\t\t\t\"unit\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"percent\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"How a game gives a value of an ability: an amount and what scales it, or worked out in code\"\n\t\t},\n\t\t\"Scaled\": {\n\t\t\t\"anyOf\": [\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t],\n\t\t\t\"description\": \"A value that can grow with the level of an ability: one number, or one per level\"\n\t\t},\n\t\t\"Scaling\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"level\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\"\n\t\t\t\t},\n\t\t\t\t\"bonusAttackDamage\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"additionalProperties\": {\n\t\t\t\t\"$ref\": \"#/definitions/Scaled\"\n\t\t\t},\n\t\t\t\"description\": \"What the stats of the caster add to an amount. `level` is for every level of a hero past the first, `bonusAttackDamage` for the attack damage it has on top of what its type and its levels give it\"\n\t\t},\n\t\t\"AbilityTarget\": {\n\t\t\t\"type\": \"string\",\n\t\t\t\"enum\": [\n\t\t\t\t\"none\",\n\t\t\t\t\"unit\",\n\t\t\t\t\"point\",\n\t\t\t\t\"direction\",\n\t\t\t\t\"passive\"\n\t\t\t],\n\t\t\t\"description\": \"How an ability is aimed\"\n\t\t},\n\t\t\"EffectDef\": {\n\t\t\t\"anyOf\": [\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"additionalProperties\": false,\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"amount\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"scaling\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaling\",\n\t\t\t\t\t\t\t\"description\": \"What the stats of the caster add to the amount: `{ abilityPower: 0.6 }` adds 60% of its ability power. `level` is for every level of the hero past the first\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"damageType\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/DamageType\",\n\t\t\t\t\t\t\t\"description\": \"For damage. Magic when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"how\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"description\": \"For damage: how what it kills dies, for the screen, over the ability's own `how`\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"ifBuff\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"description\": \"Only hits what carries this buff\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"only\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"heroes\",\n\t\t\t\t\t\t\t\"description\": \"Only hits heroes\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"kinds\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/EntityKind\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Only hits these kinds of things. Everything but resources when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"layers\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Layer\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Where what it hits has to be: on the ground, in the air. Both when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"radius\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\",\n\t\t\t\t\t\t\t\"description\": \"Makes the effect hit everything around the point of impact. Required for auras, and the size of a missile\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"affects\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\t\t\"enemies\",\n\t\t\t\t\t\t\t\t\"allies\",\n\t\t\t\t\t\t\t\t\"all\"\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"description\": \"Who an effect with a radius applies to. Follows from the kind of effect when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"on\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"caster\",\n\t\t\t\t\t\t\t\"description\": \"Applies to the caster, wherever the ability was aimed\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"type\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"damage\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t]\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"additionalProperties\": false,\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"amount\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"scaling\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaling\",\n\t\t\t\t\t\t\t\"description\": \"What the stats of the caster add to the amount: `{ abilityPower: 0.6 }` adds 60% of its ability power. `level` is for every level of the hero past the first\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"ifBuff\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"description\": \"Only hits what carries this buff\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"only\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"heroes\",\n\t\t\t\t\t\t\t\"description\": \"Only hits heroes\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"kinds\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/EntityKind\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Only hits these kinds of things. Everything but resources when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"layers\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Layer\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Where what it hits has to be: on the ground, in the air. Both when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"radius\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\",\n\t\t\t\t\t\t\t\"description\": \"Makes the effect hit everything around the point of impact. Required for auras, and the size of a missile\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"affects\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\t\t\"enemies\",\n\t\t\t\t\t\t\t\t\"allies\",\n\t\t\t\t\t\t\t\t\"all\"\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"description\": \"Who an effect with a radius applies to. Follows from the kind of effect when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"on\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"caster\",\n\t\t\t\t\t\t\t\"description\": \"Applies to the caster, wherever the ability was aimed\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"type\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"heal\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t]\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"additionalProperties\": false,\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"amount\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"scaling\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaling\",\n\t\t\t\t\t\t\t\"description\": \"What the stats of the caster add to the amount: `{ abilityPower: 0.6 }` adds 60% of its ability power. `level` is for every level of the hero past the first\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"ifBuff\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"description\": \"Only hits what carries this buff\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"only\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"heroes\",\n\t\t\t\t\t\t\t\"description\": \"Only hits heroes\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"kinds\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/EntityKind\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Only hits these kinds of things. Everything but resources when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"layers\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Layer\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Where what it hits has to be: on the ground, in the air. Both when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"radius\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\",\n\t\t\t\t\t\t\t\"description\": \"Makes the effect hit everything around the point of impact. Required for auras, and the size of a missile\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"affects\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\t\t\"enemies\",\n\t\t\t\t\t\t\t\t\"allies\",\n\t\t\t\t\t\t\t\t\"all\"\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"description\": \"Who an effect with a radius applies to. Follows from the kind of effect when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"on\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"caster\",\n\t\t\t\t\t\t\t\"description\": \"Applies to the caster, wherever the ability was aimed\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"type\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"mana\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t]\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"additionalProperties\": false,\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"duration\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\",\n\t\t\t\t\t\t\t\"description\": \"Seconds the buff lasts, when that is different from its usual duration\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"stack\": {\n\t\t\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\t\t\"description\": \"For `buff`: adds one to how many of it the target carries instead of renewing it, up to what the buff allows\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"buff\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"ifBuff\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"description\": \"Only hits what carries this buff\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"only\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"heroes\",\n\t\t\t\t\t\t\t\"description\": \"Only hits heroes\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"kinds\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/EntityKind\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Only hits these kinds of things. Everything but resources when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"layers\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Layer\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Where what it hits has to be: on the ground, in the air. Both when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"radius\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\",\n\t\t\t\t\t\t\t\"description\": \"Makes the effect hit everything around the point of impact. Required for auras, and the size of a missile\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"affects\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\t\t\"enemies\",\n\t\t\t\t\t\t\t\t\"allies\",\n\t\t\t\t\t\t\t\t\"all\"\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"description\": \"Who an effect with a radius applies to. Follows from the kind of effect when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"on\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"caster\",\n\t\t\t\t\t\t\t\"description\": \"Applies to the caster, wherever the ability was aimed\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"type\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"buff\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"buff\",\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t]\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"additionalProperties\": false,\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"speed\": {\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"description\": \"For missiles: how fast they travel, in units per second, and how far\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"distance\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"count\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\",\n\t\t\t\t\t\t\t\"description\": \"For missiles: how many are sent, spread over an angle in radians. What one of them hit the others pass by\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"spread\": {\n\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"homing\": {\n\t\t\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\t\t\"description\": \"For missiles: flies after the unit the ability was cast on, and only hits that one\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"toPoint\": {\n\t\t\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\t\t\"description\": \"For missiles: flies to the spot the ability was aimed at and stops there, `distance` away at the most\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"delay\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\",\n\t\t\t\t\t\t\t\"description\": \"For missiles: seconds they take shape where they were cast before they set off. The caster is free to go\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"sight\": {\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"description\": \"For missiles: how far the team of the caster sees around it\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"blocked\": {\n\t\t\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\t\t\"description\": \"For missiles: buildings and trees in its way stop it, without anything happening to them\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"pierce\": {\n\t\t\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\t\t\"description\": \"For missiles: goes on after it hit something. False for the ones that are used up by the first thing in their way\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"arc\": {\n\t\t\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\t\t\"height\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"time\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\t\t\"height\"\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"additionalProperties\": false,\n\t\t\t\t\t\t\t\"description\": \"For missiles: thrown, not shot. It goes `height` up in the middle of its way and comes down at its end, `time` seconds after it set off however far that is (`speed` otherwise). It flies over everything, and what is within its `radius` where it comes down is what it hits\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"bounce\": {\n\t\t\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\t\t\"count\": {\n\t\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"range\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"falloff\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\t\t\"count\",\n\t\t\t\t\t\t\t\t\"range\"\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"additionalProperties\": false,\n\t\t\t\t\t\t\t\"description\": \"For missiles: where it would be done (it reached its target, was used up by what it hit, came down, or came to the end of its way) it springs on to the nearest thing it may hit within `range` that it has not hit yet, and flies after that one, `count` times. Every spring takes `falloff` of the amounts off what it does (0.25: a quarter less each time). Its `onEnd` is where it is done for good\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"effects\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/EffectDef\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"What happens to what is hit. For missiles: everything they pass through. For `onHit`: the target of an attack. For `dash`: what is around where it ends. For `after`: what is there when the time is up. For `zone`: what is inside, over and over\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"onEnd\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/EffectDef\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"For missiles that were not stopped, and for zones: what happens where and when they end\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"ifBuff\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"description\": \"Only hits what carries this buff\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"only\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"heroes\",\n\t\t\t\t\t\t\t\"description\": \"Only hits heroes\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"kinds\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/EntityKind\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Only hits these kinds of things. Everything but resources when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"layers\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Layer\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Where what it hits has to be: on the ground, in the air. Both when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"radius\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\",\n\t\t\t\t\t\t\t\"description\": \"Makes the effect hit everything around the point of impact. Required for auras, and the size of a missile\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"affects\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\t\t\"enemies\",\n\t\t\t\t\t\t\t\t\"allies\",\n\t\t\t\t\t\t\t\t\"all\"\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"description\": \"Who an effect with a radius applies to. Follows from the kind of effect when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"on\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"caster\",\n\t\t\t\t\t\t\t\"description\": \"Applies to the caster, wherever the ability was aimed\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"type\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"missile\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t]\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"additionalProperties\": false,\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"buff\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"ifBuff\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"description\": \"Only hits what carries this buff\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"only\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"heroes\",\n\t\t\t\t\t\t\t\"description\": \"Only hits heroes\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"kinds\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/EntityKind\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Only hits these kinds of things. Everything but resources when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"layers\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Layer\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Where what it hits has to be: on the ground, in the air. Both when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"radius\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\",\n\t\t\t\t\t\t\t\"description\": \"Makes the effect hit everything around the point of impact. Required for auras, and the size of a missile\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"affects\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\t\t\"enemies\",\n\t\t\t\t\t\t\t\t\"allies\",\n\t\t\t\t\t\t\t\t\"all\"\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"description\": \"Who an effect with a radius applies to. Follows from the kind of effect when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"on\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"caster\",\n\t\t\t\t\t\t\t\"description\": \"Applies to the caster, wherever the ability was aimed\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"type\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"aura\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"buff\",\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t]\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"additionalProperties\": false,\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"duration\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\",\n\t\t\t\t\t\t\t\"description\": \"Seconds the buff lasts, when that is different from its usual duration\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"unit\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"ifBuff\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"description\": \"Only hits what carries this buff\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"only\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"heroes\",\n\t\t\t\t\t\t\t\"description\": \"Only hits heroes\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"kinds\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/EntityKind\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Only hits these kinds of things. Everything but resources when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"layers\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Layer\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Where what it hits has to be: on the ground, in the air. Both when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"radius\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\",\n\t\t\t\t\t\t\t\"description\": \"Makes the effect hit everything around the point of impact. Required for auras, and the size of a missile\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"affects\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\t\t\"enemies\",\n\t\t\t\t\t\t\t\t\"allies\",\n\t\t\t\t\t\t\t\t\"all\"\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"description\": \"Who an effect with a radius applies to. Follows from the kind of effect when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"on\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"caster\",\n\t\t\t\t\t\t\t\"description\": \"Applies to the caster, wherever the ability was aimed\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"type\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"spawn\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"type\",\n\t\t\t\t\t\t\"unit\"\n\t\t\t\t\t]\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"additionalProperties\": false,\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"buff\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"ifBuff\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"description\": \"Only hits what carries this buff\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"only\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"heroes\",\n\t\t\t\t\t\t\t\"description\": \"Only hits heroes\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"kinds\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/EntityKind\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Only hits these kinds of things. Everything but resources when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"layers\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Layer\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Where what it hits has to be: on the ground, in the air. Both when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"radius\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\",\n\t\t\t\t\t\t\t\"description\": \"Makes the effect hit everything around the point of impact. Required for auras, and the size of a missile\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"affects\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\t\t\"enemies\",\n\t\t\t\t\t\t\t\t\"allies\",\n\t\t\t\t\t\t\t\t\"all\"\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"description\": \"Who an effect with a radius applies to. Follows from the kind of effect when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"on\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"caster\",\n\t\t\t\t\t\t\t\"description\": \"Applies to the caster, wherever the ability was aimed\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"type\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"toggle\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"buff\",\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t]\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"additionalProperties\": false,\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"duration\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\",\n\t\t\t\t\t\t\t\"description\": \"Seconds the buff lasts, when that is different from its usual duration\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"ifBuff\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"description\": \"Only hits what carries this buff\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"only\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"heroes\",\n\t\t\t\t\t\t\t\"description\": \"Only hits heroes\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"kinds\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/EntityKind\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Only hits these kinds of things. Everything but resources when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"layers\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Layer\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Where what it hits has to be: on the ground, in the air. Both when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"radius\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\",\n\t\t\t\t\t\t\t\"description\": \"Makes the effect hit everything around the point of impact. Required for auras, and the size of a missile\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"affects\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\t\t\"enemies\",\n\t\t\t\t\t\t\t\t\"allies\",\n\t\t\t\t\t\t\t\t\"all\"\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"description\": \"Who an effect with a radius applies to. Follows from the kind of effect when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"on\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"caster\",\n\t\t\t\t\t\t\t\"description\": \"Applies to the caster, wherever the ability was aimed\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"type\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"reveal\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t]\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"additionalProperties\": false,\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"whileBuff\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"description\": \"For `onHit`: only while the attacker carries this buff\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"effects\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/EffectDef\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"What happens to what is hit. For missiles: everything they pass through. For `onHit`: the target of an attack. For `dash`: what is around where it ends. For `after`: what is there when the time is up. For `zone`: what is inside, over and over\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"ifBuff\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"description\": \"Only hits what carries this buff\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"only\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"heroes\",\n\t\t\t\t\t\t\t\"description\": \"Only hits heroes\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"kinds\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/EntityKind\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Only hits these kinds of things. Everything but resources when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"layers\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Layer\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Where what it hits has to be: on the ground, in the air. Both when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"radius\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\",\n\t\t\t\t\t\t\t\"description\": \"Makes the effect hit everything around the point of impact. Required for auras, and the size of a missile\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"affects\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\t\t\"enemies\",\n\t\t\t\t\t\t\t\t\"allies\",\n\t\t\t\t\t\t\t\t\"all\"\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"description\": \"Who an effect with a radius applies to. Follows from the kind of effect when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"on\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"caster\",\n\t\t\t\t\t\t\t\"description\": \"Applies to the caster, wherever the ability was aimed\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"type\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"onHit\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t]\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"ifBuff\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"description\": \"Only hits what carries this buff\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"only\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"heroes\",\n\t\t\t\t\t\t\t\"description\": \"Only hits heroes\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"kinds\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/EntityKind\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Only hits these kinds of things. Everything but resources when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"layers\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Layer\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Where what it hits has to be: on the ground, in the air. Both when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"radius\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\",\n\t\t\t\t\t\t\t\"description\": \"Makes the effect hit everything around the point of impact. Required for auras, and the size of a missile\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"affects\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\t\t\"enemies\",\n\t\t\t\t\t\t\t\t\"allies\",\n\t\t\t\t\t\t\t\t\"all\"\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"description\": \"Who an effect with a radius applies to. Follows from the kind of effect when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"on\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"caster\",\n\t\t\t\t\t\t\t\"description\": \"Applies to the caster, wherever the ability was aimed\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"type\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"resetAttack\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t]\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"additionalProperties\": false,\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"distance\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"ifBuff\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"description\": \"Only hits what carries this buff\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"only\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"heroes\",\n\t\t\t\t\t\t\t\"description\": \"Only hits heroes\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"kinds\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/EntityKind\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Only hits these kinds of things. Everything but resources when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"layers\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Layer\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Where what it hits has to be: on the ground, in the air. Both when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"radius\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\",\n\t\t\t\t\t\t\t\"description\": \"Makes the effect hit everything around the point of impact. Required for auras, and the size of a missile\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"affects\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\t\t\"enemies\",\n\t\t\t\t\t\t\t\t\"allies\",\n\t\t\t\t\t\t\t\t\"all\"\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"description\": \"Who an effect with a radius applies to. Follows from the kind of effect when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"on\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"caster\",\n\t\t\t\t\t\t\t\"description\": \"Applies to the caster, wherever the ability was aimed\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"type\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"blink\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t]\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"additionalProperties\": false,\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"to\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\t\t\"home\",\n\t\t\t\t\t\t\t\t\"point\",\n\t\t\t\t\t\t\t\t\"target\"\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"description\": \"For `teleport`: where to, however far: the caster's side's home (`homeOf`), where it was aimed, or by its target\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"ifBuff\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"description\": \"Only hits what carries this buff\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"only\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"heroes\",\n\t\t\t\t\t\t\t\"description\": \"Only hits heroes\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"kinds\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/EntityKind\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Only hits these kinds of things. Everything but resources when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"layers\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Layer\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Where what it hits has to be: on the ground, in the air. Both when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"radius\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\",\n\t\t\t\t\t\t\t\"description\": \"Makes the effect hit everything around the point of impact. Required for auras, and the size of a missile\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"affects\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\t\t\"enemies\",\n\t\t\t\t\t\t\t\t\"allies\",\n\t\t\t\t\t\t\t\t\"all\"\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"description\": \"Who an effect with a radius applies to. Follows from the kind of effect when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"on\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"caster\",\n\t\t\t\t\t\t\t\"description\": \"Applies to the caster, wherever the ability was aimed\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"type\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"teleport\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t]\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"additionalProperties\": false,\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"distance\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"leap\": {\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"description\": \"For `dash`: how high the caster rises on the way, which makes it a leap. For whoever draws it\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"climb\": {\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"description\": \"For a leap: how much higher than where it takes off the ground on its way may stand. What stands higher is a wall to it: it comes down in front of it. Over anything when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"speed\": {\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"description\": \"For missiles: how fast they travel, in units per second, and how far\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"effects\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/EffectDef\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"What happens to what is hit. For missiles: everything they pass through. For `onHit`: the target of an attack. For `dash`: what is around where it ends. For `after`: what is there when the time is up. For `zone`: what is inside, over and over\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"buff\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"description\": \"Id of the buff to put on the targets\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"ifBuff\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"description\": \"Only hits what carries this buff\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"only\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"heroes\",\n\t\t\t\t\t\t\t\"description\": \"Only hits heroes\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"kinds\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/EntityKind\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Only hits these kinds of things. Everything but resources when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"layers\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Layer\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Where what it hits has to be: on the ground, in the air. Both when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"radius\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\",\n\t\t\t\t\t\t\t\"description\": \"Makes the effect hit everything around the point of impact. Required for auras, and the size of a missile\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"affects\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\t\t\"enemies\",\n\t\t\t\t\t\t\t\t\"allies\",\n\t\t\t\t\t\t\t\t\"all\"\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"description\": \"Who an effect with a radius applies to. Follows from the kind of effect when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"on\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"caster\",\n\t\t\t\t\t\t\t\"description\": \"Applies to the caster, wherever the ability was aimed\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"type\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"dash\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t]\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"additionalProperties\": false,\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"distance\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"speed\": {\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"description\": \"For missiles: how fast they travel, in units per second, and how far\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"leap\": {\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"description\": \"For `dash`: how high the caster rises on the way, which makes it a leap. For whoever draws it\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"ifBuff\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"description\": \"Only hits what carries this buff\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"only\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"heroes\",\n\t\t\t\t\t\t\t\"description\": \"Only hits heroes\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"kinds\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/EntityKind\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Only hits these kinds of things. Everything but resources when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"layers\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Layer\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Where what it hits has to be: on the ground, in the air. Both when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"radius\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\",\n\t\t\t\t\t\t\t\"description\": \"Makes the effect hit everything around the point of impact. Required for auras, and the size of a missile\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"affects\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\t\t\"enemies\",\n\t\t\t\t\t\t\t\t\"allies\",\n\t\t\t\t\t\t\t\t\"all\"\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"description\": \"Who an effect with a radius applies to. Follows from the kind of effect when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"on\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"caster\",\n\t\t\t\t\t\t\t\"description\": \"Applies to the caster, wherever the ability was aimed\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"type\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"knockback\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t]\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"additionalProperties\": false,\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"ability\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"description\": \"For `refresh`: the ability that is ready again, when it is not the one the effect belongs to\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"ifBuff\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"description\": \"Only hits what carries this buff\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"only\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"heroes\",\n\t\t\t\t\t\t\t\"description\": \"Only hits heroes\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"kinds\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/EntityKind\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Only hits these kinds of things. Everything but resources when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"layers\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Layer\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Where what it hits has to be: on the ground, in the air. Both when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"radius\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\",\n\t\t\t\t\t\t\t\"description\": \"Makes the effect hit everything around the point of impact. Required for auras, and the size of a missile\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"affects\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\t\t\"enemies\",\n\t\t\t\t\t\t\t\t\"allies\",\n\t\t\t\t\t\t\t\t\"all\"\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"description\": \"Who an effect with a radius applies to. Follows from the kind of effect when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"on\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"caster\",\n\t\t\t\t\t\t\t\"description\": \"Applies to the caster, wherever the ability was aimed\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"type\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"refresh\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t]\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"additionalProperties\": false,\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"attack\": {\n\t\t\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\t\t\"description\": \"For `onKill`: only when it was an attack of the carrier that killed\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"effects\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/EffectDef\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"What happens to what is hit. For missiles: everything they pass through. For `onHit`: the target of an attack. For `dash`: what is around where it ends. For `after`: what is there when the time is up. For `zone`: what is inside, over and over\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"ifBuff\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"description\": \"Only hits what carries this buff\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"only\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"heroes\",\n\t\t\t\t\t\t\t\"description\": \"Only hits heroes\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"kinds\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/EntityKind\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Only hits these kinds of things. Everything but resources when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"layers\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Layer\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Where what it hits has to be: on the ground, in the air. Both when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"radius\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\",\n\t\t\t\t\t\t\t\"description\": \"Makes the effect hit everything around the point of impact. Required for auras, and the size of a missile\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"affects\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\t\t\"enemies\",\n\t\t\t\t\t\t\t\t\"allies\",\n\t\t\t\t\t\t\t\t\"all\"\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"description\": \"Who an effect with a radius applies to. Follows from the kind of effect when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"on\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"caster\",\n\t\t\t\t\t\t\t\"description\": \"Applies to the caster, wherever the ability was aimed\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"type\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"onKill\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t]\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"additionalProperties\": false,\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"effects\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/EffectDef\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"What happens to what is hit. For missiles: everything they pass through. For `onHit`: the target of an attack. For `dash`: what is around where it ends. For `after`: what is there when the time is up. For `zone`: what is inside, over and over\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"ifBuff\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"description\": \"Only hits what carries this buff\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"only\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"heroes\",\n\t\t\t\t\t\t\t\"description\": \"Only hits heroes\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"kinds\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/EntityKind\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Only hits these kinds of things. Everything but resources when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"layers\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Layer\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Where what it hits has to be: on the ground, in the air. Both when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"radius\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\",\n\t\t\t\t\t\t\t\"description\": \"Makes the effect hit everything around the point of impact. Required for auras, and the size of a missile\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"affects\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\t\t\"enemies\",\n\t\t\t\t\t\t\t\t\"allies\",\n\t\t\t\t\t\t\t\t\"all\"\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"description\": \"Who an effect with a radius applies to. Follows from the kind of effect when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"on\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"caster\",\n\t\t\t\t\t\t\t\"description\": \"Applies to the caster, wherever the ability was aimed\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"type\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"onCast\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t]\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"additionalProperties\": false,\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"effects\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/EffectDef\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"What happens to what is hit. For missiles: everything they pass through. For `onHit`: the target of an attack. For `dash`: what is around where it ends. For `after`: what is there when the time is up. For `zone`: what is inside, over and over\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"otherwise\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/EffectDef\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"For `charged`: what happens when the ability is not charged\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"ifBuff\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"description\": \"Only hits what carries this buff\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"only\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"heroes\",\n\t\t\t\t\t\t\t\"description\": \"Only hits heroes\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"kinds\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/EntityKind\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Only hits these kinds of things. Everything but resources when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"layers\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Layer\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Where what it hits has to be: on the ground, in the air. Both when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"radius\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\",\n\t\t\t\t\t\t\t\"description\": \"Makes the effect hit everything around the point of impact. Required for auras, and the size of a missile\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"affects\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\t\t\"enemies\",\n\t\t\t\t\t\t\t\t\"allies\",\n\t\t\t\t\t\t\t\t\"all\"\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"description\": \"Who an effect with a radius applies to. Follows from the kind of effect when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"on\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"caster\",\n\t\t\t\t\t\t\t\"description\": \"Applies to the caster, wherever the ability was aimed\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"type\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"charged\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t]\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"additionalProperties\": false,\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"delay\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\",\n\t\t\t\t\t\t\t\"description\": \"For missiles: seconds they take shape where they were cast before they set off. The caster is free to go\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"effects\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/EffectDef\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"What happens to what is hit. For missiles: everything they pass through. For `onHit`: the target of an attack. For `dash`: what is around where it ends. For `after`: what is there when the time is up. For `zone`: what is inside, over and over\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"impact\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"description\": \"For `after`: the name its arrival goes by for whoever draws it and plays its sound, where one ability sets off several that do not look alike: the last of a barrage. The ability's own when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"ifBuff\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"description\": \"Only hits what carries this buff\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"only\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"heroes\",\n\t\t\t\t\t\t\t\"description\": \"Only hits heroes\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"kinds\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/EntityKind\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Only hits these kinds of things. Everything but resources when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"layers\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Layer\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Where what it hits has to be: on the ground, in the air. Both when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"radius\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\",\n\t\t\t\t\t\t\t\"description\": \"Makes the effect hit everything around the point of impact. Required for auras, and the size of a missile\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"affects\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\t\t\"enemies\",\n\t\t\t\t\t\t\t\t\"allies\",\n\t\t\t\t\t\t\t\t\"all\"\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"description\": \"Who an effect with a radius applies to. Follows from the kind of effect when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"on\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"caster\",\n\t\t\t\t\t\t\t\"description\": \"Applies to the caster, wherever the ability was aimed\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"type\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"after\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t]\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"additionalProperties\": false,\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"duration\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\",\n\t\t\t\t\t\t\t\"description\": \"Seconds the buff lasts, when that is different from its usual duration\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"effects\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/EffectDef\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"What happens to what is hit. For missiles: everything they pass through. For `onHit`: the target of an attack. For `dash`: what is around where it ends. For `after`: what is there when the time is up. For `zone`: what is inside, over and over\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"onEnd\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/EffectDef\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"For missiles that were not stopped, and for zones: what happens where and when they end\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"ifBuff\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"description\": \"Only hits what carries this buff\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"only\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"heroes\",\n\t\t\t\t\t\t\t\"description\": \"Only hits heroes\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"kinds\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/EntityKind\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Only hits these kinds of things. Everything but resources when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"layers\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Layer\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Where what it hits has to be: on the ground, in the air. Both when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"radius\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\",\n\t\t\t\t\t\t\t\"description\": \"Makes the effect hit everything around the point of impact. Required for auras, and the size of a missile\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"affects\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\t\t\"enemies\",\n\t\t\t\t\t\t\t\t\"allies\",\n\t\t\t\t\t\t\t\t\"all\"\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"description\": \"Who an effect with a radius applies to. Follows from the kind of effect when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"on\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"caster\",\n\t\t\t\t\t\t\t\"description\": \"Applies to the caster, wherever the ability was aimed\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"type\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"zone\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t]\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"additionalProperties\": false,\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"duration\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\",\n\t\t\t\t\t\t\t\"description\": \"Seconds the buff lasts, when that is different from its usual duration\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"ifBuff\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"description\": \"Only hits what carries this buff\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"only\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"heroes\",\n\t\t\t\t\t\t\t\"description\": \"Only hits heroes\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"kinds\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/EntityKind\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Only hits these kinds of things. Everything but resources when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"layers\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Layer\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Where what it hits has to be: on the ground, in the air. Both when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"radius\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\",\n\t\t\t\t\t\t\t\"description\": \"Makes the effect hit everything around the point of impact. Required for auras, and the size of a missile\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"affects\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\t\t\"enemies\",\n\t\t\t\t\t\t\t\t\"allies\",\n\t\t\t\t\t\t\t\t\"all\"\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"description\": \"Who an effect with a radius applies to. Follows from the kind of effect when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"on\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"caster\",\n\t\t\t\t\t\t\t\"description\": \"Applies to the caster, wherever the ability was aimed\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"type\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"detect\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t]\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"additionalProperties\": false,\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"duration\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\",\n\t\t\t\t\t\t\t\"description\": \"Seconds the buff lasts, when that is different from its usual duration\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"count\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\",\n\t\t\t\t\t\t\t\"description\": \"For missiles: how many are sent, spread over an angle in radians. What one of them hit the others pass by\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"unit\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"ifBuff\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"description\": \"Only hits what carries this buff\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"only\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"heroes\",\n\t\t\t\t\t\t\t\"description\": \"Only hits heroes\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"kinds\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/EntityKind\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Only hits these kinds of things. Everything but resources when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"layers\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Layer\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Where what it hits has to be: on the ground, in the air. Both when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"radius\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\",\n\t\t\t\t\t\t\t\"description\": \"Makes the effect hit everything around the point of impact. Required for auras, and the size of a missile\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"affects\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\t\t\"enemies\",\n\t\t\t\t\t\t\t\t\"allies\",\n\t\t\t\t\t\t\t\t\"all\"\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"description\": \"Who an effect with a radius applies to. Follows from the kind of effect when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"on\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"caster\",\n\t\t\t\t\t\t\t\"description\": \"Applies to the caster, wherever the ability was aimed\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"type\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"raiseDead\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"type\",\n\t\t\t\t\t\t\"unit\"\n\t\t\t\t\t]\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"additionalProperties\": false,\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"dealt\": {\n\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"taken\": {\n\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"count\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\",\n\t\t\t\t\t\t\t\"description\": \"For missiles: how many are sent, spread over an angle in radians. What one of them hit the others pass by\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"duration\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\",\n\t\t\t\t\t\t\t\"description\": \"Seconds the buff lasts, when that is different from its usual duration\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"ifBuff\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"description\": \"Only hits what carries this buff\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"only\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"heroes\",\n\t\t\t\t\t\t\t\"description\": \"Only hits heroes\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"kinds\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/EntityKind\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Only hits these kinds of things. Everything but resources when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"layers\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Layer\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Where what it hits has to be: on the ground, in the air. Both when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"radius\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\",\n\t\t\t\t\t\t\t\"description\": \"Makes the effect hit everything around the point of impact. Required for auras, and the size of a missile\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"affects\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\t\t\"enemies\",\n\t\t\t\t\t\t\t\t\"allies\",\n\t\t\t\t\t\t\t\t\"all\"\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"description\": \"Who an effect with a radius applies to. Follows from the kind of effect when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"on\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"caster\",\n\t\t\t\t\t\t\t\"description\": \"Applies to the caster, wherever the ability was aimed\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"type\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"illusion\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t]\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"additionalProperties\": false,\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"amount\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"ifBuff\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"description\": \"Only hits what carries this buff\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"only\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"heroes\",\n\t\t\t\t\t\t\t\"description\": \"Only hits heroes\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"kinds\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/EntityKind\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Only hits these kinds of things. Everything but resources when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"layers\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Layer\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Where what it hits has to be: on the ground, in the air. Both when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"radius\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\",\n\t\t\t\t\t\t\t\"description\": \"Makes the effect hit everything around the point of impact. Required for auras, and the size of a missile\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"affects\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\t\t\"enemies\",\n\t\t\t\t\t\t\t\t\"allies\",\n\t\t\t\t\t\t\t\t\"all\"\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"description\": \"Who an effect with a radius applies to. Follows from the kind of effect when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"on\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"caster\",\n\t\t\t\t\t\t\t\"description\": \"Applies to the caster, wherever the ability was aimed\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"type\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"dispel\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t]\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"additionalProperties\": false,\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"sell\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"buy\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"rate\": {\n\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"amount\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"ifBuff\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"description\": \"Only hits what carries this buff\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"only\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"heroes\",\n\t\t\t\t\t\t\t\"description\": \"Only hits heroes\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"kinds\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/EntityKind\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Only hits these kinds of things. Everything but resources when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"layers\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Layer\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Where what it hits has to be: on the ground, in the air. Both when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"radius\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\",\n\t\t\t\t\t\t\t\"description\": \"Makes the effect hit everything around the point of impact. Required for auras, and the size of a missile\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"affects\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\t\t\"enemies\",\n\t\t\t\t\t\t\t\t\"allies\",\n\t\t\t\t\t\t\t\t\"all\"\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"description\": \"Who an effect with a radius applies to. Follows from the kind of effect when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"on\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"caster\",\n\t\t\t\t\t\t\t\"description\": \"Applies to the caster, wherever the ability was aimed\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"type\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"exchange\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"amount\",\n\t\t\t\t\t\t\"buy\",\n\t\t\t\t\t\t\"rate\",\n\t\t\t\t\t\t\"sell\",\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t]\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"additionalProperties\": false,\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"gain\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/StatChange\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"xp\": {\n\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"levels\": {\n\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"ifBuff\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"description\": \"Only hits what carries this buff\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"only\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"heroes\",\n\t\t\t\t\t\t\t\"description\": \"Only hits heroes\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"kinds\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/EntityKind\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Only hits these kinds of things. Everything but resources when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"layers\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Layer\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Where what it hits has to be: on the ground, in the air. Both when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"radius\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\",\n\t\t\t\t\t\t\t\"description\": \"Makes the effect hit everything around the point of impact. Required for auras, and the size of a missile\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"affects\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\t\t\"enemies\",\n\t\t\t\t\t\t\t\t\"allies\",\n\t\t\t\t\t\t\t\t\"all\"\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"description\": \"Who an effect with a radius applies to. Follows from the kind of effect when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"on\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"caster\",\n\t\t\t\t\t\t\t\"description\": \"Applies to the caster, wherever the ability was aimed\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"type\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"tome\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t]\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"additionalProperties\": false,\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"ratio\": {\n\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"amount\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"scaling\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaling\",\n\t\t\t\t\t\t\t\"description\": \"What the stats of the caster add to the amount: `{ abilityPower: 0.6 }` adds 60% of its ability power. `level` is for every level of the hero past the first\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"damageType\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/DamageType\",\n\t\t\t\t\t\t\t\"description\": \"For damage. Magic when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"ifBuff\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"description\": \"Only hits what carries this buff\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"only\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"heroes\",\n\t\t\t\t\t\t\t\"description\": \"Only hits heroes\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"kinds\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/EntityKind\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Only hits these kinds of things. Everything but resources when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"layers\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Layer\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Where what it hits has to be: on the ground, in the air. Both when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"radius\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\",\n\t\t\t\t\t\t\t\"description\": \"Makes the effect hit everything around the point of impact. Required for auras, and the size of a missile\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"affects\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\t\t\"enemies\",\n\t\t\t\t\t\t\t\t\"allies\",\n\t\t\t\t\t\t\t\t\"all\"\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"description\": \"Who an effect with a radius applies to. Follows from the kind of effect when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"on\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"caster\",\n\t\t\t\t\t\t\t\"description\": \"Applies to the caster, wherever the ability was aimed\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"type\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"manaBurn\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t]\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"additionalProperties\": false,\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"count\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\",\n\t\t\t\t\t\t\t\"description\": \"For missiles: how many are sent, spread over an angle in radians. What one of them hit the others pass by\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"ifBuff\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"description\": \"Only hits what carries this buff\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"only\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"heroes\",\n\t\t\t\t\t\t\t\"description\": \"Only hits heroes\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"kinds\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/EntityKind\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Only hits these kinds of things. Everything but resources when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"layers\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Layer\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"description\": \"Where what it hits has to be: on the ground, in the air. Both when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"radius\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\",\n\t\t\t\t\t\t\t\"description\": \"Makes the effect hit everything around the point of impact. Required for auras, and the size of a missile\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"affects\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\t\t\"enemies\",\n\t\t\t\t\t\t\t\t\"allies\",\n\t\t\t\t\t\t\t\t\"all\"\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"description\": \"Who an effect with a radius applies to. Follows from the kind of effect when left out\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"on\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"caster\",\n\t\t\t\t\t\t\t\"description\": \"Applies to the caster, wherever the ability was aimed\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"type\": {\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"townPortal\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t],\n\t\t\t\"description\": \"One thing an ability does: its `type`, which picks the handler and says which fields it has (`EffectTypes`)\"\n\t\t},\n\t\t\"BuffDef\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"stats\": {\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"additionalProperties\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"percent\": {\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"additionalProperties\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"basedOn\": {\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"description\": \"The definition this one is a copy of, made from the data (`copyOf` in a `data.json`, the editor's Copy as new): what it is drawn, heard and pictured as where the game gives it no look of its own\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"duration\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Seconds. Lasts until removed when left out\"\n\t\t\t\t},\n\t\t\t\t\"harmful\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"A debuff: by default it goes on enemies\"\n\t\t\t\t},\n\t\t\t\t\"stun\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"The unit cannot do anything. A function says so for one carrier, one moment\"\n\t\t\t\t},\n\t\t\t\t\"airborne\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Thrown into the air for as long as it lasts. Comes with a stun, the rest is for the eye\"\n\t\t\t\t},\n\t\t\t\t\"down\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Knocked down: it lies on the ground for as long as it lasts, and gets up at the end. For the eye, like `airborne`\"\n\t\t\t\t},\n\t\t\t\t\"grounds\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Brings what flies down: it counts as being on the ground, for whatever is aimed at it\"\n\t\t\t\t},\n\t\t\t\t\"invisible\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Its carrier is invisible to enemies for as long as it lasts, unless they find it (`detect`): a Wind Walk. A function says so for one carrier, one moment\"\n\t\t\t\t},\n\t\t\t\t\"root\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"The unit cannot move from the spot. It can still fight and cast. A function says so for one carrier, one moment\"\n\t\t\t\t},\n\t\t\t\t\"silence\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"The unit cannot use its abilities. What it carries and the spells every hero has still work. A function says which abilities it keeps it from (`(ability) => ability.tags?.includes('fire')`): the screen asks it too\"\n\t\t\t\t},\n\t\t\t\t\"shield\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/Scaled\",\n\t\t\t\t\t\"description\": \"Damage it takes in place of the unit, for as long as the buff lasts and there is something left of it\"\n\t\t\t\t},\n\t\t\t\t\"shieldScaling\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/Scaling\",\n\t\t\t\t\t\"description\": \"What the stats of whoever gave the shield add to it\"\n\t\t\t\t},\n\t\t\t\t\"recharge\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"The shield is as good as new once the unit has not been hurt for this many seconds\"\n\t\t\t\t},\n\t\t\t\t\"avoids\": {\n\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"description\": \"Tags of abilities that do nothing to who carries it, for as long as it does: `ground` for someone up in the air\"\n\t\t\t\t},\n\t\t\t\t\"disarm\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Who carries it does not attack while it lasts: burrowed, or disarmed. Or while its function says\"\n\t\t\t\t},\n\t\t\t\t\"spellImmune\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Who carries it is immune to spells while it lasts, as an avatar is (see `UnitTypeDef.spellImmune`). Or while its function says\"\n\t\t\t\t},\n\t\t\t\t\"dispellable\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Whether a dispel takes it off (see the effect `dispel`). Yes when left out: no for what nothing should end early. Decided in code for each dispel: by who dispels it (`by`), its level\"\n\t\t\t\t},\n\t\t\t\t\"maxStacks\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"How many of it one unit can carry at most. What puts it on says whether it adds one (`stack`)\"\n\t\t\t\t},\n\t\t\t\t\"onEnd\": {\n\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/EffectDef\"\n\t\t\t\t\t},\n\t\t\t\t\t\"description\": \"What happens around who carried it when its time is up, in the name of who put it there. Not when it is taken off\"\n\t\t\t\t},\n\t\t\t\t\"perStack\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Its stats count once for every stack it has (`stacks`), rather than once: \\\"each stack, -5% move speed\\\"\"\n\t\t\t\t},\n\t\t\t\t\"stacking\": {\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"strongest\",\n\t\t\t\t\t\t\"refresh\",\n\t\t\t\t\t\t\"perSource\",\n\t\t\t\t\t\t\"independent\"\n\t\t\t\t\t],\n\t\t\t\t\t\"description\": \"What happens when it is put on a unit that has it already. `strongest` (the default): the higher level wins, the longer among equals. `refresh`: the time starts over, the level the new one's. `perSource`: one of it for every unit that puts it on, each of its own (two poisons from two casters). `independent`: a new one every time. A function decides from the one there and the one coming: `replace` it, `renew` its time, `add` another, or `keep`\"\n\t\t\t\t},\n\t\t\t\t\"onApply\": {\n\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/EffectDef\"\n\t\t\t\t\t},\n\t\t\t\t\t\"description\": \"What happens as it is put on: effects in the name of who put it there (while they live), or code\"\n\t\t\t\t},\n\t\t\t\t\"onTick\": {\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"every\": {\n\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"effects\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/EffectDef\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"every\"\n\t\t\t\t\t],\n\t\t\t\t\t\"additionalProperties\": false,\n\t\t\t\t\t\"description\": \"What happens every `every` seconds while it lasts: effects in the name of who put it there (while they live), or code, which runs whoever is left\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\",\n\t\t\t\t\"name\"\n\t\t\t],\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"Something lasting on a unit that changes what it can do\"\n\t\t},\n\t\t\"ItemDef\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"level\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"How good it is, 1 and up (campaign plan GP3): what an item table draws by (`{ level: 3 }`)\"\n\t\t\t\t},\n\t\t\t\t\"class\": {\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"description\": \"Its kind of item, a word of the game's (`permanent`, `charged`, `powerup`): what an item table draws by too\"\n\t\t\t\t},\n\t\t\t\t\"basedOn\": {\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"description\": \"The definition this one is a copy of, made from the data (`copyOf` in a `data.json`, the editor's Copy as new): what it is drawn, heard and pictured as where the game gives it no look of its own\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"cost\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/Cost\"\n\t\t\t\t},\n\t\t\t\t\"charges\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Uses before it is gone. Items without charges stay\"\n\t\t\t\t},\n\t\t\t\t\"ability\": {\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"description\": \"Id of the ability the item gives: using the item casts it, without mana\"\n\t\t\t\t},\n\t\t\t\t\"passive\": {\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"description\": \"Id of an ability whose passive part works for whoever carries the item\"\n\t\t\t\t},\n\t\t\t\t\"from\": {\n\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"description\": \"Items it is made of. Those the buyer carries are used up and taken off the price, which is for all of it\"\n\t\t\t\t},\n\t\t\t\t\"tier\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/ItemTier\",\n\t\t\t\t\t\"description\": \"Where it goes in a shop: consumable, starter, basic, epic, legendary\"\n\t\t\t\t},\n\t\t\t\t\"bonus\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/StatChange\",\n\t\t\t\t\t\"description\": \"Applies for as long as the item is carried\"\n\t\t\t\t},\n\t\t\t\t\"stock\": {\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"max\": {\n\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"restock\": {\n\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"max\",\n\t\t\t\t\t\t\"restock\"\n\t\t\t\t\t],\n\t\t\t\t\t\"additionalProperties\": false,\n\t\t\t\t\t\"description\": \"How many a shop has at most, and the seconds it takes to get one back. As many as anyone wants when left out\"\n\t\t\t\t},\n\t\t\t\t\"hotkey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"powerUp\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Used as it is picked up, by walking over it or by the order, and never carried: a rune, a tome. Its ability is cast by whoever picked it. In a game with the runes pack (`withRunes`, whose `powerUp` says it in code)\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\",\n\t\t\t\t\"name\",\n\t\t\t\t\"cost\"\n\t\t\t],\n\t\t\t\"additionalProperties\": false\n\t\t},\n\t\t\"ItemTier\": {\n\t\t\t\"anyOf\": [\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"consumable\",\n\t\t\t\t\t\t\"starter\",\n\t\t\t\t\t\t\"basic\",\n\t\t\t\t\t\t\"epic\",\n\t\t\t\t\t\t\"legendary\"\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t],\n\t\t\t\"description\": \"Where an item is in a shop. The client's tiers, or a game's own (`GameView.shopTiers`)\"\n\t\t},\n\t\t\"UpgradeDef\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"basedOn\": {\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"description\": \"The definition this one is a copy of, made from the data (`copyOf` in a `data.json`, the editor's Copy as new): what it is drawn, heard and pictured as where the game gives it no look of its own\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"cost\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/Cost\",\n\t\t\t\t\t\"description\": \"Paid for every level\"\n\t\t\t\t},\n\t\t\t\t\"researchTime\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Seconds per level\"\n\t\t\t\t},\n\t\t\t\t\"maxLevel\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"requires\": {\n\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"description\": \"Building types needed, one list per level. Levels past the end of the list need nothing\"\n\t\t\t\t},\n\t\t\t\t\"race\": {\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"description\": \"The race it is of (`GameModule.races`): only its players may research it. Everyone's when left out\"\n\t\t\t\t},\n\t\t\t\t\"hotkey\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"effects\": {\n\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/UpgradeEffect\"\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\",\n\t\t\t\t\"name\",\n\t\t\t\t\"cost\",\n\t\t\t\t\"researchTime\",\n\t\t\t\t\"effects\"\n\t\t\t],\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"Research that improves a player's units for the rest of the game\"\n\t\t},\n\t\t\"UpgradeEffect\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"stats\": {\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"attackDamage\": {\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"description\": \"What a basic attack deals, before the armor of the target\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"attackSpeed\": {\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"description\": \"Attacks per second\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"attackRange\": {\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"description\": \"Measured between the edges of attacker and target\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"health\": {\n\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"armor\": {\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"description\": \"Against physical damage: 100 of it halve what arrives\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"magicResist\": {\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"description\": \"The same against magic damage\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"healthRegen\": {\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"description\": \"Per second\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"mana\": {\n\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"manaRegen\": {\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"description\": \"Per second\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"moveSpeed\": {\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"description\": \"Units per second. Who has none cannot move\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"sight\": {\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"description\": \"How far it sees. At night the unit type says how far that is\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"additionalProperties\": false\n\t\t\t\t},\n\t\t\t\t\"percent\": {\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"attackDamage\": {\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"description\": \"What a basic attack deals, before the armor of the target\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"attackSpeed\": {\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"description\": \"Attacks per second\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"attackRange\": {\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"description\": \"Measured between the edges of attacker and target\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"health\": {\n\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"armor\": {\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"description\": \"Against physical damage: 100 of it halve what arrives\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"magicResist\": {\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"description\": \"The same against magic damage\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"healthRegen\": {\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"description\": \"Per second\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"mana\": {\n\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"manaRegen\": {\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"description\": \"Per second\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"moveSpeed\": {\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"description\": \"Units per second. Who has none cannot move\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"sight\": {\n\t\t\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\t\t\"description\": \"How far it sees. At night the unit type says how far that is\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"additionalProperties\": false\n\t\t\t\t},\n\t\t\t\t\"units\": {\n\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"description\": \"Unit types that benefit, or a function of the type. Everything the player owns when left out\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"What a level of research changes\"\n\t\t},\n\t\t\"RaceDef\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"description\": \"What players are shown: in the lobby, on the scores\"\n\t\t\t\t},\n\t\t\t\t\"basedOn\": {\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"description\": \"The definition this one is a copy of, made from the data (`copyOf` in a `data.json`, the editor's Copy as new): what it is drawn, heard and pictured as where the game gives it no look of its own\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"description\": \"What it is like, for a tooltip\"\n\t\t\t\t},\n\t\t\t\t\"icon\": {\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"description\": \"Its picture, by the id the game's view knows icons by\"\n\t\t\t\t},\n\t\t\t\t\"resources\": {\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"additionalProperties\": {\n\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t},\n\t\t\t\t\t\"description\": \"What its players start with of each resource, by resource id, over what the game starts everyone with (`ResourceDef.start`). Not for a player given resources of their own (`PlayerInit.resources`, a map's)\"\n\t\t\t\t},\n\t\t\t\t\"start\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/StartDef\",\n\t\t\t\t\t\"description\": \"What its players get at the start of a game on a map, in place of the game's (`GameModule.start`): its own hall and workers, as data (`StartData`: what a race made in the Data panel has, `start.hall` and the rest editable there) or as code (`StartCode`)\"\n\t\t\t\t},\n\t\t\t\t\"hooks\": {\n\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/RaceHookDef\"\n\t\t\t\t\t},\n\t\t\t\t\t\"description\": \"Hook handlers of its own, run only for what is of its players (`RaceHookDef`): how it gathers, where it may build. With these a race plays differently with no test of the race in the game's code\"\n\t\t\t\t},\n\t\t\t\t\"inherits\": {\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"hooks\": {\n\t\t\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"onStart\": {\n\t\t\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"additionalProperties\": false,\n\t\t\t\t\t\"description\": \"What a race made as a copy of another (`basedOn`: `copyOf` in a `data.json`, the editor's Copy as new) keeps of the original's code: its `hooks` and its `onStart`, each kept unless said `false` here. A copy keeps both when this is left out, so a race copied from the undead gathers at a cursed mine and builds on blight as they do; one that says `hooks: false` plays by the game's rules alone. Its `start` is data, the copy's own to change; a `start` in code is kept whole. Two toggles in the Data panel, on a copy\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\",\n\t\t\t\t\"name\"\n\t\t\t],\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"A side a player can play, with a roster of its own: melee's humans and undead. Optional: a game that lists none (`GameModule.races`) has no race anywhere, no choice of one in the lobby and nothing kept from anyone. A unit type or an upgrade of a race (`UnitTypeDef.race`, `UpgradeDef.race`) is only its players' to make; one of none, everyone's. Each player has one (`raceOf`): chosen in the lobby (a seat setting the server offers by itself, `seatSettingsOf`), given to the world as it is made (`PlayerInit.race`, `MapWorldOptions.races`), or changed by the rules (`setRace`)\"\n\t\t},\n\t\t\"StartDef\": {\n\t\t\t\"anyOf\": [\n\t\t\t\t{\n\t\t\t\t\t\"$ref\": \"#/definitions/StartCode\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"$ref\": \"#/definitions/StartData\"\n\t\t\t\t}\n\t\t\t],\n\t\t\t\"description\": \"What each player gets at the beginning of a game on a map, for players the map places nothing for: as data (`StartData`: a hall, workers and what else by unit type, editable in the Data panel) or as code (`StartCode`). What they start with of each resource is in its `ResourceDef`\"\n\t\t},\n\t\t\"StartCode\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"A start in code: puts down a base at the start location, whatever it likes\"\n\t\t},\n\t\t\"StartData\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"hall\": {\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"description\": \"The unit type put down at the start location: a town hall\"\n\t\t\t\t},\n\t\t\t\t\"workers\": {\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"type\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"count\": {\n\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"type\",\n\t\t\t\t\t\t\"count\"\n\t\t\t\t\t],\n\t\t\t\t\t\"additionalProperties\": false,\n\t\t\t\t\t\"description\": \"The workers put down beside it, facing the closest gold mine\"\n\t\t\t\t},\n\t\t\t\t\"units\": {\n\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\t\"type\": {\n\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"count\": {\n\t\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\t\"type\",\n\t\t\t\t\t\t\t\"count\"\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"additionalProperties\": false\n\t\t\t\t\t},\n\t\t\t\t\t\"description\": \"What else is put down with the workers, in the same row after them: a race's first fighter\"\n\t\t\t\t},\n\t\t\t\t\"onMine\": {\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"description\": \"What the race builds on the closest gold mine to gather it, put down there already standing (the undead's cursed mine). With it, the closest mine is found at any distance; without, the workers face a mine within 20 cells\"\n\t\t\t\t},\n\t\t\t\t\"distance\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"How far from the hall the workers stand: 5 cells when left out\"\n\t\t\t\t},\n\t\t\t\t\"spread\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"The angle between two workers in the row, in radians: 0.35 when left out\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"hall\",\n\t\t\t\t\"workers\"\n\t\t\t],\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"A start as data: a hall at the start location and workers on the side of the closest gold mine, as in a classic melee game (`startingBase` puts it down). Every id is a unit type of the game's (`checkRaces` says where one is not)\"\n\t\t},\n\t\t\"RaceHookDef\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {},\n\t\t\t\"description\": \"A hook handler of a race (`RaceDef.hooks`): run only where the event is of one of the race's players. Whose an event is, is `of(event)` where it is given, or else its `player`, or else the owner of the first there is of its `entity`, `caster`, `attacker`, `worker`, `buyer`, `hero`, `producer`, `source`, `target`: the one doing it before the one it is done to (a damage is its source's)\"\n\t\t},\n\t\t\"RulesInit\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"properties\": {\n\t\t\t\t\"score\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/ScoreRules\"\n\t\t\t\t},\n\t\t\t\t\"hud\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/HudSharing\"\n\t\t\t\t},\n\t\t\t\t\"supply\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/RulesPart%3Cdef-interface-src_rules.ts-6406-7192-src_rules.ts-0-2567620292202711967698989%3E\"\n\t\t\t\t},\n\t\t\t\t\"production\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/RulesPart%3Cdef-interface-src_rules.ts-7192-7817-src_rules.ts-0-256769690053192058131377%3E\"\n\t\t\t\t},\n\t\t\t\t\"economy\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/RulesPart%3Cdef-interface-src_rules.ts-7817-8384-src_rules.ts-0-25676871182655808602831%3E\"\n\t\t\t\t},\n\t\t\t\t\"combat\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/RulesPart%3Cdef-interface-src_rules.ts-8384-12915-src_rules.ts-0-2567620803371672015716859%3E\"\n\t\t\t\t},\n\t\t\t\t\"movement\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/RulesPart%3Cdef-interface-src_rules.ts-12915-15119-src_rules.ts-0-2567619627991872051239824%3E\"\n\t\t\t\t},\n\t\t\t\t\"fog\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/RulesPart%3Cdef-interface-src_rules.ts-2336-3434-src_rules.ts-0-256761516734472119914999%3E\"\n\t\t\t\t},\n\t\t\t\t\"talk\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/RulesPart%3Cdef-interface-src_rules.ts-15119-16441-src_rules.ts-0-256767575302061161883220%3E\"\n\t\t\t\t},\n\t\t\t\t\"camps\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/RulesPart%3Cdef-interface-src_rules.ts-22230-22604-src_rules.ts-0-25676594161992489921438%3E\"\n\t\t\t\t},\n\t\t\t\t\"heroes\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/RulesPart%3Cdef-interface-src_hero.ts-953-2062-src_hero.ts-0-1454210919897661340881447%3E\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"description\": \"What a game says of its rules (`GameModule.rules`), or a pack (`GamePack.rules`): any of them, a group in part (`{ supply: { max: 200 } }`), the engine's for the rest. The damage table, the score and the HUD's sharing are given whole\"\n\t\t},\n\t\t\"ScoreRules\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"stats\": {\n\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/ScoreStatDef\"\n\t\t\t\t\t},\n\t\t\t\t\t\"description\": \"The stats, or what makes them of the game (the engine's set makes one of each resource)\"\n\t\t\t\t},\n\t\t\t\t\"every\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Seconds between two points of the timeline: 10 when left out, 0 for no timeline\"\n\t\t\t\t},\n\t\t\t\t\"maxPoints\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Points the timeline keeps at most: past it, every other point goes and they are twice as far apart, so a long game keeps its whole story in as much room. 360 when left out\"\n\t\t\t\t},\n\t\t\t\t\"live\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/ScoreAudience\",\n\t\t\t\t\t\"description\": \"Who is told a player's numbers while the game goes on: their side (`allies`) when left out. An observer is told everyone's\"\n\t\t\t\t},\n\t\t\t\t\"end\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/ScoreAudience\",\n\t\t\t\t\t\"description\": \"Who is told them once the game is decided: `everyone` when left out\"\n\t\t\t\t},\n\t\t\t\t\"refresh\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Seconds between two tellings of the numbers to the screens while the game goes on: 1 when left out\"\n\t\t\t\t},\n\t\t\t\t\"categories\": {\n\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\t\"id\": {\n\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"label\": {\n\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\t\"id\",\n\t\t\t\t\t\t\t\"label\"\n\t\t\t\t\t\t],\n\t\t\t\t\t\t\"additionalProperties\": false\n\t\t\t\t\t},\n\t\t\t\t\t\"description\": \"The tabs of the score screen, in order, with their names: those of the stats, in the order met, when left out\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"stats\"\n\t\t\t],\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"What a game counts of its players (`GameRules.score`): the stats, how often the timeline is sampled, and who is told what while the game goes on and after it ended. `presetScore()` makes one of the engine's stats\"\n\t\t},\n\t\t\"ScoreStatDef\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"description\": \"Its own, among the game's stats: `world.score.add(player, id, …)` names it\"\n\t\t\t\t},\n\t\t\t\t\"label\": {\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"description\": \"What it is called on a screen: its id when left out\"\n\t\t\t\t},\n\t\t\t\t\"description\": {\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"description\": \"What the screen says it is, in a tip\"\n\t\t\t\t},\n\t\t\t\t\"category\": {\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"description\": \"The tab of the score screen it is in: `units`, `heroes`, `resources`, `combat`, or the game's own. `other` when left out\"\n\t\t\t\t},\n\t\t\t\t\"kind\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/ScoreKind\",\n\t\t\t\t\t\"description\": \"How it adds up: `sum` when left out, `last` for one that only samples\"\n\t\t\t\t},\n\t\t\t\t\"from\": {\n\t\t\t\t\t\"anyOf\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/ScoreSource\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/ScoreSource\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"description\": \"The events it is counted from\"\n\t\t\t\t},\n\t\t\t\t\"byType\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Kept by what it was counted of too (`ScoreCount.type`): units killed by unit type, abilities cast by ability\"\n\t\t\t\t},\n\t\t\t\t\"timeline\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"On the timeline: sampled every `ScoreRules.every` seconds, for a screen's graph\"\n\t\t\t\t},\n\t\t\t\t\"format\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/ScoreFormat\",\n\t\t\t\t\t\"description\": \"How its number is written: `number` (when left out), `decimal`, `percent` (0 to 1), `time` (seconds)\"\n\t\t\t\t},\n\t\t\t\t\"hidden\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Counted, and not on the score screen: for the game's own use (an achievement, a mission's goal)\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\"\n\t\t\t],\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"A number the score keeps for each player (`GameRules.score`, `defineStat`): what it is called, how it adds up, where it comes from, and how it is shown. One with no source is counted by the game's code alone (`world.score.add`)\"\n\t\t},\n\t\t\"ScoreKind\": {\n\t\t\t\"type\": \"string\",\n\t\t\t\"enum\": [\n\t\t\t\t\"sum\",\n\t\t\t\t\"count\",\n\t\t\t\t\"max\",\n\t\t\t\t\"min\",\n\t\t\t\t\"last\"\n\t\t\t],\n\t\t\t\"description\": \"How a stat takes what it is told: `sum` adds it up (the default), `count` adds one each time whatever the amount, `max` keeps the most it was told, `min` the least, `last` what it was told last (a gauge: the army now)\"\n\t\t},\n\t\t\"ScoreSource\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"event\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/EventKey%3Cany%3E\",\n\t\t\t\t\t\"description\": \"The event: one of the engine's (`entityDied`) or of the game's own (`defineEvent`)\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"event\"\n\t\t\t],\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"Where a stat's numbers come from: an event of the world's (`'entityDied'`) or of the game's own (`defineEvent`), and what it counts of each (`scoreOn`)\"\n\t\t},\n\t\t\"EventKey<any>\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"replicate\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/EventAudience%3Cany%3E\",\n\t\t\t\t\t\"description\": \"Sent to the screens of these players, in their snapshots. Only to whoever listens in the world when left out\"\n\t\t\t\t},\n\t\t\t\t\"payload\": {\n\t\t\t\t\t\"description\": \"Only there for the type of the payload\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\"\n\t\t\t],\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"A typed key for an event of a game's own, made with `defineEvent`\"\n\t\t},\n\t\t\"EventAudience<any>\": {\n\t\t\t\"type\": \"string\",\n\t\t\t\"const\": \"everyone\",\n\t\t\t\"description\": \"Who of the players gets an event of a game's own on their screen (see `defineEvent`): everyone, or those a function picks, from what the event says and what the seat may know (`SessionView`: its player, `knows`, `sees`)\"\n\t\t},\n\t\t\"ScoreFormat\": {\n\t\t\t\"type\": \"string\",\n\t\t\t\"enum\": [\n\t\t\t\t\"number\",\n\t\t\t\t\"decimal\",\n\t\t\t\t\"percent\",\n\t\t\t\t\"time\"\n\t\t\t],\n\t\t\t\"description\": \"How a stat's number reads on a screen: a whole `number` (the default), a `decimal` one, a `percent` (0..1), or `time` (seconds, as 3:05). A screen's own (`GameView.hud.score.format`) goes over it\"\n\t\t},\n\t\t\"ScoreAudience\": {\n\t\t\t\"type\": \"string\",\n\t\t\t\"enum\": [\n\t\t\t\t\"own\",\n\t\t\t\t\"allies\",\n\t\t\t\t\"everyone\",\n\t\t\t\t\"none\"\n\t\t\t],\n\t\t\t\"description\": \"Who of the players is told a player's numbers: that player alone (`own`), their side (`allies`), `everyone`, or `none`; or a function of the world, who is told (`undefined` for an observer) and whose numbers they are\"\n\t\t},\n\t\t\"HudSharing\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"shareBuffTimes\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/HudShareRule%3C(boolean%7C%22allies%22)%3E\",\n\t\t\t\t\t\"description\": \"The seconds left of its buffs (`EntityState.buffTimes`): to everyone who sees it (`true`), to its owner and their allies (`'allies'`), or to no one (`false`, when left out)\"\n\t\t\t\t},\n\t\t\t\t\"shareAbilities\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/HudShareRule%3C(%22none%22%7C%22icons%22%7C%22cooldowns%22)%3E\",\n\t\t\t\t\t\"description\": \"Its abilities (`EntityState.abilities`) to everyone who sees it, its side's or not: those it has learned and their levels (`'icons'`), with how much of their cooldowns is left too (`'cooldowns'`), or none (`'none'`, when left out: its side has them in `own` all the same)\"\n\t\t\t\t},\n\t\t\t\t\"shareXp\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/HudShareRule%3Cboolean%3E\",\n\t\t\t\t\t\"description\": \"A hero's experience (`HeroSent.xp`) to everyone who sees it. Its side's alone when left out\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"What of a unit the players who see it are told beyond what they always are (`GameRules.hud`), each for the plates over it to show. What is not shared never reaches a player, so no screen can show it. A game's own data goes in a component with `replicate` (the client's `hudData` reads it)\"\n\t\t},\n\t\t\"HudShareRule<(boolean|\\\"allies\\\")>\": {\n\t\t\t\"anyOf\": [\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"const\": \"allies\"\n\t\t\t\t}\n\t\t\t],\n\t\t\t\"description\": \"A rule of `HudSharing`: one value for every unit, or a function deciding it for each (only heroes', only a boss's)\"\n\t\t},\n\t\t\"HudShareRule<(\\\"none\\\"|\\\"icons\\\"|\\\"cooldowns\\\")>\": {\n\t\t\t\"type\": \"string\",\n\t\t\t\"enum\": [\n\t\t\t\t\"none\",\n\t\t\t\t\"icons\",\n\t\t\t\t\"cooldowns\"\n\t\t\t],\n\t\t\t\"description\": \"A rule of `HudSharing`: one value for every unit, or a function deciding it for each (only heroes', only a boss's)\"\n\t\t},\n\t\t\"HudShareRule<boolean>\": {\n\t\t\t\"type\": \"boolean\",\n\t\t\t\"description\": \"A rule of `HudSharing`: one value for every unit, or a function deciding it for each (only heroes', only a boss's)\"\n\t\t},\n\t\t\"RulesPart<def-interface-src_rules.ts-6406-7192-src_rules.ts-0-2567620292202711967698989>\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"max\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/RulesPart%3Cnumber1860156146%3E\"\n\t\t\t\t},\n\t\t\t\t\"pools\": {\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"additionalProperties\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/RulesPart%3Cstructure-src_rules.ts-6876-6906-src_rules.ts-6861-6907-src_rules.ts-6578-6907-src_rules.ts-6406-7192-src_rules.ts-0-25676%3E\"\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"What is said of a group of the rules: any of it, a group in it in part as well. A function, a list and a table whole\"\n\t\t},\n\t\t\"RulesPart<number1860156146>\": {\n\t\t\t\"type\": \"number\",\n\t\t\t\"description\": \"What is said of a group of the rules: any of it, a group in it in part as well. A function, a list and a table whole\"\n\t\t},\n\t\t\"RulesPart<structure-src_rules.ts-6876-6906-src_rules.ts-6861-6907-src_rules.ts-6578-6907-src_rules.ts-6406-7192-src_rules.ts-0-25676>\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/RulesPart%3Cstring%3E\"\n\t\t\t\t},\n\t\t\t\t\"max\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/RulesPart%3Cnumber%3E\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"What is said of a group of the rules: any of it, a group in it in part as well. A function, a list and a table whole\"\n\t\t},\n\t\t\"RulesPart<string>\": {\n\t\t\t\"type\": \"string\",\n\t\t\t\"description\": \"What is said of a group of the rules: any of it, a group in it in part as well. A function, a list and a table whole\"\n\t\t},\n\t\t\"RulesPart<number>\": {\n\t\t\t\"type\": \"number\",\n\t\t\t\"description\": \"What is said of a group of the rules: any of it, a group in it in part as well. A function, a list and a table whole\"\n\t\t},\n\t\t\"RulesPart<def-interface-src_rules.ts-7192-7817-src_rules.ts-0-256769690053192058131377>\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"queue\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/RulesPart%3Cnumber2067800667%3E\"\n\t\t\t\t},\n\t\t\t\t\"cancelRefund\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/RulesPart%3Cnumber1635358800%3E\"\n\t\t\t\t},\n\t\t\t\t\"constructionHealth\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/RulesPart%3Cnumber1408574317%3E\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"What is said of a group of the rules: any of it, a group in it in part as well. A function, a list and a table whole\"\n\t\t},\n\t\t\"RulesPart<number2067800667>\": {\n\t\t\t\"type\": \"number\",\n\t\t\t\"description\": \"What is said of a group of the rules: any of it, a group in it in part as well. A function, a list and a table whole\"\n\t\t},\n\t\t\"RulesPart<number1635358800>\": {\n\t\t\t\"type\": \"number\",\n\t\t\t\"description\": \"What is said of a group of the rules: any of it, a group in it in part as well. A function, a list and a table whole\"\n\t\t},\n\t\t\"RulesPart<number1408574317>\": {\n\t\t\t\"type\": \"number\",\n\t\t\t\"description\": \"What is said of a group of the rules: any of it, a group in it in part as well. A function, a list and a table whole\"\n\t\t},\n\t\t\"RulesPart<def-interface-src_rules.ts-7817-8384-src_rules.ts-0-25676871182655808602831>\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"sellPrice\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/RulesPart%3Cnumber236571108%3E\"\n\t\t\t\t},\n\t\t\t\t\"repairCost\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/RulesPart%3Cnumber385641258%3E\"\n\t\t\t\t},\n\t\t\t\t\"tradeTax\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/RulesPart%3Cnumber500452615%3E\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"What is said of a group of the rules: any of it, a group in it in part as well. A function, a list and a table whole\"\n\t\t},\n\t\t\"RulesPart<number236571108>\": {\n\t\t\t\"type\": \"number\",\n\t\t\t\"description\": \"What is said of a group of the rules: any of it, a group in it in part as well. A function, a list and a table whole\"\n\t\t},\n\t\t\"RulesPart<number385641258>\": {\n\t\t\t\"type\": \"number\",\n\t\t\t\"description\": \"What is said of a group of the rules: any of it, a group in it in part as well. A function, a list and a table whole\"\n\t\t},\n\t\t\"RulesPart<number500452615>\": {\n\t\t\t\"type\": \"number\",\n\t\t\t\"description\": \"What is said of a group of the rules: any of it, a group in it in part as well. A function, a list and a table whole\"\n\t\t},\n\t\t\"RulesPart<def-interface-src_rules.ts-8384-12915-src_rules.ts-0-2567620803371672015716859>\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"attackSpeedLimits\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/RulesPart%3C%5Bnumber%2Cnumber%5D131060964%3E\"\n\t\t\t\t},\n\t\t\t\t\"corpseTime\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/RulesPart%3Cnumber202117478%3E\"\n\t\t\t\t},\n\t\t\t\t\"damageTable\": {\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"attacks\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/RulesPart%3Cstring%5B%5D%3E\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"armours\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/RulesPart%3Cstring%5B%5D%3E\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"table\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/RulesPart%3Cnumber%5B%5D%5B%5D%3E\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"additionalProperties\": false\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"What is said of a group of the rules: any of it, a group in it in part as well. A function, a list and a table whole\"\n\t\t},\n\t\t\"RulesPart<[number,number]131060964>\": {\n\t\t\t\"type\": \"array\",\n\t\t\t\"items\": {\n\t\t\t\t\"type\": \"number\"\n\t\t\t},\n\t\t\t\"minItems\": 2,\n\t\t\t\"maxItems\": 2,\n\t\t\t\"description\": \"What is said of a group of the rules: any of it, a group in it in part as well. A function, a list and a table whole\"\n\t\t},\n\t\t\"RulesPart<number202117478>\": {\n\t\t\t\"type\": \"number\",\n\t\t\t\"description\": \"What is said of a group of the rules: any of it, a group in it in part as well. A function, a list and a table whole\"\n\t\t},\n\t\t\"RulesPart<string[]>\": {\n\t\t\t\"type\": \"array\",\n\t\t\t\"items\": {\n\t\t\t\t\"type\": \"string\"\n\t\t\t},\n\t\t\t\"description\": \"What is said of a group of the rules: any of it, a group in it in part as well. A function, a list and a table whole\"\n\t\t},\n\t\t\"RulesPart<number[][]>\": {\n\t\t\t\"type\": \"array\",\n\t\t\t\"items\": {\n\t\t\t\t\"type\": \"array\",\n\t\t\t\t\"items\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"description\": \"What is said of a group of the rules: any of it, a group in it in part as well. A function, a list and a table whole\"\n\t\t},\n\t\t\"RulesPart<def-interface-src_rules.ts-12915-15119-src_rules.ts-0-2567619627991872051239824>\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"together\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"What is said of a group of the rules: any of it, a group in it in part as well. A function, a list and a table whole\"\n\t\t},\n\t\t\"RulesPart<def-interface-src_rules.ts-2336-3434-src_rules.ts-0-256761516734472119914999>\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"nightSight\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/RulesPart%3Cnumber1727028879%3E\"\n\t\t\t\t},\n\t\t\t\t\"explored\": {\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"remembered\",\n\t\t\t\t\t\t\"nothing\"\n\t\t\t\t\t]\n\t\t\t\t},\n\t\t\t\t\"startExplored\": {\n\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t},\n\t\t\t\t\"mask\": {\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"black\",\n\t\t\t\t\t\t\"dim\"\n\t\t\t\t\t]\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"What is said of a group of the rules: any of it, a group in it in part as well. A function, a list and a table whole\"\n\t\t},\n\t\t\"RulesPart<number1727028879>\": {\n\t\t\t\"type\": \"number\",\n\t\t\t\"description\": \"What is said of a group of the rules: any of it, a group in it in part as well. A function, a list and a table whole\"\n\t\t},\n\t\t\"RulesPart<def-interface-src_rules.ts-15119-16441-src_rules.ts-0-256767575302061161883220>\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"pings\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/RulesPart%3Cstructure-src_rules.ts-15901-16099-src_rules.ts-15214-16099-src_rules.ts-15119-16441-src_rules.ts-0-25676624849888%3E\"\n\t\t\t\t},\n\t\t\t\t\"lines\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/RulesPart%3Cstructure-src_rules.ts-16207-16256-src_rules.ts-16099-16256-src_rules.ts-15119-16441-src_rules.ts-0-25676381541611%3E\"\n\t\t\t\t},\n\t\t\t\t\"pauses\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/RulesPart%3Cstructure-src_rules.ts-16405-16439-src_rules.ts-16256-16439-src_rules.ts-15119-16441-src_rules.ts-0-256761718446262%3E\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"What is said of a group of the rules: any of it, a group in it in part as well. A function, a list and a table whole\"\n\t\t},\n\t\t\"RulesPart<structure-src_rules.ts-15901-16099-src_rules.ts-15214-16099-src_rules.ts-15119-16441-src_rules.ts-0-25676624849888>\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"most\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/RulesPart%3Cnumber%3E\"\n\t\t\t\t},\n\t\t\t\t\"within\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/RulesPart%3Cnumber%3E\"\n\t\t\t\t},\n\t\t\t\t\"kinds\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/RulesPart%3Cstring%5B%5D%3E\"\n\t\t\t\t},\n\t\t\t\t\"meanings\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/RulesPart%3Cindexed-type-140537781-73964-74003-140537781-73899-74004-140537781-0-218439%3Calias-140537781-74138-74264-140537781-0-218439%3Cstring%2C(%22help%22%7C%22attack%22%7C%22defend%22%7C%22rally%22%7C%22ignore%22)%3E%3E%3E\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"What is said of a group of the rules: any of it, a group in it in part as well. A function, a list and a table whole\"\n\t\t},\n\t\t\"RulesPart<indexed-type-140537781-73964-74003-140537781-73899-74004-140537781-0-218439<alias-140537781-74138-74264-140537781-0-218439<string,(\\\"help\\\"|\\\"attack\\\"|\\\"defend\\\"|\\\"rally\\\"|\\\"ignore\\\")>>>\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"additionalProperties\": {\n\t\t\t\t\"$ref\": \"#/definitions/RulesPart%3C(%22help%22%7C%22attack%22%7C%22defend%22%7C%22rally%22%7C%22ignore%22)%3E\"\n\t\t\t},\n\t\t\t\"description\": \"What is said of a group of the rules: any of it, a group in it in part as well. A function, a list and a table whole\"\n\t\t},\n\t\t\"RulesPart<(\\\"help\\\"|\\\"attack\\\"|\\\"defend\\\"|\\\"rally\\\"|\\\"ignore\\\")>\": {\n\t\t\t\"type\": \"string\",\n\t\t\t\"enum\": [\n\t\t\t\t\"help\",\n\t\t\t\t\"attack\",\n\t\t\t\t\"defend\",\n\t\t\t\t\"rally\",\n\t\t\t\t\"ignore\"\n\t\t\t],\n\t\t\t\"description\": \"What is said of a group of the rules: any of it, a group in it in part as well. A function, a list and a table whole\"\n\t\t},\n\t\t\"RulesPart<structure-src_rules.ts-16207-16256-src_rules.ts-16099-16256-src_rules.ts-15119-16441-src_rules.ts-0-25676381541611>\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"most\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/RulesPart%3Cnumber%3E\"\n\t\t\t\t},\n\t\t\t\t\"within\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/RulesPart%3Cnumber%3E\"\n\t\t\t\t},\n\t\t\t\t\"length\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/RulesPart%3Cnumber%3E\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"What is said of a group of the rules: any of it, a group in it in part as well. A function, a list and a table whole\"\n\t\t},\n\t\t\"RulesPart<structure-src_rules.ts-16405-16439-src_rules.ts-16256-16439-src_rules.ts-15119-16441-src_rules.ts-0-256761718446262>\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"each\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/RulesPart%3Cnumber%3E\"\n\t\t\t\t},\n\t\t\t\t\"seconds\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/RulesPart%3Cnumber%3E\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"What is said of a group of the rules: any of it, a group in it in part as well. A function, a list and a table whole\"\n\t\t},\n\t\t\"RulesPart<def-interface-src_rules.ts-22230-22604-src_rules.ts-0-25676594161992489921438>\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"leash\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/RulesPart%3Cnumber1257883159%3E\"\n\t\t\t\t},\n\t\t\t\t\"sleep\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/RulesPart%3Cboolean1244251315%3E\"\n\t\t\t\t},\n\t\t\t\t\"respawn\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"What is said of a group of the rules: any of it, a group in it in part as well. A function, a list and a table whole\"\n\t\t},\n\t\t\"RulesPart<number1257883159>\": {\n\t\t\t\"type\": \"number\",\n\t\t\t\"description\": \"What is said of a group of the rules: any of it, a group in it in part as well. A function, a list and a table whole\"\n\t\t},\n\t\t\"RulesPart<boolean1244251315>\": {\n\t\t\t\"type\": \"boolean\",\n\t\t\t\"description\": \"What is said of a group of the rules: any of it, a group in it in part as well. A function, a list and a table whole\"\n\t\t},\n\t\t\"RulesPart<def-interface-src_hero.ts-953-2062-src_hero.ts-0-1454210919897661340881447>\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"maxHeroes\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/RulesPart%3Cnumber1867899884%3E\"\n\t\t\t\t},\n\t\t\t\t\"maxHeroLevel\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/RulesPart%3Cnumber1322296163%3E\"\n\t\t\t\t},\n\t\t\t\t\"xpRange\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/RulesPart%3Cnumber1756145837%3E\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"What is said of a group of the rules: any of it, a group in it in part as well. A function, a list and a table whole\"\n\t\t},\n\t\t\"RulesPart<number1867899884>\": {\n\t\t\t\"type\": \"number\",\n\t\t\t\"description\": \"What is said of a group of the rules: any of it, a group in it in part as well. A function, a list and a table whole\"\n\t\t},\n\t\t\"RulesPart<number1322296163>\": {\n\t\t\t\"type\": \"number\",\n\t\t\t\"description\": \"What is said of a group of the rules: any of it, a group in it in part as well. A function, a list and a table whole\"\n\t\t},\n\t\t\"RulesPart<number1756145837>\": {\n\t\t\t\"type\": \"number\",\n\t\t\t\"description\": \"What is said of a group of the rules: any of it, a group in it in part as well. A function, a list and a table whole\"\n\t\t},\n\t\t\"GameData\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"properties\": {\n\t\t\t\t\"removed\": {\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"units\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"abilities\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"buffs\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"upgrades\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"races\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"additionalProperties\": false\n\t\t\t\t},\n\t\t\t\t\"ai\": {\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"races\": {\n\t\t\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\t\t\"additionalProperties\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/AiProfile\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"additionalProperties\": false,\n\t\t\t\t\t\"description\": \"How the game's computers play each race (campaign plan AR4), by race id: each a whole profile, in the place of the code's (`GameModule.ai.races`). What the editor's Data panel writes of a race's AI\"\n\t\t\t\t},\n\t\t\t\t\"rules\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/DataRules\",\n\t\t\t\t\t\"description\": \"The rules as data (campaign plan RC7), laid over the game's (`DataRules`): what the Data panel's Rules writes\"\n\t\t\t\t},\n\t\t\t\t\"units\": {\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"additionalProperties\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/DataValues\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"abilities\": {\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"additionalProperties\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/DataValues\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"buffs\": {\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"additionalProperties\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/DataValues\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"items\": {\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"additionalProperties\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/DataValues\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"upgrades\": {\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"additionalProperties\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/DataValues\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"races\": {\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"additionalProperties\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/DataValues\"\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"description\": \"The values of a game's definitions, by kind and id: what its `data.json` holds. `removed` takes definitions of the game's out, by kind (the Data panel's Delete): what still names one is the rules' check's to say (`checkRules`)\"\n\t\t},\n\t\t\"AiProfile\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"economy\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/EconomyOptions\",\n\t\t\t\t\t\"description\": \"The workers and the bases (`Economy`)\"\n\t\t\t\t},\n\t\t\t\t\"defence\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/DefenceOptions\",\n\t\t\t\t\t\"description\": \"What it defends and how (`Defence`)\"\n\t\t\t\t},\n\t\t\t\t\"scouting\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/ScoutingOptions\",\n\t\t\t\t\t\"description\": \"How often it looks and with whom (`Scouting`)\"\n\t\t\t\t},\n\t\t\t\t\"micro\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/MicroOptions\",\n\t\t\t\t\t\"description\": \"How its units fight (`Micro`)\"\n\t\t\t\t},\n\t\t\t\t\"squads\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/SquadsOptions\",\n\t\t\t\t\t\"description\": \"How its squads go, and the tasks they may be given (`Squads`)\"\n\t\t\t\t},\n\t\t\t\t\"team\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/TeamOptions\",\n\t\t\t\t\t\"description\": \"How it plays with its team (`Team`)\"\n\t\t\t\t},\n\t\t\t\t\"waves\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/WavesOptions\",\n\t\t\t\t\t\"description\": \"The waves it was given (`Waves`)\"\n\t\t\t\t},\n\t\t\t\t\"plans\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/PlansOptions\",\n\t\t\t\t\t\"description\": \"What it builds, trains and researches, and in what order (`Plans`)\"\n\t\t\t\t},\n\t\t\t\t\"army\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/MeleeArmyOptions\",\n\t\t\t\t\t\"description\": \"The melee army: when it attacks, how many go, how a wave goes (`melee.army`)\"\n\t\t\t\t},\n\t\t\t\t\"heroes\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/MeleeHeroesOptions\",\n\t\t\t\t\t\"description\": \"The melee player's heroes: how many, and from when (`melee.heroes`)\"\n\t\t\t\t},\n\t\t\t\t\"lanes\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/LanesPlayOptions\",\n\t\t\t\t\t\"description\": \"A lanes brain's play (`lanes`)\"\n\t\t\t\t},\n\t\t\t\t\"weights\": {\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"additionalProperties\": {\n\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t},\n\t\t\t\t\t\"description\": \"How much each goal weighs, by its id (1 when left out): the picker's (`Goals`)\"\n\t\t\t\t},\n\t\t\t\t\"flags\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/AiFlags\",\n\t\t\t\t\t\"description\": \"The classic flags (see the file's comment)\"\n\t\t\t\t},\n\t\t\t\t\"brain\": {\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"description\": \"The brain it plays with, by id (one the game lists in `GameModule.brains`): what a race's profile says (`GameAi.races`, campaign plan AR3), for a seat that names none and a map that says none. The game's first brain where nothing says\"\n\t\t\t\t},\n\t\t\t\t\"difficulty\": {\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"description\": \"The difficulty by name: `easy`, `normal`, `hard`, `insane`, or a game's own (`GameModule.ai.difficulties`). `normal` when left out\"\n\t\t\t\t},\n\t\t\t\t\"personality\": {\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"description\": \"The personality by name: `balanced`, `rusher`, `turtle`, `expander`, `harasser`, or a game's own. `balanced` when left out\"\n\t\t\t\t},\n\t\t\t\t\"every\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Seconds between thinks: its reactions\"\n\t\t\t\t},\n\t\t\t\t\"commandsPerThink\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Commands a think may send at the most, like a person's hands: no limit when left out\"\n\t\t\t\t},\n\t\t\t\t\"mistakes\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"How often it slips, 0 to 1: a think's fights left alone, a wave sent early. Drawn with the brain's own numbers\"\n\t\t\t\t},\n\t\t\t\t\"vision\": {\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"fair\",\n\t\t\t\t\t\t\"all\"\n\t\t\t\t\t],\n\t\t\t\t\t\"description\": \"`fair`: it sees what its seat does. `all`: it sees through the fog (an observer's session), for insane only\"\n\t\t\t\t},\n\t\t\t\t\"budget\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/ThinkBudgetOptions\",\n\t\t\t\t\t\"description\": \"How much of a think one tick may do (`ThinkBudgetOptions`): modules a tick, units looked at a tick, the milliseconds a module is warned over (4). Nothing sliced when left out: a game sets it where its numbers ask, at many computers\"\n\t\t\t\t},\n\t\t\t\t\"handicap\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/AiHandicap\",\n\t\t\t\t\t\"description\": \"What it is given beyond the rules, each a factor (1 is none), through the game's hooks while it is seated: `income`, what its workers bring in (1.5: half again; `beforeDeposit`); `health`, the most health of everything it has (`stats`); `damage`, the damage it deals (`beforeDamage`, before armour); `buildTime`, how long what it trains, builds and researches takes (0.8: a fifth sooner; `beforeProduce`). The classic handicap is `health`\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"How a computer player plays, as plain data: the settings of its areas (`AiSettings`) and how well it plays, every field optional, layered (see the file's comment). What a difficulty, a personality and a game's defaults give\"\n\t\t},\n\t\t\"EconomyOptions\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"workers\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Workers on the game's money at each base, where `workersPer` says nothing of it: 8 when left out\"\n\t\t\t\t},\n\t\t\t\t\"lumberjacks\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Workers on the other resource, at home only, where `workersPer` says nothing of it: 3 when left out\"\n\t\t\t\t},\n\t\t\t\t\"workersPer\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/WorkersPer\",\n\t\t\t\t\t\"description\": \"Workers on each resource at each base (`WorkersPer`): `{ gold: 5, lumber: 3 }` at every base, or a function of the base for one that differs (home, an expansion). What it leaves out is `workers` and `lumberjacks`\"\n\t\t\t\t},\n\t\t\t\t\"saturate\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Whether a base wants no more workers on a deposit than it serves (see the file's comment): yes when left out\"\n\t\t\t\t},\n\t\t\t\t\"trainWorkers\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Whether its halls train workers up to what the bases want: yes when left out\"\n\t\t\t\t},\n\t\t\t\t\"builders\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Workers trained past what the bases want, to build with: a worker sent to build is off the mine meanwhile, and a mine with only as many as it serves would gather less. 1 when left out\"\n\t\t\t\t},\n\t\t\t\t\"expansions\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Halls to have past the first, each by a mine nobody has a hall at: the seat's `expansions`, else none\"\n\t\t\t\t},\n\t\t\t\t\"expandWhen\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/AiCondition\",\n\t\t\t\t\t\"description\": \"When it may expand: a condition (`AiCondition`, as data or a function). Four minutes in and with a building that trains soldiers, when left out\"\n\t\t\t\t},\n\t\t\t\t\"workersFlee\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Whether workers a raid is on walk to a base no enemy is at, and back once theirs is clear: yes when left out\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"How a brain runs its economy (`BrainDef.economy`, the seat's `AiOptions.economy` over it, the `economy()` module's own under both). Every field has a data form a save keeps; the function forms are the brain's to give again\"\n\t\t},\n\t\t\"WorkersPer\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"additionalProperties\": {\n\t\t\t\t\"type\": \"number\"\n\t\t\t},\n\t\t\t\"description\": \"How many workers a base wants on each resource (`EconomyOptions.workersPer`): as numbers by resource id, or a function of the base (`index` 0 is home)\"\n\t\t},\n\t\t\"AiCondition\": {\n\t\t\t\"$ref\": \"#/definitions/AiConditionData\",\n\t\t\t\"description\": \"A condition: a question with a yes or no, as a function of the brain, or as plain data (`AiConditionData`)\"\n\t\t},\n\t\t\"AiConditionData\": {\n\t\t\t\"anyOf\": [\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"all\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/AiCondition\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"all\"\n\t\t\t\t\t],\n\t\t\t\t\t\"additionalProperties\": false\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"any\": {\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/AiCondition\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"any\"\n\t\t\t\t\t],\n\t\t\t\t\t\"additionalProperties\": false\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"not\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/AiCondition\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"not\"\n\t\t\t\t\t],\n\t\t\t\t\t\"additionalProperties\": false\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"resource\": {\n\t\t\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\t\t\"additionalProperties\": false,\n\t\t\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\t\t\"min\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"max\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"id\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\t\t\"id\"\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"resource\"\n\t\t\t\t\t],\n\t\t\t\t\t\"additionalProperties\": false\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"units\": {\n\t\t\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\t\t\"additionalProperties\": false,\n\t\t\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\t\t\"min\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"max\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"type\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"role\": {\n\t\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/AiRole\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"tag\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"of\": {\n\t\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/AiWhose\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"near\": {\n\t\t\t\t\t\t\t\t\t\"anyOf\": [\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Vec2\"\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"within\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"finished\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"known\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"units\"\n\t\t\t\t\t],\n\t\t\t\t\t\"additionalProperties\": false\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"supply\": {\n\t\t\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\t\t\"free\": {\n\t\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/AiRange\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"used\": {\n\t\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/AiRange\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"cap\": {\n\t\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/AiRange\"\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"additionalProperties\": false\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"supply\"\n\t\t\t\t\t],\n\t\t\t\t\t\"additionalProperties\": false\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"time\": {\n\t\t\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\t\t\"after\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"before\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"additionalProperties\": false\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"time\"\n\t\t\t\t\t],\n\t\t\t\t\t\"additionalProperties\": false\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"underAttack\": {\n\t\t\t\t\t\t\t\"anyOf\": [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\t\t\t\t\"const\": true\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\t\t\t\t\"seconds\": {\n\t\t\t\t\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"near\": {\n\t\t\t\t\t\t\t\t\t\t\t\"anyOf\": [\n\t\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Vec2\"\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\"reach\": {\n\t\t\t\t\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\"additionalProperties\": false\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"underAttack\"\n\t\t\t\t\t],\n\t\t\t\t\t\"additionalProperties\": false\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"seen\": {\n\t\t\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\t\t\"type\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"role\": {\n\t\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/AiRole\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"within\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"additionalProperties\": false\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"seen\"\n\t\t\t\t\t],\n\t\t\t\t\t\"additionalProperties\": false\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"difficulty\": {\n\t\t\t\t\t\t\t\"anyOf\": [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"difficulty\"\n\t\t\t\t\t],\n\t\t\t\t\t\"additionalProperties\": false\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"told\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"told\"\n\t\t\t\t\t],\n\t\t\t\t\t\"additionalProperties\": false\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"chance\": {\n\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"chance\"\n\t\t\t\t\t],\n\t\t\t\t\t\"additionalProperties\": false\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"hero\": {\n\t\t\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\t\t\"type\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"alive\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"level\": {\n\t\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/AiRange\"\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"additionalProperties\": false\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"hero\"\n\t\t\t\t\t],\n\t\t\t\t\t\"additionalProperties\": false\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"wave\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/AiRange\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"wave\"\n\t\t\t\t\t],\n\t\t\t\t\t\"additionalProperties\": false\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"objective\": {\n\t\t\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\t\t\"id\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"status\": {\n\t\t\t\t\t\t\t\t\t\"anyOf\": [\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/ObjectiveStatus\"\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/ObjectiveStatus\"\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\t\t\"id\",\n\t\t\t\t\t\t\t\t\"status\"\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\"additionalProperties\": false\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"objective\"\n\t\t\t\t\t],\n\t\t\t\t\t\"additionalProperties\": false\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"report\": {\n\t\t\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\t\t\"additionalProperties\": false,\n\t\t\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\t\t\"min\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"max\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"path\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"equals\": {}\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\t\t\"path\"\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"report\"\n\t\t\t\t\t],\n\t\t\t\t\t\"additionalProperties\": false\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"component\": {\n\t\t\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\t\t\"additionalProperties\": false,\n\t\t\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\t\t\"min\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"max\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"id\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"entity\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"type\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"path\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"equals\": {}\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\t\t\"id\"\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"component\"\n\t\t\t\t\t],\n\t\t\t\t\t\"additionalProperties\": false\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"region\": {\n\t\t\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\t\t\"additionalProperties\": false,\n\t\t\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\t\t\"min\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"max\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"name\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"of\": {\n\t\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/AiWhose\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"type\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"role\": {\n\t\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/AiRole\"\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\t\t\"name\"\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"region\"\n\t\t\t\t\t],\n\t\t\t\t\t\"additionalProperties\": false\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"is\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"args\": {\n\t\t\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\t\t\"additionalProperties\": {}\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"is\"\n\t\t\t\t\t],\n\t\t\t\t\t\"additionalProperties\": false\n\t\t\t\t}\n\t\t\t],\n\t\t\t\"description\": \"A condition as plain data: what a map's file, a mission's JSON or the editor holds. Each form is one question; the table in `docs/ai.md` says what each asks\"\n\t\t},\n\t\t\"AiWhose\": {\n\t\t\t\"type\": \"string\",\n\t\t\t\"enum\": [\n\t\t\t\t\"mine\",\n\t\t\t\t\"enemy\",\n\t\t\t\t\"ally\"\n\t\t\t],\n\t\t\t\"description\": \"Whose units a condition counts: the player’s own (the default), its rivals’ in sight, its allies’\"\n\t\t},\n\t\t\"Vec2\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"x\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"z\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"x\",\n\t\t\t\t\"z\"\n\t\t\t],\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"A point on the ground plane. The simulation is 2D; height always comes from the terrain.\"\n\t\t},\n\t\t\"AiRange\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"min\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"max\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"At least `min` and at most `max`, each left out for no bound: a count, an amount, a level\"\n\t\t},\n\t\t\"ObjectiveStatus\": {\n\t\t\t\"type\": \"string\",\n\t\t\t\"enum\": [\n\t\t\t\t\"open\",\n\t\t\t\t\"done\",\n\t\t\t\t\"failed\"\n\t\t\t]\n\t\t},\n\t\t\"DefenceOptions\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"radius\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"How near one of its buildings a rival's unit is an intruder: the seat's `defendRadius`, else 16\"\n\t\t\t\t},\n\t\t\t\t\"odds\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"The odds of the fight (`Strength.odds`) the defenders sent against a raid are to have: two in three when left out (about four tenths stronger than the raid)\"\n\t\t\t\t},\n\t\t\t\t\"recall\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Whether a raid more than what is at home can meet brings the army home (the goal is urgent: it takes squads out, whole, once it scores past what they are on): yes when left out\"\n\t\t\t\t},\n\t\t\t\t\"towers\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Towers a base wants once raided `towerAfter` times: 1 when left out. 0 for none\"\n\t\t\t\t},\n\t\t\t\t\"towerAfter\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Raids at one base before it wants towers: 2 when left out\"\n\t\t\t\t},\n\t\t\t\t\"tower\": {\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"description\": \"The tower to build, by type id: the cheapest of the `tower` role its workers build when left out\"\n\t\t\t\t},\n\t\t\t\t\"quiet\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Seconds a base stands clear before a raid on it is over: 15 when left out\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"How a brain defends its bases (`BrainDef.defence`, the seat's `AiOptions.defence` over it, the `defence()` module's own under both). Data, kept in a save\"\n\t\t},\n\t\t\"ScoutingOptions\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"lookEvery\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Seconds a place stays out of sight before it is worth a look again: 90 when left out\"\n\t\t\t\t},\n\t\t\t\t\"scout\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"const\": false,\n\t\t\t\t\t\"description\": \"Where to look: `false` nowhere (the goal is left out), a function for places of the game's own each think (in place of the kit's plan). The kit's plan when left out\"\n\t\t\t\t},\n\t\t\t\t\"expansions\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Whether the mines they would expand to are looked at: yes when left out\"\n\t\t\t\t},\n\t\t\t\t\"army\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Whether where their army was last seen is looked at again: yes when left out\"\n\t\t\t\t},\n\t\t\t\t\"flyers\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Whether a flying unit with nothing to do goes before the fastest on foot: yes when left out\"\n\t\t\t\t},\n\t\t\t\t\"from\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Seconds into the game before the first look: 15 when left out\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"How a brain scouts (`BrainDef.scouting`, the seat's `AiOptions.scouting` over it, the `scouting()` module's own under both). Data, but for the function form of `scout`\"\n\t\t},\n\t\t\"MicroOptions\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"targets\": {\n\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/TargetRule\"\n\t\t\t\t\t},\n\t\t\t\t\t\"description\": \"Who is worth hitting first, as ranked rules (`TargetRule`), or a function that picks (`TargetPicker`). The kit's when left out: casters 2, heroes 1.5 (with `targetHeroes`), siege 1, healers 2, over the type's declared `UnitTypeDef.ai.priority`; among those alike, the weakest, the nearest\"\n\t\t\t\t},\n\t\t\t\t\"targetHeroes\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Whether heroes are worth hitting first (the classic Target Heroes): yes when left out\"\n\t\t\t\t},\n\t\t\t\t\"focusEvery\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Seconds a squad keeps hitting the rival it was told to before another is chosen: 4\"\n\t\t\t\t},\n\t\t\t\t\"kite\": {\n\t\t\t\t\t\"anyOf\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"boolean\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\t\"const\": \"ranged\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"description\": \"Whether ranged units step back from the melee closing on them, when they are the faster: `'ranged'` (or `true`) when left out, `false` never\"\n\t\t\t\t},\n\t\t\t\t\"unitsFlee\": {\n\t\t\t\t\t\"anyOf\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\t\t\"const\": false\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"description\": \"The share of its health under which a unit leaves the fight for safety (the classic Units Flee): 0.25. `false` never\"\n\t\t\t\t},\n\t\t\t\t\"heroesFlee\": {\n\t\t\t\t\t\"anyOf\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\t\t\"const\": false\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"description\": \"The same for heroes (Heroes Flee): 0.35. `false` never\"\n\t\t\t\t},\n\t\t\t\t\"returnAt\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"The share of its health at which a unit that fled is back: 0.8\"\n\t\t\t\t},\n\t\t\t\t\"removeInjuries\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Whether the hurt go home to heal between fights, not only out of one (Remove Injuries): no when left out\"\n\t\t\t\t},\n\t\t\t\t\"ignoreInjured\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Whether a unit that fled stays out until healed, however long (Ignore Injured): no when left out, so one nothing heals (or that heals too slowly to wait a minute for) comes back once safe\"\n\t\t\t\t},\n\t\t\t\t\"spells\": {\n\t\t\t\t\t\"anyOf\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\t\t\"additionalProperties\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/SpellAim\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\t\t\"const\": false\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"description\": \"Where to use abilities, by ability id (`SpellAim`): before the ability's own `ai`, and before what the kit works out from its effects. `false` casts nothing: what is on autocast looks after itself\"\n\t\t\t\t},\n\t\t\t\t\"potions\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Whether a potion is drunk when it is needed: yes when left out\"\n\t\t\t\t},\n\t\t\t\t\"takeItems\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Whether what lies about is picked up (Take Items): yes when left out\"\n\t\t\t\t},\n\t\t\t\t\"buyItems\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Whether heroes buy from a shop in reach (Buy Items): yes when left out, where there is a shop and a list\"\n\t\t\t\t},\n\t\t\t\t\"shopping\": {\n\t\t\t\t\t\"anyOf\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/ShoppingItem\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\t\t\"const\": false\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"description\": \"What a hero shops for, in order (`ShoppingItem`), or a function that names the item a hero should buy next (none for nothing). The cheapest item whose ability heals, one of it, when left out; `false` nothing\"\n\t\t\t\t},\n\t\t\t\t\"hireUnits\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Whether it hires units at a place that hires in reach (a tavern, a mercenary camp: `UnitTypeDef.hires`): yes when left out, where there is one\"\n\t\t\t\t},\n\t\t\t\t\"hiring\": {\n\t\t\t\t\t\"anyOf\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\t\"$ref\": \"#/definitions/HiringItem\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\t\t\"const\": false\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"description\": \"What it hires, in order (`HiringItem`), or a function that names the unit to hire next of those a place offers (none for nothing). When left out: no hero, the strongest soldier for its price, with money to spare (150 past it) and supply free, four at the most. `false` nothing\"\n\t\t\t\t},\n\t\t\t\t\"smartArtillery\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Whether siege units shoot buildings when they may and stay behind the squad (Smart Artillery): yes when left out\"\n\t\t\t\t},\n\t\t\t\t\"heroes\": {\n\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/HeroPlan\"\n\t\t\t\t\t},\n\t\t\t\t\t\"description\": \"The heroes to make, in order (`HeroPlan`: which, its skills, its items, a chance), or a function that picks the next of those that could be made. The first that can be made, and the ultimate first, when left out\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"How a brain fights (`BrainDef.micro`, the seat's `AiOptions.micro` over it, the `micro()` module's own under both): whom it hits first, whether its ranged units kite, when its units flee, how it casts, what it buys, how its siege is used, which heroes it makes. Every field has a data form a save keeps; the function forms are the brain's to give again\"\n\t\t},\n\t\t\"TargetRule\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"match\": {\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"description\": \"What it picks out: a role (`caster`, `hero`, `siege`, `worker`, `healer`), `building`, a tag (`ranged`, the game's own), or a unit type by id\"\n\t\t\t\t},\n\t\t\t\t\"weight\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"What it adds to the worth of what it picks out: 1 when left out. Rules add up; a foe no rule names is worth 1\"\n\t\t\t\t},\n\t\t\t\t\"when\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/AiCondition\",\n\t\t\t\t\t\"description\": \"When it counts: a condition (`AiCondition`, as data or a function). Always when left out\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"match\"\n\t\t\t],\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"One rule of who is worth hitting first (`MicroOptions.targets`): what it picks out, how much it adds, when it counts. Plain data\"\n\t\t},\n\t\t\"SpellAim\": {\n\t\t\t\"not\": {},\n\t\t\t\"description\": \"Where to use an ability, in code (`MicroOptions.spells`, by ability id): where (`point`) and on whom (`target`), `null` for not now, `undefined` to leave it to what the kit works out from the effects. `foes` are the enemies within the ability's reach of the caster, `friends` its own and its allies' fighters near it\"\n\t\t},\n\t\t\"ShoppingItem\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"item\": {\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"description\": \"The item, by id\"\n\t\t\t\t},\n\t\t\t\t\"count\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"How many of it to carry: 1 when left out\"\n\t\t\t\t},\n\t\t\t\t\"when\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/AiCondition\",\n\t\t\t\t\t\"description\": \"When it is wanted: a condition. Always when left out\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"item\"\n\t\t\t],\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"A line of a hero's shopping list (`MicroOptions.shopping`, `HeroPlan.items`): an item to carry, how many, when\"\n\t\t},\n\t\t\"HiringItem\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"unit\": {\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"description\": \"The unit type, by id: one a place that hires lists (`UnitTypeDef.hires`)\"\n\t\t\t\t},\n\t\t\t\t\"count\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"How many of it to have: 1 when left out\"\n\t\t\t\t},\n\t\t\t\t\"when\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/AiCondition\",\n\t\t\t\t\t\"description\": \"When it is wanted: a condition. Always when left out\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"unit\"\n\t\t\t],\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"A line of what a brain hires (`MicroOptions.hiring`): a unit a place hires out, how many of it to have, when\"\n\t\t},\n\t\t\"HeroPlan\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"description\": \"The hero's unit type\"\n\t\t\t\t},\n\t\t\t\t\"skills\": {\n\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"description\": \"The order its skills are learned in, each level a line (`['stomp', 'holyLight', 'stomp', …]`); past the end, or left out, the ultimate as soon as it can be had and then whatever is furthest behind. Or a function: the skill to learn next of those it may, by id\"\n\t\t\t\t},\n\t\t\t\t\"items\": {\n\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/ShoppingItem\"\n\t\t\t\t\t},\n\t\t\t\t\t\"description\": \"What it shops for, in place of the brain's `shopping`\"\n\t\t\t\t},\n\t\t\t\t\"chance\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"The chance it is made at all, 0 to 1 (1 when left out): rolled once, with the brain's numbers, and remembered\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"A hero the brain means to have (`MicroOptions.heroes`): which, what it learns in what order, what it buys\"\n\t\t},\n\t\t\"SquadsOptions\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"spread\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"How far from its middle a unit may be before it counts as strayed: what a regroup brings in. Grows with the squad when left out (8, and three times the square root of its size above that: 12 for sixteen units)\"\n\t\t\t\t},\n\t\t\t\t\"regroupWhen\": {\n\t\t\t\t\t\"anyOf\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\t\t\"const\": false\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"description\": \"The share of its units strayed (`spread`) at which it stops to regroup, on its way and out of a fight: 0.4. `false` never\"\n\t\t\t\t},\n\t\t\t\t\"reinforce\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Whether it makes up what it lost since its task began from the pool (the idle fighters nothing holds): no when left out\"\n\t\t\t\t},\n\t\t\t\t\"retreatAt\": {\n\t\t\t\t\t\"anyOf\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\t\t\"const\": false\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"description\": \"Its odds in a fight (`Strength.odds`) under which it falls back home, away from home: 0.3. `false` fights on to the last (a mission's guards)\"\n\t\t\t\t},\n\t\t\t\t\"home\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"How near home it fights on whatever the odds: there is nowhere to fall back to. 20 when left out\"\n\t\t\t\t},\n\t\t\t\t\"micro\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Whether the micro module (`micro()`, C2) fights for it once it is in a fight: focus fire, kiting, fleeing, siege. Yes when left out; `false` for a squad left to its units (a scout, a mission's guards on an order of the game's)\"\n\t\t\t\t},\n\t\t\t\t\"tasks\": {\n\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/TaskDef\"\n\t\t\t\t\t},\n\t\t\t\t\t\"description\": \"Tasks of the game's own: registered with the brain, so that a squad on one is carried on after a load\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"How a brain's squads behave, where a squad says nothing for itself (`BrainDef.squads`, the seat's `AiOptions.squads` over it); and the tasks of the game's own (`defineTask`) a save of the brain may name\"\n\t\t},\n\t\t\"TaskDef\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"description\": \"Unique among the brain's tasks: what a save names it by. The kit's are words (`attack`); a game's prefixed (`ctf.capture`)\"\n\t\t\t\t},\n\t\t\t\t\"handles\": {\n\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\t\"retreat\",\n\t\t\t\t\t\t\t\"regroup\",\n\t\t\t\t\t\t\t\"reinforce\"\n\t\t\t\t\t\t]\n\t\t\t\t\t},\n\t\t\t\t\t\"description\": \"What the squads module leaves to it: a task that regroups in its own way, or retreats never\"\n\t\t\t\t},\n\t\t\t\t\"standing\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Whether it is a standing duty, going on as long as its squad has it (a hero walking a lane with its waves): never stuck, whatever its phase, for the `ends` invariant (`checkBrain`). No when left out\"\n\t\t\t\t},\n\t\t\t\t\"fighting\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Whether a squad on it is in a fight it means to win, which the micro fights for (`ai.area(Micro)`): yes when left out. No for a task that goes somewhere, looks, stands, gathers or falls back (the kit's `move`, `scout`, `hold`, `gather`, `retreat`, and the micro's own `flee`)\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\"\n\t\t\t],\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"A kind of task (`defineTask`): what it sends as it begins (`start`), what it looks at each think (`think`), and the place it is about (`where`, for the squads module's regroup and reinforcements and for the report). The squads module runs it with its own regroup, reinforce and retreat around it, but for what it `handles` itself\"\n\t\t},\n\t\t\"TeamOptions\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"defendUsers\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Whether it helps its allies, people and computers, where asked (the classic Defend Users): yes when left out\"\n\t\t\t\t},\n\t\t\t\t\"answerPings\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Whether it does what an ally's ping asks besides help (`GameRules.talk.pings.meanings`): a squad sent to fight at an `attack` (a push), one to hold a place at a `defend` (a hold), its squads out brought home at a `rally` (a retreat). Yes when left out\"\n\t\t\t\t},\n\t\t\t\t\"helpOdds\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"The odds of the fight (`Strength.odds`) what it sends to help is to have: one in two when left out\"\n\t\t\t\t},\n\t\t\t\t\"askForHelp\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Whether it asks its team for help with a raid more than it can meet: yes when left out\"\n\t\t\t\t},\n\t\t\t\t\"together\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Whether its waves wait for the team's plan and go together: no when left out\"\n\t\t\t\t},\n\t\t\t\t\"gatherFor\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Seconds ahead of the proposal the waves of a plan go: 20 when left out\"\n\t\t\t\t},\n\t\t\t\t\"requestFor\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Seconds a request stands: 30 when left out\"\n\t\t\t\t},\n\t\t\t\t\"say\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Whether it says a line to its side as it sends a wave, comes to help, falls back: no when left out\"\n\t\t\t\t},\n\t\t\t\t\"lines\": {\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"attacking\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"helping\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"retreating\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"additionalProperties\": false,\n\t\t\t\t\t\"description\": \"Its lines, the game's words: `{ attacking, helping, retreating }`. Nothing is said for a line not given\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"How a brain plays with its team (`BrainDef.team`, the seat's `AiOptions.team` over it, the `team()` module's own under both). Data\"\n\t\t},\n\t\t\"WavesOptions\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"groups\": {\n\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/AttackGroup\"\n\t\t\t\t\t},\n\t\t\t\t\t\"description\": \"The groups waves are made of, by name\"\n\t\t\t\t},\n\t\t\t\t\"waves\": {\n\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/AttackWave\"\n\t\t\t\t\t},\n\t\t\t\t\t\"description\": \"The waves, in order, or a function giving the wave whose turn it is (by its index), none for no more\"\n\t\t\t\t},\n\t\t\t\t\"train\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Whether a wave's group is trained (wants of the production plan): yes when left out. No for units that are given (a lanes game)\"\n\t\t\t\t},\n\t\t\t\t\"priority\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"How important a wave's units are to the purse, as wants: 30 when left out (under a hall, a barracks, a hero)\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"How a brain sends its waves (`BrainDef.waves`; the seat's `AiOptions.waves` and `groups`, the data of it, go over)\"\n\t\t},\n\t\t\"AttackGroup\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"description\": \"Its name: what a wave names it by\"\n\t\t\t\t},\n\t\t\t\t\"units\": {\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"additionalProperties\": {\n\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t},\n\t\t\t\t\t\"description\": \"So many of each unit type or role: `{ footman: 4, rifleman: 2 }`\"\n\t\t\t\t},\n\t\t\t\t\"when\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/AiCondition\",\n\t\t\t\t\t\"description\": \"Only while this holds: a wave of a group whose condition fails is skipped as its own would be\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\",\n\t\t\t\t\"units\"\n\t\t\t],\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"What a wave is made of: so many of each unit type (by id) or role (`caster`, a game's own), by name, under a condition\"\n\t\t},\n\t\t\"AttackWave\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"group\": {\n\t\t\t\t\t\"anyOf\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\t\t\"additionalProperties\": {\n\t\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"description\": \"Its group: a group's name (`WavesOptions.groups`), or the units in place (`{ footman: 4 }`)\"\n\t\t\t\t},\n\t\t\t\t\"after\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Seconds after the wave before it (after the start, for the first) before it may go: 0 when left out\"\n\t\t\t\t},\n\t\t\t\t\"minForces\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"The share of its group that is enough to go with, 0 to 1: all of it when left out\"\n\t\t\t\t},\n\t\t\t\t\"gatherFor\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Seconds it gathers at the rally before it goes, whoever is late: 15 when left out\"\n\t\t\t\t},\n\t\t\t\t\"repeat\": {\n\t\t\t\t\t\"type\": [\n\t\t\t\t\t\t\"boolean\",\n\t\t\t\t\t\t\"number\"\n\t\t\t\t\t],\n\t\t\t\t\t\"description\": \"Whether it goes again once sent: `true` for ever, a number for so many times in all. Once when left out\"\n\t\t\t\t},\n\t\t\t\t\"when\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/AiCondition\",\n\t\t\t\t\t\"description\": \"Only when this holds as its turn comes: a wave whose condition fails is skipped\"\n\t\t\t\t},\n\t\t\t\t\"together\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Whether it waits for its team's attack together (`TeamOptions.together`, C5)\"\n\t\t\t\t},\n\t\t\t\t\"target\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/WaveTarget\",\n\t\t\t\t\t\"description\": \"Where it goes: their base nearest home when left out\"\n\t\t\t\t},\n\t\t\t\t\"rally\": {\n\t\t\t\t\t\"anyOf\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Vec2\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"description\": \"Where it gathers: a place, or a name the map or the rules gave. A little out of home towards the target when left out\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"group\"\n\t\t\t],\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"One wave (the classic attack wave): its group, when it goes, how much of it is enough, how long it gathers, whether it goes again, together or not, and where to\"\n\t\t},\n\t\t\"WaveTarget\": {\n\t\t\t\"anyOf\": [\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"const\": \"nearestBase\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"const\": \"weakestBase\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"const\": \"expansion\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"const\": \"army\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"$ref\": \"#/definitions/Vec2\"\n\t\t\t\t}\n\t\t\t],\n\t\t\t\"description\": \"Where a wave goes: their base nearest home, their weakest (the fewest towers and army seen there), an expansion of theirs, their army as last seen, a place, or a function of the brain\"\n\t\t},\n\t\t\"PlansOptions\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"build\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/BuildPlanOptions\"\n\t\t\t\t},\n\t\t\t\t\"production\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/ProductionPlanOptions\"\n\t\t\t\t},\n\t\t\t\t\"order\": {\n\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/BuildOrderItem\"\n\t\t\t\t\t},\n\t\t\t\t\t\"description\": \"What to have first, in order (`BuildOrderItem`): each item waits for the one before, and nothing else the computer would make of itself is made meanwhile (`following`); what it works out for itself once done. The seat's `AiOptions.buildOrder`\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"How a brain plans (`BrainDef.plans`): the build plan's options, the production plan's, the build order\"\n\t\t},\n\t\t\"BuildPlanOptions\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"wants\": {\n\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/Want\"\n\t\t\t\t\t},\n\t\t\t\t\t\"description\": \"Standing wants: what the brain always has, built anew when lost\"\n\t\t\t\t},\n\t\t\t\t\"placement\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/PlacementTable\",\n\t\t\t\t\t\"description\": \"How each type's spot is chosen, by type id or by role: a name or a function. What a type declares (`UnitTypeDef.ai.placement`) goes over it\"\n\t\t\t\t},\n\t\t\t\t\"rebuild\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Whether a building lost is built again, for standing wants that do not say: yes when left out\"\n\t\t\t\t},\n\t\t\t\t\"repair\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Whether a worker is sent to mend a damaged building when no enemy reaches it: yes when left out\"\n\t\t\t\t},\n\t\t\t\t\"siteTimeout\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Seconds without a spot before a want that does not stand is given up: 60 when left out\"\n\t\t\t\t},\n\t\t\t\t\"within\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"How far from home a building goes at the most, where a want does not say: 22 when left out\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"How the build plan goes about it (`BrainDef.plans.build`, the `build()` module; the data of it in `AiOptions`)\"\n\t\t},\n\t\t\"Want\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"description\": \"Its name: what the memory, the report and `drop` know it by. Made up when left out\"\n\t\t\t\t},\n\t\t\t\t\"type\": {\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"description\": \"The unit type wanted: a building, or a unit (whose buildings the plan puts up; the production plan trains it)\"\n\t\t\t\t},\n\t\t\t\t\"within\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"How far from there at the most: 22 when left out\"\n\t\t\t\t},\n\t\t\t\t\"priority\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Wants are taken most important first, and the most important one that cannot be paid for saves up: 0 when left out\"\n\t\t\t\t},\n\t\t\t\t\"spare\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Money to have left over after paying for it: a luxury that waits for gold to spare. 0 when left out\"\n\t\t\t\t},\n\t\t\t\t\"keep\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Whether the want stands: kept once met, and short again when what it made is lost, so that it is built anew. Wants of a brain's or a seat's options stand; one added as the game goes is dropped once met, unless it says so\"\n\t\t\t\t},\n\t\t\t\t\"rebuild\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"For a standing want: whether a building lost is built again (the plan's `rebuild` when left out)\"\n\t\t\t\t},\n\t\t\t\t\"siteTimeout\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Seconds without a spot for it before a want that does not stand is given up: the plan's `siteTimeout` when left out\"\n\t\t\t\t},\n\t\t\t\t\"by\": {\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"description\": \"Who wants it, for the report: `order`, `production`, `economy`, a goal's or a rule's id\"\n\t\t\t\t},\n\t\t\t\t\"count\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"when\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/AiCondition\"\n\t\t\t\t},\n\t\t\t\t\"near\": {\n\t\t\t\t\t\"anyOf\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/Vec2\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t}\n\t\t\t\t\t]\n\t\t\t\t},\n\t\t\t\t\"placement\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/PlacementName\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"required\": [\n\t\t\t\t\"type\"\n\t\t\t],\n\t\t\t\"description\": \"A want with the function forms: how many as a function of the brain, a condition as code, a place found each think, a placement strategy of the game's own (`ai.plans.want`, `BuildPlanOptions.wants`)\"\n\t\t},\n\t\t\"PlacementTable\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"additionalProperties\": {\n\t\t\t\t\"$ref\": \"#/definitions/PlacementName\"\n\t\t\t},\n\t\t\t\"description\": \"Which strategy a type goes by (`AiOptions.placement`, `BuildPlanOptions.placement`): by type id or by role, a name or a function\"\n\t\t},\n\t\t\"ProductionPlanOptions\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"composition\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/Composition\",\n\t\t\t\t\t\"description\": \"The army it wants (`Composition`), or a function of the brain giving it. Even shares of every soldier type it can train when left out\"\n\t\t\t\t},\n\t\t\t\t\"counters\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"How much what answers the enemy's army seen pulls the shares about: 0 not at all, 1 (left out) as much as a share; a type that cannot hit what they field is not trained while they field it\"\n\t\t\t\t},\n\t\t\t\t\"train\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Whether it trains soldiers at all: yes when left out. Workers and heroes are the economy's and the hero module's\"\n\t\t\t\t},\n\t\t\t\t\"queue\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"How many may queue at one building: 2 when left out\"\n\t\t\t\t},\n\t\t\t\t\"supplyAhead\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Whether to want one more of what provides supply when what it would train next does not fit: yes when left out\"\n\t\t\t\t},\n\t\t\t\t\"supplyMargin\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Supply to keep free beyond the next unit's, so that the farm is up before it is needed: 4 when left out\"\n\t\t\t\t},\n\t\t\t\t\"research\": {\n\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"description\": \"Upgrades to research, in order, by id (the first it can is next), or a function of the brain giving the next; what the army it has uses most when left out\"\n\t\t\t\t},\n\t\t\t\t\"researchSpare\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Money to have left over before researching: 200 when left out (research is a luxury)\"\n\t\t\t\t},\n\t\t\t\t\"priority\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"How important training and research are to the purse (`Purse`): 0 when left out, under every want that saves\"\n\t\t\t\t},\n\t\t\t\t\"upkeepTier\": {\n\t\t\t\t\t\"anyOf\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\t\t\"const\": false\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"description\": \"The tier of the game's upkeep (`GameModule.upkeep`: 0 none, 1 the first) the composition trains no further into: a soldier that would take the supply used past it waits, and no supply is wanted past it, unless the money would lie idle (`upkeepSpare`). What is wanted outright (a wave's, a build order's) is trained all the same. `UPKEEP_TIER` (1) when left out; `false` reads no upkeep\"\n\t\t\t\t},\n\t\t\t\t\"upkeepSpare\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Money free past which upkeep is not read (it would lie idle else): `UPKEEP_SPARE` (600) when left out\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"How the production plan goes about it (`BrainDef.plans.production`, the `production()` module; the data of it in `AiOptions.composition` and `research`)\"\n\t\t},\n\t\t\"Composition\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"additionalProperties\": {\n\t\t\t\t\"type\": \"number\"\n\t\t\t},\n\t\t\t\"description\": \"The army a computer player wants, as shares by unit type id or tag (the kit's `ranged`, `melee`, `hitsAir`, a game's own): `{ footman: 2, rifleman: 1 }` is two footmen to a rifleman. A tag's share is split among the types that have it; a type named gets its own beside. Types it cannot train yet are left out until it can\"\n\t\t},\n\t\t\"BuildOrderItem\": {\n\t\t\t\"anyOf\": [\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"type\": {\n\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"count\": {\n\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\"type\"\n\t\t\t\t\t],\n\t\t\t\t\t\"additionalProperties\": false\n\t\t\t\t}\n\t\t\t],\n\t\t\t\"description\": \"One item of a build order: a type to have (one of it), or a type and how many. Counts of a type add up along the order\"\n\t\t},\n\t\t\"MeleeArmyOptions\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"firstAttack\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Seconds before its first wave: 150 when left out\"\n\t\t\t\t},\n\t\t\t\t\"attackEvery\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Seconds between two waves: 45 when left out\"\n\t\t\t\t},\n\t\t\t\t\"waveSize\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Units in its first wave, each after bringing two more: 6 when left out\"\n\t\t\t\t},\n\t\t\t\t\"goOdds\": {\n\t\t\t\t\t\"anyOf\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\t\t\"const\": false\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"description\": \"The odds (`Strength.odds`) of what it has ready against their army as last seen (the last minute) under which a wave whose time has come waits and trains on rather than go: 0.4 when left out; `false` never waits\"\n\t\t\t\t},\n\t\t\t\t\"waitAtMost\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Seconds a wave waits past its time at the most, outmatched or short of its size: then it goes with what it has, half the wave at the least, where that would hold against what is remembered to reach the target. Twice `attackEvery` when left out. A wave that could never reach its size (the supply capped) still comes\"\n\t\t\t\t},\n\t\t\t\t\"pressOn\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Whether a wave that razed what it went for goes on to the next of their buildings it knows, while it is strong enough: yes when left out\"\n\t\t\t\t},\n\t\t\t\t\"target\": {\n\t\t\t\t\t\"type\": \"string\",\n\t\t\t\t\t\"enum\": [\n\t\t\t\t\t\t\"nearest\",\n\t\t\t\t\t\t\"weakest\"\n\t\t\t\t\t],\n\t\t\t\t\t\"description\": \"Where a wave of its own goes among their buildings it knows of: the `nearest`, or the `weakest` (the one the least of their strength is remembered to reach: an expansion before a main with towers; the nearest among those alike). `weakest` when left out\"\n\t\t\t\t},\n\t\t\t\t\"counter\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Whether a wave of theirs that died at its gate is answered at once with what is ready (half a wave at the least), their base being empty of it: yes when left out. `counterAfter` is how many of their fighters must have fallen near its bases in the last twenty seconds (3 when left out)\"\n\t\t\t\t},\n\t\t\t\t\"counterAfter\": {\n\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t},\n\t\t\t\t\"pressOdds\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"The odds against their army as last seen a wave needs to go on: 0.5 when left out\"\n\t\t\t\t},\n\t\t\t\t\"reinforce\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Whether idle fighters at home join a wave that is out, making up what it lost: yes when left out\"\n\t\t\t\t},\n\t\t\t\t\"strike\": {\n\t\t\t\t\t\"anyOf\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\t\t\"const\": false\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\t\t\"awayFor\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"odds\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"additionalProperties\": false\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"description\": \"A strike while their army is away (FJ1): every sighting of their army within `awayFor` seconds (60) stands far from what a wave would go for (their base, out raiding or creeping or at a base of mine), and what is ready (half a wave at the least) would hold against what is remembered to reach it at these odds (`odds`, 0.6): the wave goes before its time. `false` for none\"\n\t\t\t\t},\n\t\t\t\t\"waveGrowth\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Units more in each wave than the one before: 2 when left out\"\n\t\t\t\t},\n\t\t\t\t\"waveMax\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"The most units a wave of its own asks for: 24 when left out\"\n\t\t\t\t},\n\t\t\t\t\"raid\": {\n\t\t\t\t\t\"anyOf\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\t\t\"const\": false\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\t\t\"size\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"odds\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"every\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"armyAwayFor\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"workersSeenFor\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"retreatAt\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"additionalProperties\": false\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"description\": \"Raids on their workers where their army is not (the `raid` goal): how many go (`size`, 3, the fastest), the odds against what is remembered at the base they need (`odds`, 0.6), seconds between raids (`every`, 60), seconds their army must not have been seen near the base (`armyAwayFor`, 45), how long ago their workers were seen at the most (`workersSeenFor`, 120), the odds the raiders fall back at (`retreatAt`, 0.5). `false` for no raids\"\n\t\t\t\t},\n\t\t\t\t\"harass\": {\n\t\t\t\t\t\"anyOf\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\t\t\"const\": false\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\t\t\"size\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"odds\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"every\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"retreatAt\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"additionalProperties\": false\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"description\": \"Harassing their main before the first wave (the `harass` goal): the party (`size`, 3: the hero and the fastest), the odds it needs at their base (`odds`, 0.6), seconds between (`every`, 90), the odds it falls back at (`retreatAt`, 0.5). `false` for none\"\n\t\t\t\t},\n\t\t\t\t\"expand\": {\n\t\t\t\t\t\"anyOf\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\t\t\"const\": false\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\t\t\"guards\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"spare\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"additionalProperties\": false\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"description\": \"Guards at a base past home that has no tower yet (the `expand` goal): how many (`guards`, 2), and how many fighters past a wave must stand idle for any to go (`spare`, 0). `false` for none\"\n\t\t\t\t},\n\t\t\t\t\"creep\": {\n\t\t\t\t\t\"anyOf\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\t\t\"const\": false\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\t\t\"every\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"from\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"reach\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"party\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"additionalProperties\": false\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"description\": \"Creeping (the `creep` goal): seconds between camps (`every`, 40), seconds into the game before the first (`from`, 30), how far from home a camp may be (`reach`, 70), the fewest in a party (`party`, 3). `false` for none\"\n\t\t\t\t},\n\t\t\t\t\"surrender\": {\n\t\t\t\t\t\"anyOf\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\t\t\"const\": false\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/MeleeSurrenderOptions\"\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"description\": \"When it gives up (the `melee.surrender` module, `MeleeSurrenderOptions`): once it has no hall, no worker and no army that could win the game back, for half a minute, when left out; a condition of the game's own (`when`), and how long it holds first (`after`). `false` plays to the last\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"How the melee player's army goes about its own waves and the goals beside them (`AiSettings.army` over the melee brain's `army(options)`). Data: a save keeps the seat's\"\n\t\t},\n\t\t\"MeleeSurrenderOptions\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"when\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/AiCondition\",\n\t\t\t\t\t\"description\": \"When it cannot come back, as a condition (data or a function of the brain). By default: no hall and no worker of its own, and its fighters (none at all, or) outmatched by their army as last seen in the last two minutes, odds under 0.2\"\n\t\t\t\t},\n\t\t\t\t\"after\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Seconds the condition holds on end before it gives up: 30 when left out\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"When the melee player gives up (`MeleeArmyOptions.surrender`): the condition, and how long it holds first\"\n\t\t},\n\t\t\"MeleeHeroesOptions\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"most\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Heroes it has once the game is under way (`secondFrom`): one before. 2 when left out\"\n\t\t\t\t},\n\t\t\t\t\"secondFrom\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Seconds into the game from which it wants every hero it may have: one before. 240 when left out\"\n\t\t\t\t},\n\t\t\t\t\"priority\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"How important a hero's price is to the purse while it is short (`Purse.reserve`): the hall, supply, the barracks, the altar, the answer to their army and another hall go before it (their wants are more important); a workshop, the next tier, a second barracks and the soldiers wait for it. 35 when left out\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"How the melee brain makes its heroes (`heroes()` of the melee brain): how many once the game is under way (the profile's `heroes`), from when, and how important their price is to the purse\"\n\t\t},\n\t\t\"LanesPlayOptions\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"home\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/Vec2\",\n\t\t\t\t\t\"description\": \"Where its heroes go to rest: a point, or a function. Its side's building by its side's ends of the lanes when left out (a core)\"\n\t\t\t\t},\n\t\t\t\t\"lanes\": {\n\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t},\n\t\t\t\t\t\"description\": \"Only these lanes, by their path's name: every lane of the map when left out\"\n\t\t\t\t},\n\t\t\t\t\"backAt\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"The share of its health under which a hero goes home to rest: 0.35\"\n\t\t\t\t},\n\t\t\t\t\"returnAt\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"The share at which a resting hero goes back out: 0.85\"\n\t\t\t\t},\n\t\t\t\t\"restFor\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Seconds a hero rests at home at most, healed or not: 40\"\n\t\t\t\t},\n\t\t\t\t\"recall\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Whether a hero going home uses an ability that takes it there (a recall), where nothing hostile is near: yes when left out\"\n\t\t\t\t},\n\t\t\t\t\"backOdds\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"The odds where a hero stands on its lane under which it falls back along it: 0.3\"\n\t\t\t\t},\n\t\t\t\t\"behind\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"How many cells behind its side's wave a hero walks: 3\"\n\t\t\t\t},\n\t\t\t\t\"help\": {\n\t\t\t\t\t\"anyOf\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\t\t\"const\": false\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\t\t\"reach\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"odds\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"additionalProperties\": false\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"description\": \"An ally's hero in a fight it goes to: within `reach` cells of it (30), where the odds with it are at least `odds` (0.4). `false` never\"\n\t\t\t\t},\n\t\t\t\t\"camps\": {\n\t\t\t\t\t\"anyOf\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\t\t\"const\": false\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\t\t\"reach\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"odds\": {\n\t\t\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"additionalProperties\": false\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"description\": \"A camp it takes with no wave to walk with: within `reach` cells (50), where the odds against it are at least `odds` (0.6). `false` never\"\n\t\t\t\t},\n\t\t\t\t\"lastHits\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"Whether a hero hits the minion its next hit kills before anything else (a last hit): yes when left out\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"How a lanes brain plays (`lanesPlay(options)`, the seat's `AiOptions.lanes` over it): its heroes' rest, how far it goes for an ally or a camp, how near its wave it walks. Data, but for `units` and `home` as functions\"\n\t\t},\n\t\t\"AiFlags\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"defendUsers\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/AiFlag%3Cboolean%3E\",\n\t\t\t\t\t\"description\": \"Helps its allies, people and computers, where asked (the team's `help` goal)\"\n\t\t\t\t},\n\t\t\t\t\"targetHeroes\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/AiFlag%3Cboolean%3E\",\n\t\t\t\t\t\"description\": \"Hits heroes first among those alike (the micro's targets)\"\n\t\t\t\t},\n\t\t\t\t\"repairStructures\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/AiFlag%3Cboolean%3E\",\n\t\t\t\t\t\"description\": \"Mends damaged buildings when no enemy reaches them (the build plan's `repair`)\"\n\t\t\t\t},\n\t\t\t\t\"heroesFlee\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/AiFlag%3C(boolean%7Cnumber)%3E\",\n\t\t\t\t\t\"description\": \"A hero leaves a fight under this share of its health: `true` at the share under it, `false` never (`micro.heroesFlee`)\"\n\t\t\t\t},\n\t\t\t\t\"unitsFlee\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/AiFlag%3C(boolean%7Cnumber)%3E\",\n\t\t\t\t\t\"description\": \"A unit leaves a fight under this share of its health: `true` at the share under it, `false` never (`micro.unitsFlee`)\"\n\t\t\t\t},\n\t\t\t\t\"groupsFlee\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/AiFlag%3C(boolean%7Cnumber)%3E\",\n\t\t\t\t\t\"description\": \"A squad falls back from a fight away from home under these odds: `true` at the odds under it, `false` never (`squads.retreatAt`)\"\n\t\t\t\t},\n\t\t\t\t\"noMercy\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/AiFlag%3C(boolean%7Cnumber)%3E\",\n\t\t\t\t\t\"description\": \"Attacks before its time when the enemy's army as last seen is weak against its own: at these odds (`true`: 0.75)\"\n\t\t\t\t},\n\t\t\t\t\"ignoreInjured\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/AiFlag%3Cboolean%3E\",\n\t\t\t\t\t\"description\": \"A unit that fled stays out until healed\"\n\t\t\t\t},\n\t\t\t\t\"removeInjuries\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/AiFlag%3Cboolean%3E\",\n\t\t\t\t\t\"description\": \"The hurt go home to heal between fights\"\n\t\t\t\t},\n\t\t\t\t\"takeItems\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/AiFlag%3Cboolean%3E\",\n\t\t\t\t\t\"description\": \"Picks up what lies about\"\n\t\t\t\t},\n\t\t\t\t\"buyItems\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/AiFlag%3Cboolean%3E\",\n\t\t\t\t\t\"description\": \"Its heroes buy from shops in reach\"\n\t\t\t\t},\n\t\t\t\t\"allowHomeChanges\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/AiFlag%3Cboolean%3E\",\n\t\t\t\t\t\"description\": \"Builds its hall again where it can, once its home falls, rather than where it first stood: yes when left out\"\n\t\t\t\t},\n\t\t\t\t\"smartArtillery\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/AiFlag%3Cboolean%3E\",\n\t\t\t\t\t\"description\": \"Siege units shoot buildings and stay behind their squad\"\n\t\t\t\t},\n\t\t\t\t\"randomPaths\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/AiFlag%3Cboolean%3E\",\n\t\t\t\t\t\"description\": \"Each wave takes a way of its own to the target, by the brain's numbers: straight, or round one side or the other\"\n\t\t\t\t},\n\t\t\t\t\"rebuild\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/AiFlag%3Cboolean%3E\",\n\t\t\t\t\t\"description\": \"Builds again what it lost\"\n\t\t\t\t},\n\t\t\t\t\"kite\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/AiFlag%3Cboolean%3E\",\n\t\t\t\t\t\"description\": \"Ranged units step back from melee they outrun\"\n\t\t\t\t},\n\t\t\t\t\"workersFlee\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/AiFlag%3Cboolean%3E\",\n\t\t\t\t\t\"description\": \"Workers a raid is on walk to a safe base\"\n\t\t\t\t},\n\t\t\t\t\"say\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/AiFlag%3Cboolean%3E\",\n\t\t\t\t\t\"description\": \"Says its lines to its side (the team's `say`)\"\n\t\t\t\t},\n\t\t\t\t\"scout\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/AiFlag%3Cboolean%3E\",\n\t\t\t\t\t\"description\": \"Sends a unit to look at the enemy (the scouting's goal)\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"The classic RTS behaviour flags, where the meaning holds, and the kit's own beside them: each a yes or no, a number where one says how much, or a function of the brain\"\n\t\t},\n\t\t\"AiFlag<boolean>\": {\n\t\t\t\"type\": \"boolean\",\n\t\t\t\"description\": \"A flag's value: as data, or worked out by the brain as it is asked (code, not kept in a save)\"\n\t\t},\n\t\t\"AiFlag<(boolean|number)>\": {\n\t\t\t\"type\": [\n\t\t\t\t\"boolean\",\n\t\t\t\t\"number\"\n\t\t\t],\n\t\t\t\"description\": \"A flag's value: as data, or worked out by the brain as it is asked (code, not kept in a save)\"\n\t\t},\n\t\t\"ThinkBudgetOptions\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"modulesPerTick\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Modules of a brain a think may run in one tick: a think with more goes on at the next tick, over the same picture of the game. All of them in one tick when left out\"\n\t\t\t\t},\n\t\t\t\t\"unitsPerTick\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Units a tick's modules may look at, as the modules say (`ai.budget.spent(n)`): a module that walks the squads or the bases stops once the tick has looked at this many, and goes on at the next tick from where it was (`later`). No limit when left out. A module that says nothing of what it looks at is never stopped by it\"\n\t\t\t\t},\n\t\t\t\t\"warnAfterMs\": {\n\t\t\t\t\t\"anyOf\": [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\t\t\"const\": false\n\t\t\t\t\t\t}\n\t\t\t\t\t],\n\t\t\t\t\t\"description\": \"Milliseconds a module may take before the log says so (once a module, a warning): what to measure first. A module is never cut short for it. 4 when left out; `false` for never\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"How much of a think one tick may do (`AiOptions.budget`, `AiProfile.budget`, `ComputerSeatOptions.budget`), counted in work and never in milliseconds: the same on every machine, so that a game with a sliced think is the same game everywhere. Nothing is limited where a field is left out\"\n\t\t},\n\t\t\"AiHandicap\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"income\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"What its workers bring in, through the game's `beforeDeposit` hook\"\n\t\t\t\t},\n\t\t\t\t\"health\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"The most health of everything it has, through a `stats` hook: the classic handicap\"\n\t\t\t\t},\n\t\t\t\t\"damage\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"The damage it deals, before armour, through a `beforeDamage` hook\"\n\t\t\t\t},\n\t\t\t\t\"buildTime\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"How long what it trains, builds and researches takes, through a `beforeProduce` hook: under 1 is sooner\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"What a computer is given beyond the rules (`AiProfile.handicap`): each a factor, 1 (or left out) for none\"\n\t\t},\n\t\t\"DataRules\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"combat\": {\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"damageTable\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/DamageTable\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"additionalProperties\": false\n\t\t\t\t},\n\t\t\t\t\"supply\": {\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"max\": {\n\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"pools\": {\n\t\t\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\t\t\"additionalProperties\": {\n\t\t\t\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\t\t\t\"name\": {\n\t\t\t\t\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\"max\": {\n\t\t\t\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\"required\": [\n\t\t\t\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\t\t\t\"max\"\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\"additionalProperties\": false\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"additionalProperties\": false\n\t\t\t\t},\n\t\t\t\t\"heroes\": {\n\t\t\t\t\t\"type\": \"object\",\n\t\t\t\t\t\"properties\": {\n\t\t\t\t\t\t\"maxHeroes\": {\n\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"maxHeroLevel\": {\n\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t},\n\t\t\t\t\t\t\"xpRange\": {\n\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t\"additionalProperties\": false\n\t\t\t\t},\n\t\t\t\t\"resources\": {\n\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/ResourceDef\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"upkeep\": {\n\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"$ref\": \"#/definitions/UpkeepTier\"\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"The rules a `data.json` may say (campaign plan RC7), each over the game's: the damage table whole, the supply's and the heroes' numbers, the resources and the upkeep's tiers whole (in place of `GameModule.resources`, `upkeep`)\"\n\t\t},\n\t\t\"DamageTable\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"attacks\": {\n\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"armours\": {\n\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t\"table\": {\n\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\"type\": \"number\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"attacks\",\n\t\t\t\t\"armours\",\n\t\t\t\t\"table\"\n\t\t\t],\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"A damage table: what a hit of each attack type is multiplied by against each armour type (`GameRules.combat.damageTable`). `table[attack][armour]`, the rows the attacks in their order, the columns the armours in theirs\"\n\t\t},\n\t\t\"ResourceDef\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"id\": {\n\t\t\t\t\t\"$ref\": \"#/definitions/ResourceKind\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"start\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"What every player starts a game with. Nothing when left out\"\n\t\t\t\t},\n\t\t\t\t\"upkeep\": {\n\t\t\t\t\t\"type\": \"boolean\",\n\t\t\t\t\t\"description\": \"What workers gather of it is cut by the upkeep of the army, see `UpkeepTier`\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"id\",\n\t\t\t\t\"name\"\n\t\t\t],\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"Something players have amounts of and pay with, like gold, lumber or crystals. A game module lists its own\"\n\t\t},\n\t\t\"UpkeepTier\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"properties\": {\n\t\t\t\t\"above\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Applies when the player uses more supply than this\"\n\t\t\t\t},\n\t\t\t\t\"name\": {\n\t\t\t\t\t\"type\": \"string\"\n\t\t\t\t},\n\t\t\t\t\"income\": {\n\t\t\t\t\t\"type\": \"number\",\n\t\t\t\t\t\"description\": \"Part of what is gathered that still arrives, 0..1\"\n\t\t\t\t}\n\t\t\t},\n\t\t\t\"required\": [\n\t\t\t\t\"above\",\n\t\t\t\t\"name\",\n\t\t\t\t\"income\"\n\t\t\t],\n\t\t\t\"additionalProperties\": false,\n\t\t\t\"description\": \"A big army is paid for with income: past a certain supply, less arrives of what workers gather of the resources with `upkeep`\"\n\t\t},\n\t\t\"DataValues\": {\n\t\t\t\"type\": \"object\",\n\t\t\t\"additionalProperties\": {\n\t\t\t\t\"anyOf\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"$ref\": \"#/definitions/DataValue\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/DataValue\"\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"$ref\": \"#/definitions/DataValues\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"type\": \"array\",\n\t\t\t\t\t\t\"items\": {\n\t\t\t\t\t\t\t\"$ref\": \"#/definitions/DataValues\"\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t},\n\t\t\t\"description\": \"Values of a definition, as its own fields nest them: `{ stats: { health: 420 }, cost: { gold: 135 } }`. What a `data.json` keeps of each definition, and what `world.redefine` is given. A list of things by place (a unit's attacks) is written as a list or as its places (`{ attacks: { 0: { damagePoint: 0.5 } } }`), each over the thing there. A list of things changed whole (an ability's effects, the units a race's start adds: a `rows` field) written as places goes over the things there, and written as a list is the list: those things, in that order, each in full\"\n\t\t},\n\t\t\"DataValue\": {\n\t\t\t\"type\": [\n\t\t\t\t\"number\",\n\t\t\t\t\"string\",\n\t\t\t\t\"boolean\"\n\t\t\t],\n\t\t\t\"description\": \"What a field of the data can hold: a number, words, yes or no\"\n\t\t}\n\t}\n}\n","import { readFileSync } from 'node:fs'\nimport type { GameModule } from '#engine/game'\nimport { composeGame } from '#engine/mods'\nimport { checkPackSchema, PackError, packFromJson, type PackMod } from '#engine/packs'\n// the pack's schema, in the server's bundle: a server reads packs where a page fetches it with them\nimport packSchema from '../pack.schema.json'\n\n/*\n * A server's packs of content (campaign plan RC6): read from its files, in the order they are named, and composed\n * into the game before any room is made. The host's packs are the game's: a player whose page has others is told as\n * they join (the welcome names the game, `melee+<pack>`, and its hash).\n */\n\n/**\n * The packs of content of a server, read from their files (`packs/<id>/pack.json`) in the order named: that order is\n * the game's, as a page's `defineGame({ packs })` is\n *\n * @beta\n */\nexport function readPacks(files: readonly string[]): PackMod[] {\n\treturn files.map((file) => {\n\t\tconst text = readFileSync(file, 'utf8')\n\t\tconst pack = packFromJson(text, file)\n\t\tconst wrong = checkPackSchema(JSON.parse(text), packSchema)\n\t\tif (wrong) throw new PackError(`${file}: ${wrong}`)\n\t\treturn pack\n\t})\n}\n\n/**\n * A server's game with its packs of content composed in (`readPacks`, `composeGame`): the game as is with none\n *\n * @beta\n */\nexport function withPackFiles(game: GameModule, files: readonly string[]): GameModule {\n\treturn files.length ? composeGame(game, readPacks(files)) : game\n}\n"],"mappings":"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"}