@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.
- package/LICENSE.md +43 -0
- package/README.md +80 -0
- package/dist/agent/act.d.ts +22 -0
- package/dist/agent/act.d.ts.map +1 -0
- package/dist/agent/act.js +88 -0
- package/dist/agent/act.js.map +1 -0
- package/dist/agent/controller.d.ts +34 -0
- package/dist/agent/controller.d.ts.map +1 -0
- package/dist/agent/controller.js +90 -0
- package/dist/agent/controller.js.map +1 -0
- package/dist/agent/events.d.ts +27 -0
- package/dist/agent/events.d.ts.map +1 -0
- package/dist/agent/events.js +31 -0
- package/dist/agent/events.js.map +1 -0
- package/dist/agent/index.d.ts +17 -0
- package/dist/agent/index.d.ts.map +1 -0
- package/dist/agent/index.js +15 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/agent/perceive.d.ts +21 -0
- package/dist/agent/perceive.d.ts.map +1 -0
- package/dist/agent/perceive.js +386 -0
- package/dist/agent/perceive.js.map +1 -0
- package/dist/agent/types.d.ts +436 -0
- package/dist/agent/types.d.ts.map +1 -0
- package/dist/agent/types.js +63 -0
- package/dist/agent/types.js.map +1 -0
- package/dist/bitflag.d.ts +199 -0
- package/dist/bitflag.d.ts.map +1 -0
- package/dist/bitflag.js +478 -0
- package/dist/bitflag.js.map +1 -0
- package/dist/cmd.d.ts +164 -0
- package/dist/cmd.d.ts.map +1 -0
- package/dist/cmd.js +350 -0
- package/dist/cmd.js.map +1 -0
- package/dist/color.d.ts +104 -0
- package/dist/color.d.ts.map +1 -0
- package/dist/color.js +240 -0
- package/dist/color.js.map +1 -0
- package/dist/combat/brand-slay.d.ts +119 -0
- package/dist/combat/brand-slay.d.ts.map +1 -0
- package/dist/combat/brand-slay.js +264 -0
- package/dist/combat/brand-slay.js.map +1 -0
- package/dist/combat/hit.d.ts +198 -0
- package/dist/combat/hit.d.ts.map +1 -0
- package/dist/combat/hit.js +310 -0
- package/dist/combat/hit.js.map +1 -0
- package/dist/combat/index.d.ts +15 -0
- package/dist/combat/index.d.ts.map +1 -0
- package/dist/combat/index.js +15 -0
- package/dist/combat/index.js.map +1 -0
- package/dist/combat/melee.d.ts +261 -0
- package/dist/combat/melee.d.ts.map +1 -0
- package/dist/combat/melee.js +402 -0
- package/dist/combat/melee.js.map +1 -0
- package/dist/combat/mon-melee.d.ts +233 -0
- package/dist/combat/mon-melee.d.ts.map +1 -0
- package/dist/combat/mon-melee.js +883 -0
- package/dist/combat/mon-melee.js.map +1 -0
- package/dist/combat/ranged.d.ts +89 -0
- package/dist/combat/ranged.d.ts.map +1 -0
- package/dist/combat/ranged.js +253 -0
- package/dist/combat/ranged.js.map +1 -0
- package/dist/constants.d.ts +134 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +171 -0
- package/dist/constants.js.map +1 -0
- package/dist/dice.d.ts +91 -0
- package/dist/dice.d.ts.map +1 -0
- package/dist/dice.js +424 -0
- package/dist/dice.js.map +1 -0
- package/dist/effects/effect-choice.d.ts +15 -0
- package/dist/effects/effect-choice.d.ts.map +1 -0
- package/dist/effects/effect-choice.js +18 -0
- package/dist/effects/effect-choice.js.map +1 -0
- package/dist/effects/effect-info.d.ts +124 -0
- package/dist/effects/effect-info.d.ts.map +1 -0
- package/dist/effects/effect-info.js +706 -0
- package/dist/effects/effect-info.js.map +1 -0
- package/dist/effects/effect.d.ts +165 -0
- package/dist/effects/effect.d.ts.map +1 -0
- package/dist/effects/effect.js +452 -0
- package/dist/effects/effect.js.map +1 -0
- package/dist/effects/handlers.d.ts +70 -0
- package/dist/effects/handlers.d.ts.map +1 -0
- package/dist/effects/handlers.js +394 -0
- package/dist/effects/handlers.js.map +1 -0
- package/dist/effects/interpreter.d.ts +291 -0
- package/dist/effects/interpreter.d.ts.map +1 -0
- package/dist/effects/interpreter.js +325 -0
- package/dist/effects/interpreter.js.map +1 -0
- package/dist/events.d.ts +188 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +138 -0
- package/dist/events.js.map +1 -0
- package/dist/expression.d.ts +83 -0
- package/dist/expression.d.ts.map +1 -0
- package/dist/expression.js +333 -0
- package/dist/expression.js.map +1 -0
- package/dist/game/cave-cmd.d.ts +195 -0
- package/dist/game/cave-cmd.d.ts.map +1 -0
- package/dist/game/cave-cmd.js +968 -0
- package/dist/game/cave-cmd.js.map +1 -0
- package/dist/game/char-sheet.d.ts +189 -0
- package/dist/game/char-sheet.d.ts.map +1 -0
- package/dist/game/char-sheet.js +381 -0
- package/dist/game/char-sheet.js.map +1 -0
- package/dist/game/chest.d.ts +94 -0
- package/dist/game/chest.d.ts.map +1 -0
- package/dist/game/chest.js +267 -0
- package/dist/game/chest.js.map +1 -0
- package/dist/game/context.d.ts +970 -0
- package/dist/game/context.d.ts.map +1 -0
- package/dist/game/context.js +353 -0
- package/dist/game/context.js.map +1 -0
- package/dist/game/curse-tick.d.ts +35 -0
- package/dist/game/curse-tick.d.ts.map +1 -0
- package/dist/game/curse-tick.js +100 -0
- package/dist/game/curse-tick.js.map +1 -0
- package/dist/game/describe.d.ts +20 -0
- package/dist/game/describe.d.ts.map +1 -0
- package/dist/game/describe.js +45 -0
- package/dist/game/describe.js.map +1 -0
- package/dist/game/display.d.ts +196 -0
- package/dist/game/display.d.ts.map +1 -0
- package/dist/game/display.js +684 -0
- package/dist/game/display.js.map +1 -0
- package/dist/game/dump-level.d.ts +37 -0
- package/dist/game/dump-level.d.ts.map +1 -0
- package/dist/game/dump-level.js +110 -0
- package/dist/game/dump-level.js.map +1 -0
- package/dist/game/effect-attack.d.ts +36 -0
- package/dist/game/effect-attack.d.ts.map +1 -0
- package/dist/game/effect-attack.js +694 -0
- package/dist/game/effect-attack.js.map +1 -0
- package/dist/game/effect-detect.d.ts +34 -0
- package/dist/game/effect-detect.d.ts.map +1 -0
- package/dist/game/effect-detect.js +456 -0
- package/dist/game/effect-detect.js.map +1 -0
- package/dist/game/effect-env.d.ts +77 -0
- package/dist/game/effect-env.d.ts.map +1 -0
- package/dist/game/effect-env.js +166 -0
- package/dist/game/effect-env.js.map +1 -0
- package/dist/game/effect-game-env.d.ts +76 -0
- package/dist/game/effect-game-env.d.ts.map +1 -0
- package/dist/game/effect-game-env.js +23 -0
- package/dist/game/effect-game-env.js.map +1 -0
- package/dist/game/effect-general.d.ts +98 -0
- package/dist/game/effect-general.d.ts.map +1 -0
- package/dist/game/effect-general.js +914 -0
- package/dist/game/effect-general.js.map +1 -0
- package/dist/game/effect-item.d.ts +124 -0
- package/dist/game/effect-item.d.ts.map +1 -0
- package/dist/game/effect-item.js +789 -0
- package/dist/game/effect-item.js.map +1 -0
- package/dist/game/effect-melee.d.ts +43 -0
- package/dist/game/effect-melee.d.ts.map +1 -0
- package/dist/game/effect-melee.js +477 -0
- package/dist/game/effect-melee.js.map +1 -0
- package/dist/game/effect-mon-origin.d.ts +67 -0
- package/dist/game/effect-mon-origin.d.ts.map +1 -0
- package/dist/game/effect-mon-origin.js +164 -0
- package/dist/game/effect-mon-origin.js.map +1 -0
- package/dist/game/effect-monster.d.ts +29 -0
- package/dist/game/effect-monster.d.ts.map +1 -0
- package/dist/game/effect-monster.js +281 -0
- package/dist/game/effect-monster.js.map +1 -0
- package/dist/game/effect-summon.d.ts +37 -0
- package/dist/game/effect-summon.d.ts.map +1 -0
- package/dist/game/effect-summon.js +113 -0
- package/dist/game/effect-summon.js.map +1 -0
- package/dist/game/effect-teleport.d.ts +130 -0
- package/dist/game/effect-teleport.d.ts.map +1 -0
- package/dist/game/effect-teleport.js +529 -0
- package/dist/game/effect-teleport.js.map +1 -0
- package/dist/game/effect-terrain.d.ts +91 -0
- package/dist/game/effect-terrain.d.ts.map +1 -0
- package/dist/game/effect-terrain.js +812 -0
- package/dist/game/effect-terrain.js.map +1 -0
- package/dist/game/energy.d.ts +45 -0
- package/dist/game/energy.d.ts.map +1 -0
- package/dist/game/energy.js +51 -0
- package/dist/game/energy.js.map +1 -0
- package/dist/game/equip-cmp.d.ts +138 -0
- package/dist/game/equip-cmp.d.ts.map +1 -0
- package/dist/game/equip-cmp.js +363 -0
- package/dist/game/equip-cmp.js.map +1 -0
- package/dist/game/floor.d.ts +182 -0
- package/dist/game/floor.d.ts.map +1 -0
- package/dist/game/floor.js +435 -0
- package/dist/game/floor.js.map +1 -0
- package/dist/game/gear.d.ts +408 -0
- package/dist/game/gear.d.ts.map +1 -0
- package/dist/game/gear.js +1037 -0
- package/dist/game/gear.js.map +1 -0
- package/dist/game/harness.d.ts +61 -0
- package/dist/game/harness.d.ts.map +1 -0
- package/dist/game/harness.js +198 -0
- package/dist/game/harness.js.map +1 -0
- package/dist/game/history.d.ts +49 -0
- package/dist/game/history.d.ts.map +1 -0
- package/dist/game/history.js +55 -0
- package/dist/game/history.js.map +1 -0
- package/dist/game/ignore-cmd.d.ts +42 -0
- package/dist/game/ignore-cmd.d.ts.map +1 -0
- package/dist/game/ignore-cmd.js +59 -0
- package/dist/game/ignore-cmd.js.map +1 -0
- package/dist/game/inscription-confirm.d.ts +80 -0
- package/dist/game/inscription-confirm.d.ts.map +1 -0
- package/dist/game/inscription-confirm.js +95 -0
- package/dist/game/inscription-confirm.js.map +1 -0
- package/dist/game/known.d.ts +297 -0
- package/dist/game/known.d.ts.map +1 -0
- package/dist/game/known.js +849 -0
- package/dist/game/known.js.map +1 -0
- package/dist/game/loop.d.ts +139 -0
- package/dist/game/loop.d.ts.map +1 -0
- package/dist/game/loop.js +631 -0
- package/dist/game/loop.js.map +1 -0
- package/dist/game/lore-color.d.ts +25 -0
- package/dist/game/lore-color.d.ts.map +1 -0
- package/dist/game/lore-color.js +95 -0
- package/dist/game/lore-color.js.map +1 -0
- package/dist/game/mon-cast.d.ts +150 -0
- package/dist/game/mon-cast.d.ts.map +1 -0
- package/dist/game/mon-cast.js +301 -0
- package/dist/game/mon-cast.js.map +1 -0
- package/dist/game/mon-cmd.d.ts +51 -0
- package/dist/game/mon-cmd.d.ts.map +1 -0
- package/dist/game/mon-cmd.js +670 -0
- package/dist/game/mon-cmd.js.map +1 -0
- package/dist/game/mon-death.d.ts +143 -0
- package/dist/game/mon-death.d.ts.map +1 -0
- package/dist/game/mon-death.js +328 -0
- package/dist/game/mon-death.js.map +1 -0
- package/dist/game/mon-group.d.ts +81 -0
- package/dist/game/mon-group.d.ts.map +1 -0
- package/dist/game/mon-group.js +323 -0
- package/dist/game/mon-group.js.map +1 -0
- package/dist/game/mon-list.d.ts +85 -0
- package/dist/game/mon-list.d.ts.map +1 -0
- package/dist/game/mon-list.js +154 -0
- package/dist/game/mon-list.js.map +1 -0
- package/dist/game/mon-message.d.ts +100 -0
- package/dist/game/mon-message.d.ts.map +1 -0
- package/dist/game/mon-message.js +336 -0
- package/dist/game/mon-message.js.map +1 -0
- package/dist/game/mon-place.d.ts +226 -0
- package/dist/game/mon-place.d.ts.map +1 -0
- package/dist/game/mon-place.js +642 -0
- package/dist/game/mon-place.js.map +1 -0
- package/dist/game/mon-ranged.d.ts +87 -0
- package/dist/game/mon-ranged.d.ts.map +1 -0
- package/dist/game/mon-ranged.js +353 -0
- package/dist/game/mon-ranged.js.map +1 -0
- package/dist/game/mon-shape.d.ts +43 -0
- package/dist/game/mon-shape.d.ts.map +1 -0
- package/dist/game/mon-shape.js +115 -0
- package/dist/game/mon-shape.js.map +1 -0
- package/dist/game/mon-side.d.ts +63 -0
- package/dist/game/mon-side.d.ts.map +1 -0
- package/dist/game/mon-side.js +403 -0
- package/dist/game/mon-side.js.map +1 -0
- package/dist/game/monster-turn.d.ts +195 -0
- package/dist/game/monster-turn.d.ts.map +1 -0
- package/dist/game/monster-turn.js +1560 -0
- package/dist/game/monster-turn.js.map +1 -0
- package/dist/game/obj-cmd.d.ts +453 -0
- package/dist/game/obj-cmd.d.ts.map +1 -0
- package/dist/game/obj-cmd.js +1530 -0
- package/dist/game/obj-cmd.js.map +1 -0
- package/dist/game/obj-list.d.ts +92 -0
- package/dist/game/obj-list.d.ts.map +1 -0
- package/dist/game/obj-list.js +251 -0
- package/dist/game/obj-list.js.map +1 -0
- package/dist/game/object-inspect.d.ts +37 -0
- package/dist/game/object-inspect.d.ts.map +1 -0
- package/dist/game/object-inspect.js +106 -0
- package/dist/game/object-inspect.js.map +1 -0
- package/dist/game/pickup.d.ts +126 -0
- package/dist/game/pickup.d.ts.map +1 -0
- package/dist/game/pickup.js +367 -0
- package/dist/game/pickup.js.map +1 -0
- package/dist/game/player-path.d.ts +133 -0
- package/dist/game/player-path.d.ts.map +1 -0
- package/dist/game/player-path.js +1268 -0
- package/dist/game/player-path.js.map +1 -0
- package/dist/game/player-side.d.ts +59 -0
- package/dist/game/player-side.d.ts.map +1 -0
- package/dist/game/player-side.js +506 -0
- package/dist/game/player-side.js.map +1 -0
- package/dist/game/player-turn.d.ts +161 -0
- package/dist/game/player-turn.d.ts.map +1 -0
- package/dist/game/player-turn.js +739 -0
- package/dist/game/player-turn.js.map +1 -0
- package/dist/game/project-cast.d.ts +268 -0
- package/dist/game/project-cast.d.ts.map +1 -0
- package/dist/game/project-cast.js +496 -0
- package/dist/game/project-cast.js.map +1 -0
- package/dist/game/project-feat.d.ts +47 -0
- package/dist/game/project-feat.d.ts.map +1 -0
- package/dist/game/project-feat.js +350 -0
- package/dist/game/project-feat.js.map +1 -0
- package/dist/game/project-monster.d.ts +106 -0
- package/dist/game/project-monster.d.ts.map +1 -0
- package/dist/game/project-monster.js +272 -0
- package/dist/game/project-monster.js.map +1 -0
- package/dist/game/project-obj.d.ts +60 -0
- package/dist/game/project-obj.d.ts.map +1 -0
- package/dist/game/project-obj.js +245 -0
- package/dist/game/project-obj.js.map +1 -0
- package/dist/game/project-player.d.ts +120 -0
- package/dist/game/project-player.d.ts.map +1 -0
- package/dist/game/project-player.js +108 -0
- package/dist/game/project-player.js.map +1 -0
- package/dist/game/quest.d.ts +101 -0
- package/dist/game/quest.d.ts.map +1 -0
- package/dist/game/quest.js +192 -0
- package/dist/game/quest.js.map +1 -0
- package/dist/game/ranged-cmd.d.ts +32 -0
- package/dist/game/ranged-cmd.d.ts.map +1 -0
- package/dist/game/ranged-cmd.js +372 -0
- package/dist/game/ranged-cmd.js.map +1 -0
- package/dist/game/scheduler.d.ts +47 -0
- package/dist/game/scheduler.d.ts.map +1 -0
- package/dist/game/scheduler.js +141 -0
- package/dist/game/scheduler.js.map +1 -0
- package/dist/game/spell-cmd.d.ts +104 -0
- package/dist/game/spell-cmd.d.ts.map +1 -0
- package/dist/game/spell-cmd.js +320 -0
- package/dist/game/spell-cmd.js.map +1 -0
- package/dist/game/spoil.d.ts +28 -0
- package/dist/game/spoil.d.ts.map +1 -0
- package/dist/game/spoil.js +435 -0
- package/dist/game/spoil.js.map +1 -0
- package/dist/game/steal.d.ts +35 -0
- package/dist/game/steal.d.ts.map +1 -0
- package/dist/game/steal.js +145 -0
- package/dist/game/steal.js.map +1 -0
- package/dist/game/take-hit-hooks.d.ts +52 -0
- package/dist/game/take-hit-hooks.d.ts.map +1 -0
- package/dist/game/take-hit-hooks.js +127 -0
- package/dist/game/take-hit-hooks.js.map +1 -0
- package/dist/game/target-loop.d.ts +119 -0
- package/dist/game/target-loop.d.ts.map +1 -0
- package/dist/game/target-loop.js +408 -0
- package/dist/game/target-loop.js.map +1 -0
- package/dist/game/target.d.ts +133 -0
- package/dist/game/target.d.ts.map +1 -0
- package/dist/game/target.js +353 -0
- package/dist/game/target.js.map +1 -0
- package/dist/game/thrust.d.ts +37 -0
- package/dist/game/thrust.d.ts.map +1 -0
- package/dist/game/thrust.js +155 -0
- package/dist/game/thrust.js.map +1 -0
- package/dist/game/trap.d.ts +218 -0
- package/dist/game/trap.d.ts.map +1 -0
- package/dist/game/trap.js +618 -0
- package/dist/game/trap.js.map +1 -0
- package/dist/game/ui-entry.d.ts +282 -0
- package/dist/game/ui-entry.d.ts.map +1 -0
- package/dist/game/ui-entry.js +1838 -0
- package/dist/game/ui-entry.js.map +1 -0
- package/dist/game/wizard.d.ts +654 -0
- package/dist/game/wizard.d.ts.map +1 -0
- package/dist/game/wizard.js +1323 -0
- package/dist/game/wizard.js.map +1 -0
- package/dist/game/world.d.ts +183 -0
- package/dist/game/world.d.ts.map +1 -0
- package/dist/game/world.js +685 -0
- package/dist/game/world.js.map +1 -0
- package/dist/gen/cave.d.ts +247 -0
- package/dist/gen/cave.d.ts.map +1 -0
- package/dist/gen/cave.js +2406 -0
- package/dist/gen/cave.js.map +1 -0
- package/dist/gen/gen-monster.d.ts +151 -0
- package/dist/gen/gen-monster.d.ts.map +1 -0
- package/dist/gen/gen-monster.js +473 -0
- package/dist/gen/gen-monster.js.map +1 -0
- package/dist/gen/generate.d.ts +240 -0
- package/dist/gen/generate.d.ts.map +1 -0
- package/dist/gen/generate.js +358 -0
- package/dist/gen/generate.js.map +1 -0
- package/dist/gen/room.d.ts +160 -0
- package/dist/gen/room.d.ts.map +1 -0
- package/dist/gen/room.js +1910 -0
- package/dist/gen/room.js.map +1 -0
- package/dist/gen/util.d.ts +466 -0
- package/dist/gen/util.d.ts.map +1 -0
- package/dist/gen/util.js +1553 -0
- package/dist/gen/util.js.map +1 -0
- package/dist/generated/dun-profiles.d.ts +44 -0
- package/dist/generated/dun-profiles.d.ts.map +1 -0
- package/dist/generated/dun-profiles.js +28 -0
- package/dist/generated/dun-profiles.js.map +1 -0
- package/dist/generated/effects.d.ts +1021 -0
- package/dist/generated/effects.d.ts.map +1 -0
- package/dist/generated/effects.js +239 -0
- package/dist/generated/effects.js.map +1 -0
- package/dist/generated/elements.d.ts +83 -0
- package/dist/generated/elements.d.ts.map +1 -0
- package/dist/generated/elements.js +60 -0
- package/dist/generated/elements.js.map +1 -0
- package/dist/generated/equip-slots.d.ts +104 -0
- package/dist/generated/equip-slots.d.ts.map +1 -0
- package/dist/generated/equip-slots.js +34 -0
- package/dist/generated/equip-slots.js.map +1 -0
- package/dist/generated/history-types.d.ts +52 -0
- package/dist/generated/history-types.d.ts.map +1 -0
- package/dist/generated/history-types.js +32 -0
- package/dist/generated/history-types.js.map +1 -0
- package/dist/generated/ignore-types.d.ts +116 -0
- package/dist/generated/ignore-types.d.ts.map +1 -0
- package/dist/generated/ignore-types.js +64 -0
- package/dist/generated/ignore-types.js.map +1 -0
- package/dist/generated/index.d.ts +32 -0
- package/dist/generated/index.d.ts.map +1 -0
- package/dist/generated/index.js +33 -0
- package/dist/generated/index.js.map +1 -0
- package/dist/generated/kind-flags.d.ts +72 -0
- package/dist/generated/kind-flags.d.ts.map +1 -0
- package/dist/generated/kind-flags.js +42 -0
- package/dist/generated/kind-flags.js.map +1 -0
- package/dist/generated/message.d.ts +624 -0
- package/dist/generated/message.d.ts.map +1 -0
- package/dist/generated/message.js +318 -0
- package/dist/generated/message.js.map +1 -0
- package/dist/generated/mon-message.d.ts +386 -0
- package/dist/generated/mon-message.d.ts.map +1 -0
- package/dist/generated/mon-message.js +136 -0
- package/dist/generated/mon-message.js.map +1 -0
- package/dist/generated/mon-race-flags.d.ts +433 -0
- package/dist/generated/mon-race-flags.d.ts.map +1 -0
- package/dist/generated/mon-race-flags.js +180 -0
- package/dist/generated/mon-race-flags.js.map +1 -0
- package/dist/generated/mon-spells.d.ts +380 -0
- package/dist/generated/mon-spells.d.ts.map +1 -0
- package/dist/generated/mon-spells.js +196 -0
- package/dist/generated/mon-spells.js.map +1 -0
- package/dist/generated/mon-temp-flags.d.ts +52 -0
- package/dist/generated/mon-temp-flags.d.ts.map +1 -0
- package/dist/generated/mon-temp-flags.js +32 -0
- package/dist/generated/mon-temp-flags.js.map +1 -0
- package/dist/generated/mon-timed.d.ts +118 -0
- package/dist/generated/mon-timed.d.ts.map +1 -0
- package/dist/generated/mon-timed.js +32 -0
- package/dist/generated/mon-timed.js.map +1 -0
- package/dist/generated/object-flags.d.ts +169 -0
- package/dist/generated/object-flags.d.ts.map +1 -0
- package/dist/generated/object-flags.js +93 -0
- package/dist/generated/object-flags.js.map +1 -0
- package/dist/generated/object-modifiers.d.ts +50 -0
- package/dist/generated/object-modifiers.d.ts.map +1 -0
- package/dist/generated/object-modifiers.js +41 -0
- package/dist/generated/object-modifiers.js.map +1 -0
- package/dist/generated/options.d.ts +284 -0
- package/dist/generated/options.d.ts.map +1 -0
- package/dist/generated/options.js +102 -0
- package/dist/generated/options.js.map +1 -0
- package/dist/generated/origins.d.ts +138 -0
- package/dist/generated/origins.d.ts.map +1 -0
- package/dist/generated/origins.js +62 -0
- package/dist/generated/origins.js.map +1 -0
- package/dist/generated/parser-errors.d.ts +264 -0
- package/dist/generated/parser-errors.d.ts.map +1 -0
- package/dist/generated/parser-errors.js +138 -0
- package/dist/generated/parser-errors.js.map +1 -0
- package/dist/generated/player-flags.d.ts +62 -0
- package/dist/generated/player-flags.d.ts.map +1 -0
- package/dist/generated/player-flags.js +46 -0
- package/dist/generated/player-flags.js.map +1 -0
- package/dist/generated/player-timed.d.ts +273 -0
- package/dist/generated/player-timed.d.ts.map +1 -0
- package/dist/generated/player-timed.js +116 -0
- package/dist/generated/player-timed.js.map +1 -0
- package/dist/generated/projections.d.ts +130 -0
- package/dist/generated/projections.d.ts.map +1 -0
- package/dist/generated/projections.js +101 -0
- package/dist/generated/projections.js.map +1 -0
- package/dist/generated/randart-properties.d.ts +388 -0
- package/dist/generated/randart-properties.d.ts.map +1 -0
- package/dist/generated/randart-properties.js +200 -0
- package/dist/generated/randart-properties.js.map +1 -0
- package/dist/generated/room-flags.d.ts +21 -0
- package/dist/generated/room-flags.d.ts.map +1 -0
- package/dist/generated/room-flags.js +19 -0
- package/dist/generated/room-flags.js.map +1 -0
- package/dist/generated/rooms.d.ts +122 -0
- package/dist/generated/rooms.d.ts.map +1 -0
- package/dist/generated/rooms.js +48 -0
- package/dist/generated/rooms.js.map +1 -0
- package/dist/generated/square-flags.d.ts +96 -0
- package/dist/generated/square-flags.d.ts.map +1 -0
- package/dist/generated/square-flags.js +54 -0
- package/dist/generated/square-flags.js.map +1 -0
- package/dist/generated/stats.d.ts +23 -0
- package/dist/generated/stats.d.ts.map +1 -0
- package/dist/generated/stats.js +20 -0
- package/dist/generated/stats.js.map +1 -0
- package/dist/generated/terrain-flags.d.ts +136 -0
- package/dist/generated/terrain-flags.d.ts.map +1 -0
- package/dist/generated/terrain-flags.js +74 -0
- package/dist/generated/terrain-flags.js.map +1 -0
- package/dist/generated/terrain.d.ts +83 -0
- package/dist/generated/terrain.d.ts.map +1 -0
- package/dist/generated/terrain.js +60 -0
- package/dist/generated/terrain.js.map +1 -0
- package/dist/generated/trap-flags.d.ts +68 -0
- package/dist/generated/trap-flags.d.ts.map +1 -0
- package/dist/generated/trap-flags.js +40 -0
- package/dist/generated/trap-flags.js.map +1 -0
- package/dist/generated/tvals.d.ts +152 -0
- package/dist/generated/tvals.d.ts.map +1 -0
- package/dist/generated/tvals.js +82 -0
- package/dist/generated/tvals.js.map +1 -0
- package/dist/generated/ui-entry-renderers.d.ts +62 -0
- package/dist/generated/ui-entry-renderers.d.ts.map +1 -0
- package/dist/generated/ui-entry-renderers.js +22 -0
- package/dist/generated/ui-entry-renderers.js.map +1 -0
- package/dist/guard.d.ts +43 -0
- package/dist/guard.d.ts.map +1 -0
- package/dist/guard.js +65 -0
- package/dist/guard.js.map +1 -0
- package/dist/host/args.d.ts +129 -0
- package/dist/host/args.d.ts.map +1 -0
- package/dist/host/args.js +251 -0
- package/dist/host/args.js.map +1 -0
- package/dist/host/bridge.d.ts +61 -0
- package/dist/host/bridge.d.ts.map +1 -0
- package/dist/host/bridge.js +176 -0
- package/dist/host/bridge.js.map +1 -0
- package/dist/host/index.d.ts +18 -0
- package/dist/host/index.d.ts.map +1 -0
- package/dist/host/index.js +18 -0
- package/dist/host/index.js.map +1 -0
- package/dist/host/io.d.ts +169 -0
- package/dist/host/io.d.ts.map +1 -0
- package/dist/host/io.js +137 -0
- package/dist/host/io.js.map +1 -0
- package/dist/host/memory.d.ts +56 -0
- package/dist/host/memory.d.ts.map +1 -0
- package/dist/host/memory.js +105 -0
- package/dist/host/memory.js.map +1 -0
- package/dist/host/raw.d.ts +114 -0
- package/dist/host/raw.d.ts.map +1 -0
- package/dist/host/raw.js +107 -0
- package/dist/host/raw.js.map +1 -0
- package/dist/index.d.ts +180 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +222 -0
- package/dist/index.js.map +1 -0
- package/dist/loc.d.ts +63 -0
- package/dist/loc.d.ts.map +1 -0
- package/dist/loc.js +128 -0
- package/dist/loc.js.map +1 -0
- package/dist/mod/hooks.d.ts +204 -0
- package/dist/mod/hooks.d.ts.map +1 -0
- package/dist/mod/hooks.js +161 -0
- package/dist/mod/hooks.js.map +1 -0
- package/dist/mod/ids.d.ts +160 -0
- package/dist/mod/ids.d.ts.map +1 -0
- package/dist/mod/ids.js +343 -0
- package/dist/mod/ids.js.map +1 -0
- package/dist/mod/registry-host.d.ts +156 -0
- package/dist/mod/registry-host.d.ts.map +1 -0
- package/dist/mod/registry-host.js +142 -0
- package/dist/mod/registry-host.js.map +1 -0
- package/dist/mod/save-blocks.d.ts +201 -0
- package/dist/mod/save-blocks.d.ts.map +1 -0
- package/dist/mod/save-blocks.js +640 -0
- package/dist/mod/save-blocks.js.map +1 -0
- package/dist/mod/vocabulary.d.ts +104 -0
- package/dist/mod/vocabulary.d.ts.map +1 -0
- package/dist/mod/vocabulary.js +0 -0
- package/dist/mod/vocabulary.js.map +1 -0
- package/dist/mon/bind.d.ts +266 -0
- package/dist/mon/bind.d.ts.map +1 -0
- package/dist/mon/bind.js +610 -0
- package/dist/mon/bind.js.map +1 -0
- package/dist/mon/desc.d.ts +72 -0
- package/dist/mon/desc.d.ts.map +1 -0
- package/dist/mon/desc.js +237 -0
- package/dist/mon/desc.js.map +1 -0
- package/dist/mon/knowledge-groups.d.ts +61 -0
- package/dist/mon/knowledge-groups.d.ts.map +1 -0
- package/dist/mon/knowledge-groups.js +121 -0
- package/dist/mon/knowledge-groups.js.map +1 -0
- package/dist/mon/lore-describe.d.ts +216 -0
- package/dist/mon/lore-describe.d.ts.map +1 -0
- package/dist/mon/lore-describe.js +1105 -0
- package/dist/mon/lore-describe.js.map +1 -0
- package/dist/mon/lore.d.ts +117 -0
- package/dist/mon/lore.d.ts.map +1 -0
- package/dist/mon/lore.js +221 -0
- package/dist/mon/lore.js.map +1 -0
- package/dist/mon/make.d.ts +147 -0
- package/dist/mon/make.d.ts.map +1 -0
- package/dist/mon/make.js +353 -0
- package/dist/mon/make.js.map +1 -0
- package/dist/mon/monster.d.ts +94 -0
- package/dist/mon/monster.d.ts.map +1 -0
- package/dist/mon/monster.js +90 -0
- package/dist/mon/monster.js.map +1 -0
- package/dist/mon/predicate.d.ts +87 -0
- package/dist/mon/predicate.d.ts.map +1 -0
- package/dist/mon/predicate.js +190 -0
- package/dist/mon/predicate.js.map +1 -0
- package/dist/mon/project-mon.d.ts +129 -0
- package/dist/mon/project-mon.d.ts.map +1 -0
- package/dist/mon/project-mon.js +652 -0
- package/dist/mon/project-mon.js.map +1 -0
- package/dist/mon/spell.d.ts +155 -0
- package/dist/mon/spell.d.ts.map +1 -0
- package/dist/mon/spell.js +355 -0
- package/dist/mon/spell.js.map +1 -0
- package/dist/mon/steal.d.ts +112 -0
- package/dist/mon/steal.d.ts.map +1 -0
- package/dist/mon/steal.js +163 -0
- package/dist/mon/steal.js.map +1 -0
- package/dist/mon/summon.d.ts +62 -0
- package/dist/mon/summon.d.ts.map +1 -0
- package/dist/mon/summon.js +111 -0
- package/dist/mon/summon.js.map +1 -0
- package/dist/mon/take-hit.d.ts +75 -0
- package/dist/mon/take-hit.d.ts.map +1 -0
- package/dist/mon/take-hit.js +122 -0
- package/dist/mon/take-hit.js.map +1 -0
- package/dist/mon/timed.d.ts +74 -0
- package/dist/mon/timed.d.ts.map +1 -0
- package/dist/mon/timed.js +219 -0
- package/dist/mon/timed.js.map +1 -0
- package/dist/mon/types.d.ts +252 -0
- package/dist/mon/types.d.ts.map +1 -0
- package/dist/mon/types.js +46 -0
- package/dist/mon/types.js.map +1 -0
- package/dist/msg.d.ts +71 -0
- package/dist/msg.d.ts.map +1 -0
- package/dist/msg.js +126 -0
- package/dist/msg.js.map +1 -0
- package/dist/obj/artifact-fake.d.ts +39 -0
- package/dist/obj/artifact-fake.d.ts.map +1 -0
- package/dist/obj/artifact-fake.js +52 -0
- package/dist/obj/artifact-fake.js.map +1 -0
- package/dist/obj/artifact-known.d.ts +50 -0
- package/dist/obj/artifact-known.d.ts.map +1 -0
- package/dist/obj/artifact-known.js +55 -0
- package/dist/obj/artifact-known.js.map +1 -0
- package/dist/obj/bind.d.ts +219 -0
- package/dist/obj/bind.d.ts.map +1 -0
- package/dist/obj/bind.js +1239 -0
- package/dist/obj/bind.js.map +1 -0
- package/dist/obj/chest.d.ts +91 -0
- package/dist/obj/chest.d.ts.map +1 -0
- package/dist/obj/chest.js +220 -0
- package/dist/obj/chest.js.map +1 -0
- package/dist/obj/desc.d.ts +79 -0
- package/dist/obj/desc.d.ts.map +1 -0
- package/dist/obj/desc.js +602 -0
- package/dist/obj/desc.js.map +1 -0
- package/dist/obj/effects-info.d.ts +78 -0
- package/dist/obj/effects-info.d.ts.map +1 -0
- package/dist/obj/effects-info.js +240 -0
- package/dist/obj/effects-info.js.map +1 -0
- package/dist/obj/flavor.d.ts +68 -0
- package/dist/obj/flavor.d.ts.map +1 -0
- package/dist/obj/flavor.js +201 -0
- package/dist/obj/flavor.js.map +1 -0
- package/dist/obj/ignore.d.ts +143 -0
- package/dist/obj/ignore.d.ts.map +1 -0
- package/dist/obj/ignore.js +345 -0
- package/dist/obj/ignore.js.map +1 -0
- package/dist/obj/knowledge.d.ts +520 -0
- package/dist/obj/knowledge.d.ts.map +1 -0
- package/dist/obj/knowledge.js +1364 -0
- package/dist/obj/knowledge.js.map +1 -0
- package/dist/obj/known-object.d.ts +293 -0
- package/dist/obj/known-object.d.ts.map +1 -0
- package/dist/obj/known-object.js +532 -0
- package/dist/obj/known-object.js.map +1 -0
- package/dist/obj/make.d.ts +292 -0
- package/dist/obj/make.d.ts.map +1 -0
- package/dist/obj/make.js +1055 -0
- package/dist/obj/make.js.map +1 -0
- package/dist/obj/object-info.d.ts +188 -0
- package/dist/obj/object-info.d.ts.map +1 -0
- package/dist/obj/object-info.js +1407 -0
- package/dist/obj/object-info.js.map +1 -0
- package/dist/obj/object.d.ts +335 -0
- package/dist/obj/object.d.ts.map +1 -0
- package/dist/obj/object.js +1051 -0
- package/dist/obj/object.js.map +1 -0
- package/dist/obj/power.d.ts +65 -0
- package/dist/obj/power.d.ts.map +1 -0
- package/dist/obj/power.js +580 -0
- package/dist/obj/power.js.map +1 -0
- package/dist/obj/randart-build.d.ts +223 -0
- package/dist/obj/randart-build.d.ts.map +1 -0
- package/dist/obj/randart-build.js +1444 -0
- package/dist/obj/randart-build.js.map +1 -0
- package/dist/obj/randart-data.d.ts +179 -0
- package/dist/obj/randart-data.d.ts.map +1 -0
- package/dist/obj/randart-data.js +1153 -0
- package/dist/obj/randart-data.js.map +1 -0
- package/dist/obj/randart.d.ts +128 -0
- package/dist/obj/randart.d.ts.map +1 -0
- package/dist/obj/randart.js +458 -0
- package/dist/obj/randart.js.map +1 -0
- package/dist/obj/randname.d.ts +33 -0
- package/dist/obj/randname.d.ts.map +1 -0
- package/dist/obj/randname.js +116 -0
- package/dist/obj/randname.js.map +1 -0
- package/dist/obj/recharge.d.ts +28 -0
- package/dist/obj/recharge.d.ts.map +1 -0
- package/dist/obj/recharge.js +52 -0
- package/dist/obj/recharge.js.map +1 -0
- package/dist/obj/types.d.ts +584 -0
- package/dist/obj/types.d.ts.map +1 -0
- package/dist/obj/types.js +104 -0
- package/dist/obj/types.js.map +1 -0
- package/dist/obj/value.d.ts +64 -0
- package/dist/obj/value.d.ts.map +1 -0
- package/dist/obj/value.js +176 -0
- package/dist/obj/value.js.map +1 -0
- package/dist/player/abilities.d.ts +57 -0
- package/dist/player/abilities.d.ts.map +1 -0
- package/dist/player/abilities.js +118 -0
- package/dist/player/abilities.js.map +1 -0
- package/dist/player/best-digger.d.ts +42 -0
- package/dist/player/best-digger.d.ts.map +1 -0
- package/dist/player/best-digger.js +89 -0
- package/dist/player/best-digger.js.map +1 -0
- package/dist/player/bind.d.ts +270 -0
- package/dist/player/bind.d.ts.map +1 -0
- package/dist/player/bind.js +698 -0
- package/dist/player/bind.js.map +1 -0
- package/dist/player/birth.d.ts +258 -0
- package/dist/player/birth.d.ts.map +1 -0
- package/dist/player/birth.js +639 -0
- package/dist/player/birth.js.map +1 -0
- package/dist/player/calcs.d.ts +403 -0
- package/dist/player/calcs.d.ts.map +1 -0
- package/dist/player/calcs.js +1189 -0
- package/dist/player/calcs.js.map +1 -0
- package/dist/player/combat-regen.d.ts +14 -0
- package/dist/player/combat-regen.d.ts.map +1 -0
- package/dist/player/combat-regen.js +29 -0
- package/dist/player/combat-regen.js.map +1 -0
- package/dist/player/exp.d.ts +96 -0
- package/dist/player/exp.d.ts.map +1 -0
- package/dist/player/exp.js +229 -0
- package/dist/player/exp.js.map +1 -0
- package/dist/player/history.d.ts +91 -0
- package/dist/player/history.d.ts.map +1 -0
- package/dist/player/history.js +145 -0
- package/dist/player/history.js.map +1 -0
- package/dist/player/options.d.ts +123 -0
- package/dist/player/options.d.ts.map +1 -0
- package/dist/player/options.js +199 -0
- package/dist/player/options.js.map +1 -0
- package/dist/player/player.d.ts +242 -0
- package/dist/player/player.d.ts.map +1 -0
- package/dist/player/player.js +119 -0
- package/dist/player/player.js.map +1 -0
- package/dist/player/shape-lore.d.ts +56 -0
- package/dist/player/shape-lore.d.ts.map +1 -0
- package/dist/player/shape-lore.js +221 -0
- package/dist/player/shape-lore.js.map +1 -0
- package/dist/player/spell.d.ts +146 -0
- package/dist/player/spell.d.ts.map +1 -0
- package/dist/player/spell.js +519 -0
- package/dist/player/spell.js.map +1 -0
- package/dist/player/take-hit.d.ts +87 -0
- package/dist/player/take-hit.d.ts.map +1 -0
- package/dist/player/take-hit.js +99 -0
- package/dist/player/take-hit.js.map +1 -0
- package/dist/player/timed.d.ts +205 -0
- package/dist/player/timed.d.ts.map +1 -0
- package/dist/player/timed.js +310 -0
- package/dist/player/timed.js.map +1 -0
- package/dist/player/types.d.ts +390 -0
- package/dist/player/types.d.ts.map +1 -0
- package/dist/player/types.js +51 -0
- package/dist/player/types.js.map +1 -0
- package/dist/rational.d.ts +99 -0
- package/dist/rational.d.ts.map +1 -0
- package/dist/rational.js +345 -0
- package/dist/rational.js.map +1 -0
- package/dist/rng.d.ts +168 -0
- package/dist/rng.d.ts.map +1 -0
- package/dist/rng.js +404 -0
- package/dist/rng.js.map +1 -0
- package/dist/save/buffer.d.ts +120 -0
- package/dist/save/buffer.d.ts.map +1 -0
- package/dist/save/buffer.js +238 -0
- package/dist/save/buffer.js.map +1 -0
- package/dist/save/compress.d.ts +79 -0
- package/dist/save/compress.d.ts.map +1 -0
- package/dist/save/compress.js +97 -0
- package/dist/save/compress.js.map +1 -0
- package/dist/save/description.d.ts +61 -0
- package/dist/save/description.d.ts.map +1 -0
- package/dist/save/description.js +51 -0
- package/dist/save/description.js.map +1 -0
- package/dist/save/integrity.d.ts +59 -0
- package/dist/save/integrity.d.ts.map +1 -0
- package/dist/save/integrity.js +103 -0
- package/dist/save/integrity.js.map +1 -0
- package/dist/score/display.d.ts +40 -0
- package/dist/score/display.d.ts.map +1 -0
- package/dist/score/display.js +86 -0
- package/dist/score/display.js.map +1 -0
- package/dist/score/score.d.ts +166 -0
- package/dist/score/score.d.ts.map +1 -0
- package/dist/score/score.js +244 -0
- package/dist/score/score.js.map +1 -0
- package/dist/score/types.d.ts +98 -0
- package/dist/score/types.d.ts.map +1 -0
- package/dist/score/types.js +27 -0
- package/dist/score/types.js.map +1 -0
- package/dist/session/boot.d.ts +207 -0
- package/dist/session/boot.d.ts.map +1 -0
- package/dist/session/boot.js +151 -0
- package/dist/session/boot.js.map +1 -0
- package/dist/session/game.d.ts +257 -0
- package/dist/session/game.d.ts.map +1 -0
- package/dist/session/game.js +2880 -0
- package/dist/session/game.js.map +1 -0
- package/dist/session/save.d.ts +847 -0
- package/dist/session/save.d.ts.map +1 -0
- package/dist/session/save.js +1306 -0
- package/dist/session/save.js.map +1 -0
- package/dist/sound/engine.d.ts +146 -0
- package/dist/sound/engine.d.ts.map +1 -0
- package/dist/sound/engine.js +332 -0
- package/dist/sound/engine.js.map +1 -0
- package/dist/sound/index.d.ts +13 -0
- package/dist/sound/index.d.ts.map +1 -0
- package/dist/sound/index.js +13 -0
- package/dist/sound/index.js.map +1 -0
- package/dist/sound/sound-prefs-data.d.ts +10 -0
- package/dist/sound/sound-prefs-data.d.ts.map +1 -0
- package/dist/sound/sound-prefs-data.js +165 -0
- package/dist/sound/sound-prefs-data.js.map +1 -0
- package/dist/sound/types.d.ts +64 -0
- package/dist/sound/types.d.ts.map +1 -0
- package/dist/sound/types.js +23 -0
- package/dist/sound/types.js.map +1 -0
- package/dist/store/bind.d.ts +43 -0
- package/dist/store/bind.d.ts.map +1 -0
- package/dist/store/bind.js +130 -0
- package/dist/store/bind.js.map +1 -0
- package/dist/store/price.d.ts +27 -0
- package/dist/store/price.d.ts.map +1 -0
- package/dist/store/price.js +81 -0
- package/dist/store/price.js.map +1 -0
- package/dist/store/store-cmd.d.ts +51 -0
- package/dist/store/store-cmd.d.ts.map +1 -0
- package/dist/store/store-cmd.js +73 -0
- package/dist/store/store-cmd.js.map +1 -0
- package/dist/store/store.d.ts +156 -0
- package/dist/store/store.d.ts.map +1 -0
- package/dist/store/store.js +616 -0
- package/dist/store/store.js.map +1 -0
- package/dist/store/transact.d.ts +198 -0
- package/dist/store/transact.d.ts.map +1 -0
- package/dist/store/transact.js +383 -0
- package/dist/store/transact.js.map +1 -0
- package/dist/store/types.d.ts +101 -0
- package/dist/store/types.d.ts.map +1 -0
- package/dist/store/types.js +14 -0
- package/dist/store/types.js.map +1 -0
- package/dist/version.d.ts +25 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +25 -0
- package/dist/version.js.map +1 -0
- package/dist/visuals/engine.d.ts +223 -0
- package/dist/visuals/engine.d.ts.map +1 -0
- package/dist/visuals/engine.js +357 -0
- package/dist/visuals/engine.js.map +1 -0
- package/dist/visuals/glyph-table.d.ts +112 -0
- package/dist/visuals/glyph-table.d.ts.map +1 -0
- package/dist/visuals/glyph-table.js +156 -0
- package/dist/visuals/glyph-table.js.map +1 -0
- package/dist/visuals/grafmode-data.d.ts +4 -0
- package/dist/visuals/grafmode-data.d.ts.map +1 -0
- package/dist/visuals/grafmode-data.js +88 -0
- package/dist/visuals/grafmode-data.js.map +1 -0
- package/dist/visuals/grafmode.d.ts +83 -0
- package/dist/visuals/grafmode.d.ts.map +1 -0
- package/dist/visuals/grafmode.js +82 -0
- package/dist/visuals/grafmode.js.map +1 -0
- package/dist/visuals/index.d.ts +19 -0
- package/dist/visuals/index.d.ts.map +1 -0
- package/dist/visuals/index.js +19 -0
- package/dist/visuals/index.js.map +1 -0
- package/dist/visuals/map-text.d.ts +79 -0
- package/dist/visuals/map-text.d.ts.map +1 -0
- package/dist/visuals/map-text.js +122 -0
- package/dist/visuals/map-text.js.map +1 -0
- package/dist/visuals/pref-expr.d.ts +56 -0
- package/dist/visuals/pref-expr.d.ts.map +1 -0
- package/dist/visuals/pref-expr.js +173 -0
- package/dist/visuals/pref-expr.js.map +1 -0
- package/dist/visuals/prefs.d.ts +280 -0
- package/dist/visuals/prefs.d.ts.map +1 -0
- package/dist/visuals/prefs.js +835 -0
- package/dist/visuals/prefs.js.map +1 -0
- package/dist/visuals/tile-prefs.d.ts +132 -0
- package/dist/visuals/tile-prefs.d.ts.map +1 -0
- package/dist/visuals/tile-prefs.js +105 -0
- package/dist/visuals/tile-prefs.js.map +1 -0
- package/dist/world/chunk.d.ts +195 -0
- package/dist/world/chunk.d.ts.map +1 -0
- package/dist/world/chunk.js +422 -0
- package/dist/world/chunk.js.map +1 -0
- package/dist/world/feature.d.ts +86 -0
- package/dist/world/feature.d.ts.map +1 -0
- package/dist/world/feature.js +153 -0
- package/dist/world/feature.js.map +1 -0
- package/dist/world/flow.d.ts +33 -0
- package/dist/world/flow.d.ts.map +1 -0
- package/dist/world/flow.js +115 -0
- package/dist/world/flow.js.map +1 -0
- package/dist/world/project.d.ts +176 -0
- package/dist/world/project.d.ts.map +1 -0
- package/dist/world/project.js +543 -0
- package/dist/world/project.js.map +1 -0
- package/dist/world/projection.d.ts +100 -0
- package/dist/world/projection.d.ts.map +1 -0
- package/dist/world/projection.js +152 -0
- package/dist/world/projection.js.map +1 -0
- package/dist/world/scatter.d.ts +25 -0
- package/dist/world/scatter.d.ts.map +1 -0
- package/dist/world/scatter.js +54 -0
- package/dist/world/scatter.js.map +1 -0
- package/dist/world/trap.d.ts +84 -0
- package/dist/world/trap.d.ts.map +1 -0
- package/dist/world/trap.js +106 -0
- package/dist/world/trap.js.map +1 -0
- package/dist/world/view.d.ts +60 -0
- package/dist/world/view.d.ts.map +1 -0
- package/dist/world/view.js +436 -0
- package/dist/world/view.js.map +1 -0
- package/dist/z-queue.d.ts +133 -0
- package/dist/z-queue.d.ts.map +1 -0
- package/dist/z-queue.js +283 -0
- package/dist/z-queue.js.map +1 -0
- package/package.json +75 -0
- package/src/agent/act.ts +114 -0
- package/src/agent/controller.ts +109 -0
- package/src/agent/events.ts +47 -0
- package/src/agent/index.ts +36 -0
- package/src/agent/perceive.ts +440 -0
- package/src/agent/types.ts +464 -0
- package/src/bitflag.ts +536 -0
- package/src/cmd.ts +547 -0
- package/src/color.ts +267 -0
- package/src/combat/brand-slay.ts +360 -0
- package/src/combat/hit.ts +471 -0
- package/src/combat/index.ts +15 -0
- package/src/combat/melee.ts +728 -0
- package/src/combat/mon-melee.ts +1153 -0
- package/src/combat/ranged.ts +397 -0
- package/src/constants.ts +324 -0
- package/src/dice.ts +467 -0
- package/src/effects/effect-choice.ts +32 -0
- package/src/effects/effect-info.ts +859 -0
- package/src/effects/effect.ts +560 -0
- package/src/effects/handlers.ts +452 -0
- package/src/effects/interpreter.ts +603 -0
- package/src/events.ts +317 -0
- package/src/expression.ts +360 -0
- package/src/game/cave-cmd.ts +1147 -0
- package/src/game/char-sheet.ts +557 -0
- package/src/game/chest.ts +348 -0
- package/src/game/context.ts +1218 -0
- package/src/game/curse-tick.ts +130 -0
- package/src/game/describe.ts +62 -0
- package/src/game/display.ts +838 -0
- package/src/game/dump-level.ts +114 -0
- package/src/game/effect-attack.ts +746 -0
- package/src/game/effect-detect.ts +542 -0
- package/src/game/effect-env.ts +252 -0
- package/src/game/effect-game-env.ts +86 -0
- package/src/game/effect-general.ts +1122 -0
- package/src/game/effect-item.ts +999 -0
- package/src/game/effect-melee.ts +557 -0
- package/src/game/effect-mon-origin.ts +196 -0
- package/src/game/effect-monster.ts +310 -0
- package/src/game/effect-summon.ts +161 -0
- package/src/game/effect-teleport.ts +645 -0
- package/src/game/effect-terrain.ts +881 -0
- package/src/game/energy.ts +61 -0
- package/src/game/equip-cmp.ts +476 -0
- package/src/game/floor.ts +524 -0
- package/src/game/gear.ts +1352 -0
- package/src/game/harness.ts +270 -0
- package/src/game/history.ts +78 -0
- package/src/game/ignore-cmd.ts +68 -0
- package/src/game/inscription-confirm.ts +115 -0
- package/src/game/known.ts +960 -0
- package/src/game/loop.ts +671 -0
- package/src/game/lore-color.ts +108 -0
- package/src/game/mon-cast.ts +409 -0
- package/src/game/mon-cmd.ts +767 -0
- package/src/game/mon-death.ts +459 -0
- package/src/game/mon-group.ts +368 -0
- package/src/game/mon-list.ts +213 -0
- package/src/game/mon-message.ts +359 -0
- package/src/game/mon-place.ts +1012 -0
- package/src/game/mon-ranged.ts +405 -0
- package/src/game/mon-shape.ts +161 -0
- package/src/game/mon-side.ts +511 -0
- package/src/game/monster-turn.ts +1750 -0
- package/src/game/obj-cmd.ts +1970 -0
- package/src/game/obj-list.ts +302 -0
- package/src/game/object-inspect.ts +162 -0
- package/src/game/pickup.ts +483 -0
- package/src/game/player-path.ts +1434 -0
- package/src/game/player-side.ts +555 -0
- package/src/game/player-turn.ts +852 -0
- package/src/game/project-cast.ts +766 -0
- package/src/game/project-feat.ts +386 -0
- package/src/game/project-monster.ts +407 -0
- package/src/game/project-obj.ts +298 -0
- package/src/game/project-player.ts +236 -0
- package/src/game/quest.ts +248 -0
- package/src/game/ranged-cmd.ts +417 -0
- package/src/game/scheduler.ts +150 -0
- package/src/game/spell-cmd.ts +404 -0
- package/src/game/spoil.ts +562 -0
- package/src/game/steal.ts +169 -0
- package/src/game/take-hit-hooks.ts +152 -0
- package/src/game/target-loop.ts +501 -0
- package/src/game/target.ts +424 -0
- package/src/game/thrust.ts +174 -0
- package/src/game/trap.ts +791 -0
- package/src/game/ui-entry.ts +2075 -0
- package/src/game/wizard.ts +1760 -0
- package/src/game/world.ts +786 -0
- package/src/gen/cave.ts +2858 -0
- package/src/gen/gen-monster.ts +592 -0
- package/src/gen/generate.ts +523 -0
- package/src/gen/room.ts +2213 -0
- package/src/gen/util.ts +1958 -0
- package/src/generated/dun-profiles.ts +30 -0
- package/src/generated/effects.ts +241 -0
- package/src/generated/elements.ts +62 -0
- package/src/generated/equip-slots.ts +36 -0
- package/src/generated/history-types.ts +34 -0
- package/src/generated/ignore-types.ts +66 -0
- package/src/generated/index.ts +33 -0
- package/src/generated/kind-flags.ts +44 -0
- package/src/generated/message.ts +320 -0
- package/src/generated/mon-message.ts +138 -0
- package/src/generated/mon-race-flags.ts +182 -0
- package/src/generated/mon-spells.ts +198 -0
- package/src/generated/mon-temp-flags.ts +34 -0
- package/src/generated/mon-timed.ts +34 -0
- package/src/generated/object-flags.ts +95 -0
- package/src/generated/object-modifiers.ts +43 -0
- package/src/generated/options.ts +104 -0
- package/src/generated/origins.ts +64 -0
- package/src/generated/parser-errors.ts +140 -0
- package/src/generated/player-flags.ts +48 -0
- package/src/generated/player-timed.ts +118 -0
- package/src/generated/projections.ts +103 -0
- package/src/generated/randart-properties.ts +202 -0
- package/src/generated/room-flags.ts +21 -0
- package/src/generated/rooms.ts +50 -0
- package/src/generated/square-flags.ts +56 -0
- package/src/generated/stats.ts +22 -0
- package/src/generated/terrain-flags.ts +76 -0
- package/src/generated/terrain.ts +62 -0
- package/src/generated/trap-flags.ts +42 -0
- package/src/generated/tvals.ts +84 -0
- package/src/generated/ui-entry-renderers.ts +24 -0
- package/src/guard.ts +70 -0
- package/src/host/args.ts +334 -0
- package/src/host/bridge.ts +193 -0
- package/src/host/index.ts +18 -0
- package/src/host/io.ts +239 -0
- package/src/host/memory.ts +134 -0
- package/src/host/raw.ts +190 -0
- package/src/index.ts +250 -0
- package/src/loc.ts +157 -0
- package/src/mod/hooks.ts +278 -0
- package/src/mod/ids.ts +390 -0
- package/src/mod/registry-host.ts +258 -0
- package/src/mod/save-blocks.ts +787 -0
- package/src/mod/vocabulary.ts +0 -0
- package/src/mon/bind.ts +899 -0
- package/src/mon/desc.ts +212 -0
- package/src/mon/knowledge-groups.ts +164 -0
- package/src/mon/lore-describe.ts +1414 -0
- package/src/mon/lore.ts +306 -0
- package/src/mon/make.ts +422 -0
- package/src/mon/monster.ts +161 -0
- package/src/mon/predicate.ts +220 -0
- package/src/mon/project-mon.ts +855 -0
- package/src/mon/spell.ts +418 -0
- package/src/mon/steal.ts +246 -0
- package/src/mon/summon.ts +145 -0
- package/src/mon/take-hit.ts +195 -0
- package/src/mon/timed.ts +300 -0
- package/src/mon/types.ts +286 -0
- package/src/msg.ts +146 -0
- package/src/obj/artifact-fake.ts +63 -0
- package/src/obj/artifact-known.ts +69 -0
- package/src/obj/bind.ts +1345 -0
- package/src/obj/chest.ts +249 -0
- package/src/obj/desc.ts +699 -0
- package/src/obj/effects-info.ts +349 -0
- package/src/obj/flavor.ts +256 -0
- package/src/obj/ignore.ts +387 -0
- package/src/obj/knowledge.ts +1577 -0
- package/src/obj/known-object.ts +718 -0
- package/src/obj/make.ts +1367 -0
- package/src/obj/object-info.ts +1737 -0
- package/src/obj/object.ts +1296 -0
- package/src/obj/power.ts +674 -0
- package/src/obj/randart-build.ts +1705 -0
- package/src/obj/randart-data.ts +1344 -0
- package/src/obj/randart.ts +552 -0
- package/src/obj/randname.ts +128 -0
- package/src/obj/recharge.ts +62 -0
- package/src/obj/types.ts +559 -0
- package/src/obj/value.ts +215 -0
- package/src/player/abilities.ts +142 -0
- package/src/player/best-digger.ts +118 -0
- package/src/player/bind.ts +1021 -0
- package/src/player/birth.ts +794 -0
- package/src/player/calcs.ts +1522 -0
- package/src/player/combat-regen.ts +33 -0
- package/src/player/exp.ts +275 -0
- package/src/player/history.ts +211 -0
- package/src/player/options.ts +244 -0
- package/src/player/player.ts +356 -0
- package/src/player/shape-lore.ts +250 -0
- package/src/player/spell.ts +627 -0
- package/src/player/take-hit.ts +173 -0
- package/src/player/timed.ts +482 -0
- package/src/player/types.ts +415 -0
- package/src/rational.ts +388 -0
- package/src/rng.ts +476 -0
- package/src/save/buffer.ts +287 -0
- package/src/save/compress.ts +132 -0
- package/src/save/description.ts +70 -0
- package/src/save/integrity.ts +133 -0
- package/src/score/display.ts +125 -0
- package/src/score/score.ts +341 -0
- package/src/score/types.ts +104 -0
- package/src/session/boot.ts +338 -0
- package/src/session/game.ts +3712 -0
- package/src/session/save.ts +2074 -0
- package/src/sound/engine.ts +375 -0
- package/src/sound/index.ts +13 -0
- package/src/sound/sound-prefs-data.ts +173 -0
- package/src/sound/types.ts +68 -0
- package/src/store/bind.ts +145 -0
- package/src/store/price.ts +97 -0
- package/src/store/store-cmd.ts +73 -0
- package/src/store/store.ts +723 -0
- package/src/store/transact.ts +644 -0
- package/src/store/types.ts +110 -0
- package/src/version.ts +26 -0
- package/src/visuals/engine.ts +444 -0
- package/src/visuals/glyph-table.ts +191 -0
- package/src/visuals/grafmode-data.ts +90 -0
- package/src/visuals/grafmode.ts +123 -0
- package/src/visuals/index.ts +19 -0
- package/src/visuals/map-text.ts +152 -0
- package/src/visuals/pref-expr.ts +182 -0
- package/src/visuals/prefs.ts +981 -0
- package/src/visuals/tile-prefs.ts +183 -0
- package/src/world/chunk.ts +531 -0
- package/src/world/feature.ts +215 -0
- package/src/world/flow.ts +128 -0
- package/src/world/project.ts +687 -0
- package/src/world/projection.ts +228 -0
- package/src/world/scatter.ts +72 -0
- package/src/world/trap.ts +155 -0
- package/src/world/view.ts +513 -0
- package/src/z-queue.ts +300 -0
package/dist/gen/cave.js
ADDED
|
@@ -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
|