@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,2406 @@
1
+ /**
2
+ * Dungeon (cave) builders, tunneling, connectivity, mineral streamers and the
3
+ * runtime-registrable dungeon-profile registry, ported from
4
+ * reference/src/gen-cave.c and the profile-selection code of generate.c
5
+ * (Angband 4.2.6).
6
+ *
7
+ * PORTED: classic_gen and modified_gen (with do_traditional_tunneling,
8
+ * build_tunnel, choose_random_entrance, build_streamer, ensure_connectedness
9
+ * and handle_level_stairs), and choose_profile's selection by depth.
10
+ *
11
+ * MODDABILITY (ratified pillar, decision 13): cave builders live in a
12
+ * string-keyed registry and dungeon profiles in a runtime-registrable list.
13
+ * A mod can register a new builder key and add a profile that uses it.
14
+ *
15
+ * PORTED (standalone): labyrinth_gen and cavern_gen (with labyrinth_chunk's
16
+ * Kruskal maze, and init_cavern/mutate_cavern/clear_small_regions/cavern_chunk's
17
+ * cellular automaton). cavern_chunk's persistent-level `join` stair machinery is
18
+ * ported but dormant (dun.join is empty for non-persistent levels).
19
+ *
20
+ * PORTED (multi-region): moria_gen (modified-style with the moria profile +
21
+ * "Moria dwellers" restriction), lair_gen (a modified half joined to a themed
22
+ * cavern), gauntlet_gen (two caverns split by an unmappable labyrinth) and
23
+ * hard_centre_gen (a greater vault surrounded by four caverns), with the
24
+ * chunk_copy merge helper, connect_caverns and vault_chunk (room.ts). All four
25
+ * builder keys (moria/lair/gauntlet/hard_centre) are registered AND selectable:
26
+ * choose() runs the same weighted profile loop as choose_profile (generate.c
27
+ * L866-878), so any profile with alloc > 0 - moria and the multi-region
28
+ * builders included - is picked by depth exactly as upstream.
29
+ *
30
+ * DEFERRED: the town builder's full store generation and persistent-level
31
+ * connectors remain deferred. The single-combat arena level is generated in
32
+ * the session layer (session/game.ts, matching arena_gen at gen-cave.c:3984),
33
+ * as upstream also special-cases it outside the normal builder loop.
34
+ */
35
+ import { DUN_PROFILE_ENTRIES, FEAT, ORIGIN, ROOM_ENTRIES, SQUARE } from "../generated/index.js";
36
+ import { DDGRID_DDD, loc, locSum } from "../loc.js";
37
+ import { Chunk, featIsBright } from "../world/chunk.js";
38
+ import { roomBuild, symmetryTransform, vaultChunk } from "./room.js";
39
+ import { monRestrict, setPitType, spreadMonsters } from "./gen-monster.js";
40
+ import { CaveFinder, Dun, Gen, allocObjects, allocStairs, caveFind, caveFindInRange, correctDir, countNeighbors, drawRectangle, fillRectangle, findNearbyGrid, generateMark, generateStarburstRoom, gridToI, iToGrid, newPlayerSpot, nextGrid, pickAndPlaceDistantMonster, placeClosedDoor, placeRandomDoor, placeTrap, randDir, setMarkedGranite, shuffle, squareIsEmpty, squareIsGraniteWithFlag, squareIswallOuter, squareIsRoom, squareIsStrongWall, SET_BOTH, SET_CORR, SET_ROOM, TYP_GOLD, TYP_GOOD, TYP_GREAT, TYP_OBJECT, TYP_RUBBLE, TYP_TRAP, DIR_N, DIR_S, DIR_E, DIR_W, DIR_SE, distance, } from "./util.js";
41
+ const ZERO_TUNNEL = { rnd: 0, chg: 0, con: 0, pen: 0, jct: 0 };
42
+ const ZERO_STREAMER = {
43
+ den: 0,
44
+ rng: 0,
45
+ mag: 0,
46
+ mc: 0,
47
+ qua: 0,
48
+ qc: 0,
49
+ };
50
+ /**
51
+ * name -> cave builder key, from list-dun-profiles.h. The port's stand-in for
52
+ * get_level_profile_index_from_name (generate.c L1600): the C resolves a profile
53
+ * name to its index in cave_profiles[] and indexes the builder from there, while
54
+ * DUN_PROFILE_ENTRIES carries the name->builder pairing directly.
55
+ * get_level_profile_name_from_index (generate.c L1612) is the inverse and is
56
+ * DUN_PROFILE_ENTRIES[i].name; both C functions exist only for wiz-stats.c's
57
+ * level dumps, never for the savefile.
58
+ */
59
+ function profileBuilderKey(name) {
60
+ for (const e of DUN_PROFILE_ENTRIES) {
61
+ if (e.name === name)
62
+ return e.builder;
63
+ }
64
+ throw new Error(`gen: unknown dungeon profile name '${name}'`);
65
+ }
66
+ /**
67
+ * room name -> room-builder key, from list-rooms.h. This is
68
+ * get_room_builder_index_from_name (generate.c L1570) plus the room_builders[i]
69
+ * .builder deref that parse_profile_room (generate.c L171-177) does with it;
70
+ * get_room_builder_name_from_index (L1590) is ROOM_ENTRIES[i].name and
71
+ * get_room_builder_count (L1561) is ROOM_ENTRIES.length. The C trio is only
72
+ * consumed by wiz-stats.c, never by the savefile.
73
+ */
74
+ function roomBuilderKey(name) {
75
+ for (const e of ROOM_ENTRIES) {
76
+ if (e.name === name)
77
+ return e.builder;
78
+ }
79
+ throw new Error(`gen: unknown room name '${name}'`);
80
+ }
81
+ /** Convert one dungeon_profile.json record into a DunProfile. */
82
+ export function loadDunProfile(rec) {
83
+ const roomProfiles = (rec.room ?? []).map((r) => ({
84
+ name: r.name,
85
+ builder: roomBuilderKey(r.name),
86
+ rating: r.rating,
87
+ height: r.height,
88
+ width: r.width,
89
+ level: r.level,
90
+ pit: r.pit !== 0,
91
+ rarity: r.rarity,
92
+ cutoff: r.cutoff,
93
+ }));
94
+ return {
95
+ name: rec.name,
96
+ builder: profileBuilderKey(rec.name),
97
+ blockSize: rec.params.block,
98
+ dunRooms: rec.params.rooms,
99
+ dunUnusual: rec.params.unusual,
100
+ maxRarity: rec.params.rarity,
101
+ tun: rec.tunnel ?? ZERO_TUNNEL,
102
+ str: rec.streamer ?? ZERO_STREAMER,
103
+ roomProfiles,
104
+ minLevel: rec["min-level"] ?? 0,
105
+ alloc: rec.alloc,
106
+ };
107
+ }
108
+ /* ------------------------------------------------------------------ *
109
+ * choose_random_entrance (gen-cave.c).
110
+ * ------------------------------------------------------------------ */
111
+ /**
112
+ * Randomly choose a marked entrance for room ridx, biased toward tgt, or
113
+ * loc(0, 0) when there is no satisfactory entrance.
114
+ */
115
+ function chooseRandomEntrance(g, ridx, tgt, bias, exc) {
116
+ const dun = g.dun;
117
+ const nEnt = dun.entN[ridx] ?? 0;
118
+ if (nEnt > 0) {
119
+ const entries = dun.ent[ridx];
120
+ const accum = new Array(nEnt + 1).fill(0);
121
+ let nchoice = 0;
122
+ for (let i = 0; i < nEnt; i++) {
123
+ const e = entries[i];
124
+ let included = squareIsGraniteWithFlag(g.c, e, SQUARE.WALL_OUTER);
125
+ if (included) {
126
+ for (const x of exc) {
127
+ const dx = e.x - x.x;
128
+ const dy = e.y - x.y;
129
+ if (Math.abs(dx) <= 1 && Math.abs(dy) <= 1 && (dx !== 0 || dy !== 0)) {
130
+ included = false;
131
+ break;
132
+ }
133
+ }
134
+ }
135
+ if (included) {
136
+ if (tgt) {
137
+ const d = distance(e, tgt);
138
+ if (d === 0)
139
+ return e;
140
+ const biased = Math.max(1, bias - d);
141
+ accum[i + 1] = accum[i] + biased * biased;
142
+ }
143
+ else {
144
+ accum[i + 1] = accum[i] + 1;
145
+ }
146
+ nchoice++;
147
+ }
148
+ else {
149
+ accum[i + 1] = accum[i];
150
+ }
151
+ }
152
+ if (nchoice > 0) {
153
+ const chosen = g.rng.randint0(accum[nEnt]);
154
+ let low = 0;
155
+ let high = nEnt;
156
+ for (;;) {
157
+ if (low === high - 1)
158
+ return entries[low];
159
+ const mid = low + Math.trunc((high - low) / 2);
160
+ if (accum[mid] <= chosen)
161
+ low = mid;
162
+ else
163
+ high = mid;
164
+ }
165
+ }
166
+ }
167
+ return loc(0, 0);
168
+ }
169
+ /* ------------------------------------------------------------------ *
170
+ * build_tunnel (gen-cave.c).
171
+ * ------------------------------------------------------------------ */
172
+ function pierceOuterWall(g, grid) {
173
+ const dun = g.dun;
174
+ if (dun.wallN < dun.wallPierceMax) {
175
+ dun.wall[dun.wallN] = grid;
176
+ dun.wallN++;
177
+ }
178
+ for (let ay = grid.y - 1; ay <= grid.y + 1; ay++) {
179
+ for (let ax = grid.x - 1; ax <= grid.x + 1; ax++) {
180
+ const adj = loc(ax, ay);
181
+ if (ax !== 0 &&
182
+ ay !== 0 &&
183
+ g.c.inBounds(adj) &&
184
+ squareIsGraniteWithFlag(g.c, adj, SQUARE.WALL_OUTER)) {
185
+ setMarkedGranite(g.c, adj, SQUARE.WALL_SOLID);
186
+ }
187
+ }
188
+ }
189
+ }
190
+ function handlePostWallStep(g, grid, st) {
191
+ let cur = grid;
192
+ const dir = st.offset;
193
+ if (dir.x !== 0 && dir.y !== 0) {
194
+ cur = loc(cur.x + dir.x, cur.y + dir.y);
195
+ if (!squareIsRoom(g.c, cur) && g.c.isGranite(cur)) {
196
+ if (g.dun.tunnN < g.dun.tunnGridMax) {
197
+ g.dun.tunn[g.dun.tunnN] = cur;
198
+ g.dun.tunnN++;
199
+ }
200
+ st.door = false;
201
+ }
202
+ st.bend = 0;
203
+ if (g.rng.randint0(32768) < 16384)
204
+ st.offset = loc(0, dir.y);
205
+ else
206
+ st.offset = loc(dir.x, 0);
207
+ }
208
+ else {
209
+ st.bend = 1;
210
+ }
211
+ return cur;
212
+ }
213
+ function findNormalToWall(g, grid, inner) {
214
+ const choices = [];
215
+ let ncardinal = 0;
216
+ const ddd = ddgridDdd();
217
+ for (let i = 0; i < 8; i++) {
218
+ const off = ddd[i];
219
+ const chk = loc(grid.x + off.x, grid.y + off.y);
220
+ if (g.c.inBounds(chk) &&
221
+ !g.c.isPerm(chk) &&
222
+ squareIsRoom(g.c, chk) === inner &&
223
+ !squareIsGraniteWithFlag(g.c, chk, SQUARE.WALL_OUTER) &&
224
+ !squareIsGraniteWithFlag(g.c, chk, SQUARE.WALL_SOLID) &&
225
+ !squareIsGraniteWithFlag(g.c, chk, SQUARE.WALL_INNER)) {
226
+ choices.push(off);
227
+ if (i < 4)
228
+ ncardinal++;
229
+ }
230
+ }
231
+ let n = choices.length;
232
+ if (n > 1 && ncardinal > 0)
233
+ n = ncardinal;
234
+ return n === 0 ? loc(0, 0) : choices[g.rng.randint0(n)];
235
+ }
236
+ function allowsWallPiercingDoor(g, grid) {
237
+ let nOut = 0;
238
+ let nIn = 0;
239
+ for (let cy = grid.y - 1; cy <= grid.y + 1; cy++) {
240
+ for (let cx = grid.x - 1; cx <= grid.x + 1; cx++) {
241
+ if ((cy === 0 && cx === 0) || !g.c.inBounds(loc(cx, cy)))
242
+ continue;
243
+ const chk = loc(cx, cy);
244
+ if ((g.c.isPassable(chk) || g.c.isRubble(chk)) &&
245
+ !g.c.isDoor(chk) &&
246
+ !g.c.isShop(chk)) {
247
+ if (squareIsRoom(g.c, chk))
248
+ nIn++;
249
+ else
250
+ nOut++;
251
+ }
252
+ }
253
+ }
254
+ return nOut > 0 && nIn > 0;
255
+ }
256
+ let cachedDddGrid = null;
257
+ function ddgridDdd() {
258
+ if (!cachedDddGrid) {
259
+ /* ddgrid_ddd order: S, N, E, W, SE, SW, NE, NW. */
260
+ cachedDddGrid = [
261
+ loc(0, 1),
262
+ loc(0, -1),
263
+ loc(1, 0),
264
+ loc(-1, 0),
265
+ loc(1, 1),
266
+ loc(-1, 1),
267
+ loc(1, -1),
268
+ loc(-1, -1),
269
+ loc(0, 0),
270
+ ];
271
+ }
272
+ return cachedDddGrid;
273
+ }
274
+ function buildTunnel(g, from, to) {
275
+ const dun = g.dun;
276
+ const c = g.c;
277
+ let grid1 = from;
278
+ let grid2 = to;
279
+ const start = grid1;
280
+ let mainLoop = 0;
281
+ const st = { door: false, bend: 0, offset: correctDir(g.rng, grid1, grid2) };
282
+ dun.tunnN = 0;
283
+ dun.wallN = 0;
284
+ while (!(grid1.x === grid2.x && grid1.y === grid2.y)) {
285
+ if (mainLoop++ > 2000)
286
+ break;
287
+ if (st.bend === 0) {
288
+ if (g.rng.randint0(100) < g.profileTun.chg) {
289
+ st.offset = correctDir(g.rng, grid1, grid2);
290
+ if (g.rng.randint0(100) < g.profileTun.rnd)
291
+ st.offset = randDir(g.rng);
292
+ }
293
+ }
294
+ else {
295
+ st.bend--;
296
+ }
297
+ let tmp = loc(grid1.x + st.offset.x, grid1.y + st.offset.y);
298
+ while (!c.inBounds(tmp)) {
299
+ st.offset = correctDir(g.rng, grid1, grid2);
300
+ if (g.rng.randint0(100) < g.profileTun.rnd)
301
+ st.offset = randDir(g.rng);
302
+ tmp = loc(grid1.x + st.offset.x, grid1.y + st.offset.y);
303
+ }
304
+ if ((c.isPerm(tmp) && !c.sqinfoHas(tmp, SQUARE.WALL_INNER)) ||
305
+ squareIsGraniteWithFlag(c, tmp, SQUARE.WALL_SOLID)) {
306
+ continue;
307
+ }
308
+ if (squareIsGraniteWithFlag(c, tmp, SQUARE.WALL_OUTER)) {
309
+ const nxt = loc(grid2.x - tmp.x, grid2.y - tmp.y);
310
+ if (nxt.x === 0 && nxt.y === 0) {
311
+ grid1 = tmp;
312
+ pierceOuterWall(g, grid1);
313
+ continue;
314
+ }
315
+ if (Math.abs(nxt.x) <= 1 &&
316
+ Math.abs(nxt.y) <= 1 &&
317
+ squareIsGraniteWithFlag(c, grid2, SQUARE.WALL_OUTER)) {
318
+ continue;
319
+ }
320
+ const iroom = dun.ent2room[tmp.y * c.width + tmp.x];
321
+ if (iroom !== -1) {
322
+ let nxtdir;
323
+ if (squareIsRoom(c, grid1)) {
324
+ nxtdir = findNormalToWall(g, tmp, false);
325
+ if (nxtdir.x === 0 && nxtdir.y === 0)
326
+ continue;
327
+ grid1 = tmp;
328
+ pierceOuterWall(g, grid1);
329
+ }
330
+ else {
331
+ let bias = 80 -
332
+ Math.trunc((80 *
333
+ Math.min(Math.max(0, g.profileTun.chg), 100) *
334
+ Math.min(Math.max(0, g.profileTun.rnd), 100)) /
335
+ 10000);
336
+ let ntry = 0;
337
+ const mtry = 20;
338
+ const exc = [tmp, grid2];
339
+ let chk = loc(0, 0);
340
+ nxtdir = loc(0, 0);
341
+ for (;;) {
342
+ if (ntry >= mtry)
343
+ break;
344
+ chk = chooseRandomEntrance(g, iroom, grid2, bias, exc);
345
+ if (chk.x === 0 && chk.y === 0) {
346
+ ntry = mtry;
347
+ break;
348
+ }
349
+ nxtdir = findNormalToWall(g, chk, false);
350
+ if (nxtdir.x !== 0 || nxtdir.y !== 0)
351
+ break;
352
+ ntry++;
353
+ bias = Math.trunc((bias * 8) / 10);
354
+ }
355
+ if (ntry >= mtry)
356
+ continue;
357
+ pierceOuterWall(g, tmp);
358
+ pierceOuterWall(g, chk);
359
+ grid1 = chk;
360
+ }
361
+ st.offset = nxtdir;
362
+ grid1 = handlePostWallStep(g, grid1, st);
363
+ continue;
364
+ }
365
+ const nxtdir = findNormalToWall(g, tmp, !squareIsRoom(c, grid1));
366
+ if (nxtdir.x === 0 && nxtdir.y === 0)
367
+ continue;
368
+ grid1 = tmp;
369
+ pierceOuterWall(g, grid1);
370
+ st.offset = nxtdir;
371
+ grid1 = handlePostWallStep(g, grid1, st);
372
+ }
373
+ else if (squareIsRoom(c, tmp)) {
374
+ grid1 = tmp;
375
+ }
376
+ else if (c.isGranite(tmp)) {
377
+ grid1 = tmp;
378
+ if (dun.tunnN < dun.tunnGridMax) {
379
+ dun.tunn[dun.tunnN] = grid1;
380
+ dun.tunnN++;
381
+ }
382
+ st.door = false;
383
+ }
384
+ else {
385
+ grid1 = tmp;
386
+ if (!st.door) {
387
+ if (dun.doorN < dun.levelDoorMax) {
388
+ dun.door[dun.doorN] = grid1;
389
+ dun.doorN++;
390
+ }
391
+ st.door = true;
392
+ }
393
+ if (g.rng.randint0(100) >= g.profileTun.con) {
394
+ const d = loc(grid1.x - start.x, grid1.y - start.y);
395
+ if (Math.abs(d.x) > 10 || Math.abs(d.y) > 10)
396
+ break;
397
+ }
398
+ }
399
+ }
400
+ for (let i = 0; i < dun.tunnN; i++)
401
+ c.setFeat(dun.tunn[i], FEAT.FLOOR);
402
+ for (let i = 0; i < dun.wallN; i++) {
403
+ const w = dun.wall[i];
404
+ c.setFeat(w, FEAT.FLOOR);
405
+ if (g.rng.randint0(100) < g.profileTun.pen && allowsWallPiercingDoor(g, w)) {
406
+ placeRandomDoor(g, w);
407
+ }
408
+ }
409
+ }
410
+ /* ------------------------------------------------------------------ *
411
+ * do_traditional_tunneling (gen-cave.c).
412
+ * ------------------------------------------------------------------ */
413
+ function nextToCorr(g, grid) {
414
+ let k = 0;
415
+ const ddd = ddgridDdd();
416
+ for (let i = 0; i < 4; i++) {
417
+ const off = ddd[i];
418
+ const g1 = loc(grid.x + off.x, grid.y + off.y);
419
+ if (g.c.isFloor(g1) && !squareIsRoom(g.c, g1))
420
+ k++;
421
+ }
422
+ return k;
423
+ }
424
+ function possibleDoorway(g, grid) {
425
+ if (nextToCorr(g, grid) < 2)
426
+ return false;
427
+ if (squareIsStrongWall(g.c, nextGrid(grid, DIR_N)) &&
428
+ squareIsStrongWall(g.c, nextGrid(grid, DIR_S))) {
429
+ return true;
430
+ }
431
+ if (squareIsStrongWall(g.c, nextGrid(grid, DIR_W)) &&
432
+ squareIsStrongWall(g.c, nextGrid(grid, DIR_E))) {
433
+ return true;
434
+ }
435
+ return false;
436
+ }
437
+ function tryDoor(g, grid) {
438
+ if (!g.c.inBounds(grid))
439
+ return;
440
+ if (squareIsStrongWall(g.c, grid))
441
+ return;
442
+ if (squareIsRoom(g.c, grid))
443
+ return;
444
+ if (g.hasTrap(grid))
445
+ return;
446
+ if (g.c.isDoor(grid))
447
+ return;
448
+ if (g.rng.randint0(100) < g.profileTun.jct && possibleDoorway(g, grid)) {
449
+ placeRandomDoor(g, grid);
450
+ }
451
+ else if (g.rng.randint0(500) < g.profileTun.jct && possibleDoorway(g, grid)) {
452
+ /* place_trap(c, grid, -1, c->depth) (gen-cave.c:830-833): pick_trap +
453
+ * randcalc power on the generation stream, not a bare markTrap. */
454
+ placeTrap(g, grid);
455
+ }
456
+ }
457
+ function doTraditionalTunneling(g) {
458
+ const dun = g.dun;
459
+ const scrambled = [];
460
+ for (let i = 0; i < dun.centN; i++)
461
+ scrambled[i] = i;
462
+ for (let i = 0; i < dun.centN; i++) {
463
+ const p1 = g.rng.randint0(dun.centN);
464
+ const p2 = g.rng.randint0(dun.centN);
465
+ const t = scrambled[p1];
466
+ scrambled[p1] = scrambled[p2];
467
+ scrambled[p2] = t;
468
+ }
469
+ dun.doorN = 0;
470
+ if (dun.centN === 0)
471
+ return;
472
+ let grid = chooseRandomEntrance(g, scrambled[dun.centN - 1], null, 80, []);
473
+ if (grid.x === 0 && grid.y === 0) {
474
+ grid = dun.cent[scrambled[dun.centN - 1]];
475
+ }
476
+ for (let i = 0; i < dun.centN; i++) {
477
+ let next = chooseRandomEntrance(g, scrambled[i], grid, 80, []);
478
+ if (next.x === 0 && next.y === 0)
479
+ next = dun.cent[scrambled[i]];
480
+ buildTunnel(g, next, grid);
481
+ grid = next;
482
+ }
483
+ for (let i = 0; i < dun.doorN; i++) {
484
+ const d = dun.door[i];
485
+ tryDoor(g, nextGrid(d, DIR_W));
486
+ tryDoor(g, nextGrid(d, DIR_E));
487
+ tryDoor(g, nextGrid(d, DIR_N));
488
+ tryDoor(g, nextGrid(d, DIR_S));
489
+ }
490
+ }
491
+ /* ------------------------------------------------------------------ *
492
+ * build_streamer (gen-cave.c).
493
+ * ------------------------------------------------------------------ */
494
+ function buildStreamer(g, feat, chance) {
495
+ const c = g.c;
496
+ let grid = loc(g.rng.randSpread(Math.trunc(c.width / 2), 15), g.rng.randSpread(Math.trunc(c.height / 2), 10));
497
+ const dirOff = ddgridDdd();
498
+ /* ddd[randint0(8)] chooses among S,N,E,W,SE,SW,NE,NW. */
499
+ const dir = dirOff[g.rng.randint0(8)];
500
+ for (;;) {
501
+ for (let i = 0; i < g.profileStr.den; i++) {
502
+ const d = g.profileStr.rng;
503
+ const change = findNearbyGrid(c, g.rng, grid, d, d);
504
+ if (change && (c.isMagma(change) || c.isQuartz(change) || c.isGranite(change))) {
505
+ c.setFeat(change, feat);
506
+ if (g.rng.oneIn(chance)) {
507
+ /* square_upgrade_mineral: magma/quartz -> the treasure variant. */
508
+ const cur = c.feat(change);
509
+ if (cur === FEAT.MAGMA)
510
+ c.setFeat(change, FEAT.MAGMA_K);
511
+ else if (cur === FEAT.QUARTZ)
512
+ c.setFeat(change, FEAT.QUARTZ_K);
513
+ }
514
+ }
515
+ }
516
+ grid = loc(grid.x + dir.x, grid.y + dir.y);
517
+ if (!c.inBounds(grid))
518
+ break;
519
+ }
520
+ }
521
+ /* ------------------------------------------------------------------ *
522
+ * ensure_connectedness (gen-cave.c flood-fill region joining).
523
+ * ------------------------------------------------------------------ */
524
+ /**
525
+ * z-queue.c's `struct queue`, the FIFO the flood fills run on. Upstream's is a
526
+ * fixed-size ring that abort()s on overflow/underflow (q_new sizes it to the
527
+ * grid count); a growing array with a read cursor is the same FIFO order with
528
+ * no capacity to overflow.
529
+ */
530
+ class IntQueue {
531
+ data = [];
532
+ head = 0;
533
+ /** q_push (z-queue.c:93). */
534
+ push(v) {
535
+ this.data.push(v);
536
+ }
537
+ /** q_pop (z-queue.c:99). */
538
+ pop() {
539
+ return this.data[this.head++];
540
+ }
541
+ get length() {
542
+ return this.data.length - this.head;
543
+ }
544
+ }
545
+ function ignorePoint(g, colors, grid) {
546
+ if (!g.c.inBounds(grid))
547
+ return true;
548
+ if (colors[grid.y * g.c.width + grid.x])
549
+ return true;
550
+ if (g.c.isPassable(grid))
551
+ return false;
552
+ if (g.c.isDoor(grid))
553
+ return false;
554
+ return true;
555
+ }
556
+ function buildColorPoint(g, colors, counts, grid, color, diagonal) {
557
+ const w = g.c.width;
558
+ const queue = new IntQueue();
559
+ const added = new Uint8Array(g.c.height * w);
560
+ queue.push(grid.y * w + grid.x);
561
+ counts[color] = 0;
562
+ const ddd = ddgridDdd();
563
+ const nDir = diagonal ? 8 : 4;
564
+ while (queue.length > 0) {
565
+ const n1 = queue.pop();
566
+ const g1 = loc(n1 % w, Math.trunc(n1 / w));
567
+ if (ignorePoint(g, colors, g1))
568
+ continue;
569
+ colors[n1] = color;
570
+ counts[color] = counts[color] + 1;
571
+ for (let i = 0; i < nDir; i++) {
572
+ const off = ddd[i];
573
+ const g2 = loc(g1.x + off.x, g1.y + off.y);
574
+ const n2 = g2.y * w + g2.x;
575
+ if (ignorePoint(g, colors, g2))
576
+ continue;
577
+ if (added[n2])
578
+ continue;
579
+ queue.push(n2);
580
+ added[n2] = 1;
581
+ }
582
+ }
583
+ }
584
+ /**
585
+ * build_colors: colour every NESW- (or, when `diagonal`, 8-)contiguous region.
586
+ * The port's long-standing callers (ensureConnectedness, cavernChunk) pass
587
+ * `diagonal = false`; connect_caverns passes `true` to match gen-cave.c.
588
+ */
589
+ function buildColors(g, colors, counts, diagonal) {
590
+ let color = 1;
591
+ for (let y = 0; y < g.c.height; y++) {
592
+ for (let x = 0; x < g.c.width; x++) {
593
+ if (ignorePoint(g, colors, loc(x, y)))
594
+ continue;
595
+ buildColorPoint(g, colors, counts, loc(x, y), color, diagonal);
596
+ color++;
597
+ }
598
+ }
599
+ }
600
+ function countColors(counts) {
601
+ let num = 0;
602
+ for (const v of counts)
603
+ if (v > 0)
604
+ num++;
605
+ return num;
606
+ }
607
+ function firstColor(counts) {
608
+ for (let i = 0; i < counts.length; i++)
609
+ if (counts[i] > 0)
610
+ return i;
611
+ return -1;
612
+ }
613
+ function fixColors(colors, counts, from, to) {
614
+ for (let i = 0; i < colors.length; i++)
615
+ if (colors[i] === from)
616
+ colors[i] = to;
617
+ counts[to] = counts[to] + counts[from];
618
+ counts[from] = 0;
619
+ }
620
+ /**
621
+ * join_region: tunnel `color`'s region to a neighbouring region. With
622
+ * `newColorArg === -1` (the default used by ensureConnectedness/cavernChunk)
623
+ * any nearest neighbour is accepted; connect_caverns passes a specific target
624
+ * colour to join two named caverns, matching gen-cave.c.
625
+ */
626
+ function joinRegion(g, colors, counts, color, newColorArg, allowVaultDisconnect) {
627
+ const w = g.c.width;
628
+ const size = g.c.height * w;
629
+ const queue = new IntQueue();
630
+ const previous = new Int32Array(size).fill(-1);
631
+ let newColor = newColorArg;
632
+ for (let i = 0; i < size; i++) {
633
+ if (colors[i] === color) {
634
+ queue.push(i);
635
+ previous[i] = i;
636
+ }
637
+ }
638
+ const ddd = ddgridDdd();
639
+ while (queue.length > 0) {
640
+ const n1 = queue.pop();
641
+ const color2 = colors[n1];
642
+ if (newColor === -1 && color2 && color2 !== color)
643
+ newColor = color2;
644
+ if (color2 === newColor) {
645
+ let n = n1;
646
+ while (colors[n] !== color) {
647
+ const grid = loc(n % w, Math.trunc(n / w));
648
+ if (colors[n] > 0)
649
+ counts[colors[n]] = counts[colors[n]] - 1;
650
+ counts[color] = counts[color] + 1;
651
+ colors[n] = color;
652
+ if (!g.c.isPerm(grid) &&
653
+ !g.c.sqinfoHas(grid, SQUARE.VAULT) &&
654
+ !(g.c.isPassable(grid) || g.c.isDoor(grid))) {
655
+ g.c.setFeat(grid, FEAT.FLOOR);
656
+ }
657
+ n = previous[n];
658
+ }
659
+ fixColors(colors, counts, newColor, color);
660
+ break;
661
+ }
662
+ for (let i = 0; i < 4; i++) {
663
+ const off = ddd[i];
664
+ const grid = loc((n1 % w) + off.x, Math.trunc(n1 / w) + off.y);
665
+ if (!g.c.inBounds(grid))
666
+ continue;
667
+ const n2 = grid.y * w + grid.x;
668
+ if (previous[n2] >= 0)
669
+ continue;
670
+ if (g.c.isPerm(grid))
671
+ continue;
672
+ if (g.c.sqinfoHas(grid, SQUARE.VAULT) && !allowVaultDisconnect)
673
+ continue;
674
+ queue.push(n2);
675
+ previous[n2] = n1;
676
+ }
677
+ }
678
+ }
679
+ /** ensure_connectedness: colour regions, then join until one remains. */
680
+ export function ensureConnectedness(g, allowVaultDisconnect) {
681
+ const size = g.c.height * g.c.width;
682
+ const colors = new Int32Array(size);
683
+ const counts = new Int32Array(size);
684
+ buildColors(g, colors, counts, false);
685
+ let num = countColors(counts);
686
+ while (num > 1) {
687
+ const color = firstColor(counts);
688
+ joinRegion(g, colors, counts, color, -1, allowVaultDisconnect);
689
+ num--;
690
+ }
691
+ }
692
+ /**
693
+ * connect_caverns (gen-cave.c L3249): join the four caverns that surround the
694
+ * hard-centre vault. `floor` holds one sample floor grid from each cavern in
695
+ * the order [left, upper, lower, right]. NEW helper.
696
+ */
697
+ export function connectCaverns(g, floor) {
698
+ const size = g.c.height * g.c.width;
699
+ const colors = new Int32Array(size);
700
+ const counts = new Int32Array(size);
701
+ const colorOfFloor = [0, 0, 0, 0];
702
+ /* Colour the regions (diagonally, as upstream), find each cavern's colour. */
703
+ buildColors(g, colors, counts, true);
704
+ for (let i = 0; i < 4; i++) {
705
+ const spot = gridToI(floor[i], g.c.width);
706
+ colorOfFloor[i] = colors[spot];
707
+ }
708
+ /* Join left and upper, right and lower. */
709
+ joinRegion(g, colors, counts, colorOfFloor[0], colorOfFloor[1], false);
710
+ joinRegion(g, colors, counts, colorOfFloor[2], colorOfFloor[3], false);
711
+ /* Re-read the (upper, lower) colours after those joins, then join the two
712
+ * big caverns. */
713
+ for (let i = 1; i < 3; i++) {
714
+ const spot = gridToI(floor[i], g.c.width);
715
+ colorOfFloor[i] = colors[spot];
716
+ }
717
+ joinRegion(g, colors, counts, colorOfFloor[1], colorOfFloor[2], false);
718
+ }
719
+ /**
720
+ * chunk_copy (gen-chunk.c L345): copy the source chunk's terrain, sqinfo,
721
+ * objects, traps, monsters and player spot into `dest` at offset (y0, x0) with
722
+ * the given rotation (reflect is always false for these builders). Draws no
723
+ * RNG. Monster indices are reassigned in `dest`; unique tracking transfers via
724
+ * attachMonster. NEW helper.
725
+ *
726
+ * NOTE: because each sub-chunk is a distinct Gen with its own unique-tracking,
727
+ * a unique could in principle be picked into two sibling sub-chunks before they
728
+ * are merged (upstream tracks r_info->cur_num globally). This is RNG-neutral
729
+ * and a pre-existing consequence of the port's per-chunk monster model.
730
+ */
731
+ function chunkCopy(dest, src, y0, x0, rotate) {
732
+ const h = src.c.height;
733
+ const w = src.c.width;
734
+ /* Terrain, sqinfo, player. */
735
+ for (let y = 0; y < h; y++) {
736
+ for (let x = 0; x < w; x++) {
737
+ const srcGrid = loc(x, y);
738
+ const destGrid = symmetryTransform(srcGrid, y0, x0, h, w, rotate, false);
739
+ dest.c.setFeat(destGrid, src.c.feat(srcGrid));
740
+ dest.c.info(destGrid).copy(src.c.info(srcGrid));
741
+ }
742
+ }
743
+ if (src.playerSpot) {
744
+ dest.playerSpot = symmetryTransform(src.playerSpot, y0, x0, h, w, rotate, false);
745
+ }
746
+ /* Dungeon objects. */
747
+ for (const po of src.objects) {
748
+ const destGrid = symmetryTransform(po.grid, y0, x0, h, w, rotate, false);
749
+ po.obj.grid = destGrid;
750
+ dest.addObject(destGrid, po.obj);
751
+ }
752
+ /* Traps. */
753
+ for (const idx of src.trapGrids) {
754
+ const srcGrid = loc(idx % w, Math.trunc(idx / w));
755
+ const destGrid = symmetryTransform(srcGrid, y0, x0, h, w, rotate, false);
756
+ dest.markTrap(destGrid);
757
+ }
758
+ /* Trap descriptors (kind + power) rolled at generation time (gap 9.2). */
759
+ for (const t of src.traps) {
760
+ const destGrid = symmetryTransform(t.grid, y0, x0, h, w, rotate, false);
761
+ dest.traps.push({ grid: destGrid, tidx: t.tidx, power: t.power });
762
+ }
763
+ /* Monsters. */
764
+ for (const pm of src.monsters) {
765
+ const destGrid = symmetryTransform(pm.grid, y0, x0, h, w, rotate, false);
766
+ dest.attachMonster(destGrid, pm.mon, dest.nextMonIndex());
767
+ }
768
+ }
769
+ /* ------------------------------------------------------------------ *
770
+ * Cave builders.
771
+ * ------------------------------------------------------------------ */
772
+ function sizePercent(rng, depth, quest) {
773
+ const i = rng.randint1(10) + Math.trunc(depth / 24);
774
+ if (quest)
775
+ return 100;
776
+ if (i < 2)
777
+ return 75;
778
+ if (i < 3)
779
+ return 80;
780
+ if (i < 4)
781
+ return 85;
782
+ if (i < 5)
783
+ return 90;
784
+ if (i < 6)
785
+ return 95;
786
+ return 100;
787
+ }
788
+ function alloc2dBool(rows, cols) {
789
+ const m = [];
790
+ for (let i = 0; i < rows; i++)
791
+ m.push(new Array(cols).fill(false));
792
+ return m;
793
+ }
794
+ /**
795
+ * build_staircase_rooms (gen-cave.c:908-936): for a persistent level, place a
796
+ * 1x1 staircase room at each seeded stair connector (dun.join) so up/down
797
+ * stairs line up with the already-generated adjacent levels. Locates the
798
+ * "staircase room" profile in the active dungeon profile (upstream asserts it
799
+ * exists), then for each join sets dun.currJoin and builds the room at the
800
+ * connector's block coords ((grid - 1) / block, via room_build's
801
+ * finds-own-space path - buildStaircase reads dun.currJoin for the real grid).
802
+ * RNG-free: the staircase builder draws no RNG. Only called under dun.persist,
803
+ * so a default (non-persistent) game never runs it and generation is unchanged.
804
+ */
805
+ function buildStaircaseRooms(g, ctx) {
806
+ const dun = g.dun;
807
+ const profile = ctx.profile.roomProfiles.find((rp) => rp.name === "staircase room");
808
+ if (!profile) {
809
+ throw new Error("gen: build_staircase_rooms: no 'staircase room' profile");
810
+ }
811
+ for (const join of dun.join) {
812
+ dun.currJoin = join;
813
+ if (!roomBuild(g, Math.trunc((join.grid.y - 1) / dun.blockHgt), Math.trunc((join.grid.x - 1) / dun.blockWid), profile, true, ctx.rooms)) {
814
+ throw new Error(`gen: build_staircase_rooms: failed to place staircase room at ` +
815
+ `row=${join.grid.y} col=${join.grid.x}`);
816
+ }
817
+ dun.nstairRoom++;
818
+ }
819
+ }
820
+ /** classic_gen. */
821
+ export const classicGen = (ctx) => {
822
+ const { rng, reg, constants, dun, profile, depth } = ctx;
823
+ const numRooms = Math.trunc((profile.dunRooms * sizePercent(rng, depth, dun.quest)) / 100);
824
+ dun.blockHgt = profile.blockSize;
825
+ dun.blockWid = profile.blockSize;
826
+ const c = new Chunk(reg, constants.dungeonHgt, constants.dungeonWid);
827
+ c.depth = depth;
828
+ const g = makeGen(ctx, c);
829
+ fillRectangle(c, 0, 0, c.height - 1, c.width - 1, FEAT.GRANITE, SQUARE.NONE);
830
+ dun.rowBlocks = Math.trunc(c.height / dun.blockHgt);
831
+ dun.colBlocks = Math.trunc(c.width / dun.blockWid);
832
+ dun.roomMap = alloc2dBool(dun.rowBlocks, dun.colBlocks);
833
+ const blocksTried = alloc2dBool(dun.rowBlocks, dun.colBlocks);
834
+ dun.pitNum = 0;
835
+ dun.centN = 0;
836
+ dun.resetEntranceData(c);
837
+ /* Persistent levels (gen-cave.c:1183-1186): place the staircase rooms that
838
+ * align with the adjacent levels before the ordinary rooms. Off by default. */
839
+ if (dun.persist)
840
+ buildStaircaseRooms(g, ctx);
841
+ let built = 0;
842
+ while (built < numRooms) {
843
+ let j = 0;
844
+ let tby = 0;
845
+ let tbx = 0;
846
+ for (let by = 0; by < dun.rowBlocks; by++) {
847
+ for (let bx = 0; bx < dun.colBlocks; bx++) {
848
+ if (blocksTried[by][bx])
849
+ continue;
850
+ j++;
851
+ if (rng.oneIn(j)) {
852
+ tby = by;
853
+ tbx = bx;
854
+ }
855
+ }
856
+ }
857
+ if (j === 0)
858
+ break;
859
+ blocksTried[tby][tbx] = true;
860
+ const key = rng.randint0(100);
861
+ let rarity = 0;
862
+ let i = 0;
863
+ while (i === rarity && i < profile.maxRarity) {
864
+ if (rng.randint0(profile.dunUnusual) < 50 + Math.trunc(c.depth / 2))
865
+ rarity++;
866
+ i++;
867
+ }
868
+ for (const rp of profile.roomProfiles) {
869
+ if (rp.rarity > rarity)
870
+ continue;
871
+ if (rp.cutoff <= key)
872
+ continue;
873
+ if (roomBuild(g, tby, tbx, rp, false, ctx.rooms)) {
874
+ built++;
875
+ break;
876
+ }
877
+ }
878
+ }
879
+ if (built < 2)
880
+ return { gen: null, error: "less than two rooms created" };
881
+ drawRectangle(c, 0, 0, c.height - 1, c.width - 1, FEAT.PERM, SQUARE.NONE, true);
882
+ doTraditionalTunneling(g);
883
+ ensureConnectedness(g, true);
884
+ for (let i = 0; i < profile.str.mag; i++)
885
+ buildStreamer(g, FEAT.MAGMA, profile.str.mc);
886
+ for (let i = 0; i < profile.str.qua; i++)
887
+ buildStreamer(g, FEAT.QUARTZ, profile.str.qc);
888
+ handleLevelStairs(g, dun.quest, rng.randRange(3, 4), rng.randRange(1, 2));
889
+ const k = Math.max(Math.min(Math.trunc(c.depth / 3), 10), 2);
890
+ allocObjects(g, SET_CORR, TYP_RUBBLE, rng.randint1(k), c.depth, 0);
891
+ allocObjects(g, SET_CORR, TYP_TRAP, Math.trunc(rng.randint1(k) / 5), c.depth, 0);
892
+ const pspot = newPlayerSpot(g, ctx.createStair ?? null);
893
+ if (!pspot)
894
+ return { gen: null, error: "could not place player" };
895
+ g.playerSpot = pspot;
896
+ let mcount = constants.levelMonsterMin + rng.randint1(8) + k;
897
+ for (; mcount > 0; mcount--)
898
+ pickAndPlaceDistantMonster(g, pspot, 0, true, c.depth);
899
+ allocObjects(g, SET_ROOM, TYP_OBJECT, rng.randNormal(constants.roomItemAv, 3), c.depth, ORIGIN.FLOOR);
900
+ allocObjects(g, SET_BOTH, TYP_OBJECT, rng.randNormal(constants.bothItemAv, 3), c.depth, ORIGIN.FLOOR);
901
+ allocObjects(g, SET_BOTH, TYP_GOLD, rng.randNormal(constants.bothGoldAv, 3), c.depth, ORIGIN.FLOOR);
902
+ return { gen: g, error: null };
903
+ };
904
+ /** modified_chunk: build the room+tunnel skeleton for a modified level. */
905
+ function modifiedChunk(ctx, height, width) {
906
+ const { rng, reg, constants, dun, profile, depth } = ctx;
907
+ const c = new Chunk(reg, height, width);
908
+ c.depth = depth;
909
+ const g = makeGen(ctx, c);
910
+ const numFloors = Math.trunc((c.height * c.width) / 7);
911
+ fillRectangle(c, 0, 0, c.height - 1, c.width - 1, FEAT.GRANITE, SQUARE.NONE);
912
+ drawRectangle(c, 0, 0, c.height - 1, c.width - 1, FEAT.PERM, SQUARE.NONE, true);
913
+ dun.rowBlocks = Math.trunc(c.height / dun.blockHgt);
914
+ dun.colBlocks = Math.trunc(c.width / dun.blockWid);
915
+ dun.roomMap = alloc2dBool(dun.rowBlocks, dun.colBlocks);
916
+ dun.pitNum = 0;
917
+ dun.centN = 0;
918
+ dun.resetEntranceData(c);
919
+ /* Persistent levels (gen-cave.c:2770-2773 / 3016-3019): both modified_chunk
920
+ * and moria_chunk place their staircase rooms here before the ordinary rooms.
921
+ * Off by default. */
922
+ if (dun.persist)
923
+ buildStaircaseRooms(g, ctx);
924
+ let nAttempt = 0;
925
+ for (;;) {
926
+ if ((c.featCount[FEAT.FLOOR] ?? 0) >= numFloors && dun.centN >= 2)
927
+ break;
928
+ if (nAttempt > 500)
929
+ return null;
930
+ nAttempt++;
931
+ const key = rng.randint0(100);
932
+ let rarity = 0;
933
+ let i = 0;
934
+ while (i === rarity && i < profile.maxRarity) {
935
+ if (rng.randint0(profile.dunUnusual) < 50 + Math.trunc(c.depth / 2))
936
+ rarity++;
937
+ i++;
938
+ }
939
+ for (const rp of profile.roomProfiles) {
940
+ if (rp.rarity > rarity)
941
+ continue;
942
+ if (rp.cutoff <= key)
943
+ continue;
944
+ if (roomBuild(g, 0, 0, rp, true, ctx.rooms))
945
+ break;
946
+ }
947
+ }
948
+ doTraditionalTunneling(g);
949
+ ensureConnectedness(g, true);
950
+ drawRectangle(c, 0, 0, c.height - 1, c.width - 1, FEAT.GRANITE, SQUARE.NONE, true);
951
+ return g;
952
+ }
953
+ /**
954
+ * modified_gen / moria_gen shared body (gen-cave.c L2872 / L3110). moria_gen is
955
+ * a modified-style level whose only differences are the profile (its own room
956
+ * list, incl. "moria room") and a mon_restrict("Moria dwellers") wrap around
957
+ * the monster placement loop. moria_chunk is byte-for-byte identical to
958
+ * modified_chunk, so both use modifiedChunk with the caller's profile.
959
+ *
960
+ * `restrict` is the pit-profile name to restrict monsters to (null for
961
+ * modified). mon_restrict for a named pit draws no RNG, so the null path is
962
+ * RNG-identical to the original modified_gen.
963
+ */
964
+ function modifiedStyleGen(ctx, restrict) {
965
+ const { rng, constants, dun, depth } = ctx;
966
+ const sp = sizePercent(rng, depth, dun.quest);
967
+ let ySize = Math.trunc((constants.dungeonHgt * (sp - 5 + rng.randint0(10))) / 100);
968
+ let xSize = Math.trunc((constants.dungeonWid * (sp - 5 + rng.randint0(10))) / 100);
969
+ ySize = Math.min(Math.max(ySize, ctx.minHeight), constants.dungeonHgt);
970
+ xSize = Math.min(Math.max(xSize, ctx.minWidth), constants.dungeonWid);
971
+ dun.blockHgt = ctx.profile.blockSize;
972
+ dun.blockWid = ctx.profile.blockSize;
973
+ const g = modifiedChunk(ctx, Math.min(constants.dungeonHgt, ySize), Math.min(constants.dungeonWid, xSize));
974
+ if (!g)
975
+ return { gen: null, error: "modified chunk could not be created" };
976
+ const c = g.c;
977
+ drawRectangle(c, 0, 0, c.height - 1, c.width - 1, FEAT.PERM, SQUARE.NONE, true);
978
+ for (let i = 0; i < ctx.profile.str.mag; i++)
979
+ buildStreamer(g, FEAT.MAGMA, ctx.profile.str.mc);
980
+ for (let i = 0; i < ctx.profile.str.qua; i++)
981
+ buildStreamer(g, FEAT.QUARTZ, ctx.profile.str.qc);
982
+ handleLevelStairs(g, dun.quest, rng.randRange(3, 4), rng.randRange(1, 2));
983
+ const k = Math.max(Math.min(Math.trunc(c.depth / 3), 10), 2);
984
+ allocObjects(g, SET_CORR, TYP_RUBBLE, rng.randint1(k), c.depth, 0);
985
+ allocObjects(g, SET_CORR, TYP_TRAP, Math.trunc(rng.randint1(k) / 5), c.depth, 0);
986
+ const pspot = newPlayerSpot(g, ctx.createStair ?? null);
987
+ if (!pspot)
988
+ return { gen: null, error: "could not place player" };
989
+ g.playerSpot = pspot;
990
+ let mcount = constants.levelMonsterMin + rng.randint1(8) + k;
991
+ /* moria: restrict to cave dwellers. "Moria dwellers" is a pit name, so the
992
+ * restrict draws no RNG (races[] is unused for the named-pit path). */
993
+ const pits = ctx.monDeps?.pits;
994
+ if (restrict !== null && ctx.monDeps && pits) {
995
+ monRestrict(rng, ctx.monDeps.table, [], pits, restrict, c.depth, c.depth, true);
996
+ }
997
+ for (; mcount > 0; mcount--)
998
+ pickAndPlaceDistantMonster(g, pspot, 0, true, c.depth);
999
+ if (restrict !== null && ctx.monDeps && pits) {
1000
+ monRestrict(rng, ctx.monDeps.table, [], pits, null, c.depth, c.depth, false);
1001
+ }
1002
+ allocObjects(g, SET_ROOM, TYP_OBJECT, rng.randNormal(constants.roomItemAv, 3), c.depth, ORIGIN.FLOOR);
1003
+ allocObjects(g, SET_BOTH, TYP_OBJECT, rng.randNormal(constants.bothItemAv, 3), c.depth, ORIGIN.FLOOR);
1004
+ allocObjects(g, SET_BOTH, TYP_GOLD, rng.randNormal(constants.bothGoldAv, 3), c.depth, ORIGIN.FLOOR);
1005
+ return { gen: g, error: null };
1006
+ }
1007
+ /** modified_gen. */
1008
+ export const modifiedGen = (ctx) => modifiedStyleGen(ctx, null);
1009
+ /** moria_gen (gen-cave.c L3110): an Oangband-style moria level. */
1010
+ export const moriaGen = (ctx) => modifiedStyleGen(ctx, "Moria dwellers");
1011
+ /* ------------------------------------------------------------------ *
1012
+ * Labyrinth generator (gen-cave.c labyrinth_chunk / labyrinth_gen).
1013
+ * ------------------------------------------------------------------ */
1014
+ /**
1015
+ * lab_get_adjoin: given an adjoining wall index i in a width-w labyrinth,
1016
+ * return the two cell indices [a, b] it separates.
1017
+ */
1018
+ function labGetAdjoin(i, w) {
1019
+ const grid = iToGrid(i, w);
1020
+ if (grid.x % 2 === 0) {
1021
+ return [gridToI(nextGrid(grid, DIR_N), w), gridToI(nextGrid(grid, DIR_S), w)];
1022
+ }
1023
+ return [gridToI(nextGrid(grid, DIR_W), w), gridToI(nextGrid(grid, DIR_E), w)];
1024
+ }
1025
+ /**
1026
+ * lab_is_tunnel: true if grid is a straight (non-intersection) passage - the
1027
+ * squares on one axis are open and on the other are walls. Doors count as open.
1028
+ */
1029
+ function labIsTunnel(c, grid) {
1030
+ const open = (g) => c.isPassable(g) || c.isClosedDoor(g);
1031
+ const west = open(nextGrid(grid, DIR_W));
1032
+ const east = open(nextGrid(grid, DIR_E));
1033
+ const north = open(nextGrid(grid, DIR_N));
1034
+ const south = open(nextGrid(grid, DIR_S));
1035
+ return north === south && west === east && north !== west;
1036
+ }
1037
+ /**
1038
+ * labyrinth_chunk: build an h x w labyrinth (in a chunk sized h+2 x w+2 with a
1039
+ * perma-rock border) using a randomized Kruskal's algorithm. NEW helpers:
1040
+ * labGetAdjoin/labIsTunnel (the maze index math and tunnel test).
1041
+ */
1042
+ function labyrinthChunk(ctx, depth, h, w, lit, soft) {
1043
+ const { rng, reg } = ctx;
1044
+ const c = new Chunk(reg, h + 2, w + 2);
1045
+ c.depth = depth;
1046
+ const g = makeGen(ctx, c);
1047
+ /* Number of squares in the labyrinth. */
1048
+ const n = h * w;
1049
+ /* sets[i] tracks connectedness; walls[i] is the shuffle list. */
1050
+ const sets = new Array(n);
1051
+ const walls = new Array(n);
1052
+ /* Bound with perma-rock; fill the interior with rock. */
1053
+ drawRectangle(c, 0, 0, h + 1, w + 1, FEAT.PERM, SQUARE.NONE, true);
1054
+ if (soft)
1055
+ fillRectangle(c, 1, 1, h, w, FEAT.GRANITE, SQUARE.WALL_SOLID);
1056
+ else
1057
+ fillRectangle(c, 1, 1, h, w, FEAT.PERM, SQUARE.NONE);
1058
+ for (let i = 0; i < n; i++) {
1059
+ walls[i] = i;
1060
+ sets[i] = -1;
1061
+ }
1062
+ /* Cut out a grid of 1x1 "cells". next_grid(grid, DIR_SE) translates
1063
+ * labyrinth-space (0-based) into chunk-space (offset by the border). */
1064
+ for (let gy = 0; gy < h; gy += 2) {
1065
+ for (let gx = 0; gx < w; gx += 2) {
1066
+ const grid = loc(gx, gy);
1067
+ const kLocal = gridToI(grid, w);
1068
+ const diag = nextGrid(grid, DIR_SE);
1069
+ sets[kLocal] = kLocal;
1070
+ c.setFeat(diag, FEAT.FLOOR);
1071
+ if (lit)
1072
+ c.sqinfoOn(diag, SQUARE.GLOW);
1073
+ }
1074
+ }
1075
+ /* Shuffle the walls, then run randomized Kruskal's algorithm. */
1076
+ shuffle(rng, walls, n);
1077
+ for (let i = 0; i < n; i++) {
1078
+ const j = walls[i];
1079
+ const grid = iToGrid(j, w);
1080
+ if ((grid.x < 1 && grid.y < 1) || (grid.x > w - 2 && grid.y > h - 2))
1081
+ continue;
1082
+ if (grid.x % 2 === grid.y % 2)
1083
+ continue;
1084
+ const [a, b] = labGetAdjoin(j, w);
1085
+ if (sets[a] !== sets[b]) {
1086
+ const sa = sets[a];
1087
+ const sb = sets[b];
1088
+ const diag = nextGrid(grid, DIR_SE);
1089
+ c.setFeat(diag, FEAT.FLOOR);
1090
+ if (lit)
1091
+ c.sqinfoOn(diag, SQUARE.GLOW);
1092
+ for (let k = 0; k < n; k++)
1093
+ if (sets[k] === sb)
1094
+ sets[k] = sa;
1095
+ }
1096
+ }
1097
+ /* Generate a closed door for every 100 squares in the labyrinth. */
1098
+ const finder = new CaveFinder(loc(1, 1), loc(c.width - 2, c.height - 2));
1099
+ let doors = Math.trunc(n / 100);
1100
+ while (doors > 0) {
1101
+ const grid = finder.get(rng);
1102
+ if (!grid)
1103
+ break;
1104
+ if (squareIsEmpty(g, grid) && labIsTunnel(c, grid)) {
1105
+ placeClosedDoor(g, grid);
1106
+ doors--;
1107
+ }
1108
+ }
1109
+ /* Unlit labyrinths hold some good items; hard (non-diggable) ones some great. */
1110
+ if (!lit)
1111
+ allocObjects(g, SET_BOTH, TYP_GOOD, rng.randNormal(3, 2), c.depth, ORIGIN.LABYRINTH);
1112
+ if (!soft)
1113
+ allocObjects(g, SET_BOTH, TYP_GREAT, rng.randNormal(2, 1), c.depth, ORIGIN.LABYRINTH);
1114
+ return g;
1115
+ }
1116
+ /** labyrinth_gen: build a labyrinth level. */
1117
+ export const labyrinthGen = (ctx) => {
1118
+ const { rng, constants, dun, depth } = ctx;
1119
+ /* The labyrinth area (excluding the enclosing walls) must be odd-sized. */
1120
+ let h = 15 + rng.randint0(Math.trunc(depth / 10)) * 2;
1121
+ let w = 51 + rng.randint0(Math.trunc(depth / 10)) * 2;
1122
+ /* Most labyrinths are lit; many lit ones are known; most have soft walls. */
1123
+ const lit = rng.randint0(depth) < 25 || rng.randint0(2) < 1;
1124
+ const known = lit && rng.randint0(depth) < 25;
1125
+ const soft = rng.randint0(depth) < 35 || rng.randint0(3) < 2;
1126
+ /* No persistent levels of this type for now. */
1127
+ if (dun.persist) {
1128
+ return { gen: null, error: "no labyrinth levels in persistent dungeons" };
1129
+ }
1130
+ /* Enforce minimum dimensions. */
1131
+ h = Math.max(h, ctx.minHeight);
1132
+ w = Math.max(w, ctx.minWidth);
1133
+ const g = labyrinthChunk(ctx, depth, h, w, lit, soft);
1134
+ const c = g.c;
1135
+ /* Determine the character location. */
1136
+ const pspot = newPlayerSpot(g, ctx.createStair ?? null);
1137
+ if (!pspot)
1138
+ return { gen: null, error: "could not place player" };
1139
+ g.playerSpot = pspot;
1140
+ /* A single set of up/down stairs, if not already present. */
1141
+ if (!caveFind(c, rng, (cc, gr) => cc.isUpstairs(gr))) {
1142
+ allocStairs(g, FEAT.LESS, 1, 0, false, dun.oneOffAbove, dun.quest);
1143
+ }
1144
+ if (!caveFind(c, rng, (cc, gr) => cc.isDownstairs(gr))) {
1145
+ allocStairs(g, FEAT.MORE, 1, 0, false, dun.oneOffBelow, dun.quest);
1146
+ }
1147
+ /* Rubble, traps and monsters, scaled by labyrinth size. */
1148
+ let k = Math.max(Math.min(Math.trunc(c.depth / 3), 10), 2);
1149
+ k = Math.trunc((3 * k * (h * w)) / (constants.dungeonHgt * constants.dungeonWid));
1150
+ allocObjects(g, SET_BOTH, TYP_RUBBLE, rng.randint1(k), c.depth, 0);
1151
+ allocObjects(g, SET_CORR, TYP_TRAP, rng.randint1(k), c.depth, 0);
1152
+ let mcount = constants.levelMonsterMin + rng.randint1(8) + k;
1153
+ for (; mcount > 0; mcount--)
1154
+ pickAndPlaceDistantMonster(g, pspot, 0, true, c.depth);
1155
+ allocObjects(g, SET_BOTH, TYP_OBJECT, rng.randNormal(k * 6, 2), c.depth, ORIGIN.LABYRINTH);
1156
+ allocObjects(g, SET_BOTH, TYP_GOLD, rng.randNormal(k * 3, 2), c.depth, ORIGIN.LABYRINTH);
1157
+ allocObjects(g, SET_BOTH, TYP_GOOD, rng.randint1(2), c.depth, ORIGIN.LABYRINTH);
1158
+ /* "Notify if we want the player to see the maze layout" (gen-cave.c:1593-1596):
1159
+ * a known labyrinth sets p->upkeep->light_level, which cave_generate consumes
1160
+ * as wiz_light(chunk, p, false) (generate.c:1255-1258). No RNG, no layout
1161
+ * effect - it perma-lights the level. */
1162
+ if (known)
1163
+ g.lightLevel = true;
1164
+ return { gen: g, error: null };
1165
+ };
1166
+ /* ------------------------------------------------------------------ *
1167
+ * Cavern generator (gen-cave.c init_cavern / mutate_cavern /
1168
+ * clear_small_regions / cavern_chunk / cavern_gen). Cellular-automaton cave.
1169
+ * ------------------------------------------------------------------ */
1170
+ const MAX_CAVERN_TRIES = 10;
1171
+ /**
1172
+ * init_cavern: fill the chunk with rock, then open `density` percent of the
1173
+ * interior to floor at random. The `join` connector list (empty for
1174
+ * non-persistent levels) builds in stairs surrounded by protected floor/rock;
1175
+ * it is ported faithfully but dormant until persistent levels wire dun.join.
1176
+ */
1177
+ function initCavern(g, density, join) {
1178
+ const c = g.c;
1179
+ const rng = g.rng;
1180
+ const h = c.height;
1181
+ const w = c.width;
1182
+ const size = h * w;
1183
+ let count = Math.trunc((size * density) / 100);
1184
+ /* Fill the entire chunk with rock. */
1185
+ fillRectangle(c, 0, 0, h - 1, w - 1, FEAT.GRANITE, SQUARE.WALL_SOLID);
1186
+ /* Add in the desired stairs (dormant for non-persistent levels). */
1187
+ for (const j of join) {
1188
+ if (j.grid.y > 0 &&
1189
+ j.grid.y < h - 1 &&
1190
+ j.grid.x > 0 &&
1191
+ j.grid.x < w - 1 &&
1192
+ !c.isStairs(j.grid)) {
1193
+ const bcrit = rng.randint0(h) + (j.grid.y > Math.trunc(h / 2) ? -10 : 10);
1194
+ const rcrit = rng.randint0(w) + (j.grid.x > Math.trunc(w / 2) ? -10 : 10);
1195
+ const offy = bcrit > j.grid.y ? 1 : -1;
1196
+ const offx = rcrit > j.grid.x ? 1 : -1;
1197
+ if (!c.isFloor(j.grid))
1198
+ count--;
1199
+ c.setFeat(j.grid, j.feat);
1200
+ let adj = loc(j.grid.x + offx, j.grid.y + offy);
1201
+ if (!c.isStairs(adj) && !c.isFloor(adj)) {
1202
+ count--;
1203
+ c.setFeat(adj, FEAT.FLOOR);
1204
+ }
1205
+ adj = loc(j.grid.x, j.grid.y + offy);
1206
+ if (!c.isStairs(adj) && !c.isFloor(adj)) {
1207
+ count--;
1208
+ c.setFeat(adj, FEAT.FLOOR);
1209
+ }
1210
+ adj = loc(j.grid.x + offx, j.grid.y);
1211
+ if (!c.isStairs(adj) && !c.isFloor(adj)) {
1212
+ count--;
1213
+ c.setFeat(adj, FEAT.FLOOR);
1214
+ }
1215
+ adj = loc(j.grid.x - offx, j.grid.y - offy);
1216
+ if (c.isGranite(adj))
1217
+ c.setFeat(adj, FEAT.PERM);
1218
+ adj = loc(j.grid.x, j.grid.y - offy);
1219
+ if (c.isGranite(adj))
1220
+ c.setFeat(adj, FEAT.PERM);
1221
+ adj = loc(j.grid.x - offx, j.grid.y);
1222
+ if (c.isGranite(adj))
1223
+ c.setFeat(adj, FEAT.PERM);
1224
+ }
1225
+ }
1226
+ while (count > 0) {
1227
+ const grid = loc(rng.randint1(w - 2), rng.randint1(h - 2));
1228
+ if (c.isGranite(grid)) {
1229
+ c.setFeat(grid, FEAT.FLOOR);
1230
+ count--;
1231
+ }
1232
+ }
1233
+ }
1234
+ /** mutate_cavern: one pass of the (4,5) cellular-automaton rules. No RNG. */
1235
+ function mutateCavern(g) {
1236
+ const c = g.c;
1237
+ const h = c.height;
1238
+ const w = c.width;
1239
+ const temp = new Int32Array(h * w);
1240
+ const passable = (cc, gr) => cc.isPassable(gr);
1241
+ for (let y = 1; y < h - 1; y++) {
1242
+ for (let x = 1; x < w - 1; x++) {
1243
+ const grid = loc(x, y);
1244
+ const count = 8 - countNeighbors(c, grid, passable, false);
1245
+ const i = gridToI(grid, w);
1246
+ if (c.isStairs(grid) || c.isPerm(grid))
1247
+ temp[i] = c.feat(grid);
1248
+ else if (count > 5)
1249
+ temp[i] = FEAT.GRANITE;
1250
+ else if (count < 4)
1251
+ temp[i] = FEAT.FLOOR;
1252
+ else
1253
+ temp[i] = c.feat(grid);
1254
+ }
1255
+ }
1256
+ for (let y = 1; y < h - 1; y++) {
1257
+ for (let x = 1; x < w - 1; x++) {
1258
+ const grid = loc(x, y);
1259
+ const feat = temp[gridToI(grid, w)];
1260
+ if (feat === FEAT.GRANITE)
1261
+ setMarkedGranite(c, grid, SQUARE.WALL_SOLID);
1262
+ else
1263
+ c.setFeat(grid, feat);
1264
+ }
1265
+ }
1266
+ }
1267
+ /**
1268
+ * clear_small_regions: delete all open regions smaller than 9 squares (turning
1269
+ * them back to granite). No stair-preservation here: join is empty for
1270
+ * non-persistent levels, so no region carries a staircase to protect.
1271
+ */
1272
+ function clearSmallRegions(g, colors, counts) {
1273
+ const c = g.c;
1274
+ const size = c.height * c.width;
1275
+ const deleted = new Uint8Array(size);
1276
+ for (let i = 0; i < size; i++) {
1277
+ if (counts[i] < 9) {
1278
+ deleted[i] = 1;
1279
+ counts[i] = 0;
1280
+ }
1281
+ }
1282
+ for (let y = 1; y < c.height - 1; y++) {
1283
+ for (let x = 1; x < c.width - 1; x++) {
1284
+ const grid = loc(x, y);
1285
+ const i = gridToI(grid, c.width);
1286
+ if (!deleted[colors[i]])
1287
+ continue;
1288
+ colors[i] = 0;
1289
+ setMarkedGranite(c, grid, SQUARE.WALL_SOLID);
1290
+ }
1291
+ }
1292
+ }
1293
+ /**
1294
+ * cavern_chunk: build a cellular-automaton cavern, regenerating (up to
1295
+ * MAX_CAVERN_TRIES times) until enough floor is open, then colour the regions,
1296
+ * delete the small ones and join the rest into one connected cave. Returns null
1297
+ * if no large-enough cavern could be made. Reuses the file's buildColors /
1298
+ * joinRegion / countColors / firstColor region machinery.
1299
+ */
1300
+ function cavernChunk(ctx, depth, h, w, join) {
1301
+ const { rng, reg } = ctx;
1302
+ const size = h * w;
1303
+ const limit = Math.trunc(size / 13);
1304
+ const density = rng.randRange(25, 40);
1305
+ const times = rng.randRange(3, 6);
1306
+ const c = new Chunk(reg, h, w);
1307
+ c.depth = depth;
1308
+ const g = makeGen(ctx, c);
1309
+ let tries = 0;
1310
+ for (; tries < MAX_CAVERN_TRIES; tries++) {
1311
+ initCavern(g, density, join);
1312
+ for (let i = 0; i < times; i++)
1313
+ mutateCavern(g);
1314
+ if ((c.featCount[FEAT.FLOOR] ?? 0) >= limit)
1315
+ break;
1316
+ }
1317
+ if (tries === MAX_CAVERN_TRIES)
1318
+ return null;
1319
+ const colors = new Int32Array(size);
1320
+ const counts = new Int32Array(size);
1321
+ buildColors(g, colors, counts, false);
1322
+ clearSmallRegions(g, colors, counts);
1323
+ let num = countColors(counts);
1324
+ while (num > 1) {
1325
+ joinRegion(g, colors, counts, firstColor(counts), -1, true);
1326
+ num--;
1327
+ }
1328
+ /* Convert the permanent rock walls near stairs back to granite (dormant). */
1329
+ for (const j of join) {
1330
+ for (let i = 0; i < 8; i++) {
1331
+ const adj = locSum(j.grid, DDGRID_DDD[i]);
1332
+ if (c.inBounds(adj) && c.isPerm(adj))
1333
+ setMarkedGranite(c, adj, SQUARE.WALL_SOLID);
1334
+ }
1335
+ }
1336
+ return g;
1337
+ }
1338
+ /** cavern_gen: build a cavern level. */
1339
+ export const cavernGen = (ctx) => {
1340
+ const { rng, constants, dun, depth } = ctx;
1341
+ let h = rng.randRange(Math.trunc(constants.dungeonHgt / 2), Math.trunc((constants.dungeonHgt * 3) / 4));
1342
+ let w = rng.randRange(Math.trunc(constants.dungeonWid / 2), Math.trunc((constants.dungeonWid * 3) / 4));
1343
+ /* Enforce minimum dimensions. */
1344
+ h = Math.max(h, ctx.minHeight);
1345
+ w = Math.max(w, ctx.minWidth);
1346
+ const g = cavernChunk(ctx, depth, h, w, dun.join);
1347
+ if (!g)
1348
+ return { gen: null, error: "cavern chunk could not be created" };
1349
+ const c = g.c;
1350
+ /* Surround the level with perma-rock. */
1351
+ drawRectangle(c, 0, 0, h - 1, w - 1, FEAT.PERM, SQUARE.NONE, true);
1352
+ /* Place 1-3 down stairs and 1-2 up stairs near some walls. */
1353
+ handleLevelStairs(g, dun.quest, rng.randRange(1, 3), rng.randRange(1, 2));
1354
+ /* Rubble, traps and monsters, scaled (with a floor of 6) by cavern size. */
1355
+ let k = Math.max(Math.min(Math.trunc(c.depth / 3), 10), 2);
1356
+ k = Math.max(Math.trunc((4 * k * (h * w)) / (constants.dungeonHgt * constants.dungeonWid)), 6);
1357
+ allocObjects(g, SET_BOTH, TYP_RUBBLE, rng.randint1(k), c.depth, 0);
1358
+ allocObjects(g, SET_CORR, TYP_TRAP, rng.randint1(k), c.depth, 0);
1359
+ /* Determine the character location. */
1360
+ const pspot = newPlayerSpot(g, ctx.createStair ?? null);
1361
+ if (!pspot)
1362
+ return { gen: null, error: "could not place player" };
1363
+ g.playerSpot = pspot;
1364
+ let mcount = rng.randint1(8) + k;
1365
+ for (; mcount > 0; mcount--)
1366
+ pickAndPlaceDistantMonster(g, pspot, 0, true, c.depth);
1367
+ allocObjects(g, SET_BOTH, TYP_OBJECT, rng.randNormal(k, 2), c.depth + 5, ORIGIN.CAVERN);
1368
+ allocObjects(g, SET_BOTH, TYP_GOLD, rng.randNormal(Math.trunc(k / 2), 2), c.depth, ORIGIN.CAVERN);
1369
+ allocObjects(g, SET_BOTH, TYP_GOOD, rng.randint0(Math.trunc(k / 4)), c.depth, ORIGIN.CAVERN);
1370
+ return { gen: g, error: null };
1371
+ };
1372
+ /* ------------------------------------------------------------------ *
1373
+ * Lair generator (gen-cave.c lair_gen L3534): a modified-style half joined to
1374
+ * a themed-monster cavern half.
1375
+ * ------------------------------------------------------------------ */
1376
+ export const lairGen = (ctx) => {
1377
+ const { rng, reg, constants, dun, depth } = ctx;
1378
+ /* Scale the level (identical ladder to modified/moria). */
1379
+ const sp = sizePercent(rng, depth, dun.quest);
1380
+ let ySize = Math.trunc((constants.dungeonHgt * (sp - 5 + rng.randint0(10))) / 100);
1381
+ let xSize = Math.trunc((constants.dungeonWid * (sp - 5 + rng.randint0(10))) / 100);
1382
+ ySize = Math.min(Math.max(ySize, ctx.minHeight), constants.dungeonHgt);
1383
+ xSize = Math.min(Math.max(xSize, ctx.minWidth), constants.dungeonWid);
1384
+ dun.blockHgt = ctx.profile.blockSize;
1385
+ dun.blockWid = ctx.profile.blockSize;
1386
+ /* Non-persistent: split the level down the middle. */
1387
+ const leftWidth = Math.trunc(xSize / 2);
1388
+ let normalWidth;
1389
+ let normalOffset;
1390
+ let lairWidth;
1391
+ let lairOffset;
1392
+ if (rng.oneIn(2)) {
1393
+ /* Normal part on the left. */
1394
+ normalWidth = leftWidth;
1395
+ normalOffset = 0;
1396
+ lairWidth = xSize - leftWidth;
1397
+ lairOffset = leftWidth;
1398
+ }
1399
+ else {
1400
+ /* Lair part on the left. */
1401
+ normalWidth = xSize - leftWidth;
1402
+ normalOffset = leftWidth;
1403
+ lairWidth = leftWidth;
1404
+ lairOffset = 0;
1405
+ }
1406
+ const normal = modifiedChunk(ctx, ySize, normalWidth);
1407
+ if (!normal)
1408
+ return { gen: null, error: "modified chunk could not be created" };
1409
+ const lair = cavernChunk(ctx, depth, ySize, lairWidth, dun.join);
1410
+ if (!lair)
1411
+ return { gen: null, error: "cavern chunk could not be created" };
1412
+ /* General amount of rubble, traps and monsters (halved for lairs). */
1413
+ const k = Math.trunc(Math.max(Math.min(Math.trunc(depth / 3), 10), 2) / 2);
1414
+ /* Put the character in the normal half. */
1415
+ const pspot = newPlayerSpot(normal, ctx.createStair ?? null);
1416
+ if (!pspot)
1417
+ return { gen: null, error: "could not place player" };
1418
+ normal.playerSpot = pspot;
1419
+ /* A smallish number of monsters for the normal half. */
1420
+ let nmon = rng.randint1(4) + k;
1421
+ for (; nmon > 0; nmon--) {
1422
+ pickAndPlaceDistantMonster(normal, pspot, 0, true, normal.c.depth);
1423
+ }
1424
+ /* Magma / quartz streamers in the normal half. */
1425
+ for (let i = 0; i < ctx.profile.str.mag; i++)
1426
+ buildStreamer(normal, FEAT.MAGMA, ctx.profile.str.mc);
1427
+ for (let i = 0; i < ctx.profile.str.qua; i++)
1428
+ buildStreamer(normal, FEAT.QUARTZ, ctx.profile.str.qc);
1429
+ /* A larger number of themed monsters for the lair. */
1430
+ const lairMon = constants.levelMonsterMin + rng.randint1(20) + k;
1431
+ const pits = ctx.monDeps?.pits;
1432
+ if (ctx.monDeps && pits) {
1433
+ const table = ctx.monDeps.table;
1434
+ const lairDepth = lair.c.depth;
1435
+ let pit = pits[0];
1436
+ for (;;) {
1437
+ pit = setPitType(rng, pits, lairDepth, 0);
1438
+ if (monRestrict(rng, table, [], pits, pit.name, lairDepth, lairDepth, true).ok)
1439
+ break;
1440
+ }
1441
+ spreadMonsters(lair, [], pits, pit.name, lairDepth, lairMon, Math.trunc(lair.c.height / 2), Math.trunc(lair.c.width / 2), Math.trunc(lair.c.height / 2), Math.trunc(lair.c.width / 2),
1442
+ /* spread_monsters (gen-cave.c L3671-3673): lair monsters carry
1443
+ * ORIGIN_CAVERN, which monster_death does NOT count for lore treasure. */
1444
+ ORIGIN.CAVERN);
1445
+ monRestrict(rng, table, [], pits, null, lairDepth, lairDepth, false);
1446
+ }
1447
+ /* Assemble the level. */
1448
+ const c = new Chunk(reg, ySize, xSize);
1449
+ c.depth = depth;
1450
+ const g = makeGen(ctx, c);
1451
+ chunkCopy(g, normal, 0, normalOffset, 0);
1452
+ chunkCopy(g, lair, 0, lairOffset, 0);
1453
+ /* Perma-rock border, then connect the two halves. */
1454
+ drawRectangle(c, 0, 0, c.height - 1, c.width - 1, FEAT.PERM, SQUARE.NONE, true);
1455
+ ensureConnectedness(g, true);
1456
+ handleLevelStairs(g, dun.quest, rng.randRange(3, 4), rng.randRange(1, 2));
1457
+ allocObjects(g, SET_CORR, TYP_RUBBLE, rng.randint1(k), c.depth, 0);
1458
+ allocObjects(g, SET_CORR, TYP_TRAP, Math.trunc(rng.randint1(k) / 5), c.depth, 0);
1459
+ allocObjects(g, SET_ROOM, TYP_OBJECT, rng.randNormal(constants.roomItemAv, 3), c.depth, ORIGIN.FLOOR);
1460
+ allocObjects(g, SET_BOTH, TYP_OBJECT, rng.randNormal(constants.bothItemAv, 3), c.depth, ORIGIN.FLOOR);
1461
+ allocObjects(g, SET_BOTH, TYP_GOLD, rng.randNormal(constants.bothGoldAv, 3), c.depth, ORIGIN.FLOOR);
1462
+ return { gen: g, error: null };
1463
+ };
1464
+ /* ------------------------------------------------------------------ *
1465
+ * Gauntlet generator (gen-cave.c gauntlet_gen L3734): two caverns with an
1466
+ * unmappable labyrinth "gauntlet" between them.
1467
+ * ------------------------------------------------------------------ */
1468
+ export const gauntletGen = (ctx) => {
1469
+ const { rng, reg, constants, dun, depth } = ctx;
1470
+ const gauntletHgt = 2 * rng.randint1(5) + 3;
1471
+ const gauntletWid = 2 * rng.randint1(10) + 19;
1472
+ const ySize = constants.dungeonHgt - rng.randint0(25 - gauntletHgt);
1473
+ /* labyrinth_chunk() yields something two grids wider than requested, hence
1474
+ * the extra "- 2" below (see gen-cave.c L3746). */
1475
+ const xSize = Math.trunc((constants.dungeonWid - gauntletWid - 2) / 2) - rng.randint0(45 - gauntletWid);
1476
+ /* No persistent levels of this type for now. */
1477
+ if (dun.persist) {
1478
+ return { gen: null, error: "no gauntlet levels in persistent dungeons" };
1479
+ }
1480
+ const gauntlet = labyrinthChunk(ctx, depth, gauntletHgt, gauntletWid, false, false);
1481
+ const left = cavernChunk(ctx, depth, ySize, xSize, []);
1482
+ if (!left)
1483
+ return { gen: null, error: "left cavern chunk could not be generated" };
1484
+ const right = cavernChunk(ctx, depth, ySize, xSize, []);
1485
+ if (!right)
1486
+ return { gen: null, error: "right cavern chunk could not be generated" };
1487
+ /* Lines between the three pieces. */
1488
+ const line1 = left.c.width;
1489
+ const line2 = line1 + gauntlet.c.width;
1490
+ /* Movement and mapping restrictions. */
1491
+ generateMark(left.c, 0, 0, left.c.height - 1, left.c.width - 1, SQUARE.NO_TELEPORT);
1492
+ generateMark(gauntlet.c, 0, 0, gauntlet.c.height - 1, gauntlet.c.width - 1, SQUARE.NO_MAP);
1493
+ generateMark(gauntlet.c, 0, 0, gauntlet.c.height - 1, gauntlet.c.width - 1, SQUARE.NO_TELEPORT);
1494
+ /* Down stairs in the right cavern, up stairs in the left. */
1495
+ allocStairs(right, FEAT.MORE, rng.randRange(2, 3), 0, false, [], dun.quest);
1496
+ allocStairs(left, FEAT.LESS, rng.randRange(1, 3), 0, false, [], dun.quest);
1497
+ /* Open the left end of the gauntlet (adjacent to a non-perm wall). */
1498
+ let tries = 0;
1499
+ for (;;) {
1500
+ const grid = loc(0, rng.randint1(gauntlet.c.height - 2));
1501
+ if (tries >= 20)
1502
+ return { gen: null, error: "could not open entrance to the labyrinth" };
1503
+ if (!gauntlet.c.isPerm(locSum(grid, loc(1, 0)))) {
1504
+ gauntlet.c.setFeat(grid, FEAT.GRANITE);
1505
+ break;
1506
+ }
1507
+ tries++;
1508
+ }
1509
+ /* Open the right end. */
1510
+ tries = 0;
1511
+ for (;;) {
1512
+ const grid = loc(gauntlet.c.width - 1, rng.randint1(gauntlet.c.height - 2));
1513
+ if (tries >= 20)
1514
+ return { gen: null, error: "could not open entrance to the labyrinth" };
1515
+ if (!gauntlet.c.isPerm(locSum(grid, loc(-1, 0)))) {
1516
+ gauntlet.c.setFeat(grid, FEAT.GRANITE);
1517
+ break;
1518
+ }
1519
+ tries++;
1520
+ }
1521
+ const k = Math.trunc(Math.max(Math.min(Math.trunc(depth / 3), 10), 2) / 2);
1522
+ /* Put the character in the arrival cavern (gen-cave.c:3857): the right cavern
1523
+ * (down stairs) when arriving via create_down_stair (an ascent), else the
1524
+ * left (up stairs). new_player_spot then lays the matching arrival stair. */
1525
+ const createDownStair = ctx.createStair === "down";
1526
+ const arrival = createDownStair ? right : left;
1527
+ const pspot = newPlayerSpot(arrival, ctx.createStair ?? null);
1528
+ if (!pspot)
1529
+ return { gen: null, error: "could not place player" };
1530
+ arrival.playerSpot = pspot;
1531
+ /* Player location relative to each cavern (matching the chunk_copy offsets). */
1532
+ let pLocInR;
1533
+ let pLocInL;
1534
+ if (arrival === right) {
1535
+ pLocInR = pspot;
1536
+ pLocInL = loc(line2 + pspot.x, pspot.y);
1537
+ }
1538
+ else {
1539
+ pLocInL = pspot;
1540
+ pLocInR = loc(pspot.x - line2, pspot.y);
1541
+ }
1542
+ /* Monsters for the left cavern. */
1543
+ let lmon = constants.levelMonsterMin + rng.randint1(4) + k;
1544
+ for (; lmon > 0; lmon--)
1545
+ pickAndPlaceDistantMonster(left, pLocInL, 0, true, left.c.depth);
1546
+ /* Monsters for the right cavern. */
1547
+ let rmon = constants.levelMonsterMin + rng.randint1(4) + k;
1548
+ for (; rmon > 0; rmon--)
1549
+ pickAndPlaceDistantMonster(right, pLocInR, 0, true, right.c.depth);
1550
+ /* A larger number of themed monsters for the gauntlet. */
1551
+ const gMon = constants.levelMonsterMin + rng.randint1(6) + k;
1552
+ const pits = ctx.monDeps?.pits;
1553
+ if (ctx.monDeps && pits) {
1554
+ const table = ctx.monDeps.table;
1555
+ const gDepth = gauntlet.c.depth;
1556
+ let pit = pits[0];
1557
+ for (;;) {
1558
+ pit = setPitType(rng, pits, gDepth, 0);
1559
+ if (monRestrict(rng, table, [], pits, pit.name, gDepth, gDepth, true).ok)
1560
+ break;
1561
+ }
1562
+ spreadMonsters(gauntlet, [], pits, pit.name, gDepth, gMon, Math.trunc(gauntlet.c.height / 2), Math.trunc(gauntlet.c.width / 2), Math.trunc(gauntlet.c.height / 2), Math.trunc(gauntlet.c.width / 2),
1563
+ /* spread_monsters (gen-cave.c L3918): gauntlet monsters carry
1564
+ * ORIGIN_CAVERN (not counted by monster_death for lore treasure). */
1565
+ ORIGIN.CAVERN);
1566
+ monRestrict(rng, table, [], pits, null, gDepth, gDepth, false);
1567
+ }
1568
+ /* Assemble the level. */
1569
+ const c = new Chunk(reg, ySize, left.c.width + gauntlet.c.width + right.c.width);
1570
+ c.depth = depth;
1571
+ const g = makeGen(ctx, c);
1572
+ fillRectangle(c, 0, 0, c.height - 1, c.width - 1, FEAT.GRANITE, SQUARE.NONE);
1573
+ /* Permanent rock between the caverns. */
1574
+ fillRectangle(c, 0, line1, c.height - 1, line2 - 1, FEAT.PERM, SQUARE.NONE);
1575
+ chunkCopy(g, left, 0, 0, 0);
1576
+ chunkCopy(g, gauntlet, Math.trunc((ySize - gauntlet.c.height) / 2), line1, 0);
1577
+ chunkCopy(g, right, 0, line2, 0);
1578
+ drawRectangle(c, 0, 0, c.height - 1, c.width - 1, FEAT.PERM, SQUARE.NONE, true);
1579
+ ensureConnectedness(g, true);
1580
+ allocObjects(g, SET_CORR, TYP_RUBBLE, rng.randint1(k), c.depth, 0);
1581
+ allocObjects(g, SET_CORR, TYP_TRAP, rng.randint1(k), c.depth, 0);
1582
+ allocObjects(g, SET_ROOM, TYP_OBJECT, rng.randNormal(constants.roomItemAv, 3), c.depth, ORIGIN.FLOOR);
1583
+ allocObjects(g, SET_BOTH, TYP_OBJECT, rng.randNormal(constants.bothItemAv, 3), c.depth, ORIGIN.FLOOR);
1584
+ allocObjects(g, SET_BOTH, TYP_GOLD, rng.randNormal(constants.bothGoldAv, 3), c.depth, ORIGIN.FLOOR);
1585
+ return { gen: g, error: null };
1586
+ };
1587
+ /* ------------------------------------------------------------------ *
1588
+ * Hard-centre generator (gen-cave.c hard_centre_gen L3291): a greater vault at
1589
+ * the centre, surrounded by four caverns (upper, lower, left, right) all joined
1590
+ * together by connect_caverns and then to the vault by ensure_connectedness.
1591
+ * ------------------------------------------------------------------ */
1592
+ export const hardCentreGen = (ctx) => {
1593
+ const { rng, reg, constants, dun, depth } = ctx;
1594
+ /* A factory that mints a Gen bound to a fresh sub-chunk (Gen/Chunk
1595
+ * construction stays here; vault_chunk lives in room.ts). */
1596
+ const makeSubGen = (h, w, d) => {
1597
+ const cc = new Chunk(reg, h, w);
1598
+ cc.depth = d;
1599
+ return makeGen(ctx, cc);
1600
+ };
1601
+ /* Make a greater vault for the centre (gen-cave.c L3295). */
1602
+ const centre = vaultChunk(rng, ctx.rooms.vaults, depth, makeSubGen);
1603
+ if (!centre) {
1604
+ return { gen: null, error: "could not create the centre vault" };
1605
+ }
1606
+ /* No persistent levels of this type for now (gen-cave.c L3314). */
1607
+ if (dun.persist) {
1608
+ return { gen: null, error: "no hard centre levels in persistent dungeons" };
1609
+ }
1610
+ /*
1611
+ * Carve out entrances to the vault. Only use one if there aren't explicitly
1612
+ * marked entrances since those vaults typically have empty space about them
1613
+ * and the extra entrances aren't useful (gen-cave.c L3327).
1614
+ */
1615
+ const ent0 = dun.entN[0] ?? 0;
1616
+ const k0 = 1 + (ent0 > 0 ? rng.randint1(3) : 0);
1617
+ dun.wallN = 0;
1618
+ for (let i = 0; i < k0; i++) {
1619
+ let grid;
1620
+ if (ent0 === 0) {
1621
+ /* No explicitly marked entrances: look for a SQUARE_WALL_OUTER grid. */
1622
+ const found = caveFind(centre.c, rng, (cc, gr) => squareIswallOuter(cc, gr));
1623
+ if (!found) {
1624
+ if (i === 0) {
1625
+ return {
1626
+ gen: null,
1627
+ error: "no SQUARE_WALL_OUTER grid for an entrance to the centre vault",
1628
+ };
1629
+ }
1630
+ break;
1631
+ }
1632
+ grid = found;
1633
+ }
1634
+ else {
1635
+ grid = chooseRandomEntrance(centre, 0, null, 0, dun.wall.slice(0, i));
1636
+ if (grid.x === 0 && grid.y === 0) {
1637
+ if (i === 0) {
1638
+ return {
1639
+ gen: null,
1640
+ error: "random selection of entrance to the centre vault failed",
1641
+ };
1642
+ }
1643
+ break;
1644
+ }
1645
+ }
1646
+ /* Store position in dun->wall, mark neighbors invalid, and floor it. */
1647
+ pierceOuterWall(centre, grid);
1648
+ centre.c.setFeat(grid, FEAT.FLOOR);
1649
+ }
1650
+ /* Measure the vault, rotate to make it wider than it is high (L3368). */
1651
+ let rotate = 0;
1652
+ let centreCavernYpos;
1653
+ let centreCavernHgt;
1654
+ let centreCavernWid;
1655
+ if (centre.c.height > centre.c.width) {
1656
+ rotate = 1;
1657
+ centreCavernYpos = Math.trunc((constants.dungeonHgt - centre.c.width) / 2);
1658
+ centreCavernHgt = centre.c.width;
1659
+ centreCavernWid = centre.c.height;
1660
+ }
1661
+ else {
1662
+ centreCavernYpos = Math.trunc((constants.dungeonHgt - centre.c.height) / 2);
1663
+ centreCavernHgt = centre.c.height;
1664
+ centreCavernWid = centre.c.width;
1665
+ }
1666
+ const upperCavernHgt = centreCavernYpos;
1667
+ const lowerCavernHgt = constants.dungeonHgt - upperCavernHgt - centreCavernHgt;
1668
+ const lowerCavernYpos = centreCavernYpos + centreCavernHgt;
1669
+ /* Make the caverns (gen-cave.c L3385): upper, lower, then left, right. The
1670
+ * upstream `join` argument is NULL for all four (non-persistent). */
1671
+ const upperCavern = cavernChunk(ctx, depth, upperCavernHgt, centreCavernWid, []);
1672
+ const lowerCavern = cavernChunk(ctx, depth, lowerCavernHgt, centreCavernWid, []);
1673
+ const leftCavernWid = Math.trunc((constants.dungeonWid - centreCavernWid) / 2);
1674
+ const rightCavernWid = constants.dungeonWid - leftCavernWid - centreCavernWid;
1675
+ const leftCavern = cavernChunk(ctx, depth, constants.dungeonHgt, leftCavernWid, []);
1676
+ const rightCavern = cavernChunk(ctx, depth, constants.dungeonHgt, rightCavernWid, []);
1677
+ /* Return on failure (gen-cave.c L3398). The C-side artifact/monster cleanup
1678
+ * is not needed here: the sub-chunks are simply discarded. */
1679
+ if (!upperCavern || !lowerCavern || !leftCavern || !rightCavern) {
1680
+ return { gen: null, error: "could not create one or more of the surrounding caverns" };
1681
+ }
1682
+ /* Make a cave to copy them into, and find a floor square in each cavern. */
1683
+ const c = new Chunk(reg, constants.dungeonHgt, constants.dungeonWid);
1684
+ c.depth = depth;
1685
+ const g = makeGen(ctx, c);
1686
+ const floor = [loc(0, 0), loc(0, 0), loc(0, 0), loc(0, 0)];
1687
+ /* find_empty_range mirrors the C single reused `grid` local: on the (never
1688
+ * hit in practice) failure it keeps the previous value. */
1689
+ let grid = loc(0, 0);
1690
+ const findEmptyRange = (tl, br) => {
1691
+ const found = caveFindInRange(c, rng, tl, br, (_cc, gr) => squareIsEmpty(g, gr));
1692
+ if (found)
1693
+ grid = found;
1694
+ };
1695
+ /* Left. */
1696
+ chunkCopy(g, leftCavern, 0, 0, 0);
1697
+ findEmptyRange(loc(0, 0), loc(leftCavernWid - 1, constants.dungeonHgt - 1));
1698
+ floor[0] = grid;
1699
+ /* Upper. */
1700
+ chunkCopy(g, upperCavern, 0, leftCavernWid, 0);
1701
+ findEmptyRange(loc(leftCavernWid, 0), loc(leftCavernWid + centreCavernWid - 1, upperCavernHgt - 1));
1702
+ floor[1] = grid;
1703
+ /* Centre. */
1704
+ chunkCopy(g, centre, centreCavernYpos, leftCavernWid, rotate);
1705
+ /* Lower. */
1706
+ chunkCopy(g, lowerCavern, lowerCavernYpos, leftCavernWid, 0);
1707
+ findEmptyRange(loc(leftCavernWid, lowerCavernYpos), loc(leftCavernWid + centreCavernWid - 1, constants.dungeonHgt - 1));
1708
+ floor[3] = grid;
1709
+ /* Right. */
1710
+ chunkCopy(g, rightCavern, 0, leftCavernWid + centreCavernWid, 0);
1711
+ findEmptyRange(loc(leftCavernWid + centreCavernWid, 0), loc(constants.dungeonWid - 1, constants.dungeonHgt - 1));
1712
+ floor[2] = grid;
1713
+ /* Encase in perma-rock (gen-cave.c L3457). */
1714
+ drawRectangle(c, 0, 0, c.height - 1, c.width - 1, FEAT.PERM, SQUARE.NONE, true);
1715
+ /* Connect up all the caverns, then to the centre entrances. */
1716
+ connectCaverns(g, floor);
1717
+ ensureConnectedness(g, false);
1718
+ const cavernArea = (leftCavernWid + rightCavernWid) * constants.dungeonHgt +
1719
+ centreCavernWid * (upperCavernHgt + lowerCavernHgt);
1720
+ /* Place 2-3 down stairs and 1-2 up stairs near some walls (minsep 0, so the
1721
+ * avoid-list is unused; NULL upstream). */
1722
+ allocStairs(g, FEAT.MORE, rng.randRange(1, 3), 0, false, [], dun.quest);
1723
+ allocStairs(g, FEAT.LESS, rng.randRange(1, 2), 0, false, [], dun.quest);
1724
+ /* Generate some rubble, traps and monsters, scaled by total cavern size. */
1725
+ let k = Math.max(Math.min(Math.trunc(c.depth / 3), 10), 2);
1726
+ k = Math.trunc((k * cavernArea) / (constants.dungeonHgt * constants.dungeonWid));
1727
+ allocObjects(g, SET_BOTH, TYP_RUBBLE, rng.randint1(k), c.depth, 0);
1728
+ allocObjects(g, SET_CORR, TYP_TRAP, rng.randint1(k), c.depth, 0);
1729
+ /* Determine the character location. */
1730
+ const pspot = newPlayerSpot(g, ctx.createStair ?? null);
1731
+ if (!pspot)
1732
+ return { gen: null, error: "could not place player" };
1733
+ g.playerSpot = pspot;
1734
+ let mcount = rng.randint1(8) + k;
1735
+ for (; mcount > 0; mcount--)
1736
+ pickAndPlaceDistantMonster(g, pspot, 0, true, c.depth);
1737
+ allocObjects(g, SET_BOTH, TYP_OBJECT, rng.randNormal(k, 2), c.depth + 5, ORIGIN.CAVERN);
1738
+ allocObjects(g, SET_BOTH, TYP_GOLD, rng.randNormal(Math.trunc(k / 2), 2), c.depth, ORIGIN.CAVERN);
1739
+ allocObjects(g, SET_BOTH, TYP_GOOD, rng.randint0(Math.trunc(k / 4)), c.depth, ORIGIN.CAVERN);
1740
+ return { gen: g, error: null };
1741
+ };
1742
+ /**
1743
+ * The eight town entrance features, in store order: the seven shops plus the
1744
+ * player's Home. Their FEAT_* indices are the store_at keys the store runtime
1745
+ * looks a store up by.
1746
+ */
1747
+ export const TOWN_STORE_FEATS = [
1748
+ FEAT.STORE_GENERAL,
1749
+ FEAT.STORE_ARMOR,
1750
+ FEAT.STORE_WEAPON,
1751
+ FEAT.STORE_BOOK,
1752
+ FEAT.STORE_ALCHEMY,
1753
+ FEAT.STORE_MAGIC,
1754
+ FEAT.STORE_BLACK,
1755
+ FEAT.HOME,
1756
+ ];
1757
+ /* ------------------------------------------------------------------ *
1758
+ * Faithful town generation (gen-cave.c town_gen_layout / town_gen).
1759
+ *
1760
+ * Every RNG draw is reproduced in the exact upstream order and count. The
1761
+ * two subtleties that would otherwise diverge the seeded stream:
1762
+ * 1. Angband's MIN/MAX (h-basic.h) are macros - (a)>(b)?(b):(a) etc - that
1763
+ * RE-EVALUATE the selected argument. When a randint0 lives inside a MIN/MAX
1764
+ * argument it is drawn once for the comparison and AGAIN when that branch
1765
+ * is chosen. macroMin/macroMax below model this with a thunk for the
1766
+ * side-effecting argument.
1767
+ * 2. build_ruin's `!randint0(3)` is the first operand of an && so it is drawn
1768
+ * for every non-building grid before the floor/perimeter tests.
1769
+ * ------------------------------------------------------------------ */
1770
+ /** MAX(a, b) = (a < b) ? b : a with the C-macro re-evaluation of b. */
1771
+ function macroMax(a, bThunk) {
1772
+ const b = bThunk();
1773
+ return a < b ? bThunk() : a;
1774
+ }
1775
+ /** MIN(a, b) = (a > b) ? b : a with the C-macro re-evaluation of b. */
1776
+ function macroMin(a, bThunk) {
1777
+ const b = bThunk();
1778
+ return a > b ? bThunk() : a;
1779
+ }
1780
+ /** get_lot_bounds (gen-cave.c L2243): the grid bounds of a town lot. */
1781
+ function getLotBounds(c, xroads, lot, lotWid, lotHgt) {
1782
+ /* 0 is the road; no lots. */
1783
+ if (lot.x === 0 || lot.y === 0)
1784
+ return { west: 0, north: 0, east: 0, south: 0 };
1785
+ let west;
1786
+ let east;
1787
+ let north;
1788
+ let south;
1789
+ if (lot.x < 0) {
1790
+ west = Math.max(2, xroads.x - 1 + lot.x * lotWid);
1791
+ east = Math.min(c.width - 3, xroads.x - 2 + (lot.x + 1) * lotWid);
1792
+ }
1793
+ else {
1794
+ west = Math.max(2, xroads.x + 2 + (lot.x - 1) * lotWid);
1795
+ east = Math.min(c.width - 3, xroads.x + 1 + lot.x * lotWid);
1796
+ }
1797
+ if (lot.y < 0) {
1798
+ north = Math.max(2, xroads.y + lot.y * lotHgt);
1799
+ south = Math.min(c.height - 3, xroads.y - 1 + (lot.y + 1) * lotHgt);
1800
+ }
1801
+ else {
1802
+ north = Math.max(2, xroads.y + 2 + (lot.y - 1) * lotHgt);
1803
+ south = Math.min(c.height - 3, xroads.y + 1 + lot.y * lotHgt);
1804
+ }
1805
+ return { west, north, east, south };
1806
+ }
1807
+ /** lot_is_clear (gen-cave.c L2273): the lot is big enough and all floor. */
1808
+ function lotIsClear(c, xroads, lot, lotWid, lotHgt) {
1809
+ const b = getLotBounds(c, xroads, lot, lotWid, lotHgt);
1810
+ if (b.east - b.west < lotWid - 1 || b.south - b.north < lotHgt - 1)
1811
+ return false;
1812
+ for (let x = b.west; x <= b.east; x++) {
1813
+ for (let y = b.north; y <= b.south; y++) {
1814
+ if (!c.isFloor(loc(x, y)))
1815
+ return false;
1816
+ }
1817
+ }
1818
+ return true;
1819
+ }
1820
+ /** lot_has_shop (gen-cave.c L2295): the lot already contains a shop entrance. */
1821
+ function lotHasShop(c, xroads, lot, lotWid, lotHgt) {
1822
+ const b = getLotBounds(c, xroads, lot, lotWid, lotHgt);
1823
+ for (let x = b.west; x <= b.east; x++) {
1824
+ for (let y = b.north; y <= b.south; y++) {
1825
+ if (c.isShop(loc(x, y)))
1826
+ return true;
1827
+ }
1828
+ }
1829
+ return false;
1830
+ }
1831
+ /** build_store (gen-cave.c L2322): lay one store's building + door for shop n. */
1832
+ function buildStore(g, n, xroads, lot, lotWid, lotHgt) {
1833
+ const c = g.c;
1834
+ const rng = g.rng;
1835
+ const { west: lotW, north: lotN, east: lotE, south: lotS } = getLotBounds(c, xroads, lot, lotWid, lotHgt);
1836
+ let doorX = 0;
1837
+ let doorY = 0;
1838
+ let buildW = 0;
1839
+ let buildN = 0;
1840
+ let buildE = 0;
1841
+ let buildS = 0;
1842
+ if (lot.x < -1 || lot.x > 1) {
1843
+ /* on the east west street */
1844
+ if (lot.y === -1) {
1845
+ /* north side of street */
1846
+ doorY = macroMax(lotN + 1, () => lotS - rng.randint0(2));
1847
+ buildS = doorY;
1848
+ buildN = doorY - 2;
1849
+ }
1850
+ else {
1851
+ /* south side */
1852
+ doorY = macroMin(lotS - 1, () => lotN + rng.randint0(2));
1853
+ buildN = doorY;
1854
+ buildS = doorY + 2;
1855
+ }
1856
+ doorX = rng.randRange(lotW + 1, lotE - 2);
1857
+ buildW = rng.randRange(Math.max(lotW, doorX - 2), doorX);
1858
+ if (!c.isFloor(loc(buildW - 1, doorY))) {
1859
+ buildW++;
1860
+ doorX = Math.max(doorX, buildW);
1861
+ }
1862
+ buildE = rng.randRange(buildW + 2, Math.min(doorX + 2, lotE));
1863
+ if (buildE - buildW > 1 && !c.isFloor(loc(buildE + 1, doorY))) {
1864
+ buildE--;
1865
+ doorX = Math.min(doorX, buildE);
1866
+ }
1867
+ }
1868
+ else if (lot.y < -1 || lot.y > 1) {
1869
+ /* on the north - south street */
1870
+ if (lot.x === -1) {
1871
+ /* west side of street */
1872
+ doorX = macroMax(lotW + 1, () => lotE - rng.randint0(2) - rng.randint0(2));
1873
+ buildE = doorX;
1874
+ buildW = doorX - 2;
1875
+ }
1876
+ else {
1877
+ /* east side */
1878
+ doorX = macroMin(lotE - 1, () => lotW + rng.randint0(2) + rng.randint0(2));
1879
+ buildW = doorX;
1880
+ buildE = doorX + 2;
1881
+ }
1882
+ doorY = rng.randRange(lotN, lotS - 1);
1883
+ buildN = rng.randRange(Math.max(lotN, doorY - 2), doorY);
1884
+ if (!c.isFloor(loc(doorX, buildN - 1))) {
1885
+ buildN++;
1886
+ doorY = Math.max(doorY, buildN);
1887
+ }
1888
+ buildS = rng.randRange(Math.max(buildN + 1, doorY), Math.min(lotS, doorY + 2));
1889
+ if (buildS - buildN > 1 && !c.isFloor(loc(doorX, buildS + 1))) {
1890
+ buildS--;
1891
+ doorY = Math.min(doorY, buildS);
1892
+ }
1893
+ }
1894
+ else {
1895
+ /* corner store */
1896
+ if (lot.x < 0) {
1897
+ /* west side */
1898
+ doorX = lotE - 1 - rng.randint0(2);
1899
+ buildE = macroMin(lotE, () => doorX + rng.randint0(2));
1900
+ buildW = rng.randRange(Math.max(lotW, doorX - 2), buildE - 2);
1901
+ }
1902
+ else {
1903
+ /* east side */
1904
+ doorX = lotW + 1 + rng.randint0(2);
1905
+ buildW = macroMax(lotW, () => doorX - rng.randint0(2));
1906
+ buildE = rng.randRange(buildW + 2, Math.min(lotE, doorX + 2));
1907
+ }
1908
+ if (lot.y < 0) {
1909
+ /* north side */
1910
+ doorY = lotS - rng.randint0(2);
1911
+ if (buildE === doorX || buildW === doorX) {
1912
+ buildS = doorY + rng.randint0(2);
1913
+ }
1914
+ else {
1915
+ /* Avoid encapsulating door */
1916
+ buildS = doorY;
1917
+ }
1918
+ buildN = Math.max(lotN, doorY - 2);
1919
+ if (buildS - buildN > 1 && !c.isFloor(loc(doorX, buildN - 1))) {
1920
+ buildN++;
1921
+ doorY = Math.max(buildN, doorY);
1922
+ }
1923
+ }
1924
+ else {
1925
+ /* south side */
1926
+ doorY = lotN + rng.randint0(2);
1927
+ if (buildE === doorX || buildW === doorX) {
1928
+ buildN = doorY - rng.randint0(2);
1929
+ }
1930
+ else {
1931
+ /* Avoid encapsulating door */
1932
+ buildN = doorY;
1933
+ }
1934
+ buildS = Math.min(lotS, doorY + 2);
1935
+ if (buildS - buildN > 1 && !c.isFloor(loc(doorX, buildS + 1))) {
1936
+ buildS--;
1937
+ doorY = Math.min(buildS, doorY);
1938
+ }
1939
+ }
1940
+ /* Avoid placing buildings without space between them */
1941
+ if (lot.x < 0 && buildE - buildW > 1 && !c.isFloor(loc(buildW - 1, doorY))) {
1942
+ buildW++;
1943
+ doorX = Math.max(doorX, buildW);
1944
+ }
1945
+ else if (lot.x > 0 &&
1946
+ buildE - buildW > 1 &&
1947
+ !c.isFloor(loc(buildE + 1, doorY))) {
1948
+ buildE--;
1949
+ doorX = Math.min(doorX, buildE);
1950
+ }
1951
+ }
1952
+ buildW = Math.max(buildW, lotW);
1953
+ buildE = Math.min(buildE, lotE);
1954
+ buildN = Math.max(buildN, lotN);
1955
+ buildS = Math.min(buildS, lotS);
1956
+ /* Build an invulnerable rectangular building */
1957
+ fillRectangle(c, buildN, buildW, buildS, buildE, FEAT.PERM, SQUARE.NONE);
1958
+ /* Clear previous contents, add a store door (the feature whose shopnum is
1959
+ * n + 1; TOWN_STORE_FEATS is that store-index -> feature mapping). */
1960
+ c.setFeat(loc(doorX, doorY), TOWN_STORE_FEATS[n]);
1961
+ }
1962
+ /** build_ruin (gen-cave.c L2461): a ruined granite building spewing rubble. */
1963
+ function buildRuin(g, xroads, lot, lotWid, lotHgt) {
1964
+ const c = g.c;
1965
+ const rng = g.rng;
1966
+ const b = getLotBounds(c, xroads, lot, lotWid, lotHgt);
1967
+ const lotWest = b.west;
1968
+ const lotNorth = b.north;
1969
+ const lotEast = b.east;
1970
+ const lotSouth = b.south;
1971
+ if (lotEast - lotWest < 1 || lotSouth - lotNorth < 1)
1972
+ return;
1973
+ /* make a building */
1974
+ const wid = rng.randRange(1, lotWid - 2);
1975
+ const hgt = rng.randRange(1, lotHgt - 2);
1976
+ const offsetX = rng.randRange(1, lotWid - 1 - wid);
1977
+ const offsetY = rng.randRange(1, lotHgt - 1 - hgt);
1978
+ const west = lotWest + offsetX;
1979
+ const north = lotNorth + offsetY;
1980
+ const south = lotSouth - (lotHgt - (hgt + offsetY));
1981
+ const east = lotEast - (lotWid - (wid + offsetX));
1982
+ fillRectangle(c, north, west, south, east, FEAT.GRANITE, SQUARE.NONE);
1983
+ /* and then destroy it and spew rubble everywhere */
1984
+ for (let x = lotWest; x <= lotEast; x++) {
1985
+ for (let y = lotNorth; y <= lotSouth; y++) {
1986
+ if (x >= west && x <= east && y >= north && y <= south) {
1987
+ if (rng.randint0(4) === 0) {
1988
+ c.setFeat(loc(x, y), FEAT.RUBBLE);
1989
+ }
1990
+ }
1991
+ else if (rng.randint0(3) === 0 &&
1992
+ c.isFloor(loc(x, y)) &&
1993
+ /* Avoid placing rubble next to a store */
1994
+ (x > lotWest || x === 2 || !c.isPerm(loc(x - 1, y))) &&
1995
+ (x < lotEast || x === c.width - 2 || !c.isPerm(loc(x + 1, y))) &&
1996
+ (y > lotNorth || y === 2 || !c.isPerm(loc(x, y - 1))) &&
1997
+ (y < lotSouth || y === c.height - 2 || !c.isPerm(loc(x, y + 1)))) {
1998
+ c.setFeat(loc(x, y), FEAT.PASS_RUBBLE);
1999
+ }
2000
+ }
2001
+ }
2002
+ }
2003
+ /**
2004
+ * cave_illuminate (cave-map.c L555), the RNG-free flag-setting subset used at
2005
+ * generation time: light (SQUARE_GLOW) every grid by day; by night keep only
2006
+ * non-floor / bright terrain lit. Shop doorways are always lit. The
2007
+ * player-knowledge side (square_memorize / square_forget) is a runtime concern
2008
+ * and is applied by the live game (game/known.ts caveIlluminateKnown), not
2009
+ * here. Exported so the runtime day/night boundary (game/loop.ts
2010
+ * processWorld, via the WorldClockEnv.caveIlluminate hook installed in
2011
+ * session/game.ts) can reuse this exact flag pass.
2012
+ */
2013
+ export function caveIlluminate(c, daytime) {
2014
+ for (let y = 0; y < c.height; y++) {
2015
+ for (let x = 0; x < c.width; x++) {
2016
+ const grid = loc(x, y);
2017
+ if (daytime || !c.isFloor(grid)) {
2018
+ c.sqinfoOn(grid, SQUARE.GLOW);
2019
+ }
2020
+ else if (!featIsBright(c.features, c.feat(grid))) {
2021
+ c.sqinfoOff(grid, SQUARE.GLOW);
2022
+ }
2023
+ }
2024
+ }
2025
+ /* Light shop doorways. */
2026
+ for (let y = 0; y < c.height; y++) {
2027
+ for (let x = 0; x < c.width; x++) {
2028
+ const grid = loc(x, y);
2029
+ if (!c.isShop(grid))
2030
+ continue;
2031
+ for (let i = 0; i < 8; i++) {
2032
+ const a = locSum(grid, DDGRID_DDD[i]);
2033
+ if (c.inBounds(a))
2034
+ c.sqinfoOn(a, SQUARE.GLOW);
2035
+ }
2036
+ }
2037
+ }
2038
+ }
2039
+ /**
2040
+ * town_gen_layout (gen-cave.c L2515): build the town for the first time and
2041
+ * return the north-wall crossroads head (pgrid) - where the single down stair
2042
+ * sits and where the player is placed. Ported statement-by-statement so the
2043
+ * RNG stream matches upstream draw-for-draw. Returns the player/stair grid.
2044
+ */
2045
+ function townGenLayout(g) {
2046
+ const c = g.c;
2047
+ const rng = g.rng;
2048
+ const townWid = c.width; /* z_info->town_wid */
2049
+ const townHgt = c.height; /* z_info->town_hgt */
2050
+ const storeMax = TOWN_STORE_FEATS.length; /* z_info->store_max */
2051
+ const numLava = 3 + rng.randint0(3);
2052
+ const ruinsPercent = 80;
2053
+ const maxAttempts = 100;
2054
+ const lotHgt = 4;
2055
+ const lotWid = 6;
2056
+ /* Declared outside the retry loop; NOT reset on a retry (upstream). */
2057
+ let maxStoreY = 0;
2058
+ let minStoreX = townWid;
2059
+ let maxStoreX = 0;
2060
+ /* Create walls */
2061
+ drawRectangle(c, 0, 0, c.height - 1, c.width - 1, FEAT.PERM, SQUARE.NONE, true);
2062
+ let pgrid = loc(0, 0);
2063
+ let xroads = loc(0, 0);
2064
+ let success = false;
2065
+ while (!success) {
2066
+ /* Initialize to ROCK for build_streamer precondition */
2067
+ for (let y = 1; y < c.height - 1; y++) {
2068
+ for (let x = 1; x < c.width - 1; x++) {
2069
+ c.setFeat(loc(x, y), FEAT.GRANITE);
2070
+ }
2071
+ }
2072
+ /* Make some lava streamers */
2073
+ for (let n = 0; n < 3 + numLava; n++)
2074
+ buildStreamer(g, FEAT.LAVA, 0);
2075
+ /* Make a town-sized starburst room. */
2076
+ generateStarburstRoom(g, 0, 0, c.height - 1, c.width - 1, false, FEAT.FLOOR, false);
2077
+ /* Turn off room illumination flag */
2078
+ for (let y = 1; y < c.height - 1; y++) {
2079
+ for (let x = 1; x < c.width - 1; x++) {
2080
+ c.sqinfoOff(loc(x, y), SQUARE.ROOM);
2081
+ }
2082
+ }
2083
+ /* Stairs along north wall */
2084
+ const px = rng.randSpread(Math.trunc(townWid / 2), Math.trunc(townWid / 6));
2085
+ let py = 1;
2086
+ while (!c.isFloor(loc(px, py)) && py < Math.trunc(townHgt / 4))
2087
+ py++;
2088
+ if (py >= Math.trunc(townHgt / 4))
2089
+ continue;
2090
+ pgrid = loc(px, py);
2091
+ /* no lava next to stairs */
2092
+ for (let x = px - 1; x <= px + 1; x++) {
2093
+ for (let y = py - 1; y <= py + 1; y++) {
2094
+ if (c.isFiery(loc(x, y)))
2095
+ c.setFeat(loc(x, y), FEAT.GRANITE);
2096
+ }
2097
+ }
2098
+ const xrx = px;
2099
+ const xry = Math.trunc(townHgt / 2) -
2100
+ rng.randint0(Math.trunc(townHgt / 4)) +
2101
+ rng.randint0(Math.trunc(townHgt / 8));
2102
+ xroads = loc(xrx, xry);
2103
+ const lotMinX = Math.trunc((-1 * xrx) / lotWid);
2104
+ const lotMaxX = Math.trunc((townWid - xrx) / lotWid);
2105
+ const lotMinY = Math.trunc((-1 * xry) / lotHgt);
2106
+ const lotMaxY = Math.trunc((townHgt - xry) / lotHgt);
2107
+ /* place stores along the streets */
2108
+ let numAttempts = 0;
2109
+ let exhausted = false;
2110
+ for (let n = 0; n < storeMax; n++) {
2111
+ let storeLot = loc(0, 0);
2112
+ let foundSpot = false;
2113
+ while (!foundSpot && numAttempts < maxAttempts) {
2114
+ numAttempts++;
2115
+ if (rng.randint0(2)) {
2116
+ /* east-west street */
2117
+ const sx = rng.randRange(lotMinX, lotMaxX);
2118
+ const sy = rng.randint0(2) ? 1 : -1;
2119
+ storeLot = loc(sx, sy);
2120
+ }
2121
+ else {
2122
+ /* north-south street */
2123
+ const sx = rng.randint0(2) ? 1 : -1;
2124
+ const sy = rng.randRange(lotMinY, lotMaxY);
2125
+ storeLot = loc(sx, sy);
2126
+ }
2127
+ if (storeLot.y === 0 || storeLot.x === 0)
2128
+ continue;
2129
+ foundSpot = lotIsClear(c, xroads, storeLot, lotWid, lotHgt);
2130
+ }
2131
+ if (numAttempts >= maxAttempts) {
2132
+ exhausted = true;
2133
+ break;
2134
+ }
2135
+ maxStoreY = Math.max(maxStoreY, xry + lotHgt * storeLot.y);
2136
+ minStoreX = Math.min(minStoreX, xrx + lotWid * storeLot.x);
2137
+ maxStoreX = Math.max(maxStoreX, xrx + lotWid * storeLot.x);
2138
+ buildStore(g, n, xroads, storeLot, lotWid, lotHgt);
2139
+ }
2140
+ if (exhausted)
2141
+ continue;
2142
+ /* place ruins */
2143
+ for (let x = lotMinX; x <= lotMaxX; x++) {
2144
+ if (x === 0)
2145
+ continue; /* 0 is the street */
2146
+ for (let y = lotMinY; y <= lotMaxY; y++) {
2147
+ if (y === 0)
2148
+ continue;
2149
+ if (rng.randint0(100) > ruinsPercent)
2150
+ continue;
2151
+ if (rng.oneIn(2) && !lotHasShop(c, xroads, loc(x, y), lotWid, lotHgt)) {
2152
+ buildRuin(g, xroads, loc(x, y), lotWid, lotHgt);
2153
+ }
2154
+ }
2155
+ }
2156
+ success = true;
2157
+ }
2158
+ /* clear the street */
2159
+ c.setFeat(loc(pgrid.x, pgrid.y + 1), FEAT.FLOOR);
2160
+ fillRectangle(c, pgrid.y + 2, pgrid.x - 1, maxStoreY, pgrid.x + 1, FEAT.FLOOR, SQUARE.NONE);
2161
+ fillRectangle(c, xroads.y, minStoreX, xroads.y + 1, maxStoreX, FEAT.FLOOR, SQUARE.NONE);
2162
+ /* Clear previous contents, add down stairs */
2163
+ c.setFeat(pgrid, FEAT.MORE);
2164
+ return pgrid;
2165
+ }
2166
+ /**
2167
+ * chunk_write (gen-chunk.c:46-61): terrain + sqinfo only, for the town store.
2168
+ */
2169
+ export function chunkWriteTerrain(src) {
2170
+ const out = new Chunk(src.features, src.height, src.width);
2171
+ /* chunk_write "Town" name (generate.c:1371 / gen-chunk.c:46). */
2172
+ out.name = "Town";
2173
+ out.depth = src.depth;
2174
+ for (let y = 0; y < src.height; y++) {
2175
+ for (let x = 0; x < src.width; x++) {
2176
+ const grid = loc(x, y);
2177
+ out.setFeat(grid, src.feat(grid));
2178
+ out.info(grid).copy(src.info(grid));
2179
+ }
2180
+ }
2181
+ return out;
2182
+ }
2183
+ /**
2184
+ * The town builder (town_gen, gen-cave.c L2664): first-time layout via
2185
+ * town_gen_layout, or re-entry from a stored Town chunk (L2682-2703). Store
2186
+ * entrances are non-passable shop terrain. Day/night is honoured when the
2187
+ * caller supplies ctx.daytime; when omitted it defaults to daytime (turn 0).
2188
+ */
2189
+ export const townGen = (ctx) => {
2190
+ const { constants } = ctx;
2191
+ const daytime = ctx.daytime ?? true;
2192
+ const stored = ctx.townLayout ?? null;
2193
+ if (stored) {
2194
+ /* town_gen re-entry (gen-cave.c:2682-2703): copy stored terrain. */
2195
+ const c = new Chunk(ctx.reg, stored.height, stored.width);
2196
+ c.depth = stored.depth;
2197
+ const g = makeGen(ctx, c);
2198
+ for (let y = 0; y < stored.height; y++) {
2199
+ for (let x = 0; x < stored.width; x++) {
2200
+ const grid = loc(x, y);
2201
+ c.setFeat(grid, stored.feat(grid));
2202
+ c.info(grid).copy(stored.info(grid));
2203
+ }
2204
+ }
2205
+ /* Find the stairs (gen-cave.c:2691-2700). */
2206
+ let pgrid = loc(1, 1);
2207
+ outer: for (let y = 0; y < c.height; y++) {
2208
+ for (let x = 0; x < c.width; x++) {
2209
+ if (c.feat(loc(x, y)) === FEAT.MORE) {
2210
+ pgrid = loc(x, y);
2211
+ break outer;
2212
+ }
2213
+ }
2214
+ }
2215
+ g.playerSpot = pgrid;
2216
+ caveIlluminate(c, daytime);
2217
+ const residents = daytime
2218
+ ? constants.townMonstersDay
2219
+ : constants.townMonstersNight;
2220
+ for (let i = 0; i < residents; i++) {
2221
+ pickAndPlaceDistantMonster(g, pgrid, 3, true, c.depth);
2222
+ }
2223
+ return { gen: g, error: null };
2224
+ }
2225
+ const c = new Chunk(ctx.reg, constants.townHgt, constants.townWid);
2226
+ c.depth = ctx.depth;
2227
+ const g = makeGen(ctx, c);
2228
+ /* Build the layout and place the player at the crossroads head. */
2229
+ const pgrid = townGenLayout(g);
2230
+ g.playerSpot = pgrid;
2231
+ /* Apply illumination. */
2232
+ caveIlluminate(c, daytime);
2233
+ /* Make some residents. */
2234
+ const residents = daytime
2235
+ ? constants.townMonstersDay
2236
+ : constants.townMonstersNight;
2237
+ for (let i = 0; i < residents; i++) {
2238
+ pickAndPlaceDistantMonster(g, pgrid, 3, true, c.depth);
2239
+ }
2240
+ return { gen: g, error: null };
2241
+ };
2242
+ /* ------------------------------------------------------------------ *
2243
+ * Stairs (handle_level_stairs, gen-cave.c:943-967).
2244
+ * ------------------------------------------------------------------ */
2245
+ function handleLevelStairs(g, quest, downCount, upCount) {
2246
+ const dun = g.dun;
2247
+ const persistent = dun.persist;
2248
+ /*
2249
+ * For persistent levels, require the stairs be at least four grids apart so
2250
+ * the connecting level's staircase rooms won't overlap; for both cases also
2251
+ * require 1/4 of the shorter dimension so they don't all land in one room.
2252
+ */
2253
+ const minsep = Math.max(Math.trunc(Math.min(g.c.width, g.c.height) / 4), persistent ? 4 : 0);
2254
+ /* For a persistent level whose neighbour already exists, that neighbour's
2255
+ * staircase rooms already placed the matching stairs here via the seeded
2256
+ * dun.join, so skip alloc_stairs for that direction (gen-cave.c:959-966). */
2257
+ if (!persistent || !dun.hasAdjacentBelow) {
2258
+ allocStairs(g, FEAT.MORE, downCount, minsep, false, dun.oneOffBelow, quest);
2259
+ }
2260
+ if (!persistent || !dun.hasAdjacentAbove) {
2261
+ allocStairs(g, FEAT.LESS, upCount, minsep, false, dun.oneOffAbove, quest);
2262
+ }
2263
+ }
2264
+ /* ------------------------------------------------------------------ *
2265
+ * Context construction (attach profile tun/str shortcuts to Gen).
2266
+ * ------------------------------------------------------------------ */
2267
+ function makeGen(ctx, c) {
2268
+ const g = new Gen(c, ctx.rng, ctx.reg, ctx.constants, ctx.dun, ctx.objDeps, ctx.monDeps, ctx.trapKinds ?? null);
2269
+ /* Attach the current profile's tunnel/streamer parameters for the tunnel
2270
+ * and streamer helpers (they are read-only during a build). */
2271
+ g.profileTun = ctx.profile.tun;
2272
+ /* new_player_spot's placement-failure message (gen-util.c:422). */
2273
+ if (ctx.msg)
2274
+ g.msg = ctx.msg;
2275
+ g.profileStr = ctx.profile.str;
2276
+ /* dun->profile = choose_profile(p) (generate.c L1157): help_greater_vault
2277
+ * (gen-room.c L3099) compares dun->profile->name against "classic". */
2278
+ ctx.dun.profileName = ctx.profile.name;
2279
+ return g;
2280
+ }
2281
+ /* ------------------------------------------------------------------ *
2282
+ * Dungeon profile registry + selection (choose_profile).
2283
+ * ------------------------------------------------------------------ */
2284
+ /** labyrinth_check: d_m's prime-number labyrinth acceptance. */
2285
+ function labyrinthCheck(rng, depth) {
2286
+ let chance = 2;
2287
+ if (depth < 13)
2288
+ return false;
2289
+ if (depth % 3 === 0)
2290
+ chance += 1;
2291
+ if (depth % 5 === 0)
2292
+ chance += 1;
2293
+ if (depth % 7 === 0)
2294
+ chance += 1;
2295
+ if (depth % 11 === 0)
2296
+ chance += 1;
2297
+ if (depth % 13 === 0)
2298
+ chance += 1;
2299
+ if (rng.randint0(100) >= chance)
2300
+ return false;
2301
+ return true;
2302
+ }
2303
+ /**
2304
+ * The dungeon-profile registry: a string-keyed set of cave builders plus the
2305
+ * ordered profile list used for selection. Both are runtime-registrable so a
2306
+ * mod can add a builder and/or a whole new profile.
2307
+ */
2308
+ export class DungeonProfiles {
2309
+ builders = new Map();
2310
+ profiles = [];
2311
+ registerBuilder(key, builder) {
2312
+ this.builders.set(key, builder);
2313
+ }
2314
+ builder(key) {
2315
+ const b = this.builders.get(key);
2316
+ if (!b)
2317
+ throw new Error(`gen: no cave builder registered for '${key}'`);
2318
+ return b;
2319
+ }
2320
+ hasBuilder(key) {
2321
+ return this.builders.has(key);
2322
+ }
2323
+ addProfile(profile) {
2324
+ this.profiles.push(profile);
2325
+ }
2326
+ find(name) {
2327
+ return this.profiles.find((p) => p.name === name) ?? null;
2328
+ }
2329
+ list() {
2330
+ return this.profiles;
2331
+ }
2332
+ /** choose_profile: select a dungeon profile for the given depth. */
2333
+ choose(rng, depth, options = {}) {
2334
+ const quest = options.quest ?? false;
2335
+ const moria = this.find("moria");
2336
+ const labyrinth = this.find("labyrinth");
2337
+ const moriaAlloc = moria ? moria.alloc : 0;
2338
+ const labyrinthAlloc = labyrinth ? labyrinth.alloc : 0;
2339
+ /* The NOSCORE_JUMPING request, ahead of every other rule (L824-836). */
2340
+ if (options.name) {
2341
+ const named = this.find(options.name);
2342
+ if (named)
2343
+ return named;
2344
+ }
2345
+ let profile = null;
2346
+ if (depth === 0) {
2347
+ profile = this.find("town");
2348
+ }
2349
+ else if (quest) {
2350
+ profile = this.find("classic");
2351
+ }
2352
+ else if (labyrinthCheck(rng, depth) && (labyrinthAlloc > 0 || labyrinthAlloc === -1)) {
2353
+ profile = labyrinth;
2354
+ }
2355
+ else if (depth >= 10 && depth < 40 && rng.oneIn(40) && (moriaAlloc > 0 || moriaAlloc === -1)) {
2356
+ profile = moria;
2357
+ }
2358
+ else {
2359
+ let totalAlloc = 0;
2360
+ for (const test of this.profiles) {
2361
+ if (test.alloc <= 0 || depth < test.minLevel)
2362
+ continue;
2363
+ totalAlloc += test.alloc;
2364
+ if (rng.randint0(totalAlloc) < test.alloc)
2365
+ profile = test;
2366
+ }
2367
+ if (!profile)
2368
+ profile = this.find("classic");
2369
+ }
2370
+ if (!profile)
2371
+ profile = this.find("classic");
2372
+ if (!profile)
2373
+ throw new Error("gen: failed to find a cave profile");
2374
+ return profile;
2375
+ }
2376
+ }
2377
+ /**
2378
+ * Build the default dungeon-profile registry: a real cave builder for every
2379
+ * upstream key (town/classic/modified/labyrinth/cavern/moria/lair/gauntlet/
2380
+ * hard_centre - all faithful ports now) plus every dungeon_profile.txt profile,
2381
+ * loaded in file order so choose_profile's weighted selection matches upstream.
2382
+ * Mods extend both via registerBuilder/addProfile. (arena is quest-only and not
2383
+ * a dungeon profile; it lives with the deferred quest system.)
2384
+ */
2385
+ export function createDungeonProfiles(profileRecords) {
2386
+ const reg = new DungeonProfiles();
2387
+ reg.registerBuilder("classic", classicGen);
2388
+ reg.registerBuilder("modified", modifiedGen);
2389
+ reg.registerBuilder("town", townGen);
2390
+ reg.registerBuilder("labyrinth", labyrinthGen);
2391
+ reg.registerBuilder("cavern", cavernGen);
2392
+ reg.registerBuilder("moria", moriaGen);
2393
+ reg.registerBuilder("lair", lairGen);
2394
+ reg.registerBuilder("gauntlet", gauntletGen);
2395
+ reg.registerBuilder("hard_centre", hardCentreGen);
2396
+ /* Load EVERY dungeon profile (choose_profile, generate.c L813): town at
2397
+ * depth 0, quest -> classic, labyrinth_check and the depth 10-40 one_in_(40)
2398
+ * moria as forced/special, then the weighted alloc/min-level pass over the
2399
+ * rest. Profiles are added in dungeon_profile.txt order, which the weighted
2400
+ * loop's running-total randint0 relies on for faithful selection + RNG. */
2401
+ for (const rec of profileRecords) {
2402
+ reg.addProfile(loadDunProfile(rec));
2403
+ }
2404
+ return reg;
2405
+ }
2406
+ //# sourceMappingURL=cave.js.map