@rpgm-tools/neo-angband-core 0.10.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 (1183) hide show
  1. package/LICENSE.md +43 -0
  2. package/README.md +80 -0
  3. package/dist/agent/act.d.ts +22 -0
  4. package/dist/agent/act.d.ts.map +1 -0
  5. package/dist/agent/act.js +88 -0
  6. package/dist/agent/act.js.map +1 -0
  7. package/dist/agent/controller.d.ts +34 -0
  8. package/dist/agent/controller.d.ts.map +1 -0
  9. package/dist/agent/controller.js +90 -0
  10. package/dist/agent/controller.js.map +1 -0
  11. package/dist/agent/events.d.ts +27 -0
  12. package/dist/agent/events.d.ts.map +1 -0
  13. package/dist/agent/events.js +31 -0
  14. package/dist/agent/events.js.map +1 -0
  15. package/dist/agent/index.d.ts +17 -0
  16. package/dist/agent/index.d.ts.map +1 -0
  17. package/dist/agent/index.js +15 -0
  18. package/dist/agent/index.js.map +1 -0
  19. package/dist/agent/perceive.d.ts +21 -0
  20. package/dist/agent/perceive.d.ts.map +1 -0
  21. package/dist/agent/perceive.js +386 -0
  22. package/dist/agent/perceive.js.map +1 -0
  23. package/dist/agent/types.d.ts +436 -0
  24. package/dist/agent/types.d.ts.map +1 -0
  25. package/dist/agent/types.js +63 -0
  26. package/dist/agent/types.js.map +1 -0
  27. package/dist/bitflag.d.ts +199 -0
  28. package/dist/bitflag.d.ts.map +1 -0
  29. package/dist/bitflag.js +478 -0
  30. package/dist/bitflag.js.map +1 -0
  31. package/dist/cmd.d.ts +164 -0
  32. package/dist/cmd.d.ts.map +1 -0
  33. package/dist/cmd.js +350 -0
  34. package/dist/cmd.js.map +1 -0
  35. package/dist/color.d.ts +104 -0
  36. package/dist/color.d.ts.map +1 -0
  37. package/dist/color.js +240 -0
  38. package/dist/color.js.map +1 -0
  39. package/dist/combat/brand-slay.d.ts +119 -0
  40. package/dist/combat/brand-slay.d.ts.map +1 -0
  41. package/dist/combat/brand-slay.js +264 -0
  42. package/dist/combat/brand-slay.js.map +1 -0
  43. package/dist/combat/hit.d.ts +198 -0
  44. package/dist/combat/hit.d.ts.map +1 -0
  45. package/dist/combat/hit.js +310 -0
  46. package/dist/combat/hit.js.map +1 -0
  47. package/dist/combat/index.d.ts +15 -0
  48. package/dist/combat/index.d.ts.map +1 -0
  49. package/dist/combat/index.js +15 -0
  50. package/dist/combat/index.js.map +1 -0
  51. package/dist/combat/melee.d.ts +261 -0
  52. package/dist/combat/melee.d.ts.map +1 -0
  53. package/dist/combat/melee.js +402 -0
  54. package/dist/combat/melee.js.map +1 -0
  55. package/dist/combat/mon-melee.d.ts +233 -0
  56. package/dist/combat/mon-melee.d.ts.map +1 -0
  57. package/dist/combat/mon-melee.js +883 -0
  58. package/dist/combat/mon-melee.js.map +1 -0
  59. package/dist/combat/ranged.d.ts +89 -0
  60. package/dist/combat/ranged.d.ts.map +1 -0
  61. package/dist/combat/ranged.js +253 -0
  62. package/dist/combat/ranged.js.map +1 -0
  63. package/dist/constants.d.ts +134 -0
  64. package/dist/constants.d.ts.map +1 -0
  65. package/dist/constants.js +171 -0
  66. package/dist/constants.js.map +1 -0
  67. package/dist/dice.d.ts +91 -0
  68. package/dist/dice.d.ts.map +1 -0
  69. package/dist/dice.js +424 -0
  70. package/dist/dice.js.map +1 -0
  71. package/dist/effects/effect-choice.d.ts +15 -0
  72. package/dist/effects/effect-choice.d.ts.map +1 -0
  73. package/dist/effects/effect-choice.js +18 -0
  74. package/dist/effects/effect-choice.js.map +1 -0
  75. package/dist/effects/effect-info.d.ts +124 -0
  76. package/dist/effects/effect-info.d.ts.map +1 -0
  77. package/dist/effects/effect-info.js +706 -0
  78. package/dist/effects/effect-info.js.map +1 -0
  79. package/dist/effects/effect.d.ts +165 -0
  80. package/dist/effects/effect.d.ts.map +1 -0
  81. package/dist/effects/effect.js +452 -0
  82. package/dist/effects/effect.js.map +1 -0
  83. package/dist/effects/handlers.d.ts +70 -0
  84. package/dist/effects/handlers.d.ts.map +1 -0
  85. package/dist/effects/handlers.js +394 -0
  86. package/dist/effects/handlers.js.map +1 -0
  87. package/dist/effects/interpreter.d.ts +291 -0
  88. package/dist/effects/interpreter.d.ts.map +1 -0
  89. package/dist/effects/interpreter.js +325 -0
  90. package/dist/effects/interpreter.js.map +1 -0
  91. package/dist/events.d.ts +188 -0
  92. package/dist/events.d.ts.map +1 -0
  93. package/dist/events.js +138 -0
  94. package/dist/events.js.map +1 -0
  95. package/dist/expression.d.ts +83 -0
  96. package/dist/expression.d.ts.map +1 -0
  97. package/dist/expression.js +333 -0
  98. package/dist/expression.js.map +1 -0
  99. package/dist/game/cave-cmd.d.ts +195 -0
  100. package/dist/game/cave-cmd.d.ts.map +1 -0
  101. package/dist/game/cave-cmd.js +968 -0
  102. package/dist/game/cave-cmd.js.map +1 -0
  103. package/dist/game/char-sheet.d.ts +189 -0
  104. package/dist/game/char-sheet.d.ts.map +1 -0
  105. package/dist/game/char-sheet.js +381 -0
  106. package/dist/game/char-sheet.js.map +1 -0
  107. package/dist/game/chest.d.ts +94 -0
  108. package/dist/game/chest.d.ts.map +1 -0
  109. package/dist/game/chest.js +267 -0
  110. package/dist/game/chest.js.map +1 -0
  111. package/dist/game/context.d.ts +970 -0
  112. package/dist/game/context.d.ts.map +1 -0
  113. package/dist/game/context.js +353 -0
  114. package/dist/game/context.js.map +1 -0
  115. package/dist/game/curse-tick.d.ts +35 -0
  116. package/dist/game/curse-tick.d.ts.map +1 -0
  117. package/dist/game/curse-tick.js +100 -0
  118. package/dist/game/curse-tick.js.map +1 -0
  119. package/dist/game/describe.d.ts +20 -0
  120. package/dist/game/describe.d.ts.map +1 -0
  121. package/dist/game/describe.js +45 -0
  122. package/dist/game/describe.js.map +1 -0
  123. package/dist/game/display.d.ts +196 -0
  124. package/dist/game/display.d.ts.map +1 -0
  125. package/dist/game/display.js +684 -0
  126. package/dist/game/display.js.map +1 -0
  127. package/dist/game/dump-level.d.ts +37 -0
  128. package/dist/game/dump-level.d.ts.map +1 -0
  129. package/dist/game/dump-level.js +110 -0
  130. package/dist/game/dump-level.js.map +1 -0
  131. package/dist/game/effect-attack.d.ts +36 -0
  132. package/dist/game/effect-attack.d.ts.map +1 -0
  133. package/dist/game/effect-attack.js +694 -0
  134. package/dist/game/effect-attack.js.map +1 -0
  135. package/dist/game/effect-detect.d.ts +34 -0
  136. package/dist/game/effect-detect.d.ts.map +1 -0
  137. package/dist/game/effect-detect.js +456 -0
  138. package/dist/game/effect-detect.js.map +1 -0
  139. package/dist/game/effect-env.d.ts +77 -0
  140. package/dist/game/effect-env.d.ts.map +1 -0
  141. package/dist/game/effect-env.js +166 -0
  142. package/dist/game/effect-env.js.map +1 -0
  143. package/dist/game/effect-game-env.d.ts +76 -0
  144. package/dist/game/effect-game-env.d.ts.map +1 -0
  145. package/dist/game/effect-game-env.js +23 -0
  146. package/dist/game/effect-game-env.js.map +1 -0
  147. package/dist/game/effect-general.d.ts +98 -0
  148. package/dist/game/effect-general.d.ts.map +1 -0
  149. package/dist/game/effect-general.js +914 -0
  150. package/dist/game/effect-general.js.map +1 -0
  151. package/dist/game/effect-item.d.ts +124 -0
  152. package/dist/game/effect-item.d.ts.map +1 -0
  153. package/dist/game/effect-item.js +789 -0
  154. package/dist/game/effect-item.js.map +1 -0
  155. package/dist/game/effect-melee.d.ts +43 -0
  156. package/dist/game/effect-melee.d.ts.map +1 -0
  157. package/dist/game/effect-melee.js +477 -0
  158. package/dist/game/effect-melee.js.map +1 -0
  159. package/dist/game/effect-mon-origin.d.ts +67 -0
  160. package/dist/game/effect-mon-origin.d.ts.map +1 -0
  161. package/dist/game/effect-mon-origin.js +164 -0
  162. package/dist/game/effect-mon-origin.js.map +1 -0
  163. package/dist/game/effect-monster.d.ts +29 -0
  164. package/dist/game/effect-monster.d.ts.map +1 -0
  165. package/dist/game/effect-monster.js +281 -0
  166. package/dist/game/effect-monster.js.map +1 -0
  167. package/dist/game/effect-summon.d.ts +37 -0
  168. package/dist/game/effect-summon.d.ts.map +1 -0
  169. package/dist/game/effect-summon.js +113 -0
  170. package/dist/game/effect-summon.js.map +1 -0
  171. package/dist/game/effect-teleport.d.ts +130 -0
  172. package/dist/game/effect-teleport.d.ts.map +1 -0
  173. package/dist/game/effect-teleport.js +529 -0
  174. package/dist/game/effect-teleport.js.map +1 -0
  175. package/dist/game/effect-terrain.d.ts +91 -0
  176. package/dist/game/effect-terrain.d.ts.map +1 -0
  177. package/dist/game/effect-terrain.js +812 -0
  178. package/dist/game/effect-terrain.js.map +1 -0
  179. package/dist/game/energy.d.ts +45 -0
  180. package/dist/game/energy.d.ts.map +1 -0
  181. package/dist/game/energy.js +51 -0
  182. package/dist/game/energy.js.map +1 -0
  183. package/dist/game/equip-cmp.d.ts +138 -0
  184. package/dist/game/equip-cmp.d.ts.map +1 -0
  185. package/dist/game/equip-cmp.js +363 -0
  186. package/dist/game/equip-cmp.js.map +1 -0
  187. package/dist/game/floor.d.ts +182 -0
  188. package/dist/game/floor.d.ts.map +1 -0
  189. package/dist/game/floor.js +435 -0
  190. package/dist/game/floor.js.map +1 -0
  191. package/dist/game/gear.d.ts +408 -0
  192. package/dist/game/gear.d.ts.map +1 -0
  193. package/dist/game/gear.js +1037 -0
  194. package/dist/game/gear.js.map +1 -0
  195. package/dist/game/harness.d.ts +61 -0
  196. package/dist/game/harness.d.ts.map +1 -0
  197. package/dist/game/harness.js +198 -0
  198. package/dist/game/harness.js.map +1 -0
  199. package/dist/game/history.d.ts +49 -0
  200. package/dist/game/history.d.ts.map +1 -0
  201. package/dist/game/history.js +55 -0
  202. package/dist/game/history.js.map +1 -0
  203. package/dist/game/ignore-cmd.d.ts +42 -0
  204. package/dist/game/ignore-cmd.d.ts.map +1 -0
  205. package/dist/game/ignore-cmd.js +59 -0
  206. package/dist/game/ignore-cmd.js.map +1 -0
  207. package/dist/game/inscription-confirm.d.ts +80 -0
  208. package/dist/game/inscription-confirm.d.ts.map +1 -0
  209. package/dist/game/inscription-confirm.js +95 -0
  210. package/dist/game/inscription-confirm.js.map +1 -0
  211. package/dist/game/known.d.ts +297 -0
  212. package/dist/game/known.d.ts.map +1 -0
  213. package/dist/game/known.js +849 -0
  214. package/dist/game/known.js.map +1 -0
  215. package/dist/game/loop.d.ts +139 -0
  216. package/dist/game/loop.d.ts.map +1 -0
  217. package/dist/game/loop.js +631 -0
  218. package/dist/game/loop.js.map +1 -0
  219. package/dist/game/lore-color.d.ts +25 -0
  220. package/dist/game/lore-color.d.ts.map +1 -0
  221. package/dist/game/lore-color.js +95 -0
  222. package/dist/game/lore-color.js.map +1 -0
  223. package/dist/game/mon-cast.d.ts +150 -0
  224. package/dist/game/mon-cast.d.ts.map +1 -0
  225. package/dist/game/mon-cast.js +301 -0
  226. package/dist/game/mon-cast.js.map +1 -0
  227. package/dist/game/mon-cmd.d.ts +51 -0
  228. package/dist/game/mon-cmd.d.ts.map +1 -0
  229. package/dist/game/mon-cmd.js +670 -0
  230. package/dist/game/mon-cmd.js.map +1 -0
  231. package/dist/game/mon-death.d.ts +143 -0
  232. package/dist/game/mon-death.d.ts.map +1 -0
  233. package/dist/game/mon-death.js +328 -0
  234. package/dist/game/mon-death.js.map +1 -0
  235. package/dist/game/mon-group.d.ts +81 -0
  236. package/dist/game/mon-group.d.ts.map +1 -0
  237. package/dist/game/mon-group.js +323 -0
  238. package/dist/game/mon-group.js.map +1 -0
  239. package/dist/game/mon-list.d.ts +85 -0
  240. package/dist/game/mon-list.d.ts.map +1 -0
  241. package/dist/game/mon-list.js +154 -0
  242. package/dist/game/mon-list.js.map +1 -0
  243. package/dist/game/mon-message.d.ts +100 -0
  244. package/dist/game/mon-message.d.ts.map +1 -0
  245. package/dist/game/mon-message.js +336 -0
  246. package/dist/game/mon-message.js.map +1 -0
  247. package/dist/game/mon-place.d.ts +226 -0
  248. package/dist/game/mon-place.d.ts.map +1 -0
  249. package/dist/game/mon-place.js +642 -0
  250. package/dist/game/mon-place.js.map +1 -0
  251. package/dist/game/mon-ranged.d.ts +87 -0
  252. package/dist/game/mon-ranged.d.ts.map +1 -0
  253. package/dist/game/mon-ranged.js +353 -0
  254. package/dist/game/mon-ranged.js.map +1 -0
  255. package/dist/game/mon-shape.d.ts +43 -0
  256. package/dist/game/mon-shape.d.ts.map +1 -0
  257. package/dist/game/mon-shape.js +115 -0
  258. package/dist/game/mon-shape.js.map +1 -0
  259. package/dist/game/mon-side.d.ts +63 -0
  260. package/dist/game/mon-side.d.ts.map +1 -0
  261. package/dist/game/mon-side.js +403 -0
  262. package/dist/game/mon-side.js.map +1 -0
  263. package/dist/game/monster-turn.d.ts +195 -0
  264. package/dist/game/monster-turn.d.ts.map +1 -0
  265. package/dist/game/monster-turn.js +1560 -0
  266. package/dist/game/monster-turn.js.map +1 -0
  267. package/dist/game/obj-cmd.d.ts +453 -0
  268. package/dist/game/obj-cmd.d.ts.map +1 -0
  269. package/dist/game/obj-cmd.js +1530 -0
  270. package/dist/game/obj-cmd.js.map +1 -0
  271. package/dist/game/obj-list.d.ts +92 -0
  272. package/dist/game/obj-list.d.ts.map +1 -0
  273. package/dist/game/obj-list.js +251 -0
  274. package/dist/game/obj-list.js.map +1 -0
  275. package/dist/game/object-inspect.d.ts +37 -0
  276. package/dist/game/object-inspect.d.ts.map +1 -0
  277. package/dist/game/object-inspect.js +106 -0
  278. package/dist/game/object-inspect.js.map +1 -0
  279. package/dist/game/pickup.d.ts +126 -0
  280. package/dist/game/pickup.d.ts.map +1 -0
  281. package/dist/game/pickup.js +367 -0
  282. package/dist/game/pickup.js.map +1 -0
  283. package/dist/game/player-path.d.ts +133 -0
  284. package/dist/game/player-path.d.ts.map +1 -0
  285. package/dist/game/player-path.js +1268 -0
  286. package/dist/game/player-path.js.map +1 -0
  287. package/dist/game/player-side.d.ts +59 -0
  288. package/dist/game/player-side.d.ts.map +1 -0
  289. package/dist/game/player-side.js +506 -0
  290. package/dist/game/player-side.js.map +1 -0
  291. package/dist/game/player-turn.d.ts +161 -0
  292. package/dist/game/player-turn.d.ts.map +1 -0
  293. package/dist/game/player-turn.js +739 -0
  294. package/dist/game/player-turn.js.map +1 -0
  295. package/dist/game/project-cast.d.ts +268 -0
  296. package/dist/game/project-cast.d.ts.map +1 -0
  297. package/dist/game/project-cast.js +496 -0
  298. package/dist/game/project-cast.js.map +1 -0
  299. package/dist/game/project-feat.d.ts +47 -0
  300. package/dist/game/project-feat.d.ts.map +1 -0
  301. package/dist/game/project-feat.js +350 -0
  302. package/dist/game/project-feat.js.map +1 -0
  303. package/dist/game/project-monster.d.ts +106 -0
  304. package/dist/game/project-monster.d.ts.map +1 -0
  305. package/dist/game/project-monster.js +272 -0
  306. package/dist/game/project-monster.js.map +1 -0
  307. package/dist/game/project-obj.d.ts +60 -0
  308. package/dist/game/project-obj.d.ts.map +1 -0
  309. package/dist/game/project-obj.js +245 -0
  310. package/dist/game/project-obj.js.map +1 -0
  311. package/dist/game/project-player.d.ts +120 -0
  312. package/dist/game/project-player.d.ts.map +1 -0
  313. package/dist/game/project-player.js +108 -0
  314. package/dist/game/project-player.js.map +1 -0
  315. package/dist/game/quest.d.ts +101 -0
  316. package/dist/game/quest.d.ts.map +1 -0
  317. package/dist/game/quest.js +192 -0
  318. package/dist/game/quest.js.map +1 -0
  319. package/dist/game/ranged-cmd.d.ts +32 -0
  320. package/dist/game/ranged-cmd.d.ts.map +1 -0
  321. package/dist/game/ranged-cmd.js +372 -0
  322. package/dist/game/ranged-cmd.js.map +1 -0
  323. package/dist/game/scheduler.d.ts +47 -0
  324. package/dist/game/scheduler.d.ts.map +1 -0
  325. package/dist/game/scheduler.js +141 -0
  326. package/dist/game/scheduler.js.map +1 -0
  327. package/dist/game/spell-cmd.d.ts +104 -0
  328. package/dist/game/spell-cmd.d.ts.map +1 -0
  329. package/dist/game/spell-cmd.js +320 -0
  330. package/dist/game/spell-cmd.js.map +1 -0
  331. package/dist/game/spoil.d.ts +28 -0
  332. package/dist/game/spoil.d.ts.map +1 -0
  333. package/dist/game/spoil.js +435 -0
  334. package/dist/game/spoil.js.map +1 -0
  335. package/dist/game/steal.d.ts +35 -0
  336. package/dist/game/steal.d.ts.map +1 -0
  337. package/dist/game/steal.js +145 -0
  338. package/dist/game/steal.js.map +1 -0
  339. package/dist/game/take-hit-hooks.d.ts +52 -0
  340. package/dist/game/take-hit-hooks.d.ts.map +1 -0
  341. package/dist/game/take-hit-hooks.js +127 -0
  342. package/dist/game/take-hit-hooks.js.map +1 -0
  343. package/dist/game/target-loop.d.ts +119 -0
  344. package/dist/game/target-loop.d.ts.map +1 -0
  345. package/dist/game/target-loop.js +408 -0
  346. package/dist/game/target-loop.js.map +1 -0
  347. package/dist/game/target.d.ts +133 -0
  348. package/dist/game/target.d.ts.map +1 -0
  349. package/dist/game/target.js +353 -0
  350. package/dist/game/target.js.map +1 -0
  351. package/dist/game/thrust.d.ts +37 -0
  352. package/dist/game/thrust.d.ts.map +1 -0
  353. package/dist/game/thrust.js +155 -0
  354. package/dist/game/thrust.js.map +1 -0
  355. package/dist/game/trap.d.ts +218 -0
  356. package/dist/game/trap.d.ts.map +1 -0
  357. package/dist/game/trap.js +618 -0
  358. package/dist/game/trap.js.map +1 -0
  359. package/dist/game/ui-entry.d.ts +282 -0
  360. package/dist/game/ui-entry.d.ts.map +1 -0
  361. package/dist/game/ui-entry.js +1838 -0
  362. package/dist/game/ui-entry.js.map +1 -0
  363. package/dist/game/wizard.d.ts +654 -0
  364. package/dist/game/wizard.d.ts.map +1 -0
  365. package/dist/game/wizard.js +1323 -0
  366. package/dist/game/wizard.js.map +1 -0
  367. package/dist/game/world.d.ts +183 -0
  368. package/dist/game/world.d.ts.map +1 -0
  369. package/dist/game/world.js +685 -0
  370. package/dist/game/world.js.map +1 -0
  371. package/dist/gen/cave.d.ts +247 -0
  372. package/dist/gen/cave.d.ts.map +1 -0
  373. package/dist/gen/cave.js +2406 -0
  374. package/dist/gen/cave.js.map +1 -0
  375. package/dist/gen/gen-monster.d.ts +151 -0
  376. package/dist/gen/gen-monster.d.ts.map +1 -0
  377. package/dist/gen/gen-monster.js +473 -0
  378. package/dist/gen/gen-monster.js.map +1 -0
  379. package/dist/gen/generate.d.ts +240 -0
  380. package/dist/gen/generate.d.ts.map +1 -0
  381. package/dist/gen/generate.js +358 -0
  382. package/dist/gen/generate.js.map +1 -0
  383. package/dist/gen/room.d.ts +160 -0
  384. package/dist/gen/room.d.ts.map +1 -0
  385. package/dist/gen/room.js +1910 -0
  386. package/dist/gen/room.js.map +1 -0
  387. package/dist/gen/util.d.ts +466 -0
  388. package/dist/gen/util.d.ts.map +1 -0
  389. package/dist/gen/util.js +1553 -0
  390. package/dist/gen/util.js.map +1 -0
  391. package/dist/generated/dun-profiles.d.ts +44 -0
  392. package/dist/generated/dun-profiles.d.ts.map +1 -0
  393. package/dist/generated/dun-profiles.js +28 -0
  394. package/dist/generated/dun-profiles.js.map +1 -0
  395. package/dist/generated/effects.d.ts +1021 -0
  396. package/dist/generated/effects.d.ts.map +1 -0
  397. package/dist/generated/effects.js +239 -0
  398. package/dist/generated/effects.js.map +1 -0
  399. package/dist/generated/elements.d.ts +83 -0
  400. package/dist/generated/elements.d.ts.map +1 -0
  401. package/dist/generated/elements.js +60 -0
  402. package/dist/generated/elements.js.map +1 -0
  403. package/dist/generated/equip-slots.d.ts +104 -0
  404. package/dist/generated/equip-slots.d.ts.map +1 -0
  405. package/dist/generated/equip-slots.js +34 -0
  406. package/dist/generated/equip-slots.js.map +1 -0
  407. package/dist/generated/history-types.d.ts +52 -0
  408. package/dist/generated/history-types.d.ts.map +1 -0
  409. package/dist/generated/history-types.js +32 -0
  410. package/dist/generated/history-types.js.map +1 -0
  411. package/dist/generated/ignore-types.d.ts +116 -0
  412. package/dist/generated/ignore-types.d.ts.map +1 -0
  413. package/dist/generated/ignore-types.js +64 -0
  414. package/dist/generated/ignore-types.js.map +1 -0
  415. package/dist/generated/index.d.ts +32 -0
  416. package/dist/generated/index.d.ts.map +1 -0
  417. package/dist/generated/index.js +33 -0
  418. package/dist/generated/index.js.map +1 -0
  419. package/dist/generated/kind-flags.d.ts +72 -0
  420. package/dist/generated/kind-flags.d.ts.map +1 -0
  421. package/dist/generated/kind-flags.js +42 -0
  422. package/dist/generated/kind-flags.js.map +1 -0
  423. package/dist/generated/message.d.ts +624 -0
  424. package/dist/generated/message.d.ts.map +1 -0
  425. package/dist/generated/message.js +318 -0
  426. package/dist/generated/message.js.map +1 -0
  427. package/dist/generated/mon-message.d.ts +386 -0
  428. package/dist/generated/mon-message.d.ts.map +1 -0
  429. package/dist/generated/mon-message.js +136 -0
  430. package/dist/generated/mon-message.js.map +1 -0
  431. package/dist/generated/mon-race-flags.d.ts +433 -0
  432. package/dist/generated/mon-race-flags.d.ts.map +1 -0
  433. package/dist/generated/mon-race-flags.js +180 -0
  434. package/dist/generated/mon-race-flags.js.map +1 -0
  435. package/dist/generated/mon-spells.d.ts +380 -0
  436. package/dist/generated/mon-spells.d.ts.map +1 -0
  437. package/dist/generated/mon-spells.js +196 -0
  438. package/dist/generated/mon-spells.js.map +1 -0
  439. package/dist/generated/mon-temp-flags.d.ts +52 -0
  440. package/dist/generated/mon-temp-flags.d.ts.map +1 -0
  441. package/dist/generated/mon-temp-flags.js +32 -0
  442. package/dist/generated/mon-temp-flags.js.map +1 -0
  443. package/dist/generated/mon-timed.d.ts +118 -0
  444. package/dist/generated/mon-timed.d.ts.map +1 -0
  445. package/dist/generated/mon-timed.js +32 -0
  446. package/dist/generated/mon-timed.js.map +1 -0
  447. package/dist/generated/object-flags.d.ts +169 -0
  448. package/dist/generated/object-flags.d.ts.map +1 -0
  449. package/dist/generated/object-flags.js +93 -0
  450. package/dist/generated/object-flags.js.map +1 -0
  451. package/dist/generated/object-modifiers.d.ts +50 -0
  452. package/dist/generated/object-modifiers.d.ts.map +1 -0
  453. package/dist/generated/object-modifiers.js +41 -0
  454. package/dist/generated/object-modifiers.js.map +1 -0
  455. package/dist/generated/options.d.ts +284 -0
  456. package/dist/generated/options.d.ts.map +1 -0
  457. package/dist/generated/options.js +102 -0
  458. package/dist/generated/options.js.map +1 -0
  459. package/dist/generated/origins.d.ts +138 -0
  460. package/dist/generated/origins.d.ts.map +1 -0
  461. package/dist/generated/origins.js +62 -0
  462. package/dist/generated/origins.js.map +1 -0
  463. package/dist/generated/parser-errors.d.ts +264 -0
  464. package/dist/generated/parser-errors.d.ts.map +1 -0
  465. package/dist/generated/parser-errors.js +138 -0
  466. package/dist/generated/parser-errors.js.map +1 -0
  467. package/dist/generated/player-flags.d.ts +62 -0
  468. package/dist/generated/player-flags.d.ts.map +1 -0
  469. package/dist/generated/player-flags.js +46 -0
  470. package/dist/generated/player-flags.js.map +1 -0
  471. package/dist/generated/player-timed.d.ts +273 -0
  472. package/dist/generated/player-timed.d.ts.map +1 -0
  473. package/dist/generated/player-timed.js +116 -0
  474. package/dist/generated/player-timed.js.map +1 -0
  475. package/dist/generated/projections.d.ts +130 -0
  476. package/dist/generated/projections.d.ts.map +1 -0
  477. package/dist/generated/projections.js +101 -0
  478. package/dist/generated/projections.js.map +1 -0
  479. package/dist/generated/randart-properties.d.ts +388 -0
  480. package/dist/generated/randart-properties.d.ts.map +1 -0
  481. package/dist/generated/randart-properties.js +200 -0
  482. package/dist/generated/randart-properties.js.map +1 -0
  483. package/dist/generated/room-flags.d.ts +21 -0
  484. package/dist/generated/room-flags.d.ts.map +1 -0
  485. package/dist/generated/room-flags.js +19 -0
  486. package/dist/generated/room-flags.js.map +1 -0
  487. package/dist/generated/rooms.d.ts +122 -0
  488. package/dist/generated/rooms.d.ts.map +1 -0
  489. package/dist/generated/rooms.js +48 -0
  490. package/dist/generated/rooms.js.map +1 -0
  491. package/dist/generated/square-flags.d.ts +96 -0
  492. package/dist/generated/square-flags.d.ts.map +1 -0
  493. package/dist/generated/square-flags.js +54 -0
  494. package/dist/generated/square-flags.js.map +1 -0
  495. package/dist/generated/stats.d.ts +23 -0
  496. package/dist/generated/stats.d.ts.map +1 -0
  497. package/dist/generated/stats.js +20 -0
  498. package/dist/generated/stats.js.map +1 -0
  499. package/dist/generated/terrain-flags.d.ts +136 -0
  500. package/dist/generated/terrain-flags.d.ts.map +1 -0
  501. package/dist/generated/terrain-flags.js +74 -0
  502. package/dist/generated/terrain-flags.js.map +1 -0
  503. package/dist/generated/terrain.d.ts +83 -0
  504. package/dist/generated/terrain.d.ts.map +1 -0
  505. package/dist/generated/terrain.js +60 -0
  506. package/dist/generated/terrain.js.map +1 -0
  507. package/dist/generated/trap-flags.d.ts +68 -0
  508. package/dist/generated/trap-flags.d.ts.map +1 -0
  509. package/dist/generated/trap-flags.js +40 -0
  510. package/dist/generated/trap-flags.js.map +1 -0
  511. package/dist/generated/tvals.d.ts +152 -0
  512. package/dist/generated/tvals.d.ts.map +1 -0
  513. package/dist/generated/tvals.js +82 -0
  514. package/dist/generated/tvals.js.map +1 -0
  515. package/dist/generated/ui-entry-renderers.d.ts +62 -0
  516. package/dist/generated/ui-entry-renderers.d.ts.map +1 -0
  517. package/dist/generated/ui-entry-renderers.js +22 -0
  518. package/dist/generated/ui-entry-renderers.js.map +1 -0
  519. package/dist/guard.d.ts +43 -0
  520. package/dist/guard.d.ts.map +1 -0
  521. package/dist/guard.js +65 -0
  522. package/dist/guard.js.map +1 -0
  523. package/dist/host/args.d.ts +129 -0
  524. package/dist/host/args.d.ts.map +1 -0
  525. package/dist/host/args.js +251 -0
  526. package/dist/host/args.js.map +1 -0
  527. package/dist/host/bridge.d.ts +61 -0
  528. package/dist/host/bridge.d.ts.map +1 -0
  529. package/dist/host/bridge.js +176 -0
  530. package/dist/host/bridge.js.map +1 -0
  531. package/dist/host/index.d.ts +18 -0
  532. package/dist/host/index.d.ts.map +1 -0
  533. package/dist/host/index.js +18 -0
  534. package/dist/host/index.js.map +1 -0
  535. package/dist/host/io.d.ts +169 -0
  536. package/dist/host/io.d.ts.map +1 -0
  537. package/dist/host/io.js +137 -0
  538. package/dist/host/io.js.map +1 -0
  539. package/dist/host/memory.d.ts +56 -0
  540. package/dist/host/memory.d.ts.map +1 -0
  541. package/dist/host/memory.js +105 -0
  542. package/dist/host/memory.js.map +1 -0
  543. package/dist/host/raw.d.ts +114 -0
  544. package/dist/host/raw.d.ts.map +1 -0
  545. package/dist/host/raw.js +107 -0
  546. package/dist/host/raw.js.map +1 -0
  547. package/dist/index.d.ts +180 -0
  548. package/dist/index.d.ts.map +1 -0
  549. package/dist/index.js +222 -0
  550. package/dist/index.js.map +1 -0
  551. package/dist/loc.d.ts +63 -0
  552. package/dist/loc.d.ts.map +1 -0
  553. package/dist/loc.js +128 -0
  554. package/dist/loc.js.map +1 -0
  555. package/dist/mod/hooks.d.ts +204 -0
  556. package/dist/mod/hooks.d.ts.map +1 -0
  557. package/dist/mod/hooks.js +161 -0
  558. package/dist/mod/hooks.js.map +1 -0
  559. package/dist/mod/ids.d.ts +160 -0
  560. package/dist/mod/ids.d.ts.map +1 -0
  561. package/dist/mod/ids.js +343 -0
  562. package/dist/mod/ids.js.map +1 -0
  563. package/dist/mod/registry-host.d.ts +156 -0
  564. package/dist/mod/registry-host.d.ts.map +1 -0
  565. package/dist/mod/registry-host.js +142 -0
  566. package/dist/mod/registry-host.js.map +1 -0
  567. package/dist/mod/save-blocks.d.ts +201 -0
  568. package/dist/mod/save-blocks.d.ts.map +1 -0
  569. package/dist/mod/save-blocks.js +640 -0
  570. package/dist/mod/save-blocks.js.map +1 -0
  571. package/dist/mod/vocabulary.d.ts +104 -0
  572. package/dist/mod/vocabulary.d.ts.map +1 -0
  573. package/dist/mod/vocabulary.js +0 -0
  574. package/dist/mod/vocabulary.js.map +1 -0
  575. package/dist/mon/bind.d.ts +266 -0
  576. package/dist/mon/bind.d.ts.map +1 -0
  577. package/dist/mon/bind.js +610 -0
  578. package/dist/mon/bind.js.map +1 -0
  579. package/dist/mon/desc.d.ts +72 -0
  580. package/dist/mon/desc.d.ts.map +1 -0
  581. package/dist/mon/desc.js +237 -0
  582. package/dist/mon/desc.js.map +1 -0
  583. package/dist/mon/knowledge-groups.d.ts +61 -0
  584. package/dist/mon/knowledge-groups.d.ts.map +1 -0
  585. package/dist/mon/knowledge-groups.js +121 -0
  586. package/dist/mon/knowledge-groups.js.map +1 -0
  587. package/dist/mon/lore-describe.d.ts +216 -0
  588. package/dist/mon/lore-describe.d.ts.map +1 -0
  589. package/dist/mon/lore-describe.js +1105 -0
  590. package/dist/mon/lore-describe.js.map +1 -0
  591. package/dist/mon/lore.d.ts +117 -0
  592. package/dist/mon/lore.d.ts.map +1 -0
  593. package/dist/mon/lore.js +221 -0
  594. package/dist/mon/lore.js.map +1 -0
  595. package/dist/mon/make.d.ts +147 -0
  596. package/dist/mon/make.d.ts.map +1 -0
  597. package/dist/mon/make.js +353 -0
  598. package/dist/mon/make.js.map +1 -0
  599. package/dist/mon/monster.d.ts +94 -0
  600. package/dist/mon/monster.d.ts.map +1 -0
  601. package/dist/mon/monster.js +90 -0
  602. package/dist/mon/monster.js.map +1 -0
  603. package/dist/mon/predicate.d.ts +87 -0
  604. package/dist/mon/predicate.d.ts.map +1 -0
  605. package/dist/mon/predicate.js +190 -0
  606. package/dist/mon/predicate.js.map +1 -0
  607. package/dist/mon/project-mon.d.ts +129 -0
  608. package/dist/mon/project-mon.d.ts.map +1 -0
  609. package/dist/mon/project-mon.js +652 -0
  610. package/dist/mon/project-mon.js.map +1 -0
  611. package/dist/mon/spell.d.ts +155 -0
  612. package/dist/mon/spell.d.ts.map +1 -0
  613. package/dist/mon/spell.js +355 -0
  614. package/dist/mon/spell.js.map +1 -0
  615. package/dist/mon/steal.d.ts +112 -0
  616. package/dist/mon/steal.d.ts.map +1 -0
  617. package/dist/mon/steal.js +163 -0
  618. package/dist/mon/steal.js.map +1 -0
  619. package/dist/mon/summon.d.ts +62 -0
  620. package/dist/mon/summon.d.ts.map +1 -0
  621. package/dist/mon/summon.js +111 -0
  622. package/dist/mon/summon.js.map +1 -0
  623. package/dist/mon/take-hit.d.ts +75 -0
  624. package/dist/mon/take-hit.d.ts.map +1 -0
  625. package/dist/mon/take-hit.js +122 -0
  626. package/dist/mon/take-hit.js.map +1 -0
  627. package/dist/mon/timed.d.ts +74 -0
  628. package/dist/mon/timed.d.ts.map +1 -0
  629. package/dist/mon/timed.js +219 -0
  630. package/dist/mon/timed.js.map +1 -0
  631. package/dist/mon/types.d.ts +252 -0
  632. package/dist/mon/types.d.ts.map +1 -0
  633. package/dist/mon/types.js +46 -0
  634. package/dist/mon/types.js.map +1 -0
  635. package/dist/msg.d.ts +71 -0
  636. package/dist/msg.d.ts.map +1 -0
  637. package/dist/msg.js +126 -0
  638. package/dist/msg.js.map +1 -0
  639. package/dist/obj/artifact-fake.d.ts +39 -0
  640. package/dist/obj/artifact-fake.d.ts.map +1 -0
  641. package/dist/obj/artifact-fake.js +52 -0
  642. package/dist/obj/artifact-fake.js.map +1 -0
  643. package/dist/obj/artifact-known.d.ts +50 -0
  644. package/dist/obj/artifact-known.d.ts.map +1 -0
  645. package/dist/obj/artifact-known.js +55 -0
  646. package/dist/obj/artifact-known.js.map +1 -0
  647. package/dist/obj/bind.d.ts +219 -0
  648. package/dist/obj/bind.d.ts.map +1 -0
  649. package/dist/obj/bind.js +1239 -0
  650. package/dist/obj/bind.js.map +1 -0
  651. package/dist/obj/chest.d.ts +91 -0
  652. package/dist/obj/chest.d.ts.map +1 -0
  653. package/dist/obj/chest.js +220 -0
  654. package/dist/obj/chest.js.map +1 -0
  655. package/dist/obj/desc.d.ts +79 -0
  656. package/dist/obj/desc.d.ts.map +1 -0
  657. package/dist/obj/desc.js +602 -0
  658. package/dist/obj/desc.js.map +1 -0
  659. package/dist/obj/effects-info.d.ts +78 -0
  660. package/dist/obj/effects-info.d.ts.map +1 -0
  661. package/dist/obj/effects-info.js +240 -0
  662. package/dist/obj/effects-info.js.map +1 -0
  663. package/dist/obj/flavor.d.ts +68 -0
  664. package/dist/obj/flavor.d.ts.map +1 -0
  665. package/dist/obj/flavor.js +201 -0
  666. package/dist/obj/flavor.js.map +1 -0
  667. package/dist/obj/ignore.d.ts +143 -0
  668. package/dist/obj/ignore.d.ts.map +1 -0
  669. package/dist/obj/ignore.js +345 -0
  670. package/dist/obj/ignore.js.map +1 -0
  671. package/dist/obj/knowledge.d.ts +520 -0
  672. package/dist/obj/knowledge.d.ts.map +1 -0
  673. package/dist/obj/knowledge.js +1364 -0
  674. package/dist/obj/knowledge.js.map +1 -0
  675. package/dist/obj/known-object.d.ts +293 -0
  676. package/dist/obj/known-object.d.ts.map +1 -0
  677. package/dist/obj/known-object.js +532 -0
  678. package/dist/obj/known-object.js.map +1 -0
  679. package/dist/obj/make.d.ts +292 -0
  680. package/dist/obj/make.d.ts.map +1 -0
  681. package/dist/obj/make.js +1055 -0
  682. package/dist/obj/make.js.map +1 -0
  683. package/dist/obj/object-info.d.ts +188 -0
  684. package/dist/obj/object-info.d.ts.map +1 -0
  685. package/dist/obj/object-info.js +1407 -0
  686. package/dist/obj/object-info.js.map +1 -0
  687. package/dist/obj/object.d.ts +335 -0
  688. package/dist/obj/object.d.ts.map +1 -0
  689. package/dist/obj/object.js +1051 -0
  690. package/dist/obj/object.js.map +1 -0
  691. package/dist/obj/power.d.ts +65 -0
  692. package/dist/obj/power.d.ts.map +1 -0
  693. package/dist/obj/power.js +580 -0
  694. package/dist/obj/power.js.map +1 -0
  695. package/dist/obj/randart-build.d.ts +223 -0
  696. package/dist/obj/randart-build.d.ts.map +1 -0
  697. package/dist/obj/randart-build.js +1444 -0
  698. package/dist/obj/randart-build.js.map +1 -0
  699. package/dist/obj/randart-data.d.ts +179 -0
  700. package/dist/obj/randart-data.d.ts.map +1 -0
  701. package/dist/obj/randart-data.js +1153 -0
  702. package/dist/obj/randart-data.js.map +1 -0
  703. package/dist/obj/randart.d.ts +128 -0
  704. package/dist/obj/randart.d.ts.map +1 -0
  705. package/dist/obj/randart.js +458 -0
  706. package/dist/obj/randart.js.map +1 -0
  707. package/dist/obj/randname.d.ts +33 -0
  708. package/dist/obj/randname.d.ts.map +1 -0
  709. package/dist/obj/randname.js +116 -0
  710. package/dist/obj/randname.js.map +1 -0
  711. package/dist/obj/recharge.d.ts +28 -0
  712. package/dist/obj/recharge.d.ts.map +1 -0
  713. package/dist/obj/recharge.js +52 -0
  714. package/dist/obj/recharge.js.map +1 -0
  715. package/dist/obj/types.d.ts +584 -0
  716. package/dist/obj/types.d.ts.map +1 -0
  717. package/dist/obj/types.js +104 -0
  718. package/dist/obj/types.js.map +1 -0
  719. package/dist/obj/value.d.ts +64 -0
  720. package/dist/obj/value.d.ts.map +1 -0
  721. package/dist/obj/value.js +176 -0
  722. package/dist/obj/value.js.map +1 -0
  723. package/dist/player/abilities.d.ts +57 -0
  724. package/dist/player/abilities.d.ts.map +1 -0
  725. package/dist/player/abilities.js +118 -0
  726. package/dist/player/abilities.js.map +1 -0
  727. package/dist/player/best-digger.d.ts +42 -0
  728. package/dist/player/best-digger.d.ts.map +1 -0
  729. package/dist/player/best-digger.js +89 -0
  730. package/dist/player/best-digger.js.map +1 -0
  731. package/dist/player/bind.d.ts +270 -0
  732. package/dist/player/bind.d.ts.map +1 -0
  733. package/dist/player/bind.js +698 -0
  734. package/dist/player/bind.js.map +1 -0
  735. package/dist/player/birth.d.ts +258 -0
  736. package/dist/player/birth.d.ts.map +1 -0
  737. package/dist/player/birth.js +639 -0
  738. package/dist/player/birth.js.map +1 -0
  739. package/dist/player/calcs.d.ts +403 -0
  740. package/dist/player/calcs.d.ts.map +1 -0
  741. package/dist/player/calcs.js +1189 -0
  742. package/dist/player/calcs.js.map +1 -0
  743. package/dist/player/combat-regen.d.ts +14 -0
  744. package/dist/player/combat-regen.d.ts.map +1 -0
  745. package/dist/player/combat-regen.js +29 -0
  746. package/dist/player/combat-regen.js.map +1 -0
  747. package/dist/player/exp.d.ts +96 -0
  748. package/dist/player/exp.d.ts.map +1 -0
  749. package/dist/player/exp.js +229 -0
  750. package/dist/player/exp.js.map +1 -0
  751. package/dist/player/history.d.ts +91 -0
  752. package/dist/player/history.d.ts.map +1 -0
  753. package/dist/player/history.js +145 -0
  754. package/dist/player/history.js.map +1 -0
  755. package/dist/player/options.d.ts +123 -0
  756. package/dist/player/options.d.ts.map +1 -0
  757. package/dist/player/options.js +199 -0
  758. package/dist/player/options.js.map +1 -0
  759. package/dist/player/player.d.ts +242 -0
  760. package/dist/player/player.d.ts.map +1 -0
  761. package/dist/player/player.js +119 -0
  762. package/dist/player/player.js.map +1 -0
  763. package/dist/player/shape-lore.d.ts +56 -0
  764. package/dist/player/shape-lore.d.ts.map +1 -0
  765. package/dist/player/shape-lore.js +221 -0
  766. package/dist/player/shape-lore.js.map +1 -0
  767. package/dist/player/spell.d.ts +146 -0
  768. package/dist/player/spell.d.ts.map +1 -0
  769. package/dist/player/spell.js +519 -0
  770. package/dist/player/spell.js.map +1 -0
  771. package/dist/player/take-hit.d.ts +87 -0
  772. package/dist/player/take-hit.d.ts.map +1 -0
  773. package/dist/player/take-hit.js +99 -0
  774. package/dist/player/take-hit.js.map +1 -0
  775. package/dist/player/timed.d.ts +205 -0
  776. package/dist/player/timed.d.ts.map +1 -0
  777. package/dist/player/timed.js +310 -0
  778. package/dist/player/timed.js.map +1 -0
  779. package/dist/player/types.d.ts +390 -0
  780. package/dist/player/types.d.ts.map +1 -0
  781. package/dist/player/types.js +51 -0
  782. package/dist/player/types.js.map +1 -0
  783. package/dist/rational.d.ts +99 -0
  784. package/dist/rational.d.ts.map +1 -0
  785. package/dist/rational.js +345 -0
  786. package/dist/rational.js.map +1 -0
  787. package/dist/rng.d.ts +168 -0
  788. package/dist/rng.d.ts.map +1 -0
  789. package/dist/rng.js +404 -0
  790. package/dist/rng.js.map +1 -0
  791. package/dist/save/buffer.d.ts +120 -0
  792. package/dist/save/buffer.d.ts.map +1 -0
  793. package/dist/save/buffer.js +238 -0
  794. package/dist/save/buffer.js.map +1 -0
  795. package/dist/save/compress.d.ts +79 -0
  796. package/dist/save/compress.d.ts.map +1 -0
  797. package/dist/save/compress.js +97 -0
  798. package/dist/save/compress.js.map +1 -0
  799. package/dist/save/description.d.ts +61 -0
  800. package/dist/save/description.d.ts.map +1 -0
  801. package/dist/save/description.js +51 -0
  802. package/dist/save/description.js.map +1 -0
  803. package/dist/save/integrity.d.ts +59 -0
  804. package/dist/save/integrity.d.ts.map +1 -0
  805. package/dist/save/integrity.js +103 -0
  806. package/dist/save/integrity.js.map +1 -0
  807. package/dist/score/display.d.ts +40 -0
  808. package/dist/score/display.d.ts.map +1 -0
  809. package/dist/score/display.js +86 -0
  810. package/dist/score/display.js.map +1 -0
  811. package/dist/score/score.d.ts +166 -0
  812. package/dist/score/score.d.ts.map +1 -0
  813. package/dist/score/score.js +244 -0
  814. package/dist/score/score.js.map +1 -0
  815. package/dist/score/types.d.ts +98 -0
  816. package/dist/score/types.d.ts.map +1 -0
  817. package/dist/score/types.js +27 -0
  818. package/dist/score/types.js.map +1 -0
  819. package/dist/session/boot.d.ts +207 -0
  820. package/dist/session/boot.d.ts.map +1 -0
  821. package/dist/session/boot.js +151 -0
  822. package/dist/session/boot.js.map +1 -0
  823. package/dist/session/game.d.ts +257 -0
  824. package/dist/session/game.d.ts.map +1 -0
  825. package/dist/session/game.js +2880 -0
  826. package/dist/session/game.js.map +1 -0
  827. package/dist/session/save.d.ts +847 -0
  828. package/dist/session/save.d.ts.map +1 -0
  829. package/dist/session/save.js +1306 -0
  830. package/dist/session/save.js.map +1 -0
  831. package/dist/sound/engine.d.ts +146 -0
  832. package/dist/sound/engine.d.ts.map +1 -0
  833. package/dist/sound/engine.js +332 -0
  834. package/dist/sound/engine.js.map +1 -0
  835. package/dist/sound/index.d.ts +13 -0
  836. package/dist/sound/index.d.ts.map +1 -0
  837. package/dist/sound/index.js +13 -0
  838. package/dist/sound/index.js.map +1 -0
  839. package/dist/sound/sound-prefs-data.d.ts +10 -0
  840. package/dist/sound/sound-prefs-data.d.ts.map +1 -0
  841. package/dist/sound/sound-prefs-data.js +165 -0
  842. package/dist/sound/sound-prefs-data.js.map +1 -0
  843. package/dist/sound/types.d.ts +64 -0
  844. package/dist/sound/types.d.ts.map +1 -0
  845. package/dist/sound/types.js +23 -0
  846. package/dist/sound/types.js.map +1 -0
  847. package/dist/store/bind.d.ts +43 -0
  848. package/dist/store/bind.d.ts.map +1 -0
  849. package/dist/store/bind.js +130 -0
  850. package/dist/store/bind.js.map +1 -0
  851. package/dist/store/price.d.ts +27 -0
  852. package/dist/store/price.d.ts.map +1 -0
  853. package/dist/store/price.js +81 -0
  854. package/dist/store/price.js.map +1 -0
  855. package/dist/store/store-cmd.d.ts +51 -0
  856. package/dist/store/store-cmd.d.ts.map +1 -0
  857. package/dist/store/store-cmd.js +73 -0
  858. package/dist/store/store-cmd.js.map +1 -0
  859. package/dist/store/store.d.ts +156 -0
  860. package/dist/store/store.d.ts.map +1 -0
  861. package/dist/store/store.js +616 -0
  862. package/dist/store/store.js.map +1 -0
  863. package/dist/store/transact.d.ts +198 -0
  864. package/dist/store/transact.d.ts.map +1 -0
  865. package/dist/store/transact.js +383 -0
  866. package/dist/store/transact.js.map +1 -0
  867. package/dist/store/types.d.ts +101 -0
  868. package/dist/store/types.d.ts.map +1 -0
  869. package/dist/store/types.js +14 -0
  870. package/dist/store/types.js.map +1 -0
  871. package/dist/version.d.ts +25 -0
  872. package/dist/version.d.ts.map +1 -0
  873. package/dist/version.js +25 -0
  874. package/dist/version.js.map +1 -0
  875. package/dist/visuals/engine.d.ts +223 -0
  876. package/dist/visuals/engine.d.ts.map +1 -0
  877. package/dist/visuals/engine.js +357 -0
  878. package/dist/visuals/engine.js.map +1 -0
  879. package/dist/visuals/glyph-table.d.ts +112 -0
  880. package/dist/visuals/glyph-table.d.ts.map +1 -0
  881. package/dist/visuals/glyph-table.js +156 -0
  882. package/dist/visuals/glyph-table.js.map +1 -0
  883. package/dist/visuals/grafmode-data.d.ts +4 -0
  884. package/dist/visuals/grafmode-data.d.ts.map +1 -0
  885. package/dist/visuals/grafmode-data.js +88 -0
  886. package/dist/visuals/grafmode-data.js.map +1 -0
  887. package/dist/visuals/grafmode.d.ts +83 -0
  888. package/dist/visuals/grafmode.d.ts.map +1 -0
  889. package/dist/visuals/grafmode.js +82 -0
  890. package/dist/visuals/grafmode.js.map +1 -0
  891. package/dist/visuals/index.d.ts +19 -0
  892. package/dist/visuals/index.d.ts.map +1 -0
  893. package/dist/visuals/index.js +19 -0
  894. package/dist/visuals/index.js.map +1 -0
  895. package/dist/visuals/map-text.d.ts +79 -0
  896. package/dist/visuals/map-text.d.ts.map +1 -0
  897. package/dist/visuals/map-text.js +122 -0
  898. package/dist/visuals/map-text.js.map +1 -0
  899. package/dist/visuals/pref-expr.d.ts +56 -0
  900. package/dist/visuals/pref-expr.d.ts.map +1 -0
  901. package/dist/visuals/pref-expr.js +173 -0
  902. package/dist/visuals/pref-expr.js.map +1 -0
  903. package/dist/visuals/prefs.d.ts +280 -0
  904. package/dist/visuals/prefs.d.ts.map +1 -0
  905. package/dist/visuals/prefs.js +835 -0
  906. package/dist/visuals/prefs.js.map +1 -0
  907. package/dist/visuals/tile-prefs.d.ts +132 -0
  908. package/dist/visuals/tile-prefs.d.ts.map +1 -0
  909. package/dist/visuals/tile-prefs.js +105 -0
  910. package/dist/visuals/tile-prefs.js.map +1 -0
  911. package/dist/world/chunk.d.ts +195 -0
  912. package/dist/world/chunk.d.ts.map +1 -0
  913. package/dist/world/chunk.js +422 -0
  914. package/dist/world/chunk.js.map +1 -0
  915. package/dist/world/feature.d.ts +86 -0
  916. package/dist/world/feature.d.ts.map +1 -0
  917. package/dist/world/feature.js +153 -0
  918. package/dist/world/feature.js.map +1 -0
  919. package/dist/world/flow.d.ts +33 -0
  920. package/dist/world/flow.d.ts.map +1 -0
  921. package/dist/world/flow.js +115 -0
  922. package/dist/world/flow.js.map +1 -0
  923. package/dist/world/project.d.ts +176 -0
  924. package/dist/world/project.d.ts.map +1 -0
  925. package/dist/world/project.js +543 -0
  926. package/dist/world/project.js.map +1 -0
  927. package/dist/world/projection.d.ts +100 -0
  928. package/dist/world/projection.d.ts.map +1 -0
  929. package/dist/world/projection.js +152 -0
  930. package/dist/world/projection.js.map +1 -0
  931. package/dist/world/scatter.d.ts +25 -0
  932. package/dist/world/scatter.d.ts.map +1 -0
  933. package/dist/world/scatter.js +54 -0
  934. package/dist/world/scatter.js.map +1 -0
  935. package/dist/world/trap.d.ts +84 -0
  936. package/dist/world/trap.d.ts.map +1 -0
  937. package/dist/world/trap.js +106 -0
  938. package/dist/world/trap.js.map +1 -0
  939. package/dist/world/view.d.ts +60 -0
  940. package/dist/world/view.d.ts.map +1 -0
  941. package/dist/world/view.js +436 -0
  942. package/dist/world/view.js.map +1 -0
  943. package/dist/z-queue.d.ts +133 -0
  944. package/dist/z-queue.d.ts.map +1 -0
  945. package/dist/z-queue.js +283 -0
  946. package/dist/z-queue.js.map +1 -0
  947. package/package.json +75 -0
  948. package/src/agent/act.ts +114 -0
  949. package/src/agent/controller.ts +109 -0
  950. package/src/agent/events.ts +47 -0
  951. package/src/agent/index.ts +36 -0
  952. package/src/agent/perceive.ts +440 -0
  953. package/src/agent/types.ts +464 -0
  954. package/src/bitflag.ts +536 -0
  955. package/src/cmd.ts +547 -0
  956. package/src/color.ts +267 -0
  957. package/src/combat/brand-slay.ts +360 -0
  958. package/src/combat/hit.ts +471 -0
  959. package/src/combat/index.ts +15 -0
  960. package/src/combat/melee.ts +728 -0
  961. package/src/combat/mon-melee.ts +1153 -0
  962. package/src/combat/ranged.ts +397 -0
  963. package/src/constants.ts +324 -0
  964. package/src/dice.ts +467 -0
  965. package/src/effects/effect-choice.ts +32 -0
  966. package/src/effects/effect-info.ts +859 -0
  967. package/src/effects/effect.ts +560 -0
  968. package/src/effects/handlers.ts +452 -0
  969. package/src/effects/interpreter.ts +603 -0
  970. package/src/events.ts +317 -0
  971. package/src/expression.ts +360 -0
  972. package/src/game/cave-cmd.ts +1147 -0
  973. package/src/game/char-sheet.ts +557 -0
  974. package/src/game/chest.ts +348 -0
  975. package/src/game/context.ts +1218 -0
  976. package/src/game/curse-tick.ts +130 -0
  977. package/src/game/describe.ts +62 -0
  978. package/src/game/display.ts +838 -0
  979. package/src/game/dump-level.ts +114 -0
  980. package/src/game/effect-attack.ts +746 -0
  981. package/src/game/effect-detect.ts +542 -0
  982. package/src/game/effect-env.ts +252 -0
  983. package/src/game/effect-game-env.ts +86 -0
  984. package/src/game/effect-general.ts +1122 -0
  985. package/src/game/effect-item.ts +999 -0
  986. package/src/game/effect-melee.ts +557 -0
  987. package/src/game/effect-mon-origin.ts +196 -0
  988. package/src/game/effect-monster.ts +310 -0
  989. package/src/game/effect-summon.ts +161 -0
  990. package/src/game/effect-teleport.ts +645 -0
  991. package/src/game/effect-terrain.ts +881 -0
  992. package/src/game/energy.ts +61 -0
  993. package/src/game/equip-cmp.ts +476 -0
  994. package/src/game/floor.ts +524 -0
  995. package/src/game/gear.ts +1352 -0
  996. package/src/game/harness.ts +270 -0
  997. package/src/game/history.ts +78 -0
  998. package/src/game/ignore-cmd.ts +68 -0
  999. package/src/game/inscription-confirm.ts +115 -0
  1000. package/src/game/known.ts +960 -0
  1001. package/src/game/loop.ts +671 -0
  1002. package/src/game/lore-color.ts +108 -0
  1003. package/src/game/mon-cast.ts +409 -0
  1004. package/src/game/mon-cmd.ts +767 -0
  1005. package/src/game/mon-death.ts +459 -0
  1006. package/src/game/mon-group.ts +368 -0
  1007. package/src/game/mon-list.ts +213 -0
  1008. package/src/game/mon-message.ts +359 -0
  1009. package/src/game/mon-place.ts +1012 -0
  1010. package/src/game/mon-ranged.ts +405 -0
  1011. package/src/game/mon-shape.ts +161 -0
  1012. package/src/game/mon-side.ts +511 -0
  1013. package/src/game/monster-turn.ts +1750 -0
  1014. package/src/game/obj-cmd.ts +1970 -0
  1015. package/src/game/obj-list.ts +302 -0
  1016. package/src/game/object-inspect.ts +162 -0
  1017. package/src/game/pickup.ts +483 -0
  1018. package/src/game/player-path.ts +1434 -0
  1019. package/src/game/player-side.ts +555 -0
  1020. package/src/game/player-turn.ts +852 -0
  1021. package/src/game/project-cast.ts +766 -0
  1022. package/src/game/project-feat.ts +386 -0
  1023. package/src/game/project-monster.ts +407 -0
  1024. package/src/game/project-obj.ts +298 -0
  1025. package/src/game/project-player.ts +236 -0
  1026. package/src/game/quest.ts +248 -0
  1027. package/src/game/ranged-cmd.ts +417 -0
  1028. package/src/game/scheduler.ts +150 -0
  1029. package/src/game/spell-cmd.ts +404 -0
  1030. package/src/game/spoil.ts +562 -0
  1031. package/src/game/steal.ts +169 -0
  1032. package/src/game/take-hit-hooks.ts +152 -0
  1033. package/src/game/target-loop.ts +501 -0
  1034. package/src/game/target.ts +424 -0
  1035. package/src/game/thrust.ts +174 -0
  1036. package/src/game/trap.ts +791 -0
  1037. package/src/game/ui-entry.ts +2075 -0
  1038. package/src/game/wizard.ts +1760 -0
  1039. package/src/game/world.ts +786 -0
  1040. package/src/gen/cave.ts +2858 -0
  1041. package/src/gen/gen-monster.ts +592 -0
  1042. package/src/gen/generate.ts +523 -0
  1043. package/src/gen/room.ts +2213 -0
  1044. package/src/gen/util.ts +1958 -0
  1045. package/src/generated/dun-profiles.ts +30 -0
  1046. package/src/generated/effects.ts +241 -0
  1047. package/src/generated/elements.ts +62 -0
  1048. package/src/generated/equip-slots.ts +36 -0
  1049. package/src/generated/history-types.ts +34 -0
  1050. package/src/generated/ignore-types.ts +66 -0
  1051. package/src/generated/index.ts +33 -0
  1052. package/src/generated/kind-flags.ts +44 -0
  1053. package/src/generated/message.ts +320 -0
  1054. package/src/generated/mon-message.ts +138 -0
  1055. package/src/generated/mon-race-flags.ts +182 -0
  1056. package/src/generated/mon-spells.ts +198 -0
  1057. package/src/generated/mon-temp-flags.ts +34 -0
  1058. package/src/generated/mon-timed.ts +34 -0
  1059. package/src/generated/object-flags.ts +95 -0
  1060. package/src/generated/object-modifiers.ts +43 -0
  1061. package/src/generated/options.ts +104 -0
  1062. package/src/generated/origins.ts +64 -0
  1063. package/src/generated/parser-errors.ts +140 -0
  1064. package/src/generated/player-flags.ts +48 -0
  1065. package/src/generated/player-timed.ts +118 -0
  1066. package/src/generated/projections.ts +103 -0
  1067. package/src/generated/randart-properties.ts +202 -0
  1068. package/src/generated/room-flags.ts +21 -0
  1069. package/src/generated/rooms.ts +50 -0
  1070. package/src/generated/square-flags.ts +56 -0
  1071. package/src/generated/stats.ts +22 -0
  1072. package/src/generated/terrain-flags.ts +76 -0
  1073. package/src/generated/terrain.ts +62 -0
  1074. package/src/generated/trap-flags.ts +42 -0
  1075. package/src/generated/tvals.ts +84 -0
  1076. package/src/generated/ui-entry-renderers.ts +24 -0
  1077. package/src/guard.ts +70 -0
  1078. package/src/host/args.ts +334 -0
  1079. package/src/host/bridge.ts +193 -0
  1080. package/src/host/index.ts +18 -0
  1081. package/src/host/io.ts +239 -0
  1082. package/src/host/memory.ts +134 -0
  1083. package/src/host/raw.ts +190 -0
  1084. package/src/index.ts +250 -0
  1085. package/src/loc.ts +157 -0
  1086. package/src/mod/hooks.ts +278 -0
  1087. package/src/mod/ids.ts +390 -0
  1088. package/src/mod/registry-host.ts +258 -0
  1089. package/src/mod/save-blocks.ts +787 -0
  1090. package/src/mod/vocabulary.ts +0 -0
  1091. package/src/mon/bind.ts +899 -0
  1092. package/src/mon/desc.ts +212 -0
  1093. package/src/mon/knowledge-groups.ts +164 -0
  1094. package/src/mon/lore-describe.ts +1414 -0
  1095. package/src/mon/lore.ts +306 -0
  1096. package/src/mon/make.ts +422 -0
  1097. package/src/mon/monster.ts +161 -0
  1098. package/src/mon/predicate.ts +220 -0
  1099. package/src/mon/project-mon.ts +855 -0
  1100. package/src/mon/spell.ts +418 -0
  1101. package/src/mon/steal.ts +246 -0
  1102. package/src/mon/summon.ts +145 -0
  1103. package/src/mon/take-hit.ts +195 -0
  1104. package/src/mon/timed.ts +300 -0
  1105. package/src/mon/types.ts +286 -0
  1106. package/src/msg.ts +146 -0
  1107. package/src/obj/artifact-fake.ts +63 -0
  1108. package/src/obj/artifact-known.ts +69 -0
  1109. package/src/obj/bind.ts +1345 -0
  1110. package/src/obj/chest.ts +249 -0
  1111. package/src/obj/desc.ts +699 -0
  1112. package/src/obj/effects-info.ts +349 -0
  1113. package/src/obj/flavor.ts +256 -0
  1114. package/src/obj/ignore.ts +387 -0
  1115. package/src/obj/knowledge.ts +1577 -0
  1116. package/src/obj/known-object.ts +718 -0
  1117. package/src/obj/make.ts +1367 -0
  1118. package/src/obj/object-info.ts +1737 -0
  1119. package/src/obj/object.ts +1296 -0
  1120. package/src/obj/power.ts +674 -0
  1121. package/src/obj/randart-build.ts +1705 -0
  1122. package/src/obj/randart-data.ts +1344 -0
  1123. package/src/obj/randart.ts +552 -0
  1124. package/src/obj/randname.ts +128 -0
  1125. package/src/obj/recharge.ts +62 -0
  1126. package/src/obj/types.ts +559 -0
  1127. package/src/obj/value.ts +215 -0
  1128. package/src/player/abilities.ts +142 -0
  1129. package/src/player/best-digger.ts +118 -0
  1130. package/src/player/bind.ts +1021 -0
  1131. package/src/player/birth.ts +794 -0
  1132. package/src/player/calcs.ts +1522 -0
  1133. package/src/player/combat-regen.ts +33 -0
  1134. package/src/player/exp.ts +275 -0
  1135. package/src/player/history.ts +211 -0
  1136. package/src/player/options.ts +244 -0
  1137. package/src/player/player.ts +356 -0
  1138. package/src/player/shape-lore.ts +250 -0
  1139. package/src/player/spell.ts +627 -0
  1140. package/src/player/take-hit.ts +173 -0
  1141. package/src/player/timed.ts +482 -0
  1142. package/src/player/types.ts +415 -0
  1143. package/src/rational.ts +388 -0
  1144. package/src/rng.ts +476 -0
  1145. package/src/save/buffer.ts +287 -0
  1146. package/src/save/compress.ts +132 -0
  1147. package/src/save/description.ts +70 -0
  1148. package/src/save/integrity.ts +133 -0
  1149. package/src/score/display.ts +125 -0
  1150. package/src/score/score.ts +341 -0
  1151. package/src/score/types.ts +104 -0
  1152. package/src/session/boot.ts +338 -0
  1153. package/src/session/game.ts +3712 -0
  1154. package/src/session/save.ts +2074 -0
  1155. package/src/sound/engine.ts +375 -0
  1156. package/src/sound/index.ts +13 -0
  1157. package/src/sound/sound-prefs-data.ts +173 -0
  1158. package/src/sound/types.ts +68 -0
  1159. package/src/store/bind.ts +145 -0
  1160. package/src/store/price.ts +97 -0
  1161. package/src/store/store-cmd.ts +73 -0
  1162. package/src/store/store.ts +723 -0
  1163. package/src/store/transact.ts +644 -0
  1164. package/src/store/types.ts +110 -0
  1165. package/src/version.ts +26 -0
  1166. package/src/visuals/engine.ts +444 -0
  1167. package/src/visuals/glyph-table.ts +191 -0
  1168. package/src/visuals/grafmode-data.ts +90 -0
  1169. package/src/visuals/grafmode.ts +123 -0
  1170. package/src/visuals/index.ts +19 -0
  1171. package/src/visuals/map-text.ts +152 -0
  1172. package/src/visuals/pref-expr.ts +182 -0
  1173. package/src/visuals/prefs.ts +981 -0
  1174. package/src/visuals/tile-prefs.ts +183 -0
  1175. package/src/world/chunk.ts +531 -0
  1176. package/src/world/feature.ts +215 -0
  1177. package/src/world/flow.ts +128 -0
  1178. package/src/world/project.ts +687 -0
  1179. package/src/world/projection.ts +228 -0
  1180. package/src/world/scatter.ts +72 -0
  1181. package/src/world/trap.ts +155 -0
  1182. package/src/world/view.ts +513 -0
  1183. package/src/z-queue.ts +300 -0
@@ -0,0 +1,1970 @@
1
+ /**
2
+ * Object commands, ported from reference/src/cmd-obj.c (Angband 4.2.6) with
3
+ * the obj-gear.c inventory verbs they sit on: take off / wield / drop, and
4
+ * the use family (eat, quaff, read, use staff, aim wand, zap rod, activate)
5
+ * running each object's effect chain through the effect interpreter with a
6
+ * player source - the same stack monster spells cast through.
7
+ *
8
+ * Knowledge rides on the ported FlavorKnowledge (obj/knowledge.ts):
9
+ * unaware flavored items aim at random when they secretly need a direction,
10
+ * single-use items learn their flavor on use, devices are marked tried.
11
+ * The deeper known-object bookkeeping (obj->known twins, work_obj copies
12
+ * for messaging) is knowledge/UI work (#24/#25) and is replaced by hooks.
13
+ *
14
+ * Inscribe/uninscribe/refill (fuel) are ported below (cmd-obj.c
15
+ * do_cmd_inscribe/do_cmd_uninscribe/do_cmd_refill + refill_lamp,
16
+ * obj-util.c obj_can_refill/obj_has_inscrip). Autoinscribe
17
+ * (do_cmd_autoinscribe/apply_autoinscription) applies the per-kind
18
+ * note_aware/note_unaware registry (obj/knowledge.ts AutoinscriptionRegistry,
19
+ * wired through ObjCmdDeps.autoNote by session/game.ts). The separate
20
+ * rune-based autoinscription (runes_autoinscribe, obj-ignore.c L217) rides the
21
+ * rune knowledge system and stays deferred (#24).
22
+ *
23
+ * DEFERRED with their subsystems (ledgered in game-obj-cmd.yaml):
24
+ * cast/study (player spells #22), the glyph-of-warding push_object
25
+ * interaction (traps #21), command repetition, and the !t take-off
26
+ * confirmation prompt (get_check, UI).
27
+ */
28
+
29
+ import type { Constants } from "../constants.js";
30
+ import { EFFECT_ENTRIES, EQUIP_SLOT_ENTRIES, OF, TMD } from "../generated/index.js";
31
+ import { DDD } from "../loc.js";
32
+ import { SKILL } from "../player/types.js";
33
+ import { EffectBuilder } from "../effects/effect.js";
34
+ import type { Effect, EffectBuilderInjections } from "../effects/effect.js";
35
+ import { sourcePlayer } from "../effects/interpreter.js";
36
+ import type { EffectRegistry } from "../effects/interpreter.js";
37
+ import type { EffectRecordJson, ObjectKind } from "../obj/types.js";
38
+ import type { GameObject, StackLimits } from "../obj/object.js";
39
+ import {
40
+ objectPackTotal,
41
+ tvalIsEdible,
42
+ tvalIsFuel,
43
+ tvalIsLauncher,
44
+ tvalIsLight,
45
+ tvalIsMeleeWeapon,
46
+ tvalIsPotion,
47
+ tvalIsRing,
48
+ tvalIsRod,
49
+ tvalIsScroll,
50
+ tvalIsStaff,
51
+ tvalIsWand,
52
+ tvalIsWearable,
53
+ tvalCanHaveCharges,
54
+ tvalCanHaveTimeout,
55
+ } from "../obj/object.js";
56
+ import {
57
+ FlavorKnowledge,
58
+ NOOP_FLAVOR_AWARE_DEPS,
59
+ buildRuneList,
60
+ objectHasRune,
61
+ playerKnowsRune,
62
+ } from "../obj/knowledge.js";
63
+ import type { FlavorAwareDeps } from "../obj/knowledge.js";
64
+ import { ignoreItemOk } from "../obj/ignore.js";
65
+ import type { GameState, ItemTargetRef, PlayerCommand } from "./context.js";
66
+ import { dropNear, floorObjectForUse, floorPile } from "./floor.js";
67
+ import type { FloorEnv } from "./floor.js";
68
+ import type { TeleportEnv } from "./effect-teleport.js";
69
+ import {
70
+ calcInventory,
71
+ combinePack,
72
+ gearGet,
73
+ gearObjectForUse,
74
+ gearToLabel,
75
+ invenCarry,
76
+ invenCarryNum,
77
+ objectSplit,
78
+ packIsOverfull,
79
+ packTotalSuppressed,
80
+ packTotalView,
81
+ wieldObject,
82
+ wieldSlot,
83
+ } from "./gear.js";
84
+ import type { CalcInventoryOpts } from "./gear.js";
85
+ import { checkForInscrip } from "./pickup.js";
86
+ import { disturb } from "./player-path.js";
87
+ import { buildEffectContext } from "./effect-env.js";
88
+ import type { EffectEnvDeps } from "./effect-env.js";
89
+ import { attachGameEnv } from "./effect-game-env.js";
90
+ import { describeObject } from "./describe.js";
91
+ import { ODESC, objDescNameFormat } from "../obj/desc.js";
92
+ import { substituteTimedMessage } from "../player/timed.js";
93
+ import { squareIsSeen } from "../world/view.js";
94
+ import { updatePlayerObjectKnowledge } from "./known.js";
95
+ import type { CastContext } from "./project-cast.js";
96
+ import type { ActionRegistry } from "./player-turn.js";
97
+ import { targetFix, targetGet, targetOkay, targetRelease } from "./target.js";
98
+
99
+ /** enum use (cmd-obj.c). */
100
+ export const USE = { TIMEOUT: 0, CHARGE: 1, SINGLE: 2 } as const;
101
+ export type UseKind = (typeof USE)[keyof typeof USE];
102
+
103
+ /** Hooks for messages and unported-subsystem gaps; all optional. */
104
+ export interface ObjCmdEnv {
105
+ /** msg / msgt / activation_message. */
106
+ msg?: (text: string) => void;
107
+ /**
108
+ * get_aim_dir: keypad 1-9, or DIR_TARGET (5) to use the player's
109
+ * current target (game/target.ts). The prompt itself is UI (#25).
110
+ */
111
+ chooseDir?: () => number;
112
+ /** get_check for the !t take-off confirmation (UI); default true. */
113
+ confirm?: (prompt: string) => boolean;
114
+ }
115
+
116
+ /** Everything the object commands need beyond the state. */
117
+ export interface ObjCmdDeps {
118
+ constants: Constants;
119
+ /** The effect interpreter with the game handlers registered. */
120
+ registry: EffectRegistry;
121
+ /** The projection cast context (bound projections, player actor). */
122
+ cast: CastContext;
123
+ /** EffectEnvDeps for buildEffectContext (bound timed table, hooks). */
124
+ envDeps: EffectEnvDeps;
125
+ /** Per-game flavor knowledge; absent, everything counts as aware. */
126
+ flavor?: FlavorKnowledge;
127
+ /**
128
+ * The ignore/notice side effects of becoming aware (object_flavor_aware
129
+ * L2276-2279, #89), used alongside `flavor`. The in-play caller
130
+ * (session/game.ts) supplies the real ignore-settings-backed deps; absent,
131
+ * falls back to NOOP_FLAVOR_AWARE_DEPS (the bare aware-bit flip).
132
+ */
133
+ flavorDeps?: FlavorAwareDeps;
134
+ /** Extra effect-builder injections (summon/shape names, mod bases). */
135
+ inject?: EffectBuilderInjections;
136
+ /** Teleport-family seams (trap predicates; wired by game/trap.ts). */
137
+ teleport?: TeleportEnv;
138
+ /** General-handler seams (trap access for glyphs; effect-general.ts). */
139
+ general?: import("./effect-general.js").GeneralEffectEnv;
140
+ /** Item-targeting seams (get_item, ego/curse tables; effect-item.ts). */
141
+ item?: import("./effect-item.js").ItemEffectEnv;
142
+ /** Summoning seams (summon table + live placement; effect-summon.ts). */
143
+ summon?: import("./effect-summon.js").SummonEffectEnv;
144
+ /** Floor-pile seams (isTrap for drop placement). */
145
+ floorEnv?: FloorEnv;
146
+ env?: ObjCmdEnv;
147
+ /**
148
+ * get_autoinscription (obj-ignore.c L229): the per-kind note_aware /
149
+ * note_unaware autoinscription lookup. session/game.ts wires this to the
150
+ * per-game AutoinscriptionRegistry (obj/knowledge.ts), so a note registered
151
+ * through the knowledge-menu manager (web) is applied on autoinscribe. Left
152
+ * absent by worldless callers (game/harness.ts), which makes autoinscribe a
153
+ * no-op exactly as upstream with no autoinscriptions configured.
154
+ */
155
+ autoNote?: (kind: ObjectKind, aware: boolean) => string | null;
156
+ /**
157
+ * player_exp_gain hook (player.c L269): object_learn_on_use rewards the
158
+ * player with experience on a first identify-by-use (obj-knowledge.c
159
+ * L1925-1936, gap 4.3). session/game.ts wires this to playerExpGain with the
160
+ * real ExpDeps (the same hook already threaded into spell/trap/chest); absent
161
+ * for worldless callers, where the XP side-channel is simply skipped.
162
+ */
163
+ expGain?: (amount: number) => void;
164
+ /**
165
+ * calc_inventory (player-calcs.c) inputs used to re-derive the quiver after a
166
+ * wield / takeoff / drop (upstream's PU_INVEN -> update_stuff): the earlier_object
167
+ * ammo tiebreak (ammoTval / objectValue), the preferred_quiver_slot keyset
168
+ * (rogueLike) and the re-arrange message gate (characterDungeon). All optional;
169
+ * absent hooks fall back to gear-order ammo assignment with no messages.
170
+ */
171
+ ammoTval?: () => number;
172
+ objectValue?: (obj: GameObject) => number;
173
+ rogueLike?: boolean;
174
+ characterDungeon?: boolean;
175
+ }
176
+
177
+ /** Build calc_inventory options from the object-command deps (gap 4.1). */
178
+ function calcInvOpts(state: GameState, deps: ObjCmdDeps): CalcInventoryOpts {
179
+ const o: CalcInventoryOpts = {
180
+ /* object_is_equipped(p->body, old_pack[i]) (player-calcs.c:1227): wielding
181
+ * an item moves it out of the pack, which must not read as a re-arrangement.
182
+ * This is the path where that happens, so the predicate belongs here. */
183
+ isEquipped: (handle: number): boolean =>
184
+ state.actor.player.equipment.includes(handle),
185
+ };
186
+ if (deps.env?.msg) o.msg = deps.env.msg;
187
+ if (deps.rogueLike) o.rogueLike = deps.rogueLike;
188
+ if (deps.characterDungeon) o.characterDungeon = deps.characterDungeon;
189
+ const at = deps.ammoTval?.() ?? state.playerState?.ammoTval;
190
+ if (at !== undefined) o.ammoTval = at;
191
+ if (deps.objectValue) o.objectValue = deps.objectValue;
192
+ return o;
193
+ }
194
+
195
+ /**
196
+ * The combine_pack / pack_overflow seams for the command handlers: the message
197
+ * sink, the calc_inventory inputs, and the drop_near environment
198
+ * pack_overflow's drop needs (deps.floorEnv is the same one inven_drop uses).
199
+ */
200
+ function packOpts(state: GameState, deps: ObjCmdDeps): PackOverflowOpts {
201
+ const o: PackOverflowOpts = { calcInv: calcInvOpts(state, deps) };
202
+ if (deps.env?.msg) o.msg = deps.env.msg;
203
+ if (deps.floorEnv) o.floorEnv = deps.floorEnv;
204
+ return o;
205
+ }
206
+
207
+ function stackLimits(constants: Constants): StackLimits {
208
+ return {
209
+ quiverSlotSize: constants.quiverSlotSize,
210
+ thrownQuiverMult: constants.thrownQuiverMult,
211
+ };
212
+ }
213
+
214
+ /* ------------------------------------------------------------------ *
215
+ * obj-gear.c inventory verbs.
216
+ * ------------------------------------------------------------------ */
217
+
218
+ /**
219
+ * inven_takeoff (obj-gear.c L1033): de-equip an object back into the pack.
220
+ * Returns whether the handle was equipped. (Upstream does not re-combine;
221
+ * the caller runs combine_pack, which the port's pack model does lazily on
222
+ * the next inven_carry.)
223
+ */
224
+ export function invenTakeoff(state: GameState, handle: number): boolean {
225
+ const player = state.actor.player;
226
+ const slot = player.equipment.indexOf(handle);
227
+ if (slot < 0) return false;
228
+ player.equipment[slot] = 0;
229
+ state.gear.pack.push(handle);
230
+ state.updateBonuses?.(); /* PU_BONUS */
231
+ return true;
232
+ }
233
+
234
+ /**
235
+ * Seams for the combine_pack / pack_overflow tail of inven_wield and
236
+ * do_cmd_takeoff. All optional; an absent msg simply drops the messages, as
237
+ * everywhere else in this module.
238
+ */
239
+ export interface PackOverflowOpts {
240
+ /** msg / msgt sink for the wield line and the overflow messages. */
241
+ msg?: (text: string) => void;
242
+ /** calc_inventory inputs used by combine_pack's tail (quiver re-derive). */
243
+ calcInv?: CalcInventoryOpts;
244
+ /** drop_near seams (isTrap / onDrop / onBreak) for the overflowed item. */
245
+ floorEnv?: FloorEnv;
246
+ }
247
+
248
+ /** The subset of z_info pack_overflow and its calc_inventory refresh read. */
249
+ type PackOverflowConstants = Pick<
250
+ Constants,
251
+ "packSize" | "quiverSize" | "quiverSlotSize" | "thrownQuiverMult"
252
+ >;
253
+
254
+ /** calcInv with the shared msg sink filled in when the caller left it out. */
255
+ function overflowCalcInv(opts: PackOverflowOpts): CalcInventoryOpts {
256
+ const base: CalcInventoryOpts = { ...opts.calcInv };
257
+ if (!base.msg && opts.msg) base.msg = opts.msg;
258
+ return base;
259
+ }
260
+
261
+ /**
262
+ * pack_overflow (obj-gear.c L1345-1390): when the pack holds more than
263
+ * pack_size slots, shed one item onto the floor. `handle` is upstream's `obj`
264
+ * argument - the item to shed, normally whatever displaced into the pack; 0 is
265
+ * upstream's NULL, meaning "shed the last item of the inventory listing".
266
+ * Returns the object that left the pack, or null when nothing overflowed.
267
+ *
268
+ * The C's three messages and their order are reproduced exactly: the warning,
269
+ * then "You drop X." with X described BEFORE the excise, then the drop, then
270
+ * "You no longer have X."
271
+ *
272
+ * For the NULL case, upkeep->inven[] is represented by the derived,
273
+ * earlier_object-sorted gear.inven view (player-calcs.c:1191-1222); gear.pack
274
+ * remains the raw master-gear order and must not select the victim.
275
+ */
276
+ export function packOverflow(
277
+ state: GameState,
278
+ handle: number,
279
+ constants: PackOverflowConstants,
280
+ opts: PackOverflowOpts = {},
281
+ ): GameObject | null {
282
+ /* packSlotsUsed's public Constants type includes unrelated z_info fields;
283
+ * only the PackOverflowConstants subset is read on this path. */
284
+ if (!packIsOverfull(state.gear, constants as Constants)) return null;
285
+
286
+ /* Disturbing (L1353). */
287
+ disturb(state);
288
+
289
+ /* Warning (L1356). */
290
+ opts.msg?.("Your pack overflows!");
291
+
292
+ /* Drop the last inventory item unless requested otherwise (L1359-1366). */
293
+ const victim =
294
+ handle !== 0
295
+ ? handle
296
+ : (state.gear.inven?.[state.gear.inven.length - 1] ?? 0);
297
+ const obj = gearGet(state.gear, victim);
298
+ /* Upstream asserts obj != NULL here (L1369). The port can reach a stale
299
+ * handle where upstream reads freed memory: combine_pack (which runs just
300
+ * before every caller's pack_overflow) may have absorbed the displaced item
301
+ * into an earlier stack, which upstream frees while still holding the
302
+ * pointer. Absorbing shrinks the pack, so an overfull pack with a stale
303
+ * handle is a corner the C does not define; the port sheds nothing. */
304
+ if (!obj) return null;
305
+
306
+ /* Describe (L1372-1374) BEFORE the excise, so the count is the pre-drop one. */
307
+ const name = describeObject(state, obj);
308
+
309
+ /* Message (L1377). */
310
+ opts.msg?.(`You drop ${name}.`);
311
+
312
+ /* Excise the object and drop it (carefully) near the player (L1379-1380).
313
+ * gear_excise_object re-runs calc_inventory (obj-gear.c L497). */
314
+ const { obj: dropped } = gearObjectForUse(
315
+ state.gear,
316
+ state.actor.player,
317
+ victim,
318
+ obj.number,
319
+ );
320
+ calcInventory(state.gear, constants as Constants, overflowCalcInv(opts));
321
+ /* drop_near(cave, &obj, 0, player->grid, false, true): prefer_pile. */
322
+ dropNear(state, dropped, 0, state.actor.grid, false, true, opts.floorEnv ?? {});
323
+
324
+ /* Describe (L1383). */
325
+ opts.msg?.(`You no longer have ${name}.`);
326
+
327
+ state.updateBonuses?.(); /* gear_excise_object's PU_BONUS (obj-gear.c L500). */
328
+ return dropped;
329
+ }
330
+
331
+ /**
332
+ * inven_wield (obj-gear.c L931-1017): wield a pack object into `intoSlot`,
333
+ * taking off whatever occupies it first, then combine_pack + pack_overflow so
334
+ * a displaced item that does not fit is dropped (L1009-1010). Returns the
335
+ * slot, or -1 when the object cannot be worn. Read
336
+ * `state.actor.player.equipment[slot]` for the handle actually worn - a wield
337
+ * out of a stack equips a FRESH split, not `handle` (L947-968).
338
+ *
339
+ * The slot is an ARGUMENT upstream (`inven_wield(struct object *obj, int slot)`,
340
+ * obj-gear.c:931) and is NOT recomputed inside: do_cmd_wield picks it, and for a
341
+ * ring the player picks it (cmd-obj.c:298-311). Re-deriving it with wield_slot
342
+ * here is what made the third ring always land in the same hand. `intoSlot` is
343
+ * optional only so the pre-existing worldless callers keep the wield_slot
344
+ * default; every in-play caller passes the slot it decided.
345
+ *
346
+ * The MSG_WIELD line and the OF_STICKY warning (L986-1008) live here rather
347
+ * than in the command handler because upstream emits them BEFORE combine_pack,
348
+ * while do_cmd_wield's "You were wielding ..." line comes AFTER it (cmd-obj.c
349
+ * L346-352) and so reads the post-combine gear label.
350
+ */
351
+ export function invenWield(
352
+ state: GameState,
353
+ handle: number,
354
+ constants: Constants,
355
+ opts: PackOverflowOpts = {},
356
+ intoSlot?: number,
357
+ ): number {
358
+ const player = state.actor.player;
359
+ const obj = gearGet(state.gear, handle);
360
+ if (!obj) return -1;
361
+
362
+ const slot =
363
+ intoSlot !== undefined
364
+ ? intoSlot
365
+ : wieldSlot(player.body, obj.tval, player.equipment);
366
+ if (slot < 0 || slot >= player.body.count) return -1;
367
+
368
+ /* `old` (L933): the slot's current occupant. Upstream just overwrites
369
+ * body.slots[slot].obj, which leaves `old` in p->gear as a pack item; the
370
+ * port's equipment[]/pack split needs the move spelled out. */
371
+ const oldHandle = player.equipment[slot] ?? 0;
372
+ if (oldHandle !== 0) invenTakeoff(state, oldHandle);
373
+
374
+ const worn = wieldObject(
375
+ state.gear,
376
+ player,
377
+ handle,
378
+ state.runeEnv,
379
+ "inven_wield",
380
+ slot,
381
+ );
382
+ if (worn < 0) return worn;
383
+ const wornHandle = player.equipment[worn] ?? 0;
384
+ const wornObj = gearGet(state.gear, wornHandle);
385
+
386
+ /* Where is the item now, and the message (L991-1006). */
387
+ if (wornObj && opts.msg) {
388
+ const verb = tvalIsMeleeWeapon(wornObj.tval)
389
+ ? "You are wielding"
390
+ : tvalIsLauncher(wornObj.tval)
391
+ ? "You are shooting with"
392
+ : tvalIsLight(wornObj.tval)
393
+ ? "Your light source is"
394
+ : "You are wearing";
395
+ opts.msg(
396
+ `${verb} ${describeObject(state, wornObj)} (${gearLabelFor(state, wornHandle)}).`,
397
+ );
398
+ /* Sticky flag gets a special mention (L1008). */
399
+ if (wornObj.flags.has(OF.STICKY)) {
400
+ opts.msg("Oops! It feels deathly cold!");
401
+ }
402
+ }
403
+
404
+ /* See if we have to overflow the pack (L1009-1010). */
405
+ combinePack(state.gear, constants, overflowCalcInv(opts));
406
+ packOverflow(state, oldHandle, constants, opts);
407
+
408
+ /* object_learn_on_wield's player_learn_rune tail-calls
409
+ * update_player_object_knowledge (obj-knowledge.c L1373): the wield learns the
410
+ * obvious runes, and the sweep is what turns that into cross-object awareness
411
+ * (e.g. a worn Ring of Strength becoming flavour-aware - KN-03). */
412
+ updatePlayerObjectKnowledge(state);
413
+ state.updateBonuses?.(); /* PU_BONUS */
414
+ return worn;
415
+ }
416
+
417
+ /**
418
+ * do_cmd_wield's SECOND cmd_get_item (cmd-obj.c:295-311), the one the port never
419
+ * had: wearing a third ring must ask WHICH HAND to free.
420
+ *
421
+ * if (tval_is_ring(obj)) {
422
+ * if (cmd_get_item(cmd, "replace", &equip_obj,
423
+ * "Replace which ring? ",
424
+ * "Error in do_cmd_wield(), please report.",
425
+ * tval_is_ring, USE_EQUIP) != CMD_OK)
426
+ * return;
427
+ * slot = equipped_item_slot(player->body, equip_obj);
428
+ * }
429
+ *
430
+ * Reached only when the ring slot wield_slot picked is already OCCUPIED: for
431
+ * rings wield_slot is `slot_by_type(p, EQUIP_RING, false)` (obj-gear.c:357),
432
+ * which prefers an EMPTY slot and only falls back to the first ring slot when
433
+ * every one is full (obj-gear.c:71-93). So one free hand means no question, and
434
+ * two full hands means the question - which is exactly "wearing a THIRD ring
435
+ * asks".
436
+ *
437
+ * Returns null when nothing is owed, otherwise the verbatim prompt/error and the
438
+ * equipment slots USE_EQUIP + tval_is_ring offers. The core command path cannot
439
+ * block on UI, so the shell asks and passes the answer back as the command's
440
+ * "slot" argument - the same division itemAllowPrompt (game/inscription-confirm.ts)
441
+ * and walkTerrainPrompt (game/player-turn.ts) already use, and the same caching
442
+ * upstream's own cmd_get_item does when the argument is already set.
443
+ */
444
+ export interface WieldRingChoice {
445
+ /** cmd_get_item's prompt (cmd-obj.c:300), verbatim. */
446
+ readonly prompt: string;
447
+ /** cmd_get_item's error (cmd-obj.c:301), verbatim. */
448
+ readonly error: string;
449
+ /** The offered equipment slots, in body order. */
450
+ readonly slots: readonly number[];
451
+ }
452
+
453
+ /** cmd_get_item's prompt for the second wield question (cmd-obj.c:300). */
454
+ export const WIELD_REPLACE_RING_PROMPT = "Replace which ring? ";
455
+ /** cmd_get_item's error for it (cmd-obj.c:301) - a "please report" paranoia line. */
456
+ export const WIELD_REPLACE_RING_ERROR = "Error in do_cmd_wield(), please report.";
457
+
458
+ export function wieldRingChoice(
459
+ state: GameState,
460
+ obj: GameObject,
461
+ ): WieldRingChoice | null {
462
+ const player = state.actor.player;
463
+ if (!tvalIsRing(obj.tval)) return null;
464
+ const slot = wieldSlot(player.body, obj.tval, player.equipment);
465
+ if (slot < 0 || slot >= player.body.count) return null;
466
+ /* "If the slot is open, wield and be done" (cmd-obj.c:291-295) - asked BEFORE
467
+ * the ring branch, so an empty hand skips the question entirely. */
468
+ if ((player.equipment[slot] ?? 0) === 0) return null;
469
+ const slots: number[] = [];
470
+ for (let i = 0; i < player.body.count; i++) {
471
+ const handle = player.equipment[i] ?? 0;
472
+ if (!handle) continue;
473
+ const worn = gearGet(state.gear, handle);
474
+ /* USE_EQUIP filtered by tval_is_ring: the worn rings, both of them. */
475
+ if (worn && tvalIsRing(worn.tval)) slots.push(i);
476
+ }
477
+ if (slots.length === 0) return null;
478
+ return {
479
+ prompt: WIELD_REPLACE_RING_PROMPT,
480
+ error: WIELD_REPLACE_RING_ERROR,
481
+ slots,
482
+ };
483
+ }
484
+
485
+ /**
486
+ * do_cmd_wield's "!t" checks for taking off (cmd-obj.c:321-330):
487
+ *
488
+ * n = check_for_inscrip(equip_obj, "!t");
489
+ * while (n--) {
490
+ * object_desc(o_name, sizeof(o_name), equip_obj,
491
+ * ODESC_PREFIX | ODESC_FULL, player);
492
+ * if (!get_check(format("Really take off %s? ", o_name))) return;
493
+ * }
494
+ *
495
+ * Note it is "!t" ALONE - unlike get_item_allow there is no "!*" term here, and
496
+ * that asymmetry is upstream's. Returns the prompt and how many times it must be
497
+ * answered (one refusal aborts the command), or null when nothing is owed.
498
+ *
499
+ * `slot` is the DESTINATION slot, i.e. after the ring question has been answered:
500
+ * the item that gets taken off is whatever occupies the hand the player chose.
501
+ */
502
+ export interface WieldTakeoffConfirm {
503
+ /** get_check's prompt (cmd-obj.c:329), verbatim. */
504
+ readonly prompt: string;
505
+ /** Occurrences of "!t" on the displaced item; each one asks again. */
506
+ readonly count: number;
507
+ }
508
+
509
+ export function wieldTakeoffConfirm(
510
+ state: GameState,
511
+ slot: number,
512
+ ): WieldTakeoffConfirm | null {
513
+ const handle = state.actor.player.equipment[slot] ?? 0;
514
+ if (!handle) return null;
515
+ const displaced = gearGet(state.gear, handle);
516
+ if (!displaced) return null;
517
+ const count = checkForInscrip(displaced, "!t");
518
+ if (!count) return null;
519
+ const name = describeObject(state, displaced, ODESC.PREFIX | ODESC.FULL);
520
+ return { prompt: `Really take off ${name}? `, count };
521
+ }
522
+
523
+ /** What inven_drop's caller needs to build its two messages. */
524
+ export interface InvenDropResult {
525
+ /** The object that went to the floor. */
526
+ dropped: GameObject;
527
+ /** gear_object_for_use's *none_left: the whole stack went. */
528
+ noneLeft: boolean;
529
+ /** Whether the object had to be taken off first (obj-gear.c L1108-1111). */
530
+ wasEquipped: boolean;
531
+ }
532
+
533
+ /**
534
+ * inven_drop (obj-gear.c L1078): drop amt of a carried object near the
535
+ * player (equipment is taken off first). Returns null when nothing was dropped.
536
+ *
537
+ * The two messages stay at the caller (the command handler), but `noneLeft` and
538
+ * `wasEquipped` come back with the result because upstream's own message block
539
+ * (L1120-1165) branches on both.
540
+ */
541
+ export function invenDrop(
542
+ state: GameState,
543
+ handle: number,
544
+ amt: number,
545
+ floorEnv: FloorEnv = {},
546
+ ): InvenDropResult | null {
547
+ if (amt <= 0) return null;
548
+ const obj = gearGet(state.gear, handle);
549
+ if (!obj) return null;
550
+ if (amt > obj.number) amt = obj.number;
551
+
552
+ /* Take off equipment, don't combine. */
553
+ let wasEquipped = false;
554
+ if (state.actor.player.equipment.includes(handle)) {
555
+ wasEquipped = true;
556
+ invenTakeoff(state, handle);
557
+ }
558
+
559
+ const { obj: dropped, noneLeft } = gearObjectForUse(
560
+ state.gear,
561
+ state.actor.player,
562
+ handle,
563
+ amt,
564
+ );
565
+ dropNear(state, dropped, 0, state.actor.grid, false, true, floorEnv);
566
+ return { dropped, noneLeft, wasEquipped };
567
+ }
568
+
569
+ /* ------------------------------------------------------------------ *
570
+ * cmd-obj.c use machinery.
571
+ * ------------------------------------------------------------------ */
572
+
573
+ /**
574
+ * gear_to_label (obj-gear.c:443) with the equipment arm supplied: this module's
575
+ * callers can be holding an equipped object (takeoff, the "you cannot remove"
576
+ * refusals), so the body slots are passed in. One implementation, game/gear.ts.
577
+ */
578
+ function gearLabelFor(state: GameState, handle: number): string {
579
+ return gearToLabel(state.gear, handle, state.actor.player.equipment);
580
+ }
581
+
582
+ /**
583
+ * gear_to_label for an object object_pack_total handed back by reference, which
584
+ * the port must map to a handle first.
585
+ */
586
+ function packLabelFor(state: GameState, obj: GameObject): string {
587
+ for (const handle of state.gear.pack) {
588
+ if (state.gear.store.get(handle) === obj) return gearLabelFor(state, handle);
589
+ }
590
+ return "";
591
+ }
592
+
593
+ /**
594
+ * equip_describe (obj-gear.c L320-332): how a slot's occupant is being used,
595
+ * for the "You cannot remove the X you are Y." refusal. A heavy weapon or
596
+ * launcher reads as the slot's heavy wording instead, and a named slot (rings,
597
+ * hands) interpolates the slot name into its describe template.
598
+ */
599
+ function equipDescribe(state: GameState, slot: number): string {
600
+ const p = state.actor.player;
601
+ const bodySlot = p.body.slots[slot];
602
+ if (!bodySlot) return "";
603
+ const entry = EQUIP_SLOT_ENTRIES.find((e) => e.name === bodySlot.type);
604
+ if (!entry) return "";
605
+ const ps = state.playerState;
606
+ if (bodySlot.type === "WEAPON" && (ps?.heavyWield || ps?.heavyShoot)) {
607
+ return entry.heavyDescribe;
608
+ }
609
+ if (entry.named) return entry.describe.replace("%s", bodySlot.name);
610
+ return entry.describe;
611
+ }
612
+
613
+ /** beam_chance (cmd-obj.c L112). */
614
+ export function beamChance(tval: number): number {
615
+ if (tvalIsWand(tval)) return 20;
616
+ if (tvalIsRod(tval)) return 10;
617
+ return 0;
618
+ }
619
+
620
+ /** The difficulty level of a used object (artifact / activation / kind). */
621
+ function objectLevel(obj: GameObject): number {
622
+ if (obj.artifact) return obj.artifact.level;
623
+ if (obj.activation) return obj.activation.level;
624
+ return obj.kind.level;
625
+ }
626
+
627
+ /**
628
+ * get_use_device_chance (obj-util.c L930): failure rate out of 1000 for
629
+ * using a device, from the device skill against the item level.
630
+ */
631
+ export function getUseDeviceChance(state: GameState, obj: GameObject): number {
632
+ const skill = state.actor.combat.skills[SKILL.DEVICE] ?? 0;
633
+ const lev = objectLevel(obj);
634
+ const x = 2 * (skill - lev) + 1;
635
+ let fail = -370 * x;
636
+ fail = Math.trunc(fail / (5 + Math.abs(x)));
637
+ fail += 380;
638
+ return fail;
639
+ }
640
+
641
+ /**
642
+ * check_devices (cmd-obj.c L59): can the device be used this turn?
643
+ * Returns 1 usable, 0 failed-but-retryable, -1 unusable.
644
+ */
645
+ export function checkDevices(
646
+ state: GameState,
647
+ obj: GameObject,
648
+ env: ObjCmdEnv = {},
649
+ ): number {
650
+ let action: string;
651
+ let what: string | null = null;
652
+ if (tvalIsRod(obj.tval)) {
653
+ action = "zap the rod";
654
+ } else if (tvalIsWand(obj.tval)) {
655
+ action = "use the wand";
656
+ what = "wand";
657
+ } else if (tvalIsStaff(obj.tval)) {
658
+ action = "use the staff";
659
+ what = "staff";
660
+ } else {
661
+ action = "activate it";
662
+ }
663
+
664
+ /* Notice empty staffs / wands. */
665
+ if (what && obj.pval <= 0) {
666
+ env.msg?.(`The ${what} has no charges left.`);
667
+ return -1;
668
+ }
669
+
670
+ const fail = getUseDeviceChance(state, obj);
671
+ if (state.rng.randint1(1000) < fail) {
672
+ env.msg?.(`You failed to ${action} properly.`);
673
+ return fail < 1001 ? 0 : -1;
674
+ }
675
+ return 1;
676
+ }
677
+
678
+ /**
679
+ * number_charging (obj-util.c L1020): how many rods in a stack are still
680
+ * charging, from the average charge time.
681
+ */
682
+ export function numberCharging(obj: GameObject): number {
683
+ const t = obj.time;
684
+ const chargeTime = t.base + Math.trunc((t.dice * (t.sides + 1)) / 2);
685
+ if (chargeTime <= 0) return 0;
686
+ if (obj.timeout <= 0) return 0;
687
+ const num = Math.trunc((obj.timeout + chargeTime - 1) / chargeTime);
688
+ return Math.min(num, obj.number);
689
+ }
690
+
691
+ /** obj_can_zap (obj-util.c L709): any rod in the stack not charging? */
692
+ export function objCanZap(obj: GameObject): boolean {
693
+ return tvalCanHaveTimeout(obj.tval) && numberCharging(obj) < obj.number;
694
+ }
695
+
696
+ /**
697
+ * object_effect (obj-util.c L886): the effect an object actually runs - the
698
+ * ACTIVATION's effect when it has one, otherwise the kind/instance effect.
699
+ * The activation wins outright, so an artifact or ego activation replaces the
700
+ * base kind's effect rather than being appended to it.
701
+ */
702
+ export function objectEffect(obj: GameObject): EffectRecordJson[] | null {
703
+ if (obj.activation) return obj.activation.effect;
704
+ return obj.effect;
705
+ }
706
+
707
+ /**
708
+ * obj_is_activatable (obj-util.c L721): a WEARABLE object that has an effect.
709
+ * Note both halves: a non-wearable with an effect (a potion) is not
710
+ * "activatable", and a wearable's effect may come from its kind rather than an
711
+ * activation - the rings of Flames / Acid / Ice / Lightning / Open Wounds /
712
+ * Digging carry an `effect:` in object.txt and no `act:`, so they ARE
713
+ * activatable even though obj.activation is null.
714
+ */
715
+ export function objIsActivatable(obj: GameObject): boolean {
716
+ if (!tvalIsWearable(obj.tval)) return false;
717
+ const effect = objectEffect(obj);
718
+ return effect !== null && effect.length > 0;
719
+ }
720
+
721
+ /**
722
+ * obj_can_activate (obj-util.c L730): activatable AND not recharging. This is
723
+ * NOT obj_can_zap: do_cmd_activate guards on this (cmd-obj.c L886) while
724
+ * do_cmd_zap_rod guards on obj_can_zap (L851), and obj_can_zap requires a ROD
725
+ * tval, so reusing it for activation refuses every artifact and ring.
726
+ */
727
+ export function objCanActivate(obj: GameObject): boolean {
728
+ return objIsActivatable(obj) && obj.timeout === 0;
729
+ }
730
+
731
+ /**
732
+ * obj_can_wear (obj-util.c L810): wield_slot(obj) >= 0 - do_cmd_wield's
733
+ * item_tester (cmd-obj.c L284). Over the shipped body plan this is exactly
734
+ * tvalIsWearable (wield_slot handles the same tval set), but it is the slot
735
+ * lookup that decides, so a body plan without some slot type changes the
736
+ * answer. NOTE the upstream wart it inherits: slot_by_type's fallback for
737
+ * "type not on this body" is `false`, i.e. slot 0 - so wield_slot returns 0,
738
+ * not -1, and obj_can_wear stays true for a slot the body does not have.
739
+ */
740
+ export function objCanWear(state: GameState, obj: GameObject): boolean {
741
+ const player = state.actor.player;
742
+ return wieldSlot(player.body, obj.tval, player.equipment) >= 0;
743
+ }
744
+
745
+ /* ------------------------------------------------------------------ *
746
+ * Inscriptions and refuelling (cmd-obj.c, obj-util.c, obj-ignore.c).
747
+ * ------------------------------------------------------------------ */
748
+
749
+ /** obj_has_inscrip (obj-util.c L841): does this object carry an inscription? */
750
+ export function objHasInscrip(obj: GameObject): boolean {
751
+ return !!obj.note;
752
+ }
753
+
754
+ /** The equipped light source, or null when no LIGHT slot is worn. */
755
+ function equippedLight(state: GameState): GameObject | null {
756
+ const lightSlot = state.actor.player.body.slots.findIndex(
757
+ (s) => s.type === "LIGHT",
758
+ );
759
+ return lightSlot >= 0 ? state.runeEnv.slotObject(lightSlot) : null;
760
+ }
761
+
762
+ /**
763
+ * obj_can_refill (obj-util.c L743): is `obj` a valid fuel source for the
764
+ * currently equipped light - a flask of oil, or another TAKES_FUEL lantern
765
+ * still holding fuel (its timeout)?
766
+ */
767
+ export function objCanRefill(state: GameState, obj: GameObject): boolean {
768
+ if (obj.flags.has(OF.NO_FUEL)) return false;
769
+
770
+ const light = equippedLight(state);
771
+ if (light && light.flags.has(OF.TAKES_FUEL)) {
772
+ if (tvalIsFuel(obj.tval)) return true;
773
+ if (
774
+ tvalIsLight(obj.tval) &&
775
+ obj.flags.has(OF.TAKES_FUEL) &&
776
+ obj.timeout > 0
777
+ ) {
778
+ return true;
779
+ }
780
+ }
781
+ return false;
782
+ }
783
+
784
+ /**
785
+ * refill_lamp (cmd-obj.c L1008): add `obj`'s fuel (its timeout for a donor
786
+ * lantern, or pval for a flask of oil) to `lamp`, capping at
787
+ * constants.fuelLamp. A stacked donor lantern splits off one empty unit
788
+ * (carried back if there's room, else dropped); a lone donor is emptied in
789
+ * place. A flask is consumed entirely (one unit, from the pack or floor).
790
+ * Recomputes the light radius (PU_TORCH) afterward.
791
+ */
792
+ export function refillLamp(
793
+ state: GameState,
794
+ lamp: GameObject,
795
+ obj: GameObject,
796
+ opts: { handle?: number; fromFloor?: boolean },
797
+ deps: ObjCmdDeps,
798
+ ): void {
799
+ const env = deps.env ?? {};
800
+
801
+ lamp.timeout += obj.timeout ? obj.timeout : obj.pval;
802
+ env.msg?.("You fuel your lamp.");
803
+ if (lamp.timeout >= deps.constants.fuelLamp) {
804
+ lamp.timeout = deps.constants.fuelLamp;
805
+ env.msg?.("Your lamp is full.");
806
+ }
807
+
808
+ if (obj.flags.has(OF.TAKES_FUEL)) {
809
+ /* Refilled from a lantern: empty it (splitting one off if stacked). */
810
+ if (obj.number > 1) {
811
+ const used = objectSplit(obj, 1);
812
+ used.timeout = 0;
813
+ const carried = opts.handle !== undefined && !opts.fromFloor;
814
+ if (carried && invenCarryNum(state.gear, used, deps.constants) > 0) {
815
+ invenCarry(state.gear, used, stackLimits(deps.constants));
816
+ } else {
817
+ /* Overflow / floor donor: drop_near's own breakage roll (randint0)
818
+ * fires here exactly as upstream, even at chance=0 (a real, faithful
819
+ * RNG draw on this rare branch only - see obj-cmd.test.ts). */
820
+ dropNear(state, used, 0, state.actor.grid, false, true, deps.floorEnv);
821
+ }
822
+ } else {
823
+ obj.timeout = 0;
824
+ }
825
+ } else {
826
+ /* Refilled from a flask: consume one unit entirely. */
827
+ if (opts.fromFloor) {
828
+ floorObjectForUse(state, obj, 1);
829
+ } else if (opts.handle !== undefined) {
830
+ gearObjectForUse(state.gear, state.actor.player, opts.handle, 1);
831
+ }
832
+ }
833
+
834
+ /* PU_TORCH: force the light-radius recalc so a just-refuelled, previously
835
+ * spent (timeout 0) lantern stops reading as dark (player/calcs.ts). */
836
+ state.updateBonuses?.();
837
+ }
838
+
839
+ /** Is `obj` presently carried (pack or equipped), by identity? */
840
+ function objIsCarried(state: GameState, obj: GameObject): boolean {
841
+ for (const [handle, stored] of state.gear.store) {
842
+ if (stored !== obj) continue;
843
+ return (
844
+ state.gear.pack.includes(handle) ||
845
+ state.actor.player.equipment.includes(handle)
846
+ );
847
+ }
848
+ return false;
849
+ }
850
+
851
+ /**
852
+ * rune_add_autoinscription (obj-ignore.c:172-186): make or extend the rune-`i`
853
+ * autoinscription on one object. No note, or the note already present as a
854
+ * substring, is a no-op; otherwise the note is APPENDED to any existing
855
+ * inscription.
856
+ *
857
+ * Two upstream details preserved:
858
+ * - `strstr(obj->note, rune_note(i))` (obj-ignore.c:176) is a substring test,
859
+ * not equality, so a note already contained in a longer inscription is not
860
+ * appended twice - and an EMPTY note is never appended at all, because
861
+ * strstr(x, "") is non-NULL;
862
+ * - `char current_note[80]` with my_strcpy/my_strcat (obj-ignore.c:174-182)
863
+ * truncates the combined inscription to 79 characters.
864
+ */
865
+ function runeAddAutoinscription(
866
+ state: GameState,
867
+ obj: GameObject,
868
+ i: number,
869
+ ): void {
870
+ const note = state.runeNotes?.get(i);
871
+ if (note === undefined) return; // !rune_note(i)
872
+ const current = obj.note ?? "";
873
+ if (obj.note && current.includes(note)) return;
874
+ obj.note = (current + note).slice(0, 79) || null;
875
+ }
876
+
877
+ /**
878
+ * runes_autoinscribe (obj-ignore.c:217-225): put every applicable rune
879
+ * autoinscription on `obj` - each rune the object carries and the player knows,
880
+ * walked in rune-list order.
881
+ */
882
+ function runesAutoinscribe(state: GameState, obj: GameObject): void {
883
+ if (!state.runeNotes) return;
884
+ const env = state.runeEnv;
885
+ const p = state.actor.player;
886
+ const runes = buildRuneList(env);
887
+ for (let i = 0; i < runes.length; i++) {
888
+ const rune = runes[i]!;
889
+ if (objectHasRune(env, obj, rune) && playerKnowsRune(p, rune)) {
890
+ runeAddAutoinscription(state, obj, i);
891
+ }
892
+ }
893
+ }
894
+
895
+ /**
896
+ * rune_autoinscribe (obj-ignore.c:193-212): the player just set a note on rune
897
+ * `i` - stamp it on every object carrying that rune, on the floor beneath them
898
+ * first and then through the gear. Gated on player_knows_rune (:198).
899
+ * Upstream caller: ui-knowledge.c rune_xtra_act (:2275).
900
+ */
901
+ export function runeAutoinscribe(state: GameState, i: number): void {
902
+ if (!state.runeNotes) return;
903
+ const env = state.runeEnv;
904
+ const rune = buildRuneList(env)[i];
905
+ if (!rune) return;
906
+ if (!playerKnowsRune(state.actor.player, rune)) return;
907
+ for (const obj of floorPile(state, state.actor.grid)) {
908
+ if (objectHasRune(env, obj, rune)) runeAddAutoinscription(state, obj, i);
909
+ }
910
+ for (const obj of state.gear.store.values()) {
911
+ if (objectHasRune(env, obj, rune)) runeAddAutoinscription(state, obj, i);
912
+ }
913
+ }
914
+
915
+ /**
916
+ * apply_autoinscription (obj-ignore.c L242): put the kind's registered
917
+ * autoinscription on `obj`, unless it is already inscribed, not carried, or
918
+ * ignored. Also clears a stale unaware autoinscription once the kind
919
+ * becomes aware. Returns 1 when an inscription was applied, 0 otherwise
920
+ * (upstream's int return, kept for parity though callers ignore it).
921
+ */
922
+ export function applyAutoinscription(
923
+ state: GameState,
924
+ obj: GameObject,
925
+ deps: ObjCmdDeps,
926
+ ): number {
927
+ const aware = deps.flavor ? deps.flavor.isAware(obj.kind) : true;
928
+ const note = deps.autoNote?.(obj.kind, aware) ?? null;
929
+
930
+ /* Remove an unaware inscription once aware, if it no longer applies. */
931
+ if (aware && obj.note) {
932
+ const unawareNote = deps.autoNote?.(obj.kind, false) ?? null;
933
+ if (unawareNote && obj.note === unawareNote && (!note || obj.note !== note)) {
934
+ obj.note = null;
935
+ }
936
+ }
937
+
938
+ /* "Make rune autoinscription go first, for now" (obj-ignore.c:258-259):
939
+ * BEFORE the no-kind-note early return, so the rune notes land even when the
940
+ * kind itself has no autoinscription. */
941
+ runesAutoinscribe(state, obj);
942
+
943
+ if (!note) return 0;
944
+ if (obj.note) return 0;
945
+ if (!objIsCarried(state, obj)) return 0;
946
+ if (ignoreItemOk(obj, state.ignore, aware)) return 0;
947
+
948
+ obj.note = note.length > 0 ? note : null;
949
+ deps.env?.msg?.(`You autoinscribe ${describeObject(state, obj)}.`);
950
+ return 1;
951
+ }
952
+
953
+ /**
954
+ * autoinscribe_ground (obj-ignore.c:340-348): apply_autoinscription over the
955
+ * whole pile beneath the player.
956
+ */
957
+ export function autoinscribeGround(state: GameState, deps: ObjCmdDeps): void {
958
+ for (const obj of floorPile(state, state.actor.grid)) {
959
+ applyAutoinscription(state, obj, deps);
960
+ }
961
+ }
962
+
963
+ /**
964
+ * autoinscribe_pack (obj-ignore.c:352-359): apply_autoinscription over p->gear
965
+ * (which upstream is one list covering pack AND equipment, as here).
966
+ */
967
+ export function autoinscribePack(state: GameState, deps: ObjCmdDeps): void {
968
+ for (const obj of state.gear.store.values()) {
969
+ applyAutoinscription(state, obj, deps);
970
+ }
971
+ }
972
+
973
+ /** randcalc(obj->time, 0, RANDOMISE): the recharge time roll. */
974
+ function rollRechargeTime(state: GameState, obj: GameObject): number {
975
+ return state.rng.randcalc(obj.time, 0, "randomise");
976
+ }
977
+
978
+ /** Build an object's effect chain from its raw records (per use). */
979
+ export function buildObjectEffectChain(
980
+ records: readonly EffectRecordJson[],
981
+ state: GameState,
982
+ inject: EffectBuilderInjections = {},
983
+ ): Effect | null {
984
+ const builder = new EffectBuilder({
985
+ ...inject,
986
+ baseValues: {
987
+ PLAYER_LEVEL: () => state.actor.player.lev,
988
+ MAX_SIGHT: () => state.z.maxSight,
989
+ DUNGEON_LEVEL: () => state.chunk.depth,
990
+ /* effects.c L308-315: WEAPON_DAMAGE is a live damroll(dd, ds)+to_d
991
+ * expression base. Keeping this provider lazy preserves the C draw
992
+ * position: the roll occurs exactly when the expression is evaluated. */
993
+ WEAPON_DAMAGE: () => {
994
+ const weapon = state.actor.weapon;
995
+ return weapon
996
+ ? state.rng.damroll(weapon.dd, weapon.ds) + weapon.toD
997
+ : 0;
998
+ },
999
+ ...inject.baseValues,
1000
+ },
1001
+ });
1002
+ for (const e of records) {
1003
+ let spec = e.eff;
1004
+ const hasType = e.type !== undefined && e.type !== "";
1005
+ if (hasType || e.radius || e.other) spec += ":" + (e.type ?? "");
1006
+ if (e.radius || e.other) spec += ":" + (e.radius ?? 0);
1007
+ if (e.other) spec += ":" + e.other;
1008
+ builder.effect(spec);
1009
+ if (e.dice) builder.dice(e.dice);
1010
+ for (const x of e.expr ?? []) builder.expr(x.name, x.base, x.expr);
1011
+ }
1012
+ return builder.build();
1013
+ }
1014
+
1015
+ /** effect_aim over an effect name, from the generated base table. */
1016
+ const EFFECT_NEEDS_AIM = new Map<string, boolean>(
1017
+ EFFECT_ENTRIES.map((e) => [e.name, e.aim]),
1018
+ );
1019
+
1020
+ /** effect_aim over a raw record chain: any effect marked as aimed. */
1021
+ export function effectRecordsNeedAim(
1022
+ records: readonly EffectRecordJson[],
1023
+ ): boolean {
1024
+ /* RANDOM/SELECT children share the parent walk; the flat list covers them. */
1025
+ return records.some(
1026
+ (e) => EFFECT_NEEDS_AIM.get(e.eff.split(":")[0] ?? e.eff) === true,
1027
+ );
1028
+ }
1029
+
1030
+ /** obj_needs_aim (obj-util.c L899), on the raw effect records. */
1031
+ export function objNeedsAim(
1032
+ obj: GameObject,
1033
+ deps: Pick<ObjCmdDeps, "flavor">,
1034
+ ): boolean {
1035
+ const aimed = effectRecordsNeedAim(obj.effect ?? []);
1036
+ const aware = deps.flavor ? deps.flavor.isAware(obj.kind) : true;
1037
+ return aimed || tvalIsWand(obj.tval) || (tvalIsRod(obj.tval) && !aware);
1038
+ }
1039
+
1040
+ /** player_is_shapechanged (player-util.c L1065): null shape = normal. */
1041
+ export function playerIsShapechanged(state: GameState): boolean {
1042
+ return state.actor.player.shape !== null;
1043
+ }
1044
+
1045
+ /**
1046
+ * player_resume_normal_shape (player-util.c L1048): back to normal form,
1047
+ * killing the vampire attack and refreshing the bonuses.
1048
+ */
1049
+ export function playerResumeNormalShape(
1050
+ state: GameState,
1051
+ env: Pick<ObjCmdEnv, "msg"> = {},
1052
+ ): void {
1053
+ state.actor.player.shape = null;
1054
+ env.msg?.("You resume your usual shape.");
1055
+ /* Kill vampire attack. */
1056
+ state.actor.player.timed[TMD.ATT_VAMP] = 0;
1057
+ state.updateBonuses?.();
1058
+ }
1059
+
1060
+ /**
1061
+ * player_get_resume_normal_shape (player-util.c L1022): a shapechanged
1062
+ * player must return to normal form before acting with hands/voice. The
1063
+ * y/n/r prompt is the confirm seam (headless default: change back and
1064
+ * proceed).
1065
+ */
1066
+ export function playerGetResumeNormalShape(
1067
+ state: GameState,
1068
+ env: Pick<ObjCmdEnv, "msg" | "confirm"> = {},
1069
+ ): boolean {
1070
+ if (!playerIsShapechanged(state)) return true;
1071
+ env.msg?.(
1072
+ `You cannot do this while in ${state.actor.player.shape!.name} form.`,
1073
+ );
1074
+ if (env.confirm?.("Change back and continue? ") ?? true) {
1075
+ playerResumeNormalShape(state, env);
1076
+ return true;
1077
+ }
1078
+ return false;
1079
+ }
1080
+
1081
+ /**
1082
+ * player_can_read (player-util.c L1166): scrolls and spellbooks need working
1083
+ * eyes, light, a clear head and intact memory. Checked in this exact order, and
1084
+ * each refusal prints its own message and spends no turn. do_cmd_read_scroll
1085
+ * (cmd-obj.c L748) calls this with show_msg true BEFORE cmd_get_item, so a blind
1086
+ * player with no scrolls at all still hears "You can't see anything." rather
1087
+ * than the "You have no scrolls to read." rejection.
1088
+ *
1089
+ * Reached from two places upstream and both are this same predicate:
1090
+ * do_cmd_read_scroll (cmd-obj.c:748) and the 'r' key's prereq
1091
+ * player_can_read_prereq (player-util.c:1264, wired at ui-game.c:131) - which
1092
+ * is why the show_msg parameter exists rather than the messages being inlined.
1093
+ * The prereq's TMD_COMMAND bypass is NOT reproduced here: while TMD_COMMAND runs
1094
+ * the turn loop redirects every command to do_cmd_mon_command before the handler
1095
+ * is reached (game/player-turn.ts L708-714), which is exactly what the bypass
1096
+ * exists to permit.
1097
+ *
1098
+ * Note the order and strings differ from player_can_cast (L1087), which folds
1099
+ * blind and no_light into one "You cannot see!" and has no amnesia check.
1100
+ */
1101
+ export function playerCanRead(
1102
+ state: GameState,
1103
+ env: Pick<ObjCmdEnv, "msg"> = {},
1104
+ showMsg = true,
1105
+ ): boolean {
1106
+ const p = state.actor.player;
1107
+ if ((p.timed[TMD.BLIND] ?? 0) > 0) {
1108
+ if (showMsg) env.msg?.("You can't see anything.");
1109
+ return false;
1110
+ }
1111
+ if (noLight(state)) {
1112
+ if (showMsg) env.msg?.("You have no light to read by.");
1113
+ return false;
1114
+ }
1115
+ if ((p.timed[TMD.CONFUSED] ?? 0) > 0) {
1116
+ if (showMsg) env.msg?.("You are too confused to read!");
1117
+ return false;
1118
+ }
1119
+ if ((p.timed[TMD.AMNESIA] ?? 0) > 0) {
1120
+ if (showMsg) env.msg?.("You can't remember how to read!");
1121
+ return false;
1122
+ }
1123
+ return true;
1124
+ }
1125
+
1126
+ /**
1127
+ * no_light (cave-view.c L913): the player's own grid is not currently seen.
1128
+ * Shared by playerCanRead here and player_can_cast (game/spell-cmd.ts).
1129
+ *
1130
+ * SQUARE_SEEN is maintained by update_view, which this port drives through the
1131
+ * `state.updateFov` host seam. This used to open with a guard - `if
1132
+ * (state.updateFov === undefined) return false` - described in its own comment as
1133
+ * "a seam guard, not a rule of the game", because a host that installed no seam
1134
+ * left SEEN clear on every grid and would have found casting and reading
1135
+ * permanently impossible.
1136
+ *
1137
+ * The guard is gone because its premise is: core installs a DEFAULT updateFov in
1138
+ * wireGame, so startGame and loadGame both maintain SEEN with no host cooperation
1139
+ * and there is no configuration left in which the flag carries no information.
1140
+ * Keeping it would mean a host that deleted the seam silently got non-upstream
1141
+ * spell rules - the guard's whole purpose was to make an unplayable state
1142
+ * playable, and that state cannot happen now.
1143
+ *
1144
+ * So this is upstream verbatim for every caller.
1145
+ */
1146
+ export function noLight(state: GameState): boolean {
1147
+ return !squareIsSeen(state.chunk, state.actor.grid);
1148
+ }
1149
+
1150
+ /**
1151
+ * player_confuse_dir (player-util.c): confusion randomises the direction
1152
+ * 75% of the time (always for "no direction").
1153
+ */
1154
+ export function playerConfuseDir(
1155
+ state: GameState,
1156
+ dir: number,
1157
+ too = false,
1158
+ ): number {
1159
+ if ((state.actor.player.timed[TMD.CONFUSED] ?? 0) > 0) {
1160
+ let newDir = dir;
1161
+ if (dir === 5 || state.rng.randint0(100) < 75) {
1162
+ /* Random direction. */
1163
+ newDir = DDD[state.rng.randint0(8)] as number;
1164
+ }
1165
+ /* player-util.c L1363-1366: running is refused after the same confusion
1166
+ * roll, with the C's message. */
1167
+ if (too) {
1168
+ state.msg?.("You are too confused.");
1169
+ return dir;
1170
+ }
1171
+ /* player-util.c L1369: emit "You are confused." and report the change only
1172
+ * when the direction actually changed (a same-direction roll is silent).
1173
+ * The RNG draw above is unconditional whenever confused, matching C. */
1174
+ if (newDir !== dir) {
1175
+ state.msg?.("You are confused.");
1176
+ return newDir;
1177
+ }
1178
+ }
1179
+ return dir;
1180
+ }
1181
+
1182
+ /**
1183
+ * object_learn_on_use (obj-knowledge.c L1925-1936), XP slice (gap 4.3): the
1184
+ * flavor-awareness half is handled by the caller (objectFlavorAware); this adds
1185
+ * the experience reward player_exp_gain(p, (lev + p->lev/2)/p->lev) with lev the
1186
+ * used object's KIND level. Integer division throughout, exactly as upstream.
1187
+ */
1188
+ function objectLearnOnUseXp(
1189
+ state: GameState,
1190
+ obj: GameObject,
1191
+ deps: ObjCmdDeps,
1192
+ ): void {
1193
+ if (!deps.expGain) return;
1194
+ const p = state.actor.player;
1195
+ const lev = obj.kind.level;
1196
+ deps.expGain(Math.trunc((lev + Math.trunc(p.lev / 2)) / p.lev));
1197
+ }
1198
+
1199
+ /** The result of useAux, for the command wrappers. */
1200
+ export interface UseResult {
1201
+ /** The effect ran (or the device fizzled) and the turn is spent. */
1202
+ turnSpent: boolean;
1203
+ /** effect_do reported the effect as used. */
1204
+ used: boolean;
1205
+ }
1206
+
1207
+ /**
1208
+ * activation_message (cmd-obj.c L127): print an activated object's custom
1209
+ * message. The message text comes from the activation (activation.txt msg:),
1210
+ * but an artifact carrying its own alt_msg (artifact.txt msg:) overrides it -
1211
+ * and the override only fires when the activation itself defines a message. The
1212
+ * text is run through print_custom_message's {name}/{kind}/{s}/{is} object-tag
1213
+ * substitution (obj-util.c L1118): {name} -> object_desc(PREFIX|BASE), {kind} ->
1214
+ * object_kind_name (easy_know), {s}/{is} keyed on the stack count.
1215
+ */
1216
+ function activationMessage(
1217
+ state: GameState,
1218
+ obj: GameObject,
1219
+ env: ObjCmdEnv,
1220
+ ): void {
1221
+ if (!obj.activation?.message) return;
1222
+ const message =
1223
+ obj.artifact && obj.artifact.altMsg
1224
+ ? obj.artifact.altMsg
1225
+ : obj.activation.message;
1226
+ const text = substituteTimedMessage(message, {
1227
+ name: describeObject(state, obj, ODESC.PREFIX | ODESC.BASE),
1228
+ kind: objDescNameFormat(obj.kind.name, null, false),
1229
+ number: obj.number,
1230
+ });
1231
+ env.msg?.(text);
1232
+ }
1233
+
1234
+ /**
1235
+ * use_aux (cmd-obj.c L407): use an object the right way - aim resolution,
1236
+ * the device check, charge/timeout/single-use deduction with restore on an
1237
+ * unused effect, and the effect chain run with a player source.
1238
+ */
1239
+ export function useAux(
1240
+ state: GameState,
1241
+ obj: GameObject,
1242
+ use: UseKind,
1243
+ deps: ObjCmdDeps,
1244
+ opts: {
1245
+ fromFloor?: boolean;
1246
+ handle?: number;
1247
+ dir?: number;
1248
+ /** cmd_get_arg_item "tgtitem": the shell's pre-resolved item-effect pick. */
1249
+ tgtItem?: ItemTargetRef;
1250
+ /** cmd_get_arg_choice "tgtcurse": the REMOVE_CURSE curse index. */
1251
+ tgtCurse?: number;
1252
+ } = {},
1253
+ ): UseResult {
1254
+ const env = deps.env ?? {};
1255
+ const fromFloor = opts.fromFloor ?? false;
1256
+ const wasAware = deps.flavor ? deps.flavor.isAware(obj.kind) : true;
1257
+
1258
+ /* Determine whether we know an item needs to be aimed. */
1259
+ const knownAim =
1260
+ tvalIsWand(obj.tval) || tvalIsRod(obj.tval) || wasAware;
1261
+
1262
+ let dir = 5;
1263
+ if (objNeedsAim(obj, deps)) {
1264
+ if (!knownAim) {
1265
+ /* Unknown things with no obvious aim get a random direction. */
1266
+ dir = DDD[state.rng.randint0(8)] as number;
1267
+ } else {
1268
+ dir = opts.dir ?? env.chooseDir?.() ?? 5;
1269
+ }
1270
+ /* Confusion wrecks aim. */
1271
+ dir = playerConfuseDir(state, dir);
1272
+ }
1273
+
1274
+ /* Check for use if necessary. */
1275
+ let canUse = 1;
1276
+ if (use === USE.CHARGE || use === USE.TIMEOUT) {
1277
+ canUse = checkDevices(state, obj, env);
1278
+ }
1279
+
1280
+ if (canUse > 0) {
1281
+ const beam = beamChance(obj.tval);
1282
+ const level = objectLevel(obj);
1283
+ const boost = Math.max(
1284
+ Math.trunc(((state.actor.combat.skills[SKILL.DEVICE] ?? 0) - level) / 2),
1285
+ 0,
1286
+ );
1287
+
1288
+ /* Sound / message (cmd-obj.c L493-504): an activation prints the generic
1289
+ * "You activate it." then its own activation_message; otherwise the kind's
1290
+ * effect_msg (always) or vis_msg (only when not blind). */
1291
+ if (obj.activation) {
1292
+ env.msg?.("You activate it.");
1293
+ activationMessage(state, obj, env);
1294
+ } else if (obj.kind.effectMsg) {
1295
+ env.msg?.(obj.kind.effectMsg);
1296
+ } else if (
1297
+ obj.kind.visMsg &&
1298
+ (state.actor.player.timed[TMD.BLIND] ?? 0) === 0
1299
+ ) {
1300
+ env.msg?.(obj.kind.visMsg);
1301
+ }
1302
+
1303
+ /* Capture the describe inputs before the effect can rearrange the pack
1304
+ * (cmd-obj.c L462-483): the gear label of the item and the count to report.
1305
+ * For anything but a CHARGE / TIMEOUT device that count is the AGGREGATE
1306
+ * across every like stack (object_pack_total, L474), and the label becomes
1307
+ * the first such stack's - "You have 3 Scrolls of Light (1st d)." A charge
1308
+ * or recharging notice is stack-specific, so those keep their own number
1309
+ * (L480). A floor object has no pack context at all (L465). */
1310
+ let describeLabel =
1311
+ opts.handle !== undefined ? gearLabelFor(state, opts.handle) : "";
1312
+ let startNumber = obj.number;
1313
+ let firstRemainder: GameObject | null = null;
1314
+ if (!fromFloor && use !== USE.CHARGE && use !== USE.TIMEOUT) {
1315
+ const view = packTotalView(state.gear);
1316
+ const agg = objectPackTotal(view, obj, false);
1317
+ startNumber = agg.total;
1318
+ firstRemainder = agg.first;
1319
+ /* One stack only: nothing to call "1st" (L477-479). */
1320
+ if (firstRemainder && firstRemainder.number === agg.total) {
1321
+ firstRemainder = null;
1322
+ }
1323
+ }
1324
+
1325
+ /* Tentatively deduct floor-object usage before the effect (the effect
1326
+ * could leave the object inaccessible). */
1327
+ let deductBefore = false;
1328
+ let charges = 0;
1329
+ let singleUsed: GameObject | null = null;
1330
+ if (fromFloor) {
1331
+ if (use === USE.SINGLE) {
1332
+ deductBefore = true;
1333
+ singleUsed = floorObjectForUse(state, obj, 1).usable;
1334
+ } else if (use === USE.CHARGE) {
1335
+ deductBefore = true;
1336
+ charges = obj.pval;
1337
+ obj.pval--;
1338
+ } else if (use === USE.TIMEOUT) {
1339
+ deductBefore = true;
1340
+ charges = obj.timeout;
1341
+ obj.timeout += rollRechargeTime(state, obj);
1342
+ }
1343
+ }
1344
+
1345
+ /* Do effect. use_aux takes its chain from object_effect(obj) (cmd-obj.c
1346
+ * L410 `struct effect *effect = object_effect(obj);`), so an activation's
1347
+ * effect REPLACES the base kind's - reading obj.effect here ran a
1348
+ * Narthanc-style artifact's (empty) kind effect instead of its activation. */
1349
+ const chain = buildObjectEffectChain(objectEffect(obj) ?? [], state, deps.inject);
1350
+ const ctx = attachGameEnv(buildEffectContext(state, deps.envDeps), {
1351
+ state,
1352
+ cast: deps.cast,
1353
+ /* take_hit consequences for effects that rebound damage onto the player
1354
+ * (EF_BANISH), so such a death records died_from too. */
1355
+ ...(deps.envDeps.takeHitHooks
1356
+ ? { takeHitHooks: deps.envDeps.takeHitHooks }
1357
+ : {}),
1358
+ /* target_get inside the handlers: a DIR_TARGET cast re-reads the
1359
+ * live target per handler, as upstream. */
1360
+ get aimed() {
1361
+ return targetOkay(state) ? targetGet(state) : undefined;
1362
+ },
1363
+ ...(deps.teleport ? { teleport: deps.teleport } : {}),
1364
+ ...(deps.general ? { general: deps.general } : {}),
1365
+ ...(deps.item ? { item: deps.item } : {}),
1366
+ ...(deps.summon ? { summon: deps.summon } : {}),
1367
+ /* Flavor knowledge for item-identifying effects (EF_IDENTIFY fires the
1368
+ * object_flavor_aware side effect of player_know_object). */
1369
+ ...(deps.flavor ? { flavor: deps.flavor } : {}),
1370
+ ...(deps.flavorDeps ? { flavorDeps: deps.flavorDeps } : {}),
1371
+ });
1372
+ const ident = { value: false };
1373
+ targetFix(state);
1374
+ /* cmd_get_item "tgtitem" / "tgtcurse" presets: the item-choosing effects
1375
+ * read state.itemTarget / state.curseTarget through the getItem seam. Set
1376
+ * before the run, cleared after (cmd_set_arg_item's scope). */
1377
+ state.itemRequest = null;
1378
+ state.itemTarget = opts.tgtItem ?? null;
1379
+ state.curseTarget = opts.tgtCurse ?? null;
1380
+ const used = deps.registry.effectDo(chain, ctx, {
1381
+ origin: sourcePlayer(),
1382
+ obj,
1383
+ ident,
1384
+ aware: wasAware,
1385
+ dir,
1386
+ beam,
1387
+ boost,
1388
+ });
1389
+ state.itemTarget = null;
1390
+ state.curseTarget = null;
1391
+ targetRelease(state);
1392
+
1393
+ if (!used && deductBefore) {
1394
+ /* Restore the tentative deduction. */
1395
+ if (use === USE.SINGLE && singleUsed) {
1396
+ dropNear(state, singleUsed, 0, state.actor.grid, false, true, deps.floorEnv);
1397
+ } else if (use === USE.CHARGE) {
1398
+ obj.pval = charges;
1399
+ } else if (use === USE.TIMEOUT) {
1400
+ obj.timeout = charges;
1401
+ }
1402
+ }
1403
+
1404
+ /* Increase knowledge. */
1405
+ if (deps.flavor) {
1406
+ const knowObj = singleUsed ?? obj;
1407
+ const flavorDeps = deps.flavorDeps ?? NOOP_FLAVOR_AWARE_DEPS;
1408
+ if (use === USE.SINGLE) {
1409
+ /* Single use items are automatically learned. */
1410
+ if (!wasAware) {
1411
+ deps.flavor.objectFlavorAware(knowObj.kind, flavorDeps);
1412
+ objectLearnOnUseXp(state, knowObj, deps);
1413
+ }
1414
+ } else if (!wasAware && ident.value) {
1415
+ deps.flavor.objectFlavorAware(knowObj.kind, flavorDeps);
1416
+ objectLearnOnUseXp(state, knowObj, deps);
1417
+ } else {
1418
+ deps.flavor.setTried(knowObj.kind);
1419
+ }
1420
+ }
1421
+
1422
+ /* Use up, deduct charge, or apply timeout if it wasn't done before. */
1423
+ if (used && !deductBefore) {
1424
+ if (use === USE.CHARGE) {
1425
+ obj.pval--;
1426
+ } else if (use === USE.TIMEOUT) {
1427
+ obj.timeout += rollRechargeTime(state, obj);
1428
+ } else if (use === USE.SINGLE && opts.handle !== undefined) {
1429
+ gearObjectForUse(state.gear, state.actor.player, opts.handle, 1);
1430
+ }
1431
+ }
1432
+
1433
+ /* Describe what's left (cmd-obj.c L633-706). Single-use items are always
1434
+ * described; a charge/timeout device only when it was just identified (and
1435
+ * never for wearables, which take the update-knowledge branch). Otherwise a
1436
+ * used charge device reports its remaining charges. */
1437
+ const describe =
1438
+ use === USE.SINGLE ||
1439
+ (!tvalIsWearable(obj.tval) && !wasAware && ident.value);
1440
+ if (describe) {
1441
+ const shown = startNumber - (used && use === USE.SINGLE ? 1 : 0);
1442
+ const name = describeObject(
1443
+ state,
1444
+ obj,
1445
+ ODESC.PREFIX | ODESC.FULL | ODESC.ALTNUM,
1446
+ shown,
1447
+ );
1448
+ if (fromFloor) env.msg?.(`You see ${name}.`);
1449
+ else {
1450
+ /* cmd-obj.c L692-698: the "1st" label is looked up AFTER the effect,
1451
+ * because the pack may have moved underneath it. */
1452
+ if (firstRemainder) describeLabel = packLabelFor(state, firstRemainder);
1453
+ env.msg?.(
1454
+ firstRemainder
1455
+ ? `You have ${name} (1st ${describeLabel}).`
1456
+ : `You have ${name} (${describeLabel}).`,
1457
+ );
1458
+ }
1459
+ } else if (
1460
+ used &&
1461
+ use === USE.CHARGE &&
1462
+ tvalCanHaveCharges(obj.tval) &&
1463
+ (deps.flavor ? deps.flavor.isAware(obj.kind) : true)
1464
+ ) {
1465
+ /* inven_item_charges / floor_item_charges (obj-gear.c L790-799). */
1466
+ env.msg?.(
1467
+ `You have ${obj.pval} charge${obj.pval === 1 ? "" : "s"} remaining.`,
1468
+ );
1469
+ }
1470
+ }
1471
+
1472
+ /* Use the turn (even a device fizzle spends it, as upstream). */
1473
+ return { turnSpent: true, used: canUse > 0 };
1474
+ }
1475
+
1476
+ /* ------------------------------------------------------------------ *
1477
+ * Command actions.
1478
+ * ------------------------------------------------------------------ */
1479
+
1480
+ /** Resolve a command's object: a gear handle or a floor pile position. */
1481
+ function commandObject(
1482
+ state: GameState,
1483
+ cmd: PlayerCommand,
1484
+ ): { obj: GameObject; handle?: number; fromFloor: boolean } | null {
1485
+ const args = cmd.args ?? {};
1486
+ const handle = typeof args["handle"] === "number" ? args["handle"] : null;
1487
+ if (handle !== null) {
1488
+ const obj = gearGet(state.gear, handle);
1489
+ return obj ? { obj, handle, fromFloor: false } : null;
1490
+ }
1491
+ const floorIdx = typeof args["floor"] === "number" ? args["floor"] : null;
1492
+ if (floorIdx !== null) {
1493
+ const pile = floorPile(state, state.actor.grid);
1494
+ const obj = pile[floorIdx];
1495
+ return obj ? { obj, fromFloor: true } : null;
1496
+ }
1497
+ return null;
1498
+ }
1499
+
1500
+ function commandDir(cmd: PlayerCommand): number | undefined {
1501
+ const d = cmd.args?.["dir"] ?? cmd.dir;
1502
+ return typeof d === "number" ? d : undefined;
1503
+ }
1504
+
1505
+ /** cmd_get_arg_item "tgtitem": the shell's pre-resolved item-effect target. */
1506
+ function commandTargetItem(cmd: PlayerCommand): ItemTargetRef | undefined {
1507
+ const t = cmd.args?.["tgtitem"];
1508
+ if (t && typeof t === "object") {
1509
+ if (typeof (t as { handle?: unknown }).handle === "number") {
1510
+ return { handle: (t as { handle: number }).handle };
1511
+ }
1512
+ if (typeof (t as { floor?: unknown }).floor === "number") {
1513
+ return { floor: (t as { floor: number }).floor };
1514
+ }
1515
+ }
1516
+ return undefined;
1517
+ }
1518
+
1519
+ /**
1520
+ * A use command over a tval filter and use kind.
1521
+ *
1522
+ * `ready` is the command's OWN pre-use guard, because upstream does NOT share
1523
+ * one: do_cmd_zap_rod tests obj_can_zap and says "That rod is still charging."
1524
+ * (cmd-obj.c L851-855) while do_cmd_activate tests obj_can_activate and says
1525
+ * "That item is still charging." (L886-890). obj_can_zap requires a rod tval,
1526
+ * so the two are not interchangeable - using obj_can_zap for activation
1527
+ * refused EVERY artifact and ring.
1528
+ */
1529
+ function useCommand(
1530
+ deps: ObjCmdDeps,
1531
+ filter: (obj: GameObject) => boolean,
1532
+ use: UseKind,
1533
+ ready?: { ok: (obj: GameObject) => boolean; msg: string },
1534
+ ) {
1535
+ return (state: GameState, cmd: PlayerCommand): number => {
1536
+ const found = commandObject(state, cmd);
1537
+ if (!found || !filter(found.obj)) return 0;
1538
+ if (ready && !ready.ok(found.obj)) {
1539
+ deps.env?.msg?.(ready.msg);
1540
+ return 0;
1541
+ }
1542
+ if (use === USE.CHARGE && found.obj.pval <= 0) {
1543
+ /* do_cmd_aim_wand (cmd-obj.c L817-820) / do_cmd_use_staff (L783-786)
1544
+ * pre-check obj_has_charges and return BEFORE use_aux, so an empty
1545
+ * device costs no turn (checkDevices' "... no charges left." path in
1546
+ * useAux would otherwise burn one). */
1547
+ deps.env?.msg?.(
1548
+ tvalIsWand(found.obj.tval)
1549
+ ? "That wand has no charges."
1550
+ : "That staff has no charges.",
1551
+ );
1552
+ return 0;
1553
+ }
1554
+ const dir = commandDir(cmd);
1555
+ const tgtItem = commandTargetItem(cmd);
1556
+ const tgtCurse = cmd.args?.["tgtcurse"];
1557
+ const result = useAux(state, found.obj, use, deps, {
1558
+ fromFloor: found.fromFloor,
1559
+ ...(found.handle !== undefined ? { handle: found.handle } : {}),
1560
+ ...(dir !== undefined ? { dir } : {}),
1561
+ ...(tgtItem ? { tgtItem } : {}),
1562
+ ...(typeof tgtCurse === "number" ? { tgtCurse } : {}),
1563
+ });
1564
+ /* "Autoinscribe if we are guaranteed to still have any"
1565
+ * (cmd-obj.c:717-719): `if (!none_left && !from_floor)`. none_left is
1566
+ * gear_object_for_use consuming the last of the stack, which here is the
1567
+ * gear handle no longer resolving. */
1568
+ if (!found.fromFloor && found.handle !== undefined) {
1569
+ const left = state.gear.store.get(found.handle);
1570
+ if (left) applyAutoinscription(state, left, deps);
1571
+ }
1572
+ return result.turnSpent ? state.z.moveEnergy : 0;
1573
+ };
1574
+ }
1575
+
1576
+ /**
1577
+ * Register the object commands (wield / takeoff / drop / eat / quaff /
1578
+ * read / use-staff / aim-wand / zap-rod / activate) on the registry.
1579
+ */
1580
+ export function installObjCommands(
1581
+ registry: ActionRegistry,
1582
+ deps: ObjCmdDeps,
1583
+ ): void {
1584
+ /* player_get_resume_normal_shape gates the hands/voice commands
1585
+ * (cmd-obj.c: takeoff/wield/drop, scroll/staff/wand/rod/activate AND
1586
+ * quaff - do_cmd_quaff_potion opens with the resume gate at cmd-obj.c L923).
1587
+ * do_cmd_eat_food (L899) is the one use command with NO gate, so eating stays
1588
+ * possible in any shape. */
1589
+ const gated = (
1590
+ fn: (state: GameState, cmd: PlayerCommand) => number,
1591
+ ): ((state: GameState, cmd: PlayerCommand) => number) => {
1592
+ return (state, cmd) =>
1593
+ playerGetResumeNormalShape(state, deps.env ?? {}) ? fn(state, cmd) : 0;
1594
+ };
1595
+
1596
+ /* do_cmd_wield (cmd-obj.c:265-353): wear/wield from the pack or the floor.
1597
+ *
1598
+ * The statement order below is upstream's, and it is load-bearing:
1599
+ * 1. cmd_get_item for the item
1600
+ * 2. slot = wield_slot(obj); equip_obj = slot_object(player, slot)
1601
+ * 3. slot empty -> inven_wield and return, asking nothing
1602
+ * 4. ring -> the SECOND cmd_get_item, "Replace which ring? ", which
1603
+ * overwrites slot with the hand the player chose
1604
+ * 5. obj_can_takeoff refusal (sticky)
1605
+ * 6. the "!t" get_check loop
1606
+ * 7. act wording from the SLOT's type, then inven_wield(obj, slot)
1607
+ * 8. the MSG_WIELD "You were ..." line
1608
+ *
1609
+ * The floor pickup belongs at step 7, not step 1: upstream carries a floor
1610
+ * item INSIDE inven_wield (`floor_object_for_use` + `inven_carry`,
1611
+ * obj-gear.c:973-976), which is past every abort above. Doing it first - as the
1612
+ * port did - leaked the item into the pack when the player escaped the ring or
1613
+ * "!t" prompt, or when the slot turned out to be stuck. */
1614
+ registry.register("wield", gated((state, cmd) => {
1615
+ const found = commandObject(state, cmd);
1616
+ if (!found) return 0;
1617
+ const player = state.actor.player;
1618
+
1619
+ /* Step 2. */
1620
+ let targetSlot = wieldSlot(player.body, found.obj.tval, player.equipment);
1621
+ if (targetSlot < 0 || targetSlot >= player.body.count) return 0;
1622
+
1623
+ /* Step 4: the ring question. The core command path cannot block on UI, so
1624
+ * the shell resolves wieldRingChoice() and passes the chosen slot back as
1625
+ * the "slot" argument, which is precisely what upstream's cmd_get_item does
1626
+ * when the command already carries the answer (cmd-obj.c:298). With no
1627
+ * answer supplied (a headless driver, the borg) the pre-prompt wield_slot
1628
+ * stands, the same "unprompted terminal takes the default" rule the confirm
1629
+ * seam uses. */
1630
+ if ((player.equipment[targetSlot] ?? 0) !== 0 && tvalIsRing(found.obj.tval)) {
1631
+ const chosen = cmd.args?.["slot"];
1632
+ if (typeof chosen === "number") {
1633
+ const chosenHandle = player.equipment[chosen] ?? 0;
1634
+ const chosenObj = chosenHandle ? gearGet(state.gear, chosenHandle) : null;
1635
+ /* Only a worn RING is a legal answer (the tval_is_ring filter on
1636
+ * USE_EQUIP); anything else is not something cmd_get_item could have
1637
+ * returned, so it is ignored rather than obeyed. */
1638
+ if (chosenObj && tvalIsRing(chosenObj.tval)) targetSlot = chosen;
1639
+ }
1640
+ }
1641
+
1642
+ const displacedHandle = player.equipment[targetSlot] ?? 0;
1643
+ const displaced = displacedHandle ? gearGet(state.gear, displacedHandle) : null;
1644
+
1645
+ /* Step 5: prevent wielding into a stickied slot (cmd-obj.c:313-320).
1646
+ * obj_can_takeoff is !OF_STICKY (obj-util.c L794), and the refusal names the
1647
+ * stuck item by its base description plus equip_describe's wording for the
1648
+ * slot. Draws no RNG and spends no energy: the command aborts before
1649
+ * inven_wield. */
1650
+ if (displaced && displaced.flags.has(OF.STICKY)) {
1651
+ deps.env?.msg?.(
1652
+ `You cannot remove the ${describeObject(state, displaced, ODESC.BASE)} ` +
1653
+ `you are ${equipDescribe(state, targetSlot)}.`,
1654
+ );
1655
+ return 0;
1656
+ }
1657
+
1658
+ /* Step 6: the "!t" checks for taking off (cmd-obj.c:321-330). One refusal
1659
+ * ends the command with no turn spent. The shell has normally answered these
1660
+ * already (it must, to ask before the item moves); the seam is here too so a
1661
+ * driver that queues the command directly still honours the inscription. */
1662
+ const ask = wieldTakeoffConfirm(state, targetSlot);
1663
+ if (ask) {
1664
+ for (let i = 0; i < ask.count; i++) {
1665
+ if (!(deps.env?.confirm?.(ask.prompt) ?? true)) return 0;
1666
+ }
1667
+ }
1668
+
1669
+ /* Step 7: the wording comes from the SLOT's type upstream
1670
+ * (slot_type_is(player, slot, EQUIP_WEAPON), cmd-obj.c:337-347), not from the
1671
+ * displaced item's tval - and it is read BEFORE inven_wield empties the
1672
+ * slot. */
1673
+ const slotType = player.body.slots[targetSlot]?.type ?? "";
1674
+ const act =
1675
+ slotType === "WEAPON"
1676
+ ? "You were wielding"
1677
+ : slotType === "BOW" || slotType === "LIGHT"
1678
+ ? "You were holding"
1679
+ : "You were wearing";
1680
+
1681
+ /* inven_wield's own floor path (obj-gear.c:973-976), reached only now. */
1682
+ let handle = found.handle;
1683
+ if (found.fromFloor) {
1684
+ const { usable } = floorObjectForUse(state, found.obj, 1);
1685
+ handle = invenCarry(state.gear, usable, stackLimits(deps.constants));
1686
+ }
1687
+ if (handle === undefined) return 0;
1688
+
1689
+ /* invenWield owns the MSG_WIELD line, the sticky warning, combine_pack and
1690
+ * pack_overflow, in that upstream order (obj-gear.c L986-1010). The slot is
1691
+ * passed, not re-derived: for a ring it is the hand the player chose. */
1692
+ const slot = invenWield(
1693
+ state,
1694
+ handle,
1695
+ deps.constants,
1696
+ packOpts(state, deps),
1697
+ targetSlot,
1698
+ );
1699
+ if (slot < 0) return 0;
1700
+ /* Step 8. The displaced item is now back in the pack - or on the floor if
1701
+ * pack_overflow shed it, in which case gear_to_label gives '\0' and the
1702
+ * port's gearLabelFor gives "" (cmd-obj.c L337-354, read AFTER
1703
+ * combine_pack, so the label is the post-combine one). */
1704
+ if (displaced) {
1705
+ const dname = describeObject(state, displaced);
1706
+ const dlabel = gearLabelFor(state, displacedHandle);
1707
+ deps.env?.msg?.(`${act} ${dname} (${dlabel}).`);
1708
+ }
1709
+ return state.z.moveEnergy;
1710
+ }));
1711
+
1712
+ /* do_cmd_takeoff: energy is half a turn. */
1713
+ registry.register("takeoff", gated((state, cmd) => {
1714
+ const args = cmd.args ?? {};
1715
+ const handle = typeof args["handle"] === "number" ? args["handle"] : null;
1716
+ if (handle === null) return 0;
1717
+ const obj = gearGet(state.gear, handle);
1718
+ const tval = obj?.tval ?? 0;
1719
+ /* obj_can_takeoff (obj-util.c:794-796) is the takeoff-item filter in
1720
+ * do_cmd_takeoff (cmd-obj.c:251): a sticky item is not selectable, so this
1721
+ * direct command entry likewise aborts silently and spends no energy. */
1722
+ if (obj?.flags.has(OF.STICKY)) return 0;
1723
+ if (!invenTakeoff(state, handle)) return 0;
1724
+ /* inven_takeoff sets PU_INVEN and calls update_stuff ITSELF (obj-gear.c
1725
+ * L1058-1062), one line before its message - because that message names the
1726
+ * item at its NEW pack letter, and the item has only just stopped being
1727
+ * equipment. The port deferred this to the caller's combine_pack below, which
1728
+ * is after the message: the letter was read off a listing the item was not
1729
+ * in yet. (The old comment here cited L1060 for combine_pack; L1060 is
1730
+ * update_stuff inside inven_takeoff.) */
1731
+ calcInventory(state.gear, deps.constants, calcInvOpts(state, deps));
1732
+ /* inven_takeoff's message (obj-gear.c L1046-1065): the slot wording, then
1733
+ * the item named at its new pack label. */
1734
+ if (obj) {
1735
+ const act = tvalIsMeleeWeapon(tval)
1736
+ ? "You were wielding"
1737
+ : tvalIsLauncher(tval) || tvalIsLight(tval)
1738
+ ? "You were holding"
1739
+ : "You were wearing";
1740
+ deps.env?.msg?.(
1741
+ `${act} ${describeObject(state, obj)} (${gearLabelFor(state, handle)}).`,
1742
+ );
1743
+ }
1744
+ /* combine_pack (which re-runs calc_inventory for inven_takeoff's PU_INVEN,
1745
+ * obj-gear.c L1060) then pack_overflow on the item just taken off
1746
+ * (cmd-obj.c L255-257). */
1747
+ const opts = packOpts(state, deps);
1748
+ combinePack(state.gear, deps.constants, calcInvOpts(state, deps));
1749
+ packOverflow(state, handle, deps.constants, opts);
1750
+ return Math.trunc(state.z.moveEnergy / 2);
1751
+ }));
1752
+
1753
+ /* do_cmd_drop: energy is half a turn. */
1754
+ registry.register("drop", gated((state, cmd) => {
1755
+ const args = cmd.args ?? {};
1756
+ const handle = typeof args["handle"] === "number" ? args["handle"] : null;
1757
+ if (handle === null) return 0;
1758
+ const obj = gearGet(state.gear, handle);
1759
+ if (!obj) return 0;
1760
+ /* Cannot drop stickied equipment (cmd-obj.c L377-381). The C checks
1761
+ * object_is_equipped && !obj_can_takeoff and aborts before inven_drop, so no
1762
+ * energy is spent and no RNG is drawn. */
1763
+ if (
1764
+ state.actor.player.equipment.includes(handle) &&
1765
+ obj.flags.has(OF.STICKY)
1766
+ ) {
1767
+ deps.env?.msg?.("Hmmm, it seems to be stuck.");
1768
+ return 0;
1769
+ }
1770
+ const amt =
1771
+ typeof args["quantity"] === "number" ? args["quantity"] : obj.number;
1772
+ /* Label captured before the drop (inven_drop L1099). */
1773
+ const label = gearLabelFor(state, handle);
1774
+ const result = invenDrop(state, handle, amt, deps.floorEnv);
1775
+ if (!result) return 0;
1776
+ const { dropped, noneLeft, wasEquipped } = result;
1777
+ /* gear_object_for_use excises through gear_excise_object, which re-runs
1778
+ * calc_inventory (obj-gear.c L497) - BEFORE inven_drop's messages, not after
1779
+ * them. It matters for the "1st" letter below, which upstream looks up at
1780
+ * L1157 against the rebuilt listing; `label` above stays the pre-drop one
1781
+ * (L1099), exactly as upstream captures it. */
1782
+ calcInventory(state.gear, deps.constants, calcInvOpts(state, deps));
1783
+ /* inven_drop's messages (obj-gear.c L1120-1165): the drop, then what's
1784
+ * left. Dropping an equipped item omits the take-off line the port's
1785
+ * inven_takeoff does not emit - ledgered. */
1786
+ deps.env?.msg?.(`You drop ${describeObject(state, dropped)} (${label}).`);
1787
+ if (dropped.artifact) {
1788
+ deps.env?.msg?.(
1789
+ `You no longer have the ${describeObject(state, dropped, ODESC.FULL | ODESC.SINGULAR)} (${label}).`,
1790
+ );
1791
+ } else {
1792
+ /* The remaining count is the AGGREGATE over every like stack still in the
1793
+ * pack (object_pack_total, L1149), and the letter is the first such
1794
+ * stack's - so dropping 2 of 5 flasks split over slots c and f says
1795
+ * "You have 3 Flasks of oil (1st c)." Suppressed when the item was
1796
+ * equipped, or carries a stack-specific charge / recharging notice
1797
+ * (L1138-1141), in which case the aggregate would contradict it. */
1798
+ let total: number;
1799
+ let first: GameObject | null;
1800
+ let descTarget: GameObject;
1801
+ if (wasEquipped || packTotalSuppressed(obj)) {
1802
+ first = null;
1803
+ if (noneLeft) {
1804
+ total = 0;
1805
+ descTarget = dropped;
1806
+ } else {
1807
+ total = obj.number;
1808
+ descTarget = obj;
1809
+ }
1810
+ } else {
1811
+ const view = packTotalView(state.gear);
1812
+ ({ total, first } = objectPackTotal(view, obj, false));
1813
+ descTarget = total ? obj : dropped;
1814
+ }
1815
+ const name = describeObject(
1816
+ state,
1817
+ descTarget,
1818
+ ODESC.PREFIX | ODESC.FULL | ODESC.ALTNUM,
1819
+ total,
1820
+ );
1821
+ if (!first) {
1822
+ deps.env?.msg?.(`You have ${name} (${label}).`);
1823
+ } else {
1824
+ const firstLabel = packLabelFor(state, first);
1825
+ deps.env?.msg?.(
1826
+ total > first.number
1827
+ ? `You have ${name} (1st ${firstLabel}).`
1828
+ : `You have ${name} (${firstLabel}).`,
1829
+ );
1830
+ }
1831
+ }
1832
+ return Math.trunc(state.z.moveEnergy / 2);
1833
+ }));
1834
+
1835
+ registry.register(
1836
+ "eat",
1837
+ useCommand(deps, (o) => tvalIsEdible(o.tval), USE.SINGLE),
1838
+ );
1839
+ /* do_cmd_quaff_potion (cmd-obj.c L917-931): the resume-shape gate, THEN the
1840
+ * potion pick. Unlike do_cmd_eat_food it is gated. */
1841
+ registry.register(
1842
+ "quaff",
1843
+ gated(useCommand(deps, (o) => tvalIsPotion(o.tval), USE.SINGLE)),
1844
+ );
1845
+ /*
1846
+ * do_cmd_read_scroll (cmd-obj.c L740-758): resume shape, THEN
1847
+ * player_can_read(player, true) - blind / no light / confused / amnesia each
1848
+ * refuse with their own message and cost no turn - and only then the scroll
1849
+ * pick, so the read gate fires before "You have no scrolls to read.".
1850
+ *
1851
+ * Two lanes ported this independently and reached OPPOSITE orderings, because
1852
+ * upstream checks the same condition in two places and each lane found one:
1853
+ * this handler order, and `player_can_read_prereq` hung on the 'r' key entry
1854
+ * (ui-game.c:131), which ui-game.c:596 runs BEFORE the command is ever
1855
+ * pushed. Both are real. The prereq means a blind player pressing 'r' never
1856
+ * reaches the shape-resume prompt at all, while anything that pushes
1857
+ * CMD_READ_SCROLL without going through that key entry still takes
1858
+ * resume-then-read. Collapsing the pair into a single head-of-handler guard
1859
+ * reproduces the keyboard order but breaks the handler's own, so the guard
1860
+ * stays here in C order and the prereq belongs at the port's dispatch layer
1861
+ * next to the key binding - the same split playercan applied to cast/study.
1862
+ */
1863
+ registry.register(
1864
+ "read",
1865
+ gated((state, cmd) => {
1866
+ if (!playerCanRead(state, deps.env ?? {})) return 0;
1867
+ return useCommand(deps, (o) => tvalIsScroll(o.tval), USE.SINGLE)(
1868
+ state,
1869
+ cmd,
1870
+ );
1871
+ }),
1872
+ );
1873
+ registry.register(
1874
+ "use-staff",
1875
+ gated(useCommand(deps, (o) => tvalIsStaff(o.tval), USE.CHARGE)),
1876
+ );
1877
+ registry.register(
1878
+ "aim-wand",
1879
+ gated(useCommand(deps, (o) => tvalIsWand(o.tval), USE.CHARGE)),
1880
+ );
1881
+ /* do_cmd_zap_rod (cmd-obj.c L832): tval_is_rod filter, obj_can_zap guard. */
1882
+ registry.register(
1883
+ "zap-rod",
1884
+ gated(
1885
+ useCommand(deps, (o) => tvalIsRod(o.tval), USE.TIMEOUT, {
1886
+ ok: objCanZap,
1887
+ msg: "That rod is still charging.",
1888
+ }),
1889
+ ),
1890
+ );
1891
+ /* do_cmd_activate (cmd-obj.c L866): obj_is_activatable filter (a WEARABLE
1892
+ * with an object_effect - which includes the kind-effect rings that have no
1893
+ * activation), then the obj_can_activate guard. */
1894
+ registry.register(
1895
+ "activate",
1896
+ gated(
1897
+ useCommand(deps, objIsActivatable, USE.TIMEOUT, {
1898
+ ok: objCanActivate,
1899
+ msg: "That item is still charging.",
1900
+ }),
1901
+ ),
1902
+ );
1903
+
1904
+ /* do_cmd_inscribe (cmd-obj.c L179): set the note. Upstream's
1905
+ * quark_add("") does NOT return 0 (z-quark.c L31: it adds a new,
1906
+ * non-zero, empty quark, so obj->note stays truthy and object_desc
1907
+ * renders " {}"). This port intentionally maps "" -> null instead: it is
1908
+ * a deliberate normalization (not literal parity) that keeps note's
1909
+ * truthiness meaningful for objectStackable/objectCombine (object.ts
1910
+ * L844/L920), which already compare/merge notes by truthiness. No
1911
+ * energy; upstream's PN_COMBINE/PN_IGNORE notice + PR_INVEN/PR_EQUIP
1912
+ * redraw are UI bookkeeping this port doesn't model (combine already runs
1913
+ * lazily on the next inven_carry; ignore/display refresh is #25). */
1914
+ registry.register("inscribe", gated((state, cmd) => {
1915
+ const found = commandObject(state, cmd);
1916
+ if (!found) return 0;
1917
+ const raw = cmd.args?.["inscription"];
1918
+ const text = typeof raw === "string" ? raw : "";
1919
+ found.obj.note = text.length > 0 ? text : null;
1920
+ return 0;
1921
+ }));
1922
+
1923
+ /* do_cmd_uninscribe (cmd-obj.c L153). */
1924
+ registry.register("uninscribe", gated((state, cmd) => {
1925
+ const found = commandObject(state, cmd);
1926
+ if (!found || !objHasInscrip(found.obj)) return 0;
1927
+ found.obj.note = null;
1928
+ deps.env?.msg?.("Inscription removed.");
1929
+ return 0;
1930
+ }));
1931
+
1932
+ /* do_cmd_autoinscribe (cmd-obj.c L219): not gated by
1933
+ * player_get_resume_normal_shape - upstream just no-ops while
1934
+ * shapechanged, with no resume prompt. */
1935
+ registry.register("autoinscribe", (state, _cmd) => {
1936
+ if (playerIsShapechanged(state)) return 0;
1937
+ autoinscribeGround(state, deps);
1938
+ autoinscribePack(state, deps);
1939
+ return 0;
1940
+ });
1941
+
1942
+ /* do_cmd_refill (cmd-obj.c L1071): validate the equipped light, then
1943
+ * refill it from the chosen fuel source. Half a turn on success. */
1944
+ registry.register("refill", gated((state, cmd) => {
1945
+ const light = equippedLight(state);
1946
+ if (!light || !tvalIsLight(light.tval)) {
1947
+ deps.env?.msg?.("You are not wielding a light.");
1948
+ return 0;
1949
+ }
1950
+ if (light.flags.has(OF.NO_FUEL) || !light.flags.has(OF.TAKES_FUEL)) {
1951
+ deps.env?.msg?.("Your light cannot be refilled.");
1952
+ return 0;
1953
+ }
1954
+
1955
+ const found = commandObject(state, cmd);
1956
+ if (!found || !objCanRefill(state, found.obj)) return 0;
1957
+
1958
+ refillLamp(
1959
+ state,
1960
+ light,
1961
+ found.obj,
1962
+ {
1963
+ fromFloor: found.fromFloor,
1964
+ ...(found.handle !== undefined ? { handle: found.handle } : {}),
1965
+ },
1966
+ deps,
1967
+ );
1968
+ return Math.trunc(state.z.moveEnergy / 2);
1969
+ }));
1970
+ }