@rpgm-tools/neo-angband-core 0.17.0 → 0.19.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 (853) hide show
  1. package/README.md +1 -1
  2. package/dist/agent/perceive.d.ts.map +1 -1
  3. package/dist/agent/perceive.js +2 -2
  4. package/dist/agent/perceive.js.map +1 -1
  5. package/dist/combat/blow-vectors.d.ts +78 -0
  6. package/dist/combat/blow-vectors.d.ts.map +1 -0
  7. package/dist/combat/blow-vectors.fixtures.d.ts +19 -0
  8. package/dist/combat/blow-vectors.fixtures.d.ts.map +1 -0
  9. package/dist/combat/blow-vectors.fixtures.js +81 -0
  10. package/dist/combat/blow-vectors.fixtures.js.map +1 -0
  11. package/dist/combat/blow-vectors.js +195 -0
  12. package/dist/combat/blow-vectors.js.map +1 -0
  13. package/dist/combat/brand-slay.d.ts +22 -9
  14. package/dist/combat/brand-slay.d.ts.map +1 -1
  15. package/dist/combat/brand-slay.js +37 -10
  16. package/dist/combat/brand-slay.js.map +1 -1
  17. package/dist/combat/index.d.ts +1 -1
  18. package/dist/combat/index.js +1 -1
  19. package/dist/combat/melee.d.ts +25 -13
  20. package/dist/combat/melee.d.ts.map +1 -1
  21. package/dist/combat/melee.js +23 -19
  22. package/dist/combat/melee.js.map +1 -1
  23. package/dist/combat/mon-melee.d.ts +166 -7
  24. package/dist/combat/mon-melee.d.ts.map +1 -1
  25. package/dist/combat/mon-melee.js +469 -338
  26. package/dist/combat/mon-melee.js.map +1 -1
  27. package/dist/combat/ranged.d.ts +15 -13
  28. package/dist/combat/ranged.d.ts.map +1 -1
  29. package/dist/combat/ranged.js +31 -29
  30. package/dist/combat/ranged.js.map +1 -1
  31. package/dist/constants.d.ts +14 -0
  32. package/dist/constants.d.ts.map +1 -1
  33. package/dist/constants.js +14 -0
  34. package/dist/constants.js.map +1 -1
  35. package/dist/datafile.d.ts +48 -0
  36. package/dist/datafile.d.ts.map +1 -0
  37. package/dist/datafile.js +102 -0
  38. package/dist/datafile.js.map +1 -0
  39. package/dist/effects/effect-info-registry.d.ts +237 -0
  40. package/dist/effects/effect-info-registry.d.ts.map +1 -0
  41. package/dist/effects/effect-info-registry.js +138 -0
  42. package/dist/effects/effect-info-registry.js.map +1 -0
  43. package/dist/effects/effect-info-vectors.d.ts +92 -0
  44. package/dist/effects/effect-info-vectors.d.ts.map +1 -0
  45. package/dist/effects/effect-info-vectors.fixtures.d.ts +18 -0
  46. package/dist/effects/effect-info-vectors.fixtures.d.ts.map +1 -0
  47. package/dist/effects/effect-info-vectors.fixtures.js +51 -0
  48. package/dist/effects/effect-info-vectors.fixtures.js.map +1 -0
  49. package/dist/effects/effect-info-vectors.js +315 -0
  50. package/dist/effects/effect-info-vectors.js.map +1 -0
  51. package/dist/effects/effect-info.d.ts +5 -1
  52. package/dist/effects/effect-info.d.ts.map +1 -1
  53. package/dist/effects/effect-info.js +245 -143
  54. package/dist/effects/effect-info.js.map +1 -1
  55. package/dist/effects/effect.d.ts +4 -3
  56. package/dist/effects/effect.d.ts.map +1 -1
  57. package/dist/effects/effect.js +88 -70
  58. package/dist/effects/effect.js.map +1 -1
  59. package/dist/effects/handlers.d.ts +1 -1
  60. package/dist/effects/handlers.d.ts.map +1 -1
  61. package/dist/effects/handlers.js +5 -2
  62. package/dist/effects/handlers.js.map +1 -1
  63. package/dist/effects/interpreter.d.ts +11 -0
  64. package/dist/effects/interpreter.d.ts.map +1 -1
  65. package/dist/effects/interpreter.js +13 -0
  66. package/dist/effects/interpreter.js.map +1 -1
  67. package/dist/game/cave-cmd.d.ts +9 -5
  68. package/dist/game/cave-cmd.d.ts.map +1 -1
  69. package/dist/game/cave-cmd.js +56 -11
  70. package/dist/game/cave-cmd.js.map +1 -1
  71. package/dist/game/cave-square.d.ts +42 -0
  72. package/dist/game/cave-square.d.ts.map +1 -0
  73. package/dist/game/cave-square.js +73 -0
  74. package/dist/game/cave-square.js.map +1 -0
  75. package/dist/game/char-sheet.d.ts +12 -8
  76. package/dist/game/char-sheet.d.ts.map +1 -1
  77. package/dist/game/char-sheet.js +41 -8
  78. package/dist/game/char-sheet.js.map +1 -1
  79. package/dist/game/chest.d.ts +2 -3
  80. package/dist/game/chest.d.ts.map +1 -1
  81. package/dist/game/chest.js +17 -6
  82. package/dist/game/chest.js.map +1 -1
  83. package/dist/game/context.d.ts +213 -22
  84. package/dist/game/context.d.ts.map +1 -1
  85. package/dist/game/context.js +109 -17
  86. package/dist/game/context.js.map +1 -1
  87. package/dist/game/describe.d.ts +13 -0
  88. package/dist/game/describe.d.ts.map +1 -1
  89. package/dist/game/describe.js +32 -4
  90. package/dist/game/describe.js.map +1 -1
  91. package/dist/game/display.d.ts +97 -8
  92. package/dist/game/display.d.ts.map +1 -1
  93. package/dist/game/display.js +157 -13
  94. package/dist/game/display.js.map +1 -1
  95. package/dist/game/dump-level.d.ts.map +1 -1
  96. package/dist/game/dump-level.js +3 -2
  97. package/dist/game/dump-level.js.map +1 -1
  98. package/dist/game/effect-attack.d.ts.map +1 -1
  99. package/dist/game/effect-attack.js +36 -27
  100. package/dist/game/effect-attack.js.map +1 -1
  101. package/dist/game/effect-detect.d.ts +4 -4
  102. package/dist/game/effect-detect.d.ts.map +1 -1
  103. package/dist/game/effect-detect.js +42 -9
  104. package/dist/game/effect-detect.js.map +1 -1
  105. package/dist/game/effect-env.d.ts +7 -1
  106. package/dist/game/effect-env.d.ts.map +1 -1
  107. package/dist/game/effect-env.js +1 -0
  108. package/dist/game/effect-env.js.map +1 -1
  109. package/dist/game/effect-general.d.ts +5 -3
  110. package/dist/game/effect-general.d.ts.map +1 -1
  111. package/dist/game/effect-general.js +94 -43
  112. package/dist/game/effect-general.js.map +1 -1
  113. package/dist/game/effect-item.d.ts +2 -1
  114. package/dist/game/effect-item.d.ts.map +1 -1
  115. package/dist/game/effect-item.js +136 -82
  116. package/dist/game/effect-item.js.map +1 -1
  117. package/dist/game/effect-melee.d.ts +10 -4
  118. package/dist/game/effect-melee.d.ts.map +1 -1
  119. package/dist/game/effect-melee.js +44 -19
  120. package/dist/game/effect-melee.js.map +1 -1
  121. package/dist/game/effect-monster.d.ts +26 -4
  122. package/dist/game/effect-monster.d.ts.map +1 -1
  123. package/dist/game/effect-monster.js +52 -9
  124. package/dist/game/effect-monster.js.map +1 -1
  125. package/dist/game/effect-teleport.d.ts +58 -19
  126. package/dist/game/effect-teleport.d.ts.map +1 -1
  127. package/dist/game/effect-teleport.js +143 -27
  128. package/dist/game/effect-teleport.js.map +1 -1
  129. package/dist/game/effect-terrain.d.ts +1 -3
  130. package/dist/game/effect-terrain.d.ts.map +1 -1
  131. package/dist/game/effect-terrain.js +38 -22
  132. package/dist/game/effect-terrain.js.map +1 -1
  133. package/dist/game/equip-cmp.d.ts.map +1 -1
  134. package/dist/game/equip-cmp.js +10 -7
  135. package/dist/game/equip-cmp.js.map +1 -1
  136. package/dist/game/floor.d.ts +16 -4
  137. package/dist/game/floor.d.ts.map +1 -1
  138. package/dist/game/floor.js +22 -4
  139. package/dist/game/floor.js.map +1 -1
  140. package/dist/game/gear.d.ts +101 -13
  141. package/dist/game/gear.d.ts.map +1 -1
  142. package/dist/game/gear.js +183 -24
  143. package/dist/game/gear.js.map +1 -1
  144. package/dist/game/harness.d.ts +2 -1
  145. package/dist/game/harness.d.ts.map +1 -1
  146. package/dist/game/harness.js +12 -1
  147. package/dist/game/harness.js.map +1 -1
  148. package/dist/game/ignore-cmd.d.ts +47 -8
  149. package/dist/game/ignore-cmd.d.ts.map +1 -1
  150. package/dist/game/ignore-cmd.js +76 -8
  151. package/dist/game/ignore-cmd.js.map +1 -1
  152. package/dist/game/known.d.ts +223 -34
  153. package/dist/game/known.d.ts.map +1 -1
  154. package/dist/game/known.js +301 -67
  155. package/dist/game/known.js.map +1 -1
  156. package/dist/game/loop.d.ts +4 -3
  157. package/dist/game/loop.d.ts.map +1 -1
  158. package/dist/game/loop.js +49 -29
  159. package/dist/game/loop.js.map +1 -1
  160. package/dist/game/lore-color.d.ts.map +1 -1
  161. package/dist/game/lore-color.js +13 -1
  162. package/dist/game/lore-color.js.map +1 -1
  163. package/dist/game/mon-cast.d.ts +3 -4
  164. package/dist/game/mon-cast.d.ts.map +1 -1
  165. package/dist/game/mon-cast.js +30 -2
  166. package/dist/game/mon-cast.js.map +1 -1
  167. package/dist/game/mon-cmd.d.ts +4 -5
  168. package/dist/game/mon-cmd.d.ts.map +1 -1
  169. package/dist/game/mon-cmd.js +80 -27
  170. package/dist/game/mon-cmd.js.map +1 -1
  171. package/dist/game/mon-death.d.ts +8 -4
  172. package/dist/game/mon-death.d.ts.map +1 -1
  173. package/dist/game/mon-death.js +6 -9
  174. package/dist/game/mon-death.js.map +1 -1
  175. package/dist/game/mon-group.d.ts +9 -5
  176. package/dist/game/mon-group.d.ts.map +1 -1
  177. package/dist/game/mon-group.js +9 -5
  178. package/dist/game/mon-group.js.map +1 -1
  179. package/dist/game/mon-message.d.ts +90 -40
  180. package/dist/game/mon-message.d.ts.map +1 -1
  181. package/dist/game/mon-message.js +267 -59
  182. package/dist/game/mon-message.js.map +1 -1
  183. package/dist/game/mon-place.d.ts +11 -1
  184. package/dist/game/mon-place.d.ts.map +1 -1
  185. package/dist/game/mon-place.js +15 -3
  186. package/dist/game/mon-place.js.map +1 -1
  187. package/dist/game/mon-ranged.d.ts +2 -1
  188. package/dist/game/mon-ranged.d.ts.map +1 -1
  189. package/dist/game/mon-ranged.js +11 -5
  190. package/dist/game/mon-ranged.js.map +1 -1
  191. package/dist/game/mon-side.d.ts +3 -2
  192. package/dist/game/mon-side.d.ts.map +1 -1
  193. package/dist/game/mon-side.js +15 -14
  194. package/dist/game/mon-side.js.map +1 -1
  195. package/dist/game/monster-turn.d.ts +20 -10
  196. package/dist/game/monster-turn.d.ts.map +1 -1
  197. package/dist/game/monster-turn.js +110 -83
  198. package/dist/game/monster-turn.js.map +1 -1
  199. package/dist/game/notice.d.ts +36 -0
  200. package/dist/game/notice.d.ts.map +1 -0
  201. package/dist/game/notice.js +79 -0
  202. package/dist/game/notice.js.map +1 -0
  203. package/dist/game/obj-cmd.d.ts +14 -3
  204. package/dist/game/obj-cmd.d.ts.map +1 -1
  205. package/dist/game/obj-cmd.js +109 -35
  206. package/dist/game/obj-cmd.js.map +1 -1
  207. package/dist/game/obj-list.d.ts +13 -2
  208. package/dist/game/obj-list.d.ts.map +1 -1
  209. package/dist/game/obj-list.js +42 -27
  210. package/dist/game/obj-list.js.map +1 -1
  211. package/dist/game/object-inspect.d.ts +9 -0
  212. package/dist/game/object-inspect.d.ts.map +1 -1
  213. package/dist/game/object-inspect.js +4 -0
  214. package/dist/game/object-inspect.js.map +1 -1
  215. package/dist/game/pickup.d.ts +0 -2
  216. package/dist/game/pickup.d.ts.map +1 -1
  217. package/dist/game/pickup.js +18 -3
  218. package/dist/game/pickup.js.map +1 -1
  219. package/dist/game/player-side-vectors.d.ts +174 -0
  220. package/dist/game/player-side-vectors.d.ts.map +1 -0
  221. package/dist/game/player-side-vectors.fixtures.d.ts +33 -0
  222. package/dist/game/player-side-vectors.fixtures.d.ts.map +1 -0
  223. package/dist/game/player-side-vectors.fixtures.js +191 -0
  224. package/dist/game/player-side-vectors.fixtures.js.map +1 -0
  225. package/dist/game/player-side-vectors.js +292 -0
  226. package/dist/game/player-side-vectors.js.map +1 -0
  227. package/dist/game/player-side.d.ts +129 -1
  228. package/dist/game/player-side.d.ts.map +1 -1
  229. package/dist/game/player-side.js +520 -338
  230. package/dist/game/player-side.js.map +1 -1
  231. package/dist/game/player-turn.d.ts +26 -7
  232. package/dist/game/player-turn.d.ts.map +1 -1
  233. package/dist/game/player-turn.js +180 -33
  234. package/dist/game/player-turn.js.map +1 -1
  235. package/dist/game/poly.d.ts +38 -0
  236. package/dist/game/poly.d.ts.map +1 -0
  237. package/dist/game/poly.js +82 -0
  238. package/dist/game/poly.js.map +1 -0
  239. package/dist/game/project-cast.d.ts +20 -3
  240. package/dist/game/project-cast.d.ts.map +1 -1
  241. package/dist/game/project-cast.js +49 -6
  242. package/dist/game/project-cast.js.map +1 -1
  243. package/dist/game/project-feat-vectors.d.ts +124 -0
  244. package/dist/game/project-feat-vectors.d.ts.map +1 -0
  245. package/dist/game/project-feat-vectors.fixtures.d.ts +13 -0
  246. package/dist/game/project-feat-vectors.fixtures.d.ts.map +1 -0
  247. package/dist/game/project-feat-vectors.fixtures.js +94 -0
  248. package/dist/game/project-feat-vectors.fixtures.js.map +1 -0
  249. package/dist/game/project-feat-vectors.js +229 -0
  250. package/dist/game/project-feat-vectors.js.map +1 -0
  251. package/dist/game/project-feat.d.ts +53 -1
  252. package/dist/game/project-feat.d.ts.map +1 -1
  253. package/dist/game/project-feat.js +374 -247
  254. package/dist/game/project-feat.js.map +1 -1
  255. package/dist/game/project-monster.d.ts +21 -8
  256. package/dist/game/project-monster.d.ts.map +1 -1
  257. package/dist/game/project-monster.js +45 -21
  258. package/dist/game/project-monster.js.map +1 -1
  259. package/dist/game/project-obj.d.ts +62 -0
  260. package/dist/game/project-obj.d.ts.map +1 -1
  261. package/dist/game/project-obj.js +72 -41
  262. package/dist/game/project-obj.js.map +1 -1
  263. package/dist/game/project-player.d.ts +8 -0
  264. package/dist/game/project-player.d.ts.map +1 -1
  265. package/dist/game/project-player.js.map +1 -1
  266. package/dist/game/projection-handlers.d.ts +81 -0
  267. package/dist/game/projection-handlers.d.ts.map +1 -0
  268. package/dist/game/projection-handlers.js +93 -0
  269. package/dist/game/projection-handlers.js.map +1 -0
  270. package/dist/game/quest.d.ts +23 -0
  271. package/dist/game/quest.d.ts.map +1 -1
  272. package/dist/game/quest.js +42 -0
  273. package/dist/game/quest.js.map +1 -1
  274. package/dist/game/ranged-cmd.d.ts +13 -2
  275. package/dist/game/ranged-cmd.d.ts.map +1 -1
  276. package/dist/game/ranged-cmd.js +32 -15
  277. package/dist/game/ranged-cmd.js.map +1 -1
  278. package/dist/game/repeat.d.ts +78 -0
  279. package/dist/game/repeat.d.ts.map +1 -0
  280. package/dist/game/repeat.js +88 -0
  281. package/dist/game/repeat.js.map +1 -0
  282. package/dist/game/shape-inspect.d.ts +74 -0
  283. package/dist/game/shape-inspect.d.ts.map +1 -0
  284. package/dist/game/shape-inspect.js +114 -0
  285. package/dist/game/shape-inspect.js.map +1 -0
  286. package/dist/game/spell-cmd.d.ts +15 -4
  287. package/dist/game/spell-cmd.d.ts.map +1 -1
  288. package/dist/game/spell-cmd.js +35 -6
  289. package/dist/game/spell-cmd.js.map +1 -1
  290. package/dist/game/spoil.d.ts.map +1 -1
  291. package/dist/game/spoil.js +89 -40
  292. package/dist/game/spoil.js.map +1 -1
  293. package/dist/game/steal.d.ts +3 -3
  294. package/dist/game/steal.d.ts.map +1 -1
  295. package/dist/game/steal.js +9 -4
  296. package/dist/game/steal.js.map +1 -1
  297. package/dist/game/target-loop.d.ts +10 -4
  298. package/dist/game/target-loop.d.ts.map +1 -1
  299. package/dist/game/target-loop.js +14 -8
  300. package/dist/game/target-loop.js.map +1 -1
  301. package/dist/game/target.d.ts +4 -4
  302. package/dist/game/target.d.ts.map +1 -1
  303. package/dist/game/target.js +19 -8
  304. package/dist/game/target.js.map +1 -1
  305. package/dist/game/ui-entry.d.ts +63 -4
  306. package/dist/game/ui-entry.d.ts.map +1 -1
  307. package/dist/game/ui-entry.js +151 -23
  308. package/dist/game/ui-entry.js.map +1 -1
  309. package/dist/game/wizard.d.ts +23 -5
  310. package/dist/game/wizard.d.ts.map +1 -1
  311. package/dist/game/wizard.js +93 -11
  312. package/dist/game/wizard.js.map +1 -1
  313. package/dist/game/world.d.ts.map +1 -1
  314. package/dist/game/world.js +6 -0
  315. package/dist/game/world.js.map +1 -1
  316. package/dist/gen/cave.d.ts +49 -8
  317. package/dist/gen/cave.d.ts.map +1 -1
  318. package/dist/gen/cave.js +200 -28
  319. package/dist/gen/cave.js.map +1 -1
  320. package/dist/gen/generate.d.ts +54 -9
  321. package/dist/gen/generate.d.ts.map +1 -1
  322. package/dist/gen/generate.js +55 -8
  323. package/dist/gen/generate.js.map +1 -1
  324. package/dist/gen/glyph-vectors.d.ts +109 -0
  325. package/dist/gen/glyph-vectors.d.ts.map +1 -0
  326. package/dist/gen/glyph-vectors.fixtures.d.ts +18 -0
  327. package/dist/gen/glyph-vectors.fixtures.d.ts.map +1 -0
  328. package/dist/gen/glyph-vectors.fixtures.js +94 -0
  329. package/dist/gen/glyph-vectors.fixtures.js.map +1 -0
  330. package/dist/gen/glyph-vectors.js +218 -0
  331. package/dist/gen/glyph-vectors.js.map +1 -0
  332. package/dist/gen/glyph.d.ts +104 -0
  333. package/dist/gen/glyph.d.ts.map +1 -0
  334. package/dist/gen/glyph.js +72 -0
  335. package/dist/gen/glyph.js.map +1 -0
  336. package/dist/gen/room.d.ts +26 -2
  337. package/dist/gen/room.d.ts.map +1 -1
  338. package/dist/gen/room.js +414 -264
  339. package/dist/gen/room.js.map +1 -1
  340. package/dist/gen/util.d.ts +11 -2
  341. package/dist/gen/util.d.ts.map +1 -1
  342. package/dist/gen/util.js +31 -9
  343. package/dist/gen/util.js.map +1 -1
  344. package/dist/i18n/i18n.d.ts +207 -0
  345. package/dist/i18n/i18n.d.ts.map +1 -0
  346. package/dist/i18n/i18n.js +405 -0
  347. package/dist/i18n/i18n.js.map +1 -0
  348. package/dist/i18n/text.d.ts +62 -0
  349. package/dist/i18n/text.d.ts.map +1 -0
  350. package/dist/i18n/text.js +120 -0
  351. package/dist/i18n/text.js.map +1 -0
  352. package/dist/index.d.ts +25 -0
  353. package/dist/index.d.ts.map +1 -1
  354. package/dist/index.js +38 -0
  355. package/dist/index.js.map +1 -1
  356. package/dist/mod/extension.d.ts +115 -0
  357. package/dist/mod/extension.d.ts.map +1 -0
  358. package/dist/mod/extension.js +85 -0
  359. package/dist/mod/extension.js.map +1 -0
  360. package/dist/mod/hooks.d.ts +27 -1
  361. package/dist/mod/hooks.d.ts.map +1 -1
  362. package/dist/mod/hooks.js +23 -0
  363. package/dist/mod/hooks.js.map +1 -1
  364. package/dist/mod/ids.d.ts +10 -0
  365. package/dist/mod/ids.d.ts.map +1 -1
  366. package/dist/mod/ids.js +90 -21
  367. package/dist/mod/ids.js.map +1 -1
  368. package/dist/mod/record-keys.d.ts +43 -0
  369. package/dist/mod/record-keys.d.ts.map +1 -0
  370. package/dist/mod/record-keys.js +100 -0
  371. package/dist/mod/record-keys.js.map +1 -0
  372. package/dist/mod/registry-host.d.ts +472 -0
  373. package/dist/mod/registry-host.d.ts.map +1 -1
  374. package/dist/mod/registry-host.js +313 -0
  375. package/dist/mod/registry-host.js.map +1 -1
  376. package/dist/mod/save-blocks.d.ts +1 -1
  377. package/dist/mon/bind.d.ts.map +1 -1
  378. package/dist/mon/bind.js +6 -3
  379. package/dist/mon/bind.js.map +1 -1
  380. package/dist/mon/knowledge-groups.d.ts +0 -6
  381. package/dist/mon/knowledge-groups.d.ts.map +1 -1
  382. package/dist/mon/knowledge-groups.js +12 -8
  383. package/dist/mon/knowledge-groups.js.map +1 -1
  384. package/dist/mon/lore-describe.d.ts +46 -10
  385. package/dist/mon/lore-describe.d.ts.map +1 -1
  386. package/dist/mon/lore-describe.js +74 -22
  387. package/dist/mon/lore-describe.js.map +1 -1
  388. package/dist/mon/lore-file.d.ts +126 -0
  389. package/dist/mon/lore-file.d.ts.map +1 -0
  390. package/dist/mon/lore-file.js +291 -0
  391. package/dist/mon/lore-file.js.map +1 -0
  392. package/dist/mon/lore.d.ts +9 -3
  393. package/dist/mon/lore.d.ts.map +1 -1
  394. package/dist/mon/lore.js +9 -3
  395. package/dist/mon/lore.js.map +1 -1
  396. package/dist/mon/make.d.ts +4 -2
  397. package/dist/mon/make.d.ts.map +1 -1
  398. package/dist/mon/make.js +4 -2
  399. package/dist/mon/make.js.map +1 -1
  400. package/dist/mon/monster.d.ts +1 -1
  401. package/dist/mon/monster.js +1 -1
  402. package/dist/mon/predicate.d.ts +14 -9
  403. package/dist/mon/predicate.d.ts.map +1 -1
  404. package/dist/mon/predicate.js +14 -9
  405. package/dist/mon/predicate.js.map +1 -1
  406. package/dist/mon/project-mon.d.ts +16 -3
  407. package/dist/mon/project-mon.d.ts.map +1 -1
  408. package/dist/mon/project-mon.js +14 -5
  409. package/dist/mon/project-mon.js.map +1 -1
  410. package/dist/mon/steal.d.ts +31 -4
  411. package/dist/mon/steal.d.ts.map +1 -1
  412. package/dist/mon/steal.js +20 -7
  413. package/dist/mon/steal.js.map +1 -1
  414. package/dist/mon/take-hit.d.ts +8 -3
  415. package/dist/mon/take-hit.d.ts.map +1 -1
  416. package/dist/mon/take-hit.js +8 -3
  417. package/dist/mon/take-hit.js.map +1 -1
  418. package/dist/mon/timed.d.ts +15 -9
  419. package/dist/mon/timed.d.ts.map +1 -1
  420. package/dist/mon/timed.js +13 -8
  421. package/dist/mon/timed.js.map +1 -1
  422. package/dist/mon/types.d.ts +11 -4
  423. package/dist/mon/types.d.ts.map +1 -1
  424. package/dist/mon/types.js.map +1 -1
  425. package/dist/msg.d.ts +40 -1
  426. package/dist/msg.d.ts.map +1 -1
  427. package/dist/msg.js +42 -1
  428. package/dist/msg.js.map +1 -1
  429. package/dist/obj/artifact-fake.d.ts +46 -14
  430. package/dist/obj/artifact-fake.d.ts.map +1 -1
  431. package/dist/obj/artifact-fake.js +46 -17
  432. package/dist/obj/artifact-fake.js.map +1 -1
  433. package/dist/obj/bind.d.ts +23 -0
  434. package/dist/obj/bind.d.ts.map +1 -1
  435. package/dist/obj/bind.js +49 -9
  436. package/dist/obj/bind.js.map +1 -1
  437. package/dist/obj/desc-vectors.d.ts +68 -0
  438. package/dist/obj/desc-vectors.d.ts.map +1 -0
  439. package/dist/obj/desc-vectors.fixtures.d.ts +12 -0
  440. package/dist/obj/desc-vectors.fixtures.d.ts.map +1 -0
  441. package/dist/obj/desc-vectors.fixtures.js +95 -0
  442. package/dist/obj/desc-vectors.fixtures.js.map +1 -0
  443. package/dist/obj/desc-vectors.js +86 -0
  444. package/dist/obj/desc-vectors.js.map +1 -0
  445. package/dist/obj/desc.d.ts +17 -2
  446. package/dist/obj/desc.d.ts.map +1 -1
  447. package/dist/obj/desc.js +129 -54
  448. package/dist/obj/desc.js.map +1 -1
  449. package/dist/obj/effects-info.d.ts.map +1 -1
  450. package/dist/obj/effects-info.js +87 -58
  451. package/dist/obj/effects-info.js.map +1 -1
  452. package/dist/obj/fake-object.d.ts +70 -0
  453. package/dist/obj/fake-object.d.ts.map +1 -0
  454. package/dist/obj/fake-object.js +104 -0
  455. package/dist/obj/fake-object.js.map +1 -0
  456. package/dist/obj/ignore.d.ts +55 -12
  457. package/dist/obj/ignore.d.ts.map +1 -1
  458. package/dist/obj/ignore.js +68 -31
  459. package/dist/obj/ignore.js.map +1 -1
  460. package/dist/obj/knowledge.d.ts +46 -3
  461. package/dist/obj/knowledge.d.ts.map +1 -1
  462. package/dist/obj/knowledge.js +169 -151
  463. package/dist/obj/knowledge.js.map +1 -1
  464. package/dist/obj/known-object.d.ts +68 -8
  465. package/dist/obj/known-object.d.ts.map +1 -1
  466. package/dist/obj/known-object.js +90 -10
  467. package/dist/obj/known-object.js.map +1 -1
  468. package/dist/obj/make.d.ts +15 -4
  469. package/dist/obj/make.d.ts.map +1 -1
  470. package/dist/obj/make.js +49 -27
  471. package/dist/obj/make.js.map +1 -1
  472. package/dist/obj/object-info.d.ts +10 -0
  473. package/dist/obj/object-info.d.ts.map +1 -1
  474. package/dist/obj/object-info.js +93 -7
  475. package/dist/obj/object-info.js.map +1 -1
  476. package/dist/obj/object.d.ts +42 -4
  477. package/dist/obj/object.d.ts.map +1 -1
  478. package/dist/obj/object.js +362 -147
  479. package/dist/obj/object.js.map +1 -1
  480. package/dist/obj/power.d.ts +23 -4
  481. package/dist/obj/power.d.ts.map +1 -1
  482. package/dist/obj/power.js +231 -65
  483. package/dist/obj/power.js.map +1 -1
  484. package/dist/obj/randart-build.d.ts +27 -22
  485. package/dist/obj/randart-build.d.ts.map +1 -1
  486. package/dist/obj/randart-build.js +435 -357
  487. package/dist/obj/randart-build.js.map +1 -1
  488. package/dist/obj/randart-data.d.ts +62 -24
  489. package/dist/obj/randart-data.d.ts.map +1 -1
  490. package/dist/obj/randart-data.js +333 -189
  491. package/dist/obj/randart-data.js.map +1 -1
  492. package/dist/obj/randart-file.d.ts +21 -0
  493. package/dist/obj/randart-file.d.ts.map +1 -0
  494. package/dist/obj/randart-file.js +119 -0
  495. package/dist/obj/randart-file.js.map +1 -0
  496. package/dist/obj/randart-log.d.ts +63 -0
  497. package/dist/obj/randart-log.d.ts.map +1 -0
  498. package/dist/obj/randart-log.js +72 -0
  499. package/dist/obj/randart-log.js.map +1 -0
  500. package/dist/obj/randart-registry.d.ts +178 -0
  501. package/dist/obj/randart-registry.d.ts.map +1 -0
  502. package/dist/obj/randart-registry.js +140 -0
  503. package/dist/obj/randart-registry.js.map +1 -0
  504. package/dist/obj/randart-vectors.d.ts +97 -0
  505. package/dist/obj/randart-vectors.d.ts.map +1 -0
  506. package/dist/obj/randart-vectors.fixtures.d.ts +18 -0
  507. package/dist/obj/randart-vectors.fixtures.d.ts.map +1 -0
  508. package/dist/obj/randart-vectors.fixtures.js +50 -0
  509. package/dist/obj/randart-vectors.fixtures.js.map +1 -0
  510. package/dist/obj/randart-vectors.js +311 -0
  511. package/dist/obj/randart-vectors.js.map +1 -0
  512. package/dist/obj/randart.d.ts +53 -15
  513. package/dist/obj/randart.d.ts.map +1 -1
  514. package/dist/obj/randart.js +166 -44
  515. package/dist/obj/randart.js.map +1 -1
  516. package/dist/obj/rune-registry.d.ts +180 -0
  517. package/dist/obj/rune-registry.d.ts.map +1 -0
  518. package/dist/obj/rune-registry.js +161 -0
  519. package/dist/obj/rune-registry.js.map +1 -0
  520. package/dist/obj/rune-vectors.d.ts +99 -0
  521. package/dist/obj/rune-vectors.d.ts.map +1 -0
  522. package/dist/obj/rune-vectors.fixtures.d.ts +44 -0
  523. package/dist/obj/rune-vectors.fixtures.d.ts.map +1 -0
  524. package/dist/obj/rune-vectors.fixtures.js +125 -0
  525. package/dist/obj/rune-vectors.fixtures.js.map +1 -0
  526. package/dist/obj/rune-vectors.js +159 -0
  527. package/dist/obj/rune-vectors.js.map +1 -0
  528. package/dist/obj/tval-registry.d.ts +174 -0
  529. package/dist/obj/tval-registry.d.ts.map +1 -0
  530. package/dist/obj/tval-registry.js +151 -0
  531. package/dist/obj/tval-registry.js.map +1 -0
  532. package/dist/obj/tval-vectors.d.ts +85 -0
  533. package/dist/obj/tval-vectors.d.ts.map +1 -0
  534. package/dist/obj/tval-vectors.fixtures.d.ts +24 -0
  535. package/dist/obj/tval-vectors.fixtures.d.ts.map +1 -0
  536. package/dist/obj/tval-vectors.fixtures.js +41 -0
  537. package/dist/obj/tval-vectors.fixtures.js.map +1 -0
  538. package/dist/obj/tval-vectors.js +76 -0
  539. package/dist/obj/tval-vectors.js.map +1 -0
  540. package/dist/obj/types.d.ts +18 -7
  541. package/dist/obj/types.d.ts.map +1 -1
  542. package/dist/obj/types.js.map +1 -1
  543. package/dist/obj/value.d.ts.map +1 -1
  544. package/dist/obj/value.js +23 -20
  545. package/dist/obj/value.js.map +1 -1
  546. package/dist/parser.d.ts +116 -0
  547. package/dist/parser.d.ts.map +1 -0
  548. package/dist/parser.js +205 -0
  549. package/dist/parser.js.map +1 -0
  550. package/dist/player/bind.d.ts.map +1 -1
  551. package/dist/player/bind.js +3 -2
  552. package/dist/player/bind.js.map +1 -1
  553. package/dist/player/birth.d.ts +2 -1
  554. package/dist/player/birth.d.ts.map +1 -1
  555. package/dist/player/birth.js +2 -1
  556. package/dist/player/birth.js.map +1 -1
  557. package/dist/player/calcs.d.ts +58 -23
  558. package/dist/player/calcs.d.ts.map +1 -1
  559. package/dist/player/calcs.js +93 -36
  560. package/dist/player/calcs.js.map +1 -1
  561. package/dist/player/death.d.ts +50 -0
  562. package/dist/player/death.d.ts.map +1 -0
  563. package/dist/player/death.js +46 -0
  564. package/dist/player/death.js.map +1 -0
  565. package/dist/player/options-file.d.ts +129 -0
  566. package/dist/player/options-file.d.ts.map +1 -0
  567. package/dist/player/options-file.js +309 -0
  568. package/dist/player/options-file.js.map +1 -0
  569. package/dist/player/options.d.ts +2 -1
  570. package/dist/player/options.d.ts.map +1 -1
  571. package/dist/player/options.js +2 -1
  572. package/dist/player/options.js.map +1 -1
  573. package/dist/player/player.d.ts +50 -3
  574. package/dist/player/player.d.ts.map +1 -1
  575. package/dist/player/player.js +10 -1
  576. package/dist/player/player.js.map +1 -1
  577. package/dist/player/shape-lore.d.ts +11 -7
  578. package/dist/player/shape-lore.d.ts.map +1 -1
  579. package/dist/player/shape-lore.js +24 -11
  580. package/dist/player/shape-lore.js.map +1 -1
  581. package/dist/player/timed.d.ts +5 -3
  582. package/dist/player/timed.d.ts.map +1 -1
  583. package/dist/player/timed.js +11 -2
  584. package/dist/player/timed.js.map +1 -1
  585. package/dist/player/types.d.ts +29 -4
  586. package/dist/player/types.d.ts.map +1 -1
  587. package/dist/player/types.js +24 -1
  588. package/dist/player/types.js.map +1 -1
  589. package/dist/session/boot.d.ts +26 -2
  590. package/dist/session/boot.d.ts.map +1 -1
  591. package/dist/session/boot.js +21 -2
  592. package/dist/session/boot.js.map +1 -1
  593. package/dist/session/game.d.ts +15 -0
  594. package/dist/session/game.d.ts.map +1 -1
  595. package/dist/session/game.js +616 -140
  596. package/dist/session/game.js.map +1 -1
  597. package/dist/session/save-migrate.d.ts.map +1 -1
  598. package/dist/session/save-migrate.js +76 -1
  599. package/dist/session/save-migrate.js.map +1 -1
  600. package/dist/session/save.d.ts +75 -12
  601. package/dist/session/save.d.ts.map +1 -1
  602. package/dist/session/save.js +177 -18
  603. package/dist/session/save.js.map +1 -1
  604. package/dist/store/bind.d.ts +1 -1
  605. package/dist/store/bind.d.ts.map +1 -1
  606. package/dist/store/bind.js +4 -3
  607. package/dist/store/bind.js.map +1 -1
  608. package/dist/store/mass-produce-vectors.d.ts +40 -0
  609. package/dist/store/mass-produce-vectors.d.ts.map +1 -0
  610. package/dist/store/mass-produce-vectors.fixtures.d.ts +13 -0
  611. package/dist/store/mass-produce-vectors.fixtures.d.ts.map +1 -0
  612. package/dist/store/mass-produce-vectors.fixtures.js +52 -0
  613. package/dist/store/mass-produce-vectors.fixtures.js.map +1 -0
  614. package/dist/store/mass-produce-vectors.js +44 -0
  615. package/dist/store/mass-produce-vectors.js.map +1 -0
  616. package/dist/store/store.d.ts +104 -7
  617. package/dist/store/store.d.ts.map +1 -1
  618. package/dist/store/store.js +199 -77
  619. package/dist/store/store.js.map +1 -1
  620. package/dist/store/transact.d.ts +14 -5
  621. package/dist/store/transact.d.ts.map +1 -1
  622. package/dist/store/transact.js +17 -16
  623. package/dist/store/transact.js.map +1 -1
  624. package/dist/store/types.d.ts +3 -2
  625. package/dist/store/types.d.ts.map +1 -1
  626. package/dist/store/types.js +1 -1
  627. package/dist/version.d.ts +1 -1
  628. package/dist/version.js +1 -1
  629. package/dist/visuals/index.d.ts +1 -0
  630. package/dist/visuals/index.d.ts.map +1 -1
  631. package/dist/visuals/index.js +1 -0
  632. package/dist/visuals/index.js.map +1 -1
  633. package/dist/visuals/map-text.d.ts +8 -0
  634. package/dist/visuals/map-text.d.ts.map +1 -1
  635. package/dist/visuals/map-text.js +1 -1
  636. package/dist/visuals/map-text.js.map +1 -1
  637. package/dist/visuals/object-glyph.d.ts +56 -0
  638. package/dist/visuals/object-glyph.d.ts.map +1 -0
  639. package/dist/visuals/object-glyph.js +52 -0
  640. package/dist/visuals/object-glyph.js.map +1 -0
  641. package/dist/visuals/prefs.d.ts +14 -22
  642. package/dist/visuals/prefs.d.ts.map +1 -1
  643. package/dist/visuals/prefs.js +4 -23
  644. package/dist/visuals/prefs.js.map +1 -1
  645. package/dist/visuals/tile-prefs.d.ts +26 -0
  646. package/dist/visuals/tile-prefs.d.ts.map +1 -1
  647. package/dist/visuals/tile-prefs.js +28 -0
  648. package/dist/visuals/tile-prefs.js.map +1 -1
  649. package/dist/world/feature.d.ts +2 -1
  650. package/dist/world/feature.d.ts.map +1 -1
  651. package/dist/world/feature.js +2 -0
  652. package/dist/world/feature.js.map +1 -1
  653. package/dist/world/project.d.ts +10 -2
  654. package/dist/world/project.d.ts.map +1 -1
  655. package/dist/world/project.js +18 -6
  656. package/dist/world/project.js.map +1 -1
  657. package/dist/world/projection.d.ts +37 -3
  658. package/dist/world/projection.d.ts.map +1 -1
  659. package/dist/world/projection.js +69 -7
  660. package/dist/world/projection.js.map +1 -1
  661. package/dist/world/trap.d.ts +2 -1
  662. package/dist/world/trap.d.ts.map +1 -1
  663. package/dist/world/trap.js +2 -1
  664. package/dist/world/trap.js.map +1 -1
  665. package/dist/world/view.d.ts +1 -1
  666. package/dist/world/view.d.ts.map +1 -1
  667. package/dist/world/view.js +11 -3
  668. package/dist/world/view.js.map +1 -1
  669. package/package.json +1 -1
  670. package/src/agent/perceive.ts +2 -3
  671. package/src/combat/blow-vectors.fixtures.ts +99 -0
  672. package/src/combat/blow-vectors.json +482 -0
  673. package/src/combat/blow-vectors.ts +280 -0
  674. package/src/combat/brand-slay.ts +42 -11
  675. package/src/combat/index.ts +1 -1
  676. package/src/combat/melee.ts +37 -19
  677. package/src/combat/mon-melee.ts +611 -364
  678. package/src/combat/ranged.ts +43 -26
  679. package/src/constants.ts +15 -0
  680. package/src/datafile.ts +148 -0
  681. package/src/effects/effect-info-registry.ts +313 -0
  682. package/src/effects/effect-info-vectors.fixtures.ts +61 -0
  683. package/src/effects/effect-info-vectors.json +11532 -0
  684. package/src/effects/effect-info-vectors.ts +376 -0
  685. package/src/effects/effect-info.ts +1005 -859
  686. package/src/effects/effect.ts +96 -62
  687. package/src/effects/handlers.ts +5 -2
  688. package/src/effects/interpreter.ts +14 -0
  689. package/src/game/cave-cmd.ts +54 -11
  690. package/src/game/cave-square.ts +80 -0
  691. package/src/game/char-sheet.ts +59 -16
  692. package/src/game/chest.ts +25 -8
  693. package/src/game/context.ts +293 -27
  694. package/src/game/describe.ts +34 -4
  695. package/src/game/display.ts +215 -19
  696. package/src/game/dump-level.ts +3 -2
  697. package/src/game/effect-attack.ts +41 -28
  698. package/src/game/effect-detect.ts +45 -8
  699. package/src/game/effect-env.ts +8 -0
  700. package/src/game/effect-general.ts +110 -47
  701. package/src/game/effect-item.ts +1075 -999
  702. package/src/game/effect-melee.ts +51 -18
  703. package/src/game/effect-monster.ts +55 -9
  704. package/src/game/effect-teleport.ts +173 -38
  705. package/src/game/effect-terrain.ts +38 -22
  706. package/src/game/equip-cmp.ts +483 -480
  707. package/src/game/floor.ts +22 -4
  708. package/src/game/gear.ts +210 -21
  709. package/src/game/harness.ts +12 -1
  710. package/src/game/ignore-cmd.ts +96 -8
  711. package/src/game/known.ts +392 -73
  712. package/src/game/loop.ts +52 -27
  713. package/src/game/lore-color.ts +13 -1
  714. package/src/game/mon-cast.ts +31 -6
  715. package/src/game/mon-cmd.ts +95 -30
  716. package/src/game/mon-death.ts +14 -12
  717. package/src/game/mon-group.ts +9 -5
  718. package/src/game/mon-message.ts +363 -65
  719. package/src/game/mon-place.ts +15 -3
  720. package/src/game/mon-ranged.ts +14 -5
  721. package/src/game/mon-side.ts +16 -12
  722. package/src/game/monster-turn.ts +120 -87
  723. package/src/game/notice.ts +85 -0
  724. package/src/game/obj-cmd.ts +126 -37
  725. package/src/game/obj-list.ts +44 -26
  726. package/src/game/object-inspect.ts +13 -0
  727. package/src/game/pickup.ts +29 -5
  728. package/src/game/player-side-vectors.fixtures.ts +215 -0
  729. package/src/game/player-side-vectors.json +6914 -0
  730. package/src/game/player-side-vectors.ts +420 -0
  731. package/src/game/player-side.ts +622 -340
  732. package/src/game/player-turn.ts +228 -31
  733. package/src/game/poly.ts +107 -0
  734. package/src/game/project-cast.ts +63 -6
  735. package/src/game/project-feat-vectors.fixtures.ts +106 -0
  736. package/src/game/project-feat-vectors.json +6554 -0
  737. package/src/game/project-feat-vectors.ts +323 -0
  738. package/src/game/project-feat.ts +417 -248
  739. package/src/game/project-monster.ts +69 -30
  740. package/src/game/project-obj-vectors.json +1 -0
  741. package/src/game/project-obj.ts +115 -47
  742. package/src/game/project-player.ts +8 -0
  743. package/src/game/projection-handlers.ts +110 -0
  744. package/src/game/quest.ts +48 -0
  745. package/src/game/ranged-cmd.ts +45 -11
  746. package/src/game/repeat.ts +92 -0
  747. package/src/game/shape-inspect.ts +166 -0
  748. package/src/game/spell-cmd.ts +39 -6
  749. package/src/game/spoil.ts +92 -39
  750. package/src/game/steal.ts +9 -4
  751. package/src/game/target-loop.ts +14 -7
  752. package/src/game/target.ts +18 -7
  753. package/src/game/ui-entry.ts +172 -22
  754. package/src/game/wizard.ts +97 -10
  755. package/src/game/world.ts +6 -0
  756. package/src/gen/cave.ts +225 -26
  757. package/src/gen/generate.ts +62 -9
  758. package/src/gen/glyph-vectors.fixtures.ts +122 -0
  759. package/src/gen/glyph-vectors.json +5996 -0
  760. package/src/gen/glyph-vectors.ts +280 -0
  761. package/src/gen/glyph.ts +132 -0
  762. package/src/gen/room.ts +446 -265
  763. package/src/gen/util.ts +32 -9
  764. package/src/i18n/i18n.ts +492 -0
  765. package/src/i18n/text.ts +117 -0
  766. package/src/index.ts +307 -254
  767. package/src/mod/extension.ts +136 -0
  768. package/src/mod/hooks.ts +54 -1
  769. package/src/mod/ids.ts +92 -21
  770. package/src/mod/record-keys.ts +102 -0
  771. package/src/mod/registry-host.ts +1063 -234
  772. package/src/mon/bind.ts +14 -8
  773. package/src/mon/knowledge-groups.ts +12 -12
  774. package/src/mon/lore-describe.ts +93 -26
  775. package/src/mon/lore-file.ts +365 -0
  776. package/src/mon/lore.ts +9 -3
  777. package/src/mon/make.ts +4 -2
  778. package/src/mon/monster.ts +1 -1
  779. package/src/mon/predicate.ts +14 -9
  780. package/src/mon/project-mon.ts +23 -5
  781. package/src/mon/steal.ts +39 -7
  782. package/src/mon/take-hit.ts +8 -3
  783. package/src/mon/timed.ts +15 -9
  784. package/src/mon/types.ts +11 -4
  785. package/src/msg.ts +55 -1
  786. package/src/obj/artifact-fake.ts +47 -17
  787. package/src/obj/bind.ts +52 -9
  788. package/src/obj/desc-vectors.fixtures.ts +119 -0
  789. package/src/obj/desc-vectors.json +2360 -0
  790. package/src/obj/desc-vectors.ts +128 -0
  791. package/src/obj/desc.ts +159 -55
  792. package/src/obj/effects-info.ts +90 -59
  793. package/src/obj/fake-object.ts +135 -0
  794. package/src/obj/ignore.ts +95 -28
  795. package/src/obj/knowledge.ts +291 -139
  796. package/src/obj/known-object.ts +126 -11
  797. package/src/obj/make.ts +53 -29
  798. package/src/obj/object-info.ts +103 -6
  799. package/src/obj/object.ts +392 -151
  800. package/src/obj/power.ts +297 -59
  801. package/src/obj/randart-build.ts +1875 -1705
  802. package/src/obj/randart-data.ts +1547 -1344
  803. package/src/obj/randart-file.ts +130 -0
  804. package/src/obj/randart-log.ts +78 -0
  805. package/src/obj/randart-registry.ts +247 -0
  806. package/src/obj/randart-vectors.fixtures.ts +61 -0
  807. package/src/obj/randart-vectors.json +836 -0
  808. package/src/obj/randart-vectors.ts +369 -0
  809. package/src/obj/randart.ts +722 -552
  810. package/src/obj/rune-registry.ts +270 -0
  811. package/src/obj/rune-vectors.fixtures.ts +164 -0
  812. package/src/obj/rune-vectors.json +121 -0
  813. package/src/obj/rune-vectors.ts +229 -0
  814. package/src/obj/tval-kind-vectors.json +391 -0
  815. package/src/obj/tval-registry.ts +243 -0
  816. package/src/obj/tval-vectors.fixtures.ts +50 -0
  817. package/src/obj/tval-vectors.json +38 -0
  818. package/src/obj/tval-vectors.ts +126 -0
  819. package/src/obj/types.ts +18 -7
  820. package/src/obj/value.ts +26 -20
  821. package/src/parser.ts +214 -0
  822. package/src/player/bind.ts +5 -2
  823. package/src/player/birth.ts +2 -1
  824. package/src/player/calcs.ts +123 -42
  825. package/src/player/death.ts +78 -0
  826. package/src/player/options-file.ts +387 -0
  827. package/src/player/options.ts +2 -1
  828. package/src/player/player.ts +60 -4
  829. package/src/player/shape-lore.ts +33 -14
  830. package/src/player/timed.ts +14 -4
  831. package/src/player/types.ts +30 -4
  832. package/src/session/boot.ts +51 -1
  833. package/src/session/game.ts +720 -163
  834. package/src/session/save-migrate.ts +81 -1
  835. package/src/session/save.ts +264 -22
  836. package/src/store/bind.ts +4 -3
  837. package/src/store/mass-produce-vectors.fixtures.ts +64 -0
  838. package/src/store/mass-produce-vectors.json +1169 -0
  839. package/src/store/mass-produce-vectors.ts +67 -0
  840. package/src/store/store.ts +266 -63
  841. package/src/store/transact.ts +39 -16
  842. package/src/store/types.ts +3 -2
  843. package/src/version.ts +1 -1
  844. package/src/visuals/index.ts +1 -0
  845. package/src/visuals/map-text.ts +1 -1
  846. package/src/visuals/object-glyph.ts +76 -0
  847. package/src/visuals/prefs.ts +22 -32
  848. package/src/visuals/tile-prefs.ts +32 -0
  849. package/src/world/feature.ts +4 -1
  850. package/src/world/project.ts +19 -4
  851. package/src/world/projection.ts +79 -8
  852. package/src/world/trap.ts +25 -21
  853. package/src/world/view.ts +11 -1
@@ -1,552 +1,722 @@
1
- /**
2
- * Random artifact generation: the top-level design loop and entry point,
3
- * ported from reference/src/obj-randart.c (Angband 4.2.6) lines 2672-3219.
4
- * This module ports copy_artifact (L2676), artifact_gen_name (L2713),
5
- * design_artifact (L2751), create_artifact_set (L2954), artifact_set_data_new
6
- * (via artifactSetDataNew in randart-data.ts) and do_randart (L3154). It drives
7
- * the measurement half (collectArtifactData, artifactPower in randart-data.ts)
8
- * and the building primitives (getBaseItem, artifactPrep, buildFreqTable,
9
- * trySupercharge, addAbility, removeContradictory, makeBad in randart-build.ts).
10
- *
11
- * do_randart is pure with respect to its seed: the same seed always yields the
12
- * same (Artifact|null)[] set. It measures the registry's standard artifact set
13
- * to build the generation frequencies, then designs a fresh copy of every
14
- * artifact into a new array, leaving ObjRegistry.artifacts untouched.
15
- *
16
- * Faithful notes / deferrals / approximations:
17
- * - RNG mode: upstream do_randart sets Rand_quick = true and seeds Rand_value
18
- * with randart_seed, i.e. it draws from the "quick" LCRNG, not the WELL
19
- * stream. This port creates its Rng in quick mode to match the draw stream
20
- * (new Rng(seed, { quick: true })).
21
- * - artifact_gen_name / wordlist (FAITHFUL when the TOLKIEN corpus is supplied):
22
- * upstream builds a Markov-chain name from the RANDNAME_TOLKIEN word list in
23
- * the names datafile (randname.c randname_make + name_sections). randname_make
24
- * and build_prob are ported faithfully in randname.ts, and the corpus ships in
25
- * the content pack as names.json section 1 (loaded into
26
- * CoreRegistries.nameSections at boot). doRandart now accepts that word list
27
- * (tolkienWords); artifactGenName then calls randnameMake(RANDNAME_TOLKIEN,
28
- * MIN_NAME_LEN, MAX_NAME_LEN) exactly like upstream artifact_gen_name
29
- * (obj-randart.c L2713), so the name draws consume the same RNG values in the
30
- * same order and the generated names match upstream. This is verified against
31
- * an independent Python oracle in randart.test.ts.
32
- *
33
- * Game path: session/game.ts swapRandartSet() threads the corpus -
34
- * doRandart(reg.objects, seed, reg.nameSections.get(RANDNAME_TOLKIEN))
35
- * (game.ts:2511-2514) - so the in-game randart names draw faithfully too.
36
- * randNameFallback (a local syllable table) is retained only as a defensive
37
- * guard for callers that pass no corpus (it never runs on the wired paths).
38
- * - Spoiler file (DEFERRED): upstream do_randart optionally writes randart.txt
39
- * (create_file / write_randart_entry, obj-randart.c L3057-L3215) and always
40
- * writes randart.log. Both are spoiler/log dumps that never affect any
41
- * artifact field or RNG draw; they are dropped. do_randart therefore takes
42
- * no create_file argument and returns the artifact array directly. Noted as
43
- * a deferral. The second measurement pass upstream runs after generation
44
- * (store_base_power/parse_frequencies on the finished set, L3184-L3187) exists
45
- * only to populate the log and consumes no RNG, so it is dropped too.
46
- * - copy_artifact activation/alt_msg quirk (FAITHFUL): upstream copy_artifact
47
- * memcpy's the whole struct and then explicitly nulls a_dst->activation and
48
- * a_dst->alt_msg (obj-randart.c L2689-L2690). copyArtifact reproduces this:
49
- * the destination always loses its activation (set null) and alt_msg (set "")
50
- * even though every other field is copied. This means a rollback in the
51
- * design loop can drop an activation that add_activation had added. Faithful.
52
- * - design_artifact stale-kind quirk (FAITHFUL): upstream looks up `kind` once
53
- * before the fixed-artifact skip loop and never refreshes it inside the loop
54
- * (obj-randart.c L2754, L2778). The QUEST_ART test therefore keeps reading
55
- * the initial artifact's kind while the name test tracks the advancing
56
- * artifact. In the standard set the quest artifacts are last and contiguous,
57
- * so this is unobservable, but it is reproduced exactly. Noted.
58
- */
59
-
60
- import { KF, TV } from "../generated/index.js";
61
- import { Rng } from "../rng.js";
62
- import type { ObjRegistry } from "./bind.js";
63
- import { tvalFindName } from "./bind.js";
64
- import { buildProb, randnameMake, type NameProbs } from "./randname.js";
65
- import {
66
- addAbility,
67
- getBaseItem,
68
- artifactPrep,
69
- buildFreqTable,
70
- makeBad,
71
- removeContradictory,
72
- trySupercharge,
73
- } from "./randart-build.js";
74
- import type { ArtifactSetData } from "./randart-data.js";
75
- import {
76
- artifactPower,
77
- collectArtifactData,
78
- getBaseItemTval,
79
- } from "./randart-data.js";
80
- import type { Artifact } from "./types.js";
81
- import { TV_MAX } from "./types.js";
82
-
83
- /* Re-export makeBad so consumers can reach it from the top-level module. */
84
- export { makeBad };
85
-
86
- /* ------------------------------------------------------------------ */
87
- /* Constants (obj-randart.h) */
88
- /* ------------------------------------------------------------------ */
89
-
90
- /** MAX_TRIES (obj-randart.h L28). */
91
- const MAX_TRIES = 200;
92
-
93
- /** MIN_NAME_LEN / MAX_NAME_LEN (obj-randart.h L31-L32). */
94
- const MIN_NAME_LEN = 5;
95
- const MAX_NAME_LEN = 9;
96
-
97
- /**
98
- * RANDNAME_TOLKIEN (randname.h L26): the names.txt section index whose word list
99
- * feeds artifact_gen_name (obj-randart.c L2717). Section 1 in names.json.
100
- */
101
- export const RANDNAME_TOLKIEN = 1;
102
-
103
- /* ------------------------------------------------------------------ */
104
- /* copy_artifact (obj-randart.c L2676) */
105
- /* ------------------------------------------------------------------ */
106
-
107
- /**
108
- * copy_artifact (obj-randart.c L2676): copy every artifact field from src to
109
- * dst in place. Faithful to the upstream memcpy-then-null: the destination's
110
- * activation is always cleared to null and its alt_msg to "" (obj-randart.c
111
- * L2689-L2690), even though all other fields (name, text, combat, flags,
112
- * modifiers, element info, deep copies of slays/brands/curses, time) are
113
- * copied. dst keeps its own identity so array references stay valid.
114
- */
115
- export function copyArtifact(src: Artifact, dst: Artifact): void {
116
- dst.name = src.name;
117
- dst.text = src.text;
118
- dst.aidx = src.aidx;
119
- dst.tval = src.tval;
120
- dst.sval = src.sval;
121
- dst.toH = src.toH;
122
- dst.toD = src.toD;
123
- dst.toA = src.toA;
124
- dst.ac = src.ac;
125
- dst.dd = src.dd;
126
- dst.ds = src.ds;
127
- dst.weight = src.weight;
128
- dst.cost = src.cost;
129
- dst.flags = src.flags.clone();
130
- dst.modifiers = src.modifiers.slice();
131
- dst.elInfo = src.elInfo.map((e) => ({ resLevel: e.resLevel, flags: e.flags }));
132
- dst.brands = src.brands ? src.brands.slice() : null;
133
- dst.slays = src.slays ? src.slays.slice() : null;
134
- dst.curses = src.curses ? src.curses.slice() : null;
135
- dst.level = src.level;
136
- dst.allocProb = src.allocProb;
137
- dst.allocMin = src.allocMin;
138
- dst.allocMax = src.allocMax;
139
- dst.time = { ...src.time };
140
-
141
- /* Upstream nulls these after the memcpy (obj-randart.c L2689-L2690). */
142
- dst.activation = null;
143
- dst.altMsg = "";
144
- }
145
-
146
- /**
147
- * A full deep copy of an artifact, preserving activation and alt_msg. Used to
148
- * snapshot the registry's standard artifacts into a mutable working set so
149
- * do_randart never mutates ObjRegistry.artifacts. This is NOT copy_artifact
150
- * (which deliberately drops activation/alt_msg); it is the honest clone the
151
- * working array needs.
152
- */
153
- function cloneArtifact(src: Artifact): Artifact {
154
- return {
155
- name: src.name,
156
- text: src.text,
157
- aidx: src.aidx,
158
- tval: src.tval,
159
- sval: src.sval,
160
- toH: src.toH,
161
- toD: src.toD,
162
- toA: src.toA,
163
- ac: src.ac,
164
- dd: src.dd,
165
- ds: src.ds,
166
- weight: src.weight,
167
- cost: src.cost,
168
- flags: src.flags.clone(),
169
- modifiers: src.modifiers.slice(),
170
- elInfo: src.elInfo.map((e) => ({ resLevel: e.resLevel, flags: e.flags })),
171
- brands: src.brands ? src.brands.slice() : null,
172
- slays: src.slays ? src.slays.slice() : null,
173
- curses: src.curses ? src.curses.slice() : null,
174
- level: src.level,
175
- allocProb: src.allocProb,
176
- allocMin: src.allocMin,
177
- allocMax: src.allocMax,
178
- activation: src.activation,
179
- altMsg: src.altMsg,
180
- time: { ...src.time },
181
- };
182
- }
183
-
184
- /* ------------------------------------------------------------------ */
185
- /* artifact_gen_name (obj-randart.c L2713) */
186
- /* ------------------------------------------------------------------ */
187
-
188
- /**
189
- * my_strcap (z-util.c L529): capitalize only the first character, leaving the
190
- * rest untouched. randnameMake returns an all-lowercase word, so this matches
191
- * upstream exactly.
192
- */
193
- function myStrcap(word: string): string {
194
- if (word.length === 0) return word;
195
- return word.charAt(0).toUpperCase() + word.slice(1);
196
- }
197
-
198
- /**
199
- * Tolkien-flavoured syllable fragments used ONLY as the seam fallback when no
200
- * RANDNAME_TOLKIEN corpus is supplied to doRandart (see module SEAM note). This
201
- * is NOT the upstream algorithm; it keeps the game path deterministic and
202
- * crash-free until game.ts threads the real corpus.
203
- */
204
- const NAME_SYLLABLES: readonly string[] = [
205
- "an", "ar", "el", "en", "or", "ith", "gal", "dor", "mir", "las",
206
- "thal", "rond", "wen", "dil", "beth", "ath", "ien", "ael", "uin", "gorn",
207
- "iel", "und", "ost", "loth", "mor", "fin", "hir", "eth", "ond", "aur",
208
- ];
209
-
210
- /**
211
- * Seam fallback (non-faithful): assemble a plausible name from the syllable
212
- * table. Fragments are drawn until the length reaches MIN_NAME_LEN, then clamped
213
- * to MAX_NAME_LEN. Deterministic and bounded; used only when doRandart has no
214
- * corpus.
215
- */
216
- function randNameFallback(rng: Rng): string {
217
- let word = "";
218
- while (word.length < MIN_NAME_LEN) {
219
- word += NAME_SYLLABLES[rng.randint0(NAME_SYLLABLES.length)]!;
220
- }
221
- if (word.length > MAX_NAME_LEN) word = word.slice(0, MAX_NAME_LEN);
222
- return word;
223
- }
224
-
225
- /**
226
- * artifact_gen_name (obj-randart.c L2713): generate a random artifact name.
227
- * Faithful to upstream: draw a Markov word from the RANDNAME_TOLKIEN corpus via
228
- * randnameMake(MIN_NAME_LEN, MAX_NAME_LEN) (obj-randart.c L2717), my_strcap the
229
- * first letter (L2719), then one_in_(3) selects the "'Name'" form over
230
- * "of Name" (L2721-L2724). The upstream `struct artifact *a` argument is unused.
231
- *
232
- * `probs` is the precomputed transition table (build_prob over the corpus). When
233
- * null (seam fallback, no corpus), the syllable table is used instead - this is
234
- * NOT upstream-faithful and consumes a different number of RNG draws.
235
- */
236
- export function artifactGenName(rng: Rng, probs: NameProbs | null): string {
237
- const word =
238
- probs !== null
239
- ? randnameMake(rng, MIN_NAME_LEN, MAX_NAME_LEN, probs)
240
- : randNameFallback(rng);
241
- const capped = myStrcap(word);
242
- if (rng.oneIn(3)) return `'${capped}'`;
243
- return `of ${capped}`;
244
- }
245
-
246
- /* ------------------------------------------------------------------ */
247
- /* describe_artifact (obj-randart.c L2731) */
248
- /* ------------------------------------------------------------------ */
249
-
250
- /**
251
- * describe_artifact (obj-randart.c L2731): give the artifact a boring
252
- * "Random <type> of power <n>" description. Consumes no RNG.
253
- */
254
- function describeArtifact(art: Artifact, power: number): void {
255
- art.text = `Random ${tvalFindName(art.tval)} of power ${power}`;
256
- }
257
-
258
- /* ------------------------------------------------------------------ */
259
- /* design_artifact (obj-randart.c L2751) */
260
- /* ------------------------------------------------------------------ */
261
-
262
- /**
263
- * design_artifact (obj-randart.c L2751): design a random artifact into
264
- * arts[aidx], possibly skipping forward past fixed artifacts. Returns the
265
- * (possibly advanced) working index, matching upstream's *aidx after return;
266
- * the caller then increments it. `tv` is TV_NULL to pick a tval from the
267
- * learned frequencies, or a specific tval to force.
268
- *
269
- * The artifact is assigned a target power from the range of powers for its
270
- * tval, given a suitable base item, optionally supercharged, then has abilities
271
- * (or, for a cursed artifact, curses) added until its power lands between 19/20
272
- * and 23/20 of the target.
273
- */
274
- export function designArtifact(
275
- reg: ObjRegistry,
276
- arts: (Artifact | null)[],
277
- data: ArtifactSetData,
278
- tv: number,
279
- aidx: number,
280
- rng: Rng,
281
- nameProbs: NameProbs | null,
282
- ): number {
283
- /* Defensive guard (upstream relies on aidx staying in range). */
284
- if (aidx < 1 || aidx >= arts.length) return aidx;
285
-
286
- let art = arts[aidx] as Artifact;
287
- /* Upstream captures kind once and never refreshes it in the skip loop. */
288
- let kind = reg.lookupKind(art.tval, art.sval);
289
- const kindAtEntry = kind;
290
- let artLevel = art.level;
291
-
292
- /* Set tval if necessary. */
293
- let tval = tv === TV.NULL ? getBaseItemTval(data, rng) : tv;
294
-
295
- /* Choose a power for the artifact. */
296
- let power = rng.randSample(
297
- data.avgTvPower[tval]!,
298
- data.maxTvPower[tval]!,
299
- data.minTvPower[tval]!,
300
- 20,
301
- 20,
302
- );
303
-
304
- /* Choose a name. */
305
- const newName = artifactGenName(rng, nameProbs);
306
-
307
- /* Skip fixed artifacts (stale-kind quirk preserved: kindAtEntry). */
308
- while (
309
- art.name.includes("The One Ring") ||
310
- (kindAtEntry !== null && kindAtEntry.kindFlags.has(KF.QUEST_ART))
311
- ) {
312
- aidx++;
313
- if (aidx >= arts.length) return aidx;
314
- art = arts[aidx] as Artifact;
315
- artLevel = art.level;
316
- }
317
-
318
- /* Apply the new name. */
319
- art.name = newName;
320
-
321
- /* Flip the sign on power if it's negative (unlikely) and damage. */
322
- let hurtMe = false;
323
- if (power < 0) {
324
- hurtMe = true;
325
- power = -power;
326
- }
327
-
328
- /* Structure to hold the old artifact for rollbacks. */
329
- const aOld = cloneArtifact(art);
330
-
331
- let tries: number;
332
- let ap = 0;
333
-
334
- /* Choose a base item not too powerful, so we'll have to add to it. */
335
- for (tries = 0; tries < MAX_TRIES; tries++) {
336
- if (tval === TV.NULL) tval = getBaseItemTval(data, rng);
337
- kind = getBaseItem(reg, tval, rng);
338
- artifactPrep(reg, art, kind, data, rng);
339
-
340
- /* Get the kind again in case it's changed. */
341
- kind = reg.lookupKind(art.tval, art.sval);
342
-
343
- const basePower = artifactPower(reg, art);
344
-
345
- /* New base item power too close to target artifact power. */
346
- if (basePower > Math.trunc((power * 6) / 10) + 1 && power - basePower < 20) {
347
- continue;
348
- }
349
-
350
- /* Acceptable. */
351
- break;
352
- }
353
-
354
- /* Generate the cumulative frequency table for this base item type. */
355
- const artFreq = buildFreqTable(art, data);
356
-
357
- /* Copy artifact info temporarily. */
358
- copyArtifact(art, aOld);
359
-
360
- /* Give this artifact a shot at being supercharged. */
361
- trySupercharge(reg, art, power, data, rng);
362
- ap = artifactPower(reg, art);
363
- if (ap > Math.trunc((power * 23) / 20) + 1) {
364
- /* Too powerful -- put it back. */
365
- copyArtifact(aOld, art);
366
- }
367
-
368
- /* Give this artifact a chance to be cursed - note it retains its power. */
369
- if (rng.oneIn(Math.trunc(arts.length / Math.max(2, data.negPowerTotal)))) {
370
- hurtMe = true;
371
- }
372
-
373
- /* Do the actual artifact design. */
374
- for (tries = 0; tries < MAX_TRIES; tries++) {
375
- /* Copy artifact info temporarily. */
376
- copyArtifact(art, aOld);
377
-
378
- /* Add an ability. */
379
- addAbility(reg, art, power, artFreq, data, rng);
380
- removeContradictory(reg, art, data.timedFoil, data.activationSummarize);
381
-
382
- /* Check the power, handle negative power. */
383
- ap = artifactPower(reg, art);
384
- if (ap < 0) {
385
- ap = -ap;
386
- break;
387
- }
388
-
389
- /* Curse the designated artifacts. */
390
- if (hurtMe) {
391
- makeBad(reg, art, artLevel, rng, data.timedFoil);
392
- if (rng.oneIn(3)) {
393
- hurtMe = false;
394
- }
395
- }
396
-
397
- /* Check power. */
398
- if (ap > Math.trunc((power * 23) / 20) + 1) {
399
- /* Too powerful -- put it back. */
400
- copyArtifact(aOld, art);
401
- continue;
402
- } else if (ap >= Math.trunc((power * 19) / 20)) {
403
- /* Just right. */
404
- break;
405
- }
406
- }
407
-
408
- /* Set rarity based on power. kind is the final base item kind. */
409
- const baseKind = kind as NonNullable<typeof kind>;
410
- let allocNew = Math.trunc(4000000 / (ap * ap));
411
- allocNew = Math.trunc(allocNew / (baseKind.allocProb ? baseKind.allocProb : 20));
412
- if (allocNew > 99) allocNew = 99;
413
- if (allocNew < 1) allocNew = 1;
414
- art.allocProb = allocNew;
415
-
416
- /* Set depth according to power. */
417
- art.allocMax = Math.min(127, Math.trunc((ap * 3) / 5));
418
- art.allocMin = Math.min(100, Math.trunc(((ap + 100) * 100) / data.maxPower));
419
-
420
- /* Have a chance to be less rare or deep, more likely the less power. */
421
- if (rng.oneIn(5 + Math.trunc(power / 20))) {
422
- art.allocProb += rng.randint1(20);
423
- if (art.allocProb > 99) art.allocProb = 99;
424
- } else if (rng.oneIn(5 + Math.trunc(power / 20))) {
425
- art.allocMin = Math.trunc(art.allocMin / 2);
426
- if (art.allocMin < 1) art.allocMin = 1;
427
- }
428
-
429
- /* Sanity check. */
430
- art.allocMax = Math.max(art.allocMax, Math.min(art.allocMin * 2, 127));
431
-
432
- /*
433
- * If there is no activation or effect from the kind, level currently does
434
- * nothing. Set it to alloc_min in case changes elsewhere start using level.
435
- */
436
- if (!art.activation && !baseKind.activation && !baseKind.effect) {
437
- art.level = art.allocMin;
438
- }
439
-
440
- /* Describe it. */
441
- describeArtifact(art, ap);
442
-
443
- return aidx;
444
- }
445
-
446
- /* ------------------------------------------------------------------ */
447
- /* create_artifact_set (obj-randart.c L2954) */
448
- /* ------------------------------------------------------------------ */
449
-
450
- /**
451
- * create_artifact_set (obj-randart.c L2954): design a full set of random
452
- * artifacts into `arts`. The resulting set has at least 80% as many artifacts
453
- * of any given tval as the original set (tvals with fewer than 5 original
454
- * artifacts get equal or more). Remaining slots are filled with random-tval
455
- * artifacts. The final slot (index arts.length - 1) is left as the original,
456
- * matching the upstream `aidx < z_info->a_max - 1` bound.
457
- */
458
- export function createArtifactSet(
459
- reg: ObjRegistry,
460
- arts: (Artifact | null)[],
461
- data: ArtifactSetData,
462
- rng: Rng,
463
- nameProbs: NameProbs | null,
464
- ): void {
465
- let aidx = 1;
466
- const tvalTotal = new Array<number>(TV_MAX).fill(0);
467
- let notDone = true;
468
-
469
- /* Get min tval frequencies for the new artifacts (at least 80% each). */
470
- for (let i = 0; i < TV_MAX; i++) {
471
- tvalTotal[i] = Math.trunc((4 * (data.tvNum[i]! + 1)) / 5);
472
- }
473
-
474
- /* Allocate a minimal set of artifacts to the tvals. */
475
- while (notDone) {
476
- notDone = false;
477
-
478
- /* Multiple passes through tvals until all have enough artifacts. */
479
- for (let i = 0; i < TV_MAX; i++) {
480
- if (tvalTotal[i]! > 0) {
481
- aidx = designArtifact(reg, arts, data, i, aidx, rng, nameProbs);
482
- tvalTotal[i]!--;
483
- aidx++;
484
- notDone = true;
485
- }
486
- }
487
- }
488
-
489
- /* Allocate remaining artifacts at random. */
490
- while (aidx < arts.length - 1) {
491
- aidx = designArtifact(reg, arts, data, TV.NULL, aidx, rng, nameProbs);
492
- aidx++;
493
- }
494
- }
495
-
496
- /* ------------------------------------------------------------------ */
497
- /* do_randart (obj-randart.c L3154) */
498
- /* ------------------------------------------------------------------ */
499
-
500
- /**
501
- * do_randart (obj-randart.c L3154): generate a full random artifact set from a
502
- * seed and return it as a fresh (Artifact|null)[] array (index 0 null),
503
- * leaving ObjRegistry.artifacts untouched. Pure with respect to the seed.
504
- *
505
- * Upstream seeds the "quick" LCRNG (Rand_value = seed, Rand_quick = true), so
506
- * this port creates its Rng in quick mode and draws in upstream order:
507
- * measure the standard set (store_base_power + parse_frequencies via
508
- * collectArtifactData), then design every artifact (create_artifact_set). The
509
- * upstream log file, the post-generation measurement pass, and the optional
510
- * spoiler file (create_file / write_randart_entry) are all dropped as they
511
- * never affect an artifact field or an RNG draw (see module note).
512
- */
513
- export function doRandart(
514
- reg: ObjRegistry,
515
- randartSeed: number,
516
- tolkienWords?: readonly string[],
517
- extras?: Pick<ArtifactSetData, "timedFoil" | "activationSummarize">,
518
- ): (Artifact | null)[] {
519
- /* Prepare to use the Angband "simple" (quick LCRNG) RNG. */
520
- const rng = new Rng(randartSeed, { quick: true });
521
-
522
- /*
523
- * Build the RANDNAME_TOLKIEN transition table once (build_prob is cached
524
- * per-type upstream, randname.c L94-L103; here we build it once per run).
525
- * When no corpus is supplied (the current game-path seam), fall back to the
526
- * non-faithful syllable generator (see module SEAM note). An empty word list
527
- * is treated as "no corpus" because build_prob/randname_make would otherwise
528
- * loop forever on an empty table.
529
- */
530
- const nameProbs: NameProbs | null =
531
- tolkienWords && tolkienWords.length > 0 ? buildProb(tolkienWords) : null;
532
-
533
- /* Store the original power ratings and determine generation probabilities.
534
- * `extras` threads the curse TIMED_INC foil tables (gap 3.3) and the
535
- * activation redundancy summarizer (gap 3.8); absent, both checks are
536
- * skipped as before. */
537
- const data = collectArtifactData(reg, rng);
538
- if (extras?.timedFoil) data.timedFoil = extras.timedFoil;
539
- if (extras?.activationSummarize) {
540
- data.activationSummarize = extras.activationSummarize;
541
- }
542
-
543
- /* Work on a fresh copy so the registry's standard artifacts are preserved. */
544
- const arts: (Artifact | null)[] = reg.artifacts.map((a) =>
545
- a ? cloneArtifact(a) : null,
546
- );
547
-
548
- /* Generate the random artifacts. */
549
- createArtifactSet(reg, arts, data, rng, nameProbs);
550
-
551
- return arts;
552
- }
1
+ /**
2
+ * Random artifact generation: the top-level design loop and entry point,
3
+ * ported from reference/src/obj-randart.c (Angband 4.2.6) lines 2672-3219.
4
+ * This module ports copy_artifact (L2676), artifact_gen_name (L2713),
5
+ * design_artifact (L2751), create_artifact_set (L2954), artifact_set_data_new
6
+ * (via artifactSetDataNew in randart-data.ts) and do_randart (L3154). It drives
7
+ * the measurement half (collectArtifactData, artifactPower in randart-data.ts)
8
+ * and the building primitives (getBaseItem, artifactPrep, buildFreqTable,
9
+ * trySupercharge, addAbility, removeContradictory, makeBad in randart-build.ts).
10
+ *
11
+ * do_randart is pure with respect to its seed: the same seed always yields the
12
+ * same (Artifact|null)[] set. It measures the registry's standard artifact set
13
+ * to build the generation frequencies, then designs a fresh copy of every
14
+ * artifact into a new array, leaving ObjRegistry.artifacts untouched.
15
+ *
16
+ * Faithful notes / deferrals / approximations:
17
+ * - RNG mode: upstream do_randart sets Rand_quick = true and seeds Rand_value
18
+ * with randart_seed, i.e. it draws from the "quick" LCRNG, not the WELL
19
+ * stream. This port creates its Rng in quick mode to match the draw stream
20
+ * (new Rng(seed, { quick: true })).
21
+ * - artifact_gen_name / wordlist (FAITHFUL when the TOLKIEN corpus is supplied):
22
+ * upstream builds a Markov-chain name from the RANDNAME_TOLKIEN word list in
23
+ * the names datafile (randname.c randname_make + name_sections). randname_make
24
+ * and build_prob are ported faithfully in randname.ts, and the corpus ships in
25
+ * the content pack as names.json section 1 (loaded into
26
+ * CoreRegistries.nameSections at boot). doRandart now accepts that word list
27
+ * (tolkienWords); artifactGenName then calls randnameMake(RANDNAME_TOLKIEN,
28
+ * MIN_NAME_LEN, MAX_NAME_LEN) exactly like upstream artifact_gen_name
29
+ * (obj-randart.c L2713), so the name draws consume the same RNG values in the
30
+ * same order and the generated names match upstream. This is verified against
31
+ * an independent Python oracle in randart.test.ts.
32
+ *
33
+ * Game path: session/game.ts swapRandartSet() threads the corpus -
34
+ * doRandart(reg.objects, seed, reg.nameSections.get(RANDNAME_TOLKIEN))
35
+ * (game.ts:2511-2514) - so the in-game randart names draw faithfully too.
36
+ * randNameFallback (a local syllable table) is retained only as a defensive
37
+ * guard for callers that pass no corpus (it never runs on the wired paths).
38
+ * - randart.log (PORT_TODO 5.5): PORTED bar one line. do_randart opens and
39
+ * closes it through the host; the maintainer's disposition on 2026-08-04 was
40
+ * pursue parity, so the "it never affects an artifact field" argument the old
41
+ * note made here was never a reason to omit it. What is and is not written is
42
+ * MEASURED by obj/randart-log.census.test.ts rather than claimed in prose,
43
+ * including the one site the census's span filter cannot see. See
44
+ * randart-log.ts for the sink and the reason it is a module-level static.
45
+ * - randart.txt: PORTED. obj/randart-file.ts, gated by do_randart's create_file
46
+ * (obj-randart.c L3195-L3215), which is a required parameter here.
47
+ * - The second measurement pass upstream runs after generation
48
+ * (store_base_power/parse_frequencies on the finished set, L3181-L3186):
49
+ * PORTED. It exists only to populate the log's closing statistics, and the
50
+ * set it measures is a parameter here because this port does not overwrite
51
+ * the a_info global the way upstream does.
52
+ * - copy_artifact activation/alt_msg quirk (FAITHFUL): upstream copy_artifact
53
+ * memcpy's the whole struct and then explicitly nulls a_dst->activation and
54
+ * a_dst->alt_msg (obj-randart.c L2689-L2690). copyArtifact reproduces this:
55
+ * the destination always loses its activation (set null) and alt_msg (set "")
56
+ * even though every other field is copied. This means a rollback in the
57
+ * design loop can drop an activation that add_activation had added. Faithful.
58
+ * - design_artifact stale-kind quirk (FAITHFUL): upstream looks up `kind` once
59
+ * before the fixed-artifact skip loop and never refreshes it inside the loop
60
+ * (obj-randart.c L2754, L2778). The QUEST_ART test therefore keeps reading
61
+ * the initial artifact's kind while the name test tracks the advancing
62
+ * artifact. In the standard set the quest artifacts are last and contiguous,
63
+ * so this is unobservable, but it is reproduced exactly. Noted.
64
+ */
65
+
66
+ import { FileMode, FileType, HostDir, host } from "../host/io.js";
67
+ import type { HostIo } from "../host/io.js";
68
+ import { RANDART_TXT, writeRandartFile } from "./randart-file.js";
69
+ import { randartLog, randartLogf, setRandartLog } from "./randart-log.js";
70
+ import { KF, TV } from "../generated/index.js";
71
+ import { Rng } from "../rng.js";
72
+ import type { Constants } from "../constants.js";
73
+ import type { ObjRegistry } from "./bind.js";
74
+ import { tvalFindName } from "./bind.js";
75
+ import { buildProb, randnameMake, type NameProbs } from "./randname.js";
76
+ import {
77
+ addAbility,
78
+ getBaseItem,
79
+ artifactPrep,
80
+ buildFreqTable,
81
+ makeBad,
82
+ removeContradictory,
83
+ trySupercharge,
84
+ } from "./randart-build.js";
85
+ import type { ArtifactSetData } from "./randart-data.js";
86
+ import {
87
+ artifactPower,
88
+ collectArtifactData,
89
+ getBaseItemTval,
90
+ } from "./randart-data.js";
91
+ import type { Artifact } from "./types.js";
92
+ import { TV_MAX } from "./types.js";
93
+
94
+ /* Re-export makeBad so consumers can reach it from the top-level module. */
95
+ export { makeBad };
96
+
97
+ /* ------------------------------------------------------------------ */
98
+ /* Constants (obj-randart.h) */
99
+ /* ------------------------------------------------------------------ */
100
+
101
+ /** MAX_TRIES (obj-randart.h L28). */
102
+ const MAX_TRIES = 200;
103
+
104
+ /** MIN_NAME_LEN / MAX_NAME_LEN (obj-randart.h L31-L32). */
105
+ const MIN_NAME_LEN = 5;
106
+ const MAX_NAME_LEN = 9;
107
+
108
+ /**
109
+ * RANDNAME_TOLKIEN (randname.h L26): the names.txt section index whose word list
110
+ * feeds artifact_gen_name (obj-randart.c L2717). Section 1 in names.json.
111
+ */
112
+ export const RANDNAME_TOLKIEN = 1;
113
+
114
+ /* ------------------------------------------------------------------ */
115
+ /* copy_artifact (obj-randart.c L2676) */
116
+ /* ------------------------------------------------------------------ */
117
+
118
+ /**
119
+ * copy_artifact (obj-randart.c L2676): copy every artifact field from src to
120
+ * dst in place. Faithful to the upstream memcpy-then-null: the destination's
121
+ * activation is always cleared to null and its alt_msg to "" (obj-randart.c
122
+ * L2689-L2690), even though all other fields (name, text, combat, flags,
123
+ * modifiers, element info, deep copies of slays/brands/curses, time) are
124
+ * copied. dst keeps its own identity so array references stay valid.
125
+ */
126
+ export function copyArtifact(src: Artifact, dst: Artifact): void {
127
+ dst.name = src.name;
128
+ dst.text = src.text;
129
+ dst.aidx = src.aidx;
130
+ dst.tval = src.tval;
131
+ dst.sval = src.sval;
132
+ dst.toH = src.toH;
133
+ dst.toD = src.toD;
134
+ dst.toA = src.toA;
135
+ dst.ac = src.ac;
136
+ dst.dd = src.dd;
137
+ dst.ds = src.ds;
138
+ dst.weight = src.weight;
139
+ dst.cost = src.cost;
140
+ dst.flags = src.flags.clone();
141
+ dst.modifiers = src.modifiers.slice();
142
+ dst.elInfo = src.elInfo.map((e) => ({ resLevel: e.resLevel, flags: e.flags }));
143
+ dst.brands = src.brands ? src.brands.slice() : null;
144
+ dst.slays = src.slays ? src.slays.slice() : null;
145
+ dst.curses = src.curses ? src.curses.slice() : null;
146
+ dst.level = src.level;
147
+ dst.allocProb = src.allocProb;
148
+ dst.allocMin = src.allocMin;
149
+ dst.allocMax = src.allocMax;
150
+ dst.time = { ...src.time };
151
+
152
+ /* Upstream nulls these after the memcpy (obj-randart.c L2689-L2690). */
153
+ dst.activation = null;
154
+ dst.altMsg = "";
155
+ }
156
+
157
+ /**
158
+ * A full deep copy of an artifact, preserving activation and alt_msg. Used to
159
+ * snapshot the registry's standard artifacts into a mutable working set so
160
+ * do_randart never mutates ObjRegistry.artifacts. This is NOT copy_artifact
161
+ * (which deliberately drops activation/alt_msg); it is the honest clone the
162
+ * working array needs.
163
+ *
164
+ * EXPORTED so a test harness can start from the same object shape production
165
+ * mutates - a hand-built Artifact would be the harness asserting what the
166
+ * struct looks like rather than measuring it.
167
+ */
168
+ export function cloneArtifact(src: Artifact): Artifact {
169
+ return {
170
+ name: src.name,
171
+ text: src.text,
172
+ aidx: src.aidx,
173
+ tval: src.tval,
174
+ sval: src.sval,
175
+ toH: src.toH,
176
+ toD: src.toD,
177
+ toA: src.toA,
178
+ ac: src.ac,
179
+ dd: src.dd,
180
+ ds: src.ds,
181
+ weight: src.weight,
182
+ cost: src.cost,
183
+ flags: src.flags.clone(),
184
+ modifiers: src.modifiers.slice(),
185
+ elInfo: src.elInfo.map((e) => ({ resLevel: e.resLevel, flags: e.flags })),
186
+ brands: src.brands ? src.brands.slice() : null,
187
+ slays: src.slays ? src.slays.slice() : null,
188
+ curses: src.curses ? src.curses.slice() : null,
189
+ level: src.level,
190
+ allocProb: src.allocProb,
191
+ allocMin: src.allocMin,
192
+ allocMax: src.allocMax,
193
+ activation: src.activation,
194
+ altMsg: src.altMsg,
195
+ time: { ...src.time },
196
+ };
197
+ }
198
+
199
+ /* ------------------------------------------------------------------ */
200
+ /* artifact_gen_name (obj-randart.c L2713) */
201
+ /* ------------------------------------------------------------------ */
202
+
203
+ /**
204
+ * my_strcap (z-util.c L529): capitalize only the first character, leaving the
205
+ * rest untouched. randnameMake returns an all-lowercase word, so this matches
206
+ * upstream exactly.
207
+ */
208
+ function myStrcap(word: string): string {
209
+ if (word.length === 0) return word;
210
+ return word.charAt(0).toUpperCase() + word.slice(1);
211
+ }
212
+
213
+ /**
214
+ * Tolkien-flavoured syllable fragments used ONLY as the seam fallback when no
215
+ * RANDNAME_TOLKIEN corpus is supplied to doRandart (see module SEAM note). This
216
+ * is NOT the upstream algorithm; it keeps the game path deterministic and
217
+ * crash-free until game.ts threads the real corpus.
218
+ */
219
+ const NAME_SYLLABLES: readonly string[] = [
220
+ "an", "ar", "el", "en", "or", "ith", "gal", "dor", "mir", "las",
221
+ "thal", "rond", "wen", "dil", "beth", "ath", "ien", "ael", "uin", "gorn",
222
+ "iel", "und", "ost", "loth", "mor", "fin", "hir", "eth", "ond", "aur",
223
+ ];
224
+
225
+ /**
226
+ * Seam fallback (non-faithful): assemble a plausible name from the syllable
227
+ * table. Fragments are drawn until the length reaches MIN_NAME_LEN, then clamped
228
+ * to MAX_NAME_LEN. Deterministic and bounded; used only when doRandart has no
229
+ * corpus.
230
+ */
231
+ function randNameFallback(rng: Rng): string {
232
+ let word = "";
233
+ while (word.length < MIN_NAME_LEN) {
234
+ word += NAME_SYLLABLES[rng.randint0(NAME_SYLLABLES.length)]!;
235
+ }
236
+ if (word.length > MAX_NAME_LEN) word = word.slice(0, MAX_NAME_LEN);
237
+ return word;
238
+ }
239
+
240
+ /**
241
+ * artifact_gen_name (obj-randart.c L2713): generate a random artifact name.
242
+ * Faithful to upstream: draw a Markov word from the RANDNAME_TOLKIEN corpus via
243
+ * randnameMake(MIN_NAME_LEN, MAX_NAME_LEN) (obj-randart.c L2717), my_strcap the
244
+ * first letter (L2719), then one_in_(3) selects the "'Name'" form over
245
+ * "of Name" (L2721-L2724). The upstream `struct artifact *a` argument is unused.
246
+ *
247
+ * `probs` is the precomputed transition table (build_prob over the corpus). When
248
+ * null (seam fallback, no corpus), the syllable table is used instead - this is
249
+ * NOT upstream-faithful and consumes a different number of RNG draws.
250
+ */
251
+ export function artifactGenName(rng: Rng, probs: NameProbs | null): string {
252
+ const word =
253
+ probs !== null
254
+ ? randnameMake(rng, MIN_NAME_LEN, MAX_NAME_LEN, probs)
255
+ : randNameFallback(rng);
256
+ const capped = myStrcap(word);
257
+ if (rng.oneIn(3)) return `'${capped}'`;
258
+ return `of ${capped}`;
259
+ }
260
+
261
+ /* ------------------------------------------------------------------ */
262
+ /* describe_artifact (obj-randart.c L2731) */
263
+ /* ------------------------------------------------------------------ */
264
+
265
+ /**
266
+ * describe_artifact (obj-randart.c L2731): give the artifact a boring
267
+ * "Random <type> of power <n>" description. Consumes no RNG.
268
+ */
269
+ function describeArtifact(art: Artifact, power: number): void {
270
+ art.text = `Random ${tvalFindName(art.tval)} of power ${power}`;
271
+ }
272
+
273
+ /* ------------------------------------------------------------------ */
274
+ /* design_artifact (obj-randart.c L2751) */
275
+ /* ------------------------------------------------------------------ */
276
+
277
+ /**
278
+ * design_artifact (obj-randart.c L2751): design a random artifact into
279
+ * arts[aidx], possibly skipping forward past fixed artifacts. Returns the
280
+ * (possibly advanced) working index, matching upstream's *aidx after return;
281
+ * the caller then increments it. `tv` is TV_NULL to pick a tval from the
282
+ * learned frequencies, or a specific tval to force.
283
+ *
284
+ * The artifact is assigned a target power from the range of powers for its
285
+ * tval, given a suitable base item, optionally supercharged, then has abilities
286
+ * (or, for a cursed artifact, curses) added until its power lands between 19/20
287
+ * and 23/20 of the target.
288
+ */
289
+ export function designArtifact(
290
+ reg: ObjRegistry,
291
+ constants: Constants,
292
+ arts: (Artifact | null)[],
293
+ data: ArtifactSetData,
294
+ tv: number,
295
+ aidx: number,
296
+ rng: Rng,
297
+ nameProbs: NameProbs | null,
298
+ ): number {
299
+ /* Defensive guard (upstream relies on aidx staying in range). */
300
+ if (aidx < 1 || aidx >= arts.length) return aidx;
301
+
302
+ let art = arts[aidx] as Artifact;
303
+ /* Upstream captures kind once and never refreshes it in the skip loop. */
304
+ let kind = reg.lookupKind(art.tval, art.sval);
305
+ const kindAtEntry = kind;
306
+ let artLevel = art.level;
307
+
308
+ /* Set tval if necessary. */
309
+ let tval = tv === TV.NULL ? getBaseItemTval(data, rng) : tv;
310
+
311
+ /* Choose a power for the artifact. */
312
+ let power = rng.randSample(
313
+ data.avgTvPower[tval]!,
314
+ data.maxTvPower[tval]!,
315
+ data.minTvPower[tval]!,
316
+ 20,
317
+ 20,
318
+ );
319
+
320
+ /* Choose a name. */
321
+ const newName = artifactGenName(rng, nameProbs);
322
+
323
+ /* Skip fixed artifacts (stale-kind quirk preserved: kindAtEntry). */
324
+ while (
325
+ art.name.includes("The One Ring") ||
326
+ (kindAtEntry !== null && kindAtEntry.kindFlags.has(KF.QUEST_ART))
327
+ ) {
328
+ aidx++;
329
+ if (aidx >= arts.length) return aidx;
330
+ art = arts[aidx] as Artifact;
331
+ artLevel = art.level;
332
+ }
333
+
334
+ /* Apply the new name. */
335
+ art.name = newName;
336
+
337
+ randartLog(">>>>>>>>>>>>>>>>>>>>>>>>>> CREATING NEW ARTIFACT\n");
338
+ randartLogf(
339
+ () => `Artifact ${String(aidx)}: power = ${String(power)}\n`,
340
+ );
341
+
342
+ /* Flip the sign on power if it's negative (unlikely) and damage. */
343
+ let hurtMe = false;
344
+ if (power < 0) {
345
+ hurtMe = true;
346
+ power = -power;
347
+ }
348
+
349
+ /* Structure to hold the old artifact for rollbacks. */
350
+ const aOld = cloneArtifact(art);
351
+
352
+ let tries: number;
353
+ let ap = 0;
354
+
355
+ /* Choose a base item not too powerful, so we'll have to add to it. */
356
+ for (tries = 0; tries < MAX_TRIES; tries++) {
357
+ if (tval === TV.NULL) tval = getBaseItemTval(data, rng);
358
+ kind = getBaseItem(reg, tval, rng);
359
+ artifactPrep(reg, art, kind, data, rng);
360
+
361
+ /* Get the kind again in case it's changed. */
362
+ kind = reg.lookupKind(art.tval, art.sval);
363
+
364
+ const basePower = artifactPower(reg, constants, art, "for base item power", rng);
365
+ randartLogf(() => `Base item power ${String(basePower)}\n`);
366
+
367
+ /* New base item power too close to target artifact power. */
368
+ if (basePower > Math.trunc((power * 6) / 10) + 1 && power - basePower < 20) {
369
+ randartLog("Power too high!\n");
370
+ continue;
371
+ }
372
+
373
+ /* Acceptable. */
374
+ break;
375
+ }
376
+
377
+ /* Failed to get a good base item. */
378
+ if (tries >= MAX_TRIES) {
379
+ randartLog(
380
+ "Warning! Couldn't get appropriate power level on base item.\n",
381
+ );
382
+ }
383
+
384
+ /* Generate the cumulative frequency table for this base item type. */
385
+ const artFreq = buildFreqTable(art, data);
386
+
387
+ /* Copy artifact info temporarily. */
388
+ copyArtifact(art, aOld);
389
+
390
+ /* Give this artifact a shot at being supercharged. */
391
+ trySupercharge(reg, art, power, data, rng);
392
+ ap = artifactPower(reg, constants, art, "result of supercharge", rng);
393
+ if (ap > Math.trunc((power * 23) / 20) + 1) {
394
+ /* Too powerful -- put it back. */
395
+ copyArtifact(aOld, art);
396
+ randartLog("--- Supercharge is too powerful! Rolling back.\n");
397
+ }
398
+
399
+ /* Give this artifact a chance to be cursed - note it retains its power. */
400
+ if (rng.oneIn(Math.trunc(arts.length / Math.max(2, data.negPowerTotal)))) {
401
+ hurtMe = true;
402
+ }
403
+
404
+ /* Do the actual artifact design. */
405
+ for (tries = 0; tries < MAX_TRIES; tries++) {
406
+ /* Copy artifact info temporarily. */
407
+ copyArtifact(art, aOld);
408
+
409
+ /* Add an ability. */
410
+ addAbility(reg, art, power, artFreq, data, rng);
411
+ removeContradictory(reg, art, data.timedFoil, data.activationSummarize);
412
+
413
+ /* Check the power, handle negative power. */
414
+ ap = artifactPower(reg, constants, art, "artifact attempt", rng);
415
+ if (ap < 0) {
416
+ ap = -ap;
417
+ break;
418
+ }
419
+
420
+ /* Curse the designated artifacts. */
421
+ if (hurtMe) {
422
+ makeBad(reg, art, artLevel, rng, data.timedFoil);
423
+ if (rng.oneIn(3)) {
424
+ hurtMe = false;
425
+ }
426
+ }
427
+
428
+ /* Check power. */
429
+ if (ap > Math.trunc((power * 23) / 20) + 1) {
430
+ /* Too powerful -- put it back. */
431
+ copyArtifact(aOld, art);
432
+ randartLog("--- Too powerful! Rolling back.\n");
433
+ continue;
434
+ } else if (ap >= Math.trunc((power * 19) / 20)) {
435
+ /* Just right. */
436
+ break;
437
+ }
438
+ }
439
+
440
+ /* Couldn't generate an artifact with the number of permitted iterations. */
441
+ if (tries >= MAX_TRIES) {
442
+ randartLog(
443
+ "Warning! Couldn't get appropriate power level on artifact.\n",
444
+ );
445
+ }
446
+
447
+ /* Set rarity based on power. kind is the final base item kind. */
448
+ const baseKind = kind as NonNullable<typeof kind>;
449
+ let allocNew = Math.trunc(4000000 / (ap * ap));
450
+ allocNew = Math.trunc(allocNew / (baseKind.allocProb ? baseKind.allocProb : 20));
451
+ if (allocNew > 99) allocNew = 99;
452
+ if (allocNew < 1) allocNew = 1;
453
+ art.allocProb = allocNew;
454
+
455
+ /* Set depth according to power. */
456
+ art.allocMax = Math.min(127, Math.trunc((ap * 3) / 5));
457
+ art.allocMin = Math.min(100, Math.trunc(((ap + 100) * 100) / data.maxPower));
458
+
459
+ /* Have a chance to be less rare or deep, more likely the less power. */
460
+ if (rng.oneIn(5 + Math.trunc(power / 20))) {
461
+ art.allocProb += rng.randint1(20);
462
+ if (art.allocProb > 99) art.allocProb = 99;
463
+ } else if (rng.oneIn(5 + Math.trunc(power / 20))) {
464
+ art.allocMin = Math.trunc(art.allocMin / 2);
465
+ if (art.allocMin < 1) art.allocMin = 1;
466
+ }
467
+
468
+ /* Sanity check. */
469
+ art.allocMax = Math.max(art.allocMax, Math.min(art.allocMin * 2, 127));
470
+
471
+ /*
472
+ * If there is no activation or effect from the kind, level currently does
473
+ * nothing. Set it to alloc_min in case changes elsewhere start using level.
474
+ */
475
+ if (!art.activation && !baseKind.activation && !baseKind.effect) {
476
+ art.level = art.allocMin;
477
+ }
478
+
479
+ randartLogf(
480
+ () =>
481
+ `New depths are min ${String(art.allocMin)}, max ${String(art.allocMax)}\n`,
482
+ );
483
+ randartLogf(
484
+ () => `Power-based alloc_prob is ${String(art.allocProb)}\n`,
485
+ );
486
+
487
+ /* Success. */
488
+ randartLog("<<<<<<<<<<<<<<<<<<<<<<<<<< ARTIFACT COMPLETED\n");
489
+ randartLogf(
490
+ () =>
491
+ `Number of tries for artifact ${String(aidx)} was: ${String(tries)}\n`,
492
+ );
493
+
494
+ /* Describe it. */
495
+ describeArtifact(art, ap);
496
+
497
+ return aidx;
498
+ }
499
+
500
+ /* ------------------------------------------------------------------ */
501
+ /* create_artifact_set (obj-randart.c L2954) */
502
+ /* ------------------------------------------------------------------ */
503
+
504
+ /**
505
+ * create_artifact_set (obj-randart.c L2954): design a full set of random
506
+ * artifacts into `arts`. The resulting set has at least 80% as many artifacts
507
+ * of any given tval as the original set (tvals with fewer than 5 original
508
+ * artifacts get equal or more). Remaining slots are filled with random-tval
509
+ * artifacts. The final slot (index arts.length - 1) is left as the original,
510
+ * matching the upstream `aidx < z_info->a_max - 1` bound.
511
+ */
512
+ export function createArtifactSet(
513
+ reg: ObjRegistry,
514
+ constants: Constants,
515
+ arts: (Artifact | null)[],
516
+ data: ArtifactSetData,
517
+ rng: Rng,
518
+ nameProbs: NameProbs | null,
519
+ ): void {
520
+ let aidx = 1;
521
+ const tvalTotal = new Array<number>(TV_MAX).fill(0);
522
+ let notDone = true;
523
+
524
+ /* Get min tval frequencies for the new artifacts (at least 80% each). */
525
+ for (let i = 0; i < TV_MAX; i++) {
526
+ tvalTotal[i] = Math.trunc((4 * (data.tvNum[i]! + 1)) / 5);
527
+ }
528
+
529
+ /* Allocate a minimal set of artifacts to the tvals. */
530
+ while (notDone) {
531
+ notDone = false;
532
+
533
+ /* Multiple passes through tvals until all have enough artifacts. */
534
+ for (let i = 0; i < TV_MAX; i++) {
535
+ if (tvalTotal[i]! > 0) {
536
+ aidx = designArtifact(reg, constants, arts, data, i, aidx, rng, nameProbs);
537
+ tvalTotal[i]!--;
538
+ aidx++;
539
+ notDone = true;
540
+ }
541
+ }
542
+ }
543
+
544
+ /* Allocate remaining artifacts at random. */
545
+ while (aidx < arts.length - 1) {
546
+ aidx = designArtifact(
547
+ reg,
548
+ constants,
549
+ arts,
550
+ data,
551
+ TV.NULL,
552
+ aidx,
553
+ rng,
554
+ nameProbs,
555
+ );
556
+ aidx++;
557
+ }
558
+ }
559
+
560
+ /* ------------------------------------------------------------------ */
561
+ /* do_randart (obj-randart.c L3154) */
562
+ /* ------------------------------------------------------------------ */
563
+
564
+ /**
565
+ * do_randart (obj-randart.c L3154): generate a full random artifact set from a
566
+ * seed and return it as a fresh (Artifact|null)[] array (index 0 null),
567
+ * leaving ObjRegistry.artifacts untouched. Pure with respect to the seed.
568
+ *
569
+ * Upstream seeds the "quick" LCRNG (Rand_value = seed, Rand_quick = true), so
570
+ * this port creates its Rng in quick mode and draws in upstream order:
571
+ * measure the standard set (store_base_power + parse_frequencies via
572
+ * collectArtifactData), design every artifact (create_artifact_set), then
573
+ * measure the finished set the same way. The log file (randart.log), the
574
+ * post-generation measurement pass and the optional spoiler file (create_file /
575
+ * write_randart_entry) are all ported (PORT_TODO 5.5); none of them affects an
576
+ * artifact field or an RNG draw.
577
+ */
578
+ /** path_build(ANGBAND_DIR_USER, "randart.log") (obj-randart.c L3165). */
579
+ export const RANDART_LOG = "randart.log";
580
+
581
+ export function doRandart(
582
+ reg: ObjRegistry,
583
+ /**
584
+ * z_info / the player constants object_prep reads. Upstream reaches them as
585
+ * globals; the port passes them, and they arrive here because artifact_power
586
+ * builds a real fake object through make_fake_artifact.
587
+ */
588
+ constants: Constants,
589
+ randartSeed: number,
590
+ /**
591
+ * do_randart's `create_file` (obj-randart.c:3154). True writes randart.txt
592
+ * beside randart.log; upstream passes true from birth, from loading a save
593
+ * and from the spoiler generator, and false from the statistics harnesses.
594
+ *
595
+ * REQUIRED, with no default: the two callers that want it differ from the
596
+ * many that do not, and a default would silently pick one of them.
597
+ */
598
+ createFile: boolean,
599
+ tolkienWords?: readonly string[],
600
+ extras?: Pick<ArtifactSetData, "timedFoil" | "activationSummarize">,
601
+ io: HostIo = host(),
602
+ onLogError?: (message: string) => void,
603
+ ): (Artifact | null)[] {
604
+ /*
605
+ * OPEN randart.log (obj-randart.c L3164-L3171).
606
+ *
607
+ * Upstream file_opens ANGBAND_DIR_USER/randart.log for writing before it
608
+ * touches an artifact, and `exit(1)`s if it cannot. That exit is the one
609
+ * thing the port cannot copy: a browser tab has no process to kill, and a
610
+ * desktop player did not ask to lose a character over a log file. So the
611
+ * failure goes through the host instead - the open is probed by truncating
612
+ * the file to empty (which is what MODE_WRITE does), and if that fails the
613
+ * sink stays closed, `onLogError` gets upstream's own message, and every
614
+ * emitter downstream is the no-op it already is with no log running.
615
+ * Generation continues, which is the deliberate divergence.
616
+ *
617
+ * BUFFERED, not appended line by line: HostIo.write is one whole-file call
618
+ * (that is what a localStorage-backed adapter can express), and a run of the
619
+ * standard set emits tens of thousands of lines.
620
+ */
621
+ /* Prepare to use the Angband "simple" (quick LCRNG) RNG. */
622
+ const rng = new Rng(randartSeed, { quick: true });
623
+
624
+ const lines: string[] = [];
625
+ let generated: (Artifact | null)[] = [];
626
+ let logging = false;
627
+ if (io.write(HostDir.USER, RANDART_LOG, "", FileMode.WRITE, FileType.TEXT) === "ok") {
628
+ logging = true;
629
+ setRandartLog((text) => lines.push(text));
630
+ } else {
631
+ onLogError?.("Error - can't open randart.log for writing.");
632
+ }
633
+
634
+ try {
635
+ /*
636
+ * Build the RANDNAME_TOLKIEN transition table once (build_prob is cached
637
+ * per-type upstream, randname.c L94-L103; here we build it once per run).
638
+ * When no corpus is supplied (the current game-path seam), fall back to the
639
+ * non-faithful syllable generator (see module SEAM note). An empty word list
640
+ * is treated as "no corpus" because build_prob/randname_make would otherwise
641
+ * loop forever on an empty table.
642
+ */
643
+ const nameProbs: NameProbs | null =
644
+ tolkienWords && tolkienWords.length > 0 ? buildProb(tolkienWords) : null;
645
+
646
+ /* Store the original power ratings and determine generation probabilities
647
+ * for the STANDARD set (L3175-L3178). `extras` threads the curse TIMED_INC
648
+ * foil tables (gap 3.3) and the activation redundancy summarizer (gap 3.8);
649
+ * absent, both checks are skipped as before. */
650
+ const data = collectArtifactData(reg, constants, reg.artifacts, rng);
651
+ if (extras?.timedFoil) data.timedFoil = extras.timedFoil;
652
+ if (extras?.activationSummarize) {
653
+ data.activationSummarize = extras.activationSummarize;
654
+ }
655
+
656
+ /* Work on a fresh copy so the registry's standard artifacts are preserved. */
657
+ const arts: (Artifact | null)[] = reg.artifacts.map((a) =>
658
+ a ? cloneArtifact(a) : null,
659
+ );
660
+
661
+ /* Generate the random artifacts. */
662
+ createArtifactSet(reg, constants, arts, data, rng, nameProbs);
663
+
664
+ generated = arts;
665
+
666
+ /*
667
+ * LOOK AT THE FREQUENCIES ON THE FINISHED ITEMS (L3181-L3186).
668
+ *
669
+ * A whole second measurement pass over the set just generated, into a
670
+ * throwaway ArtifactSetData that upstream frees on the next line. Nothing
671
+ * reads it: its ONLY product is the text both passes write to the log, so
672
+ * a reader can compare the set the game just made against the standard one
673
+ * it was derived from. That is why it was invisible while the log was full
674
+ * of holes, and why it can be added now without changing an artifact - it
675
+ * runs after generation and draws no RNG (see collectArtifactData).
676
+ *
677
+ * `extras` is deliberately NOT applied here: upstream measures the finished
678
+ * set with a plain artifact_set_data_new, and both extras are generation-
679
+ * time checks that this pass never reaches.
680
+ */
681
+ collectArtifactData(reg, constants, arts, rng);
682
+
683
+ return arts;
684
+ } finally {
685
+ /* CLOSE (L3189-L3193). In a `finally` so a throw mid-generation cannot
686
+ * leave the sink installed for the next caller - upstream's log_file is a
687
+ * static, and a stale one would narrate the following run into this one. */
688
+ setRandartLog(null);
689
+ if (logging) {
690
+ const outcome = io.write(
691
+ HostDir.USER,
692
+ RANDART_LOG,
693
+ lines.join(""),
694
+ FileMode.WRITE,
695
+ FileType.TEXT,
696
+ );
697
+ if (outcome !== "ok") onLogError?.("Error - can't close randart.log file.");
698
+ }
699
+
700
+ /*
701
+ * WRITE A DATA FILE IF REQUIRED (L3195-L3215), after the log is closed,
702
+ * which is upstream's order - it reuses the same handle variable for both.
703
+ *
704
+ * Upstream `quit_fmt`s if this file cannot be closed. The port cannot: the
705
+ * same reasoning as the randart.log open above applies, so the failure goes
706
+ * to onLogError and the artifact set is returned regardless. Writing the
707
+ * file is a courtesy; losing the character over it is not.
708
+ */
709
+ if (createFile) {
710
+ const outcome = io.write(
711
+ HostDir.USER,
712
+ RANDART_TXT,
713
+ writeRandartFile(reg, generated, randartSeed),
714
+ FileMode.WRITE,
715
+ FileType.TEXT,
716
+ );
717
+ if (outcome !== "ok") {
718
+ onLogError?.(`Error - can't close ${RANDART_TXT}.`);
719
+ }
720
+ }
721
+ }
722
+ }