azeroth-shell 0.0.0-stage → 0.1.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/README.md +138 -2
- package/bin/azsh.mjs +19 -0
- package/docs/aifid/eventai-texts.json +4414 -0
- package/docs/dungeons/atlas/atlas-rows-dungeons.json +8989 -0
- package/docs/dungeons/survey/pack-id-migration.json +2418 -0
- package/package.json +26 -4
- package/src/autopilot/bossSkip.ts +26 -0
- package/src/autopilot/combatRotation.ts +824 -0
- package/src/autopilot/config.ts +533 -0
- package/src/autopilot/consumables.ts +58 -0
- package/src/autopilot/encounterStand.ts +121 -0
- package/src/autopilot/objectGatePlan.ts +53 -0
- package/src/autopilot/pathEngage.ts +229 -0
- package/src/autopilot/plan.ts +1643 -0
- package/src/autopilot/questPolicy.ts +274 -0
- package/src/autopilot/routeStep.ts +69 -0
- package/src/autopilot/sessionReport.ts +88 -0
- package/src/autopilot/topOffPower.ts +26 -0
- package/src/autopilot/view.ts +476 -0
- package/src/cli/bootCheck.mjs +34 -0
- package/src/cli/bootData.mjs +57 -0
- package/src/cli/combatLogFilter.ts +153 -0
- package/src/cli/combatLogFormat.ts +613 -0
- package/src/cli/fileRepository.mjs +105 -0
- package/src/cli/gzLoader.mjs +31 -0
- package/src/cli/liveGauntletEscort.ts +978 -0
- package/src/cli/liveStratUndead.ts +305 -0
- package/src/cli/macroHostCombat.ts +545 -0
- package/src/cli/macroHostWorld.ts +330 -0
- package/src/cli/prefs.mjs +31 -0
- package/src/cli/register.mjs +4 -0
- package/src/cli/repl.mjs +693 -0
- package/src/cli/rotationText.mjs +226 -0
- package/src/cli/squad.mjs +105 -0
- package/src/cli/term.mjs +126 -0
- package/src/cli/text.mjs +122 -0
- package/src/cli/tui.mjs +626 -0
- package/src/cli/tuiArena.mjs +109 -0
- package/src/cli/tuiAutopilotSettings.mjs +93 -0
- package/src/cli/tuiBags.mjs +180 -0
- package/src/cli/tuiBars.mjs +133 -0
- package/src/cli/tuiBg.mjs +243 -0
- package/src/cli/tuiBulk.mjs +162 -0
- package/src/cli/tuiCheck.mjs +1882 -0
- package/src/cli/tuiCity.mjs +396 -0
- package/src/cli/tuiCompanionKit.mjs +300 -0
- package/src/cli/tuiCompanions.mjs +471 -0
- package/src/cli/tuiDev.mjs +62 -0
- package/src/cli/tuiDungeon.mjs +606 -0
- package/src/cli/tuiEnchantTarget.mjs +120 -0
- package/src/cli/tuiFight.mjs +577 -0
- package/src/cli/tuiFightPanes.mjs +228 -0
- package/src/cli/tuiFloor.mjs +506 -0
- package/src/cli/tuiGear.mjs +158 -0
- package/src/cli/tuiHonor.mjs +74 -0
- package/src/cli/tuiLoot.mjs +97 -0
- package/src/cli/tuiMacros.mjs +144 -0
- package/src/cli/tuiOutfitter.mjs +115 -0
- package/src/cli/tuiPet.mjs +116 -0
- package/src/cli/tuiProfessions.mjs +800 -0
- package/src/cli/tuiRaid.mjs +127 -0
- package/src/cli/tuiRep.mjs +65 -0
- package/src/cli/tuiSaves.mjs +69 -0
- package/src/cli/tuiSearch.mjs +363 -0
- package/src/cli/tuiSettings.mjs +151 -0
- package/src/cli/tuiSimLab.mjs +80 -0
- package/src/cli/tuiTalents.mjs +60 -0
- package/src/cli/tuiViews.mjs +1178 -0
- package/src/cli/tuiWiki.mjs +181 -0
- package/src/cli/tuiWorld.mjs +361 -0
- package/src/cli/wikiIndex.ts +259 -0
- package/src/cli/wikiSources.ts +355 -0
- package/src/data/alteracValley.ts +176 -0
- package/src/data/appearanceOptions.ts +534 -0
- package/src/data/applyLootChanceSanity.ts +21 -0
- package/src/data/createFlavor.ts +96 -0
- package/src/data/customItems.ts +33 -0
- package/src/data/dataStore.ts +1904 -0
- package/src/data/dungeonGameObjects.ts +84 -0
- package/src/data/dungeonPlateCover.ts +66 -0
- package/src/data/dungeonSpawns.ts +294 -0
- package/src/data/dungeons/alterac-valley.ts +135 -0
- package/src/data/dungeons/apply-dungeon-rooms.ts +29 -0
- package/src/data/dungeons/apply-encounter-outdoors.ts +72 -0
- package/src/data/dungeons/apply-instance-geometry.ts +65 -0
- package/src/data/dungeons/apply-spawn-placement.ts +171 -0
- package/src/data/dungeons/blackfathom-deeps.ts +539 -0
- package/src/data/dungeons/blackrock-depths.ts +1410 -0
- package/src/data/dungeons/blackwing-lair.ts +129 -0
- package/src/data/dungeons/boss-eventai.ts +8 -0
- package/src/data/dungeons/deadmines.ts +410 -0
- package/src/data/dungeons/dire-maul-east.ts +980 -0
- package/src/data/dungeons/dire-maul-north.ts +661 -0
- package/src/data/dungeons/dire-maul-west.ts +785 -0
- package/src/data/dungeons/dungeon-rooms.json +5479 -0
- package/src/data/dungeons/encounter-outdoors.json +359 -0
- package/src/data/dungeons/eventai/blackfathom-deeps.ts +308 -0
- package/src/data/dungeons/eventai/blackrock-depths.ts +1257 -0
- package/src/data/dungeons/eventai/deadmines.ts +264 -0
- package/src/data/dungeons/eventai/dire-maul-east.ts +320 -0
- package/src/data/dungeons/eventai/gnomeregan.ts +213 -0
- package/src/data/dungeons/eventai/helpers.ts +79 -0
- package/src/data/dungeons/eventai/index.ts +133 -0
- package/src/data/dungeons/eventai/lbrs.ts +372 -0
- package/src/data/dungeons/eventai/maraudon.ts +323 -0
- package/src/data/dungeons/eventai/ragefire-chasm.ts +108 -0
- package/src/data/dungeons/eventai/razorfen-downs.ts +207 -0
- package/src/data/dungeons/eventai/razorfen-kraul.ts +326 -0
- package/src/data/dungeons/eventai/scholomance.ts +680 -0
- package/src/data/dungeons/eventai/shadowfang-keep.ts +230 -0
- package/src/data/dungeons/eventai/sm-cathedral.ts +75 -0
- package/src/data/dungeons/eventai/sm-graveyard.ts +150 -0
- package/src/data/dungeons/eventai/sm-library.ts +55 -0
- package/src/data/dungeons/eventai/stratholme-undead.ts +127 -0
- package/src/data/dungeons/eventai/sunken-temple.ts +582 -0
- package/src/data/dungeons/eventai/the-stockade.ts +228 -0
- package/src/data/dungeons/eventai/ubrs.ts +225 -0
- package/src/data/dungeons/eventai/uldaman.ts +355 -0
- package/src/data/dungeons/eventai/wailing-caverns.ts +396 -0
- package/src/data/dungeons/eventai/zul-farrak.ts +591 -0
- package/src/data/dungeons/gnomeregan.ts +909 -0
- package/src/data/dungeons/index.ts +126 -0
- package/src/data/dungeons/instance-geometry.json +5233 -0
- package/src/data/dungeons/key-sources.json +169 -0
- package/src/data/dungeons/lbrs.ts +996 -0
- package/src/data/dungeons/maraudon.ts +786 -0
- package/src/data/dungeons/molten-core.ts +194 -0
- package/src/data/dungeons/naxxramas.ts +173 -0
- package/src/data/dungeons/onyxias-lair.ts +71 -0
- package/src/data/dungeons/pack-from-clusters.ts +1256 -0
- package/src/data/dungeons/ragefire-chasm.ts +238 -0
- package/src/data/dungeons/razorfen-downs.ts +698 -0
- package/src/data/dungeons/razorfen-kraul.ts +766 -0
- package/src/data/dungeons/ruins-of-ahnqiraj.ts +121 -0
- package/src/data/dungeons/scholomance.ts +1076 -0
- package/src/data/dungeons/shadowfang-keep.ts +740 -0
- package/src/data/dungeons/sm-armory.ts +266 -0
- package/src/data/dungeons/sm-cathedral.ts +430 -0
- package/src/data/dungeons/sm-graveyard.ts +482 -0
- package/src/data/dungeons/sm-library.ts +337 -0
- package/src/data/dungeons/stratholme-live.ts +808 -0
- package/src/data/dungeons/stratholme-undead.ts +1164 -0
- package/src/data/dungeons/sunken-temple.ts +931 -0
- package/src/data/dungeons/temple-of-ahnqiraj.ts +119 -0
- package/src/data/dungeons/the-stockade.ts +282 -0
- package/src/data/dungeons/ubrs.ts +1286 -0
- package/src/data/dungeons/uldaman.ts +685 -0
- package/src/data/dungeons/wailing-caverns.ts +356 -0
- package/src/data/dungeons/zul-farrak.ts +686 -0
- package/src/data/dungeons/zul-gurub.ts +142 -0
- package/src/data/dungeons.ts +42 -0
- package/src/data/fixtures.ts +446 -0
- package/src/data/gameobject-data.json.gz +0 -0
- package/src/data/item-data.json.gz +0 -0
- package/src/data/loot-data.json.gz +0 -0
- package/src/data/maps/atlas-approach-maps.json +1425 -0
- package/src/data/maps/atlas-landmarks.json +1711 -0
- package/src/data/maps/dungeon-viewports.json +392 -0
- package/src/data/materializeModDamagePercentTaken.ts +70 -0
- package/src/data/materializeModHealthRegen.ts +89 -0
- package/src/data/materializeSpellReputation.ts +26 -0
- package/src/data/pageTexts.ts +19 -0
- package/src/data/profession-data.json.gz +0 -0
- package/src/data/raid-plans/blackfathom-deeps.json +86 -0
- package/src/data/raid-plans/blackrock-depths.json +419 -0
- package/src/data/raid-plans/blackwing-lair.json +328 -0
- package/src/data/raid-plans/deadmines.json +55 -0
- package/src/data/raid-plans/dire-maul-east.json +94 -0
- package/src/data/raid-plans/dire-maul-north.json +70 -0
- package/src/data/raid-plans/dire-maul-west.json +75 -0
- package/src/data/raid-plans/gnomeregan.json +131 -0
- package/src/data/raid-plans/lbrs.json +176 -0
- package/src/data/raid-plans/maraudon.json +102 -0
- package/src/data/raid-plans/molten-core.json +169 -0
- package/src/data/raid-plans/naxxramas.json +354 -0
- package/src/data/raid-plans/onyxias-lair.json +18 -0
- package/src/data/raid-plans/ragefire-chasm.json +30 -0
- package/src/data/raid-plans/razorfen-downs.json +75 -0
- package/src/data/raid-plans/razorfen-kraul.json +53 -0
- package/src/data/raid-plans/ruins-of-ahnqiraj.json +110 -0
- package/src/data/raid-plans/scholomance.json +170 -0
- package/src/data/raid-plans/shadowfang-keep.json +87 -0
- package/src/data/raid-plans/sm-armory.json +53 -0
- package/src/data/raid-plans/sm-cathedral.json +72 -0
- package/src/data/raid-plans/sm-graveyard.json +63 -0
- package/src/data/raid-plans/sm-library.json +23 -0
- package/src/data/raid-plans/stratholme-live.json +129 -0
- package/src/data/raid-plans/stratholme-undead.json +73 -0
- package/src/data/raid-plans/sunken-temple.json +159 -0
- package/src/data/raid-plans/temple-of-ahnqiraj.json +163 -0
- package/src/data/raid-plans/the-stockade.json +42 -0
- package/src/data/raid-plans/ubrs.json +53 -0
- package/src/data/raid-plans/uldaman.json +131 -0
- package/src/data/raid-plans/wailing-caverns.json +90 -0
- package/src/data/raid-plans/zul-farrak.json +156 -0
- package/src/data/raid-plans/zul-gurub.json +307 -0
- package/src/data/raidInstanceCreatures.mjs +473 -0
- package/src/data/rotations/index.ts +152 -0
- package/src/data/rotations/layout.mjs +72 -0
- package/src/data/rotations/pve/druid/balance.json +754 -0
- package/src/data/rotations/pve/druid/feral-tank.json +2448 -0
- package/src/data/rotations/pve/druid/feral.json +2660 -0
- package/src/data/rotations/pve/druid/restoration.json +424 -0
- package/src/data/rotations/pve/hunter/beast-mastery.json +1288 -0
- package/src/data/rotations/pve/hunter/marksmanship.json +1260 -0
- package/src/data/rotations/pve/hunter/survival.json +1373 -0
- package/src/data/rotations/pve/mage/arcane-leveling.json +1798 -0
- package/src/data/rotations/pve/mage/fire-leveling.json +2003 -0
- package/src/data/rotations/pve/mage/frost-leveling.json +1912 -0
- package/src/data/rotations/pve/paladin/holy.json +755 -0
- package/src/data/rotations/pve/paladin/protection.json +962 -0
- package/src/data/rotations/pve/paladin/retribution.json +924 -0
- package/src/data/rotations/pve/priest/discipline.json +772 -0
- package/src/data/rotations/pve/priest/holy.json +707 -0
- package/src/data/rotations/pve/priest/shadow.json +787 -0
- package/src/data/rotations/pve/rogue/assassination.json +2477 -0
- package/src/data/rotations/pve/rogue/combat.json +2483 -0
- package/src/data/rotations/pve/rogue/subtlety.json +2523 -0
- package/src/data/rotations/pve/shaman/elemental.json +1157 -0
- package/src/data/rotations/pve/shaman/enhancement.json +1085 -0
- package/src/data/rotations/pve/shaman/restoration.json +592 -0
- package/src/data/rotations/pve/warlock/affliction.json +1185 -0
- package/src/data/rotations/pve/warlock/demonology.json +1165 -0
- package/src/data/rotations/pve/warlock/destruction.json +1177 -0
- package/src/data/rotations/pve/warrior/arms.json +3020 -0
- package/src/data/rotations/pve/warrior/fury.json +2996 -0
- package/src/data/rotations/pve/warrior/protection.json +2509 -0
- package/src/data/rotations/pvp/druid/balance.json +593 -0
- package/src/data/rotations/pvp/druid/feral.json +3276 -0
- package/src/data/rotations/pvp/druid/restoration.json +662 -0
- package/src/data/rotations/pvp/hunter/beast-mastery.json +665 -0
- package/src/data/rotations/pvp/hunter/marksmanship.json +2406 -0
- package/src/data/rotations/pvp/hunter/survival.json +635 -0
- package/src/data/rotations/pvp/mage/arcane.json +574 -0
- package/src/data/rotations/pvp/mage/fire.json +570 -0
- package/src/data/rotations/pvp/mage/frost.json +4048 -0
- package/src/data/rotations/pvp/paladin/holy.json +1498 -0
- package/src/data/rotations/pvp/paladin/protection.json +567 -0
- package/src/data/rotations/pvp/paladin/retribution.json +1110 -0
- package/src/data/rotations/pvp/priest/discipline.json +1848 -0
- package/src/data/rotations/pvp/priest/holy.json +6340 -0
- package/src/data/rotations/pvp/priest/shadow.json +2329 -0
- package/src/data/rotations/pvp/rogue/assassination.json +1322 -0
- package/src/data/rotations/pvp/rogue/combat.json +5258 -0
- package/src/data/rotations/pvp/rogue/subtlety.json +586 -0
- package/src/data/rotations/pvp/shaman/elemental.json +1404 -0
- package/src/data/rotations/pvp/shaman/enhancement.json +1277 -0
- package/src/data/rotations/pvp/shaman/restoration.json +2490 -0
- package/src/data/rotations/pvp/warlock/affliction.json +1017 -0
- package/src/data/rotations/pvp/warlock/demonology.json +1503 -0
- package/src/data/rotations/pvp/warlock/destruction.json +638 -0
- package/src/data/rotations/pvp/warrior/arms.json +5665 -0
- package/src/data/rotations/pvp/warrior/fury.json +850 -0
- package/src/data/rotations/pvp/warrior/protection.json +777 -0
- package/src/data/schema/bundle.ts +511 -0
- package/src/data/schema/constants.ts +49 -0
- package/src/data/schema/creature.ts +454 -0
- package/src/data/schema/encounter.ts +155 -0
- package/src/data/schema/faction.ts +48 -0
- package/src/data/schema/item.ts +297 -0
- package/src/data/schema/itemEnchant.ts +105 -0
- package/src/data/schema/npc.ts +44 -0
- package/src/data/schema/primitives.ts +295 -0
- package/src/data/schema/profession.ts +226 -0
- package/src/data/schema/quest.ts +154 -0
- package/src/data/schema/spell.ts +1149 -0
- package/src/data/schema/talent.ts +74 -0
- package/src/data/schema/world.ts +124 -0
- package/src/data/schema.ts +19 -0
- package/src/data/skinNodeGapFill.ts +68 -0
- package/src/data/spell-data.json.gz +0 -0
- package/src/data/spell-script-targets.json +1 -0
- package/src/data/tables/ahItemValues.json.gz +0 -0
- package/src/data/tables/alteracValley.json.gz +0 -0
- package/src/data/tables/areatriggerTeleports.json.gz +0 -0
- package/src/data/tables/botNames.json.gz +0 -0
- package/src/data/tables/botPopulation.json.gz +0 -0
- package/src/data/tables/botSpecs.json.gz +0 -0
- package/src/data/tables/classLevelStats.json.gz +0 -0
- package/src/data/tables/combatConditions.json.gz +0 -0
- package/src/data/tables/combatSkillMeta.json.gz +0 -0
- package/src/data/tables/conditions.json.gz +0 -0
- package/src/data/tables/createInfo.json.gz +0 -0
- package/src/data/tables/creatureAggroGroups.json.gz +0 -0
- package/src/data/tables/creatureClassLevelStats.json.gz +0 -0
- package/src/data/tables/creaturePatrolPacks.json.gz +0 -0
- package/src/data/tables/creatureWeapons.json.gz +0 -0
- package/src/data/tables/creatures.json.gz +0 -0
- package/src/data/tables/dbScripts.json.gz +0 -0
- package/src/data/tables/dungeonAuthoredRoutes.json.gz +0 -0
- package/src/data/tables/dungeonCensus.json.gz +0 -0
- package/src/data/tables/dungeonGameObjects.json.gz +0 -0
- package/src/data/tables/dungeonPacks.json.gz +0 -0
- package/src/data/tables/dungeonPlateCover.json.gz +0 -0
- package/src/data/tables/dungeonSpawns.json.gz +0 -0
- package/src/data/tables/dungeonSurvey.json.gz +0 -0
- package/src/data/tables/durabilityCosts.json.gz +0 -0
- package/src/data/tables/durabilityQuality.json.gz +0 -0
- package/src/data/tables/enchantApplicators.json.gz +0 -0
- package/src/data/tables/entranceYards.json.gz +0 -0
- package/src/data/tables/explorationBasexp.json.gz +0 -0
- package/src/data/tables/factionTemplates.json.gz +0 -0
- package/src/data/tables/factions.json.gz +0 -0
- package/src/data/tables/gameEvents.json.gz +0 -0
- package/src/data/tables/gameObjects.json.gz +0 -0
- package/src/data/tables/gossipMenus.json.gz +0 -0
- package/src/data/tables/graveyards.json.gz +0 -0
- package/src/data/tables/instanceMaps.json.gz +0 -0
- package/src/data/tables/itemEnchantmentTemplates.json.gz +0 -0
- package/src/data/tables/itemEnchants.json.gz +0 -0
- package/src/data/tables/itemRandomProperties.json.gz +0 -0
- package/src/data/tables/itemSets.json.gz +0 -0
- package/src/data/tables/items.json.gz +0 -0
- package/src/data/tables/levelStats.json.gz +0 -0
- package/src/data/tables/locks.json.gz +0 -0
- package/src/data/tables/map-pin-overrides.json.gz +0 -0
- package/src/data/tables/nonCombatTrash.json.gz +0 -0
- package/src/data/tables/npcTexts.json.gz +0 -0
- package/src/data/tables/pageTexts.json.gz +0 -0
- package/src/data/tables/petAbilityTP.json.gz +0 -0
- package/src/data/tables/petGenericSpells.json.gz +0 -0
- package/src/data/tables/petLevelStats.json.gz +0 -0
- package/src/data/tables/playerbot-weightscales.json.gz +0 -0
- package/src/data/tables/pools.json.gz +0 -0
- package/src/data/tables/quests.json.gz +0 -0
- package/src/data/tables/races.json.gz +0 -0
- package/src/data/tables/reputationRanks.json.gz +0 -0
- package/src/data/tables/reputationRates.json.gz +0 -0
- package/src/data/tables/reputationSpillover.json.gz +0 -0
- package/src/data/tables/ridingSpells.json.gz +0 -0
- package/src/data/tables/scriptTexts.json.gz +0 -0
- package/src/data/tables/soundClipVolume.json.gz +0 -0
- package/src/data/tables/spellChain.json.gz +0 -0
- package/src/data/tables/spellTeleports.json.gz +0 -0
- package/src/data/tables/spells.json.gz +0 -0
- package/src/data/tables/talents.json.gz +0 -0
- package/src/data/tables/tavernTriggers.json.gz +0 -0
- package/src/data/tables/taxiNodes.json.gz +0 -0
- package/src/data/tables/taxiRoutes.json.gz +0 -0
- package/src/data/tables/trainers.json.gz +0 -0
- package/src/data/tables/transports.json.gz +0 -0
- package/src/data/tables/travelAnchors.json.gz +0 -0
- package/src/data/tables/travelEdges.json.gz +0 -0
- package/src/data/tables/unitConditions.json.gz +0 -0
- package/src/data/tables/vendors.json.gz +0 -0
- package/src/data/tables/weaponEnchantProcs.json.gz +0 -0
- package/src/data/tables/xp.json.gz +0 -0
- package/src/data/tables/zoneWeather.json.gz +0 -0
- package/src/data/tables/zones.json.gz +0 -0
- package/src/data/world-data.ts +56 -0
- package/src/data/zone-music-sets.json +2751 -0
- package/src/dev/etl/lib/loot-chance-sanity.mjs +180 -0
- package/src/domain/audioQuality.mjs +145 -0
- package/src/domain/bg/ab.ts +433 -0
- package/src/domain/bg/arenaComps.ts +644 -0
- package/src/domain/bg/arenaLab.ts +695 -0
- package/src/domain/bg/arenaPace.ts +31 -0
- package/src/domain/bg/arenaRating.ts +110 -0
- package/src/domain/bg/av.ts +758 -0
- package/src/domain/bg/avEnemyPush.ts +219 -0
- package/src/domain/bg/avField.ts +283 -0
- package/src/domain/bg/avInstanceNodes.ts +197 -0
- package/src/domain/bg/avInstanceScore.ts +123 -0
- package/src/domain/bg/avQuestItems.ts +55 -0
- package/src/domain/bg/avQuests.ts +210 -0
- package/src/domain/bg/avSession.ts +196 -0
- package/src/domain/bg/bgConsumes.ts +269 -0
- package/src/domain/bg/bgPrebuff.ts +153 -0
- package/src/domain/bg/bgRest.ts +91 -0
- package/src/domain/bg/bgRoster.ts +305 -0
- package/src/domain/bg/bgTier.ts +61 -0
- package/src/domain/bg/bgWorld.ts +862 -0
- package/src/domain/bg/match.ts +215 -0
- package/src/domain/bg/strategies.ts +552 -0
- package/src/domain/bg/types.ts +291 -0
- package/src/domain/bg/wsg.ts +264 -0
- package/src/domain/bots/actionProviderSpec.ts +38 -0
- package/src/domain/bots/autoConsumables.ts +285 -0
- package/src/domain/bots/betweenPullRest.ts +239 -0
- package/src/domain/bots/botGear.ts +248 -0
- package/src/domain/bots/botInventory.ts +486 -0
- package/src/domain/bots/botPet.ts +535 -0
- package/src/domain/bots/botPool.ts +291 -0
- package/src/domain/bots/botProfessions.ts +334 -0
- package/src/domain/bots/companionActionBar.ts +209 -0
- package/src/domain/bots/companionHireCost.ts +236 -0
- package/src/domain/bots/companionMissions.ts +582 -0
- package/src/domain/bots/companionPrep.ts +254 -0
- package/src/domain/bots/companionQuests.ts +270 -0
- package/src/domain/bots/companionRep.ts +75 -0
- package/src/domain/bots/companionRotation.ts +708 -0
- package/src/domain/bots/companionXp.ts +66 -0
- package/src/domain/bots/consumableShare.ts +811 -0
- package/src/domain/bots/dpsMate.ts +1072 -0
- package/src/domain/bots/groupCompReport.ts +132 -0
- package/src/domain/bots/healthstoneSupply.ts +198 -0
- package/src/domain/bots/outfitter.ts +1056 -0
- package/src/domain/bots/paladinPrePull.ts +231 -0
- package/src/domain/bots/partyRole.ts +40 -0
- package/src/domain/bots/playerbotEquipCache.ts +412 -0
- package/src/domain/bots/playerbotItemEligibility.ts +60 -0
- package/src/domain/bots/playerbotStatWeight.ts +853 -0
- package/src/domain/bots/playerbotUnavailableItems.ts +147 -0
- package/src/domain/bots/prePullBuffs.ts +219 -0
- package/src/domain/bots/prePullStealth.ts +82 -0
- package/src/domain/bots/runSupply.ts +251 -0
- package/src/domain/bots/runSupplyPrefs.ts +28 -0
- package/src/domain/bots/sendTradeGoods.ts +394 -0
- package/src/domain/bots/soulShardCap.ts +35 -0
- package/src/domain/bots/squad.ts +842 -0
- package/src/domain/bots/squadRun.ts +81 -0
- package/src/domain/character/actionbar.ts +346 -0
- package/src/domain/character/appearance.ts +165 -0
- package/src/domain/character/buildCombatant.ts +1726 -0
- package/src/domain/character/character.ts +356 -0
- package/src/domain/character/characterName.ts +43 -0
- package/src/domain/character/characterRatings.ts +424 -0
- package/src/domain/character/characterSpells.ts +282 -0
- package/src/domain/character/characterStats.ts +1559 -0
- package/src/domain/character/characterTalents.ts +398 -0
- package/src/domain/character/combatSkills.ts +994 -0
- package/src/domain/character/displayForm.ts +72 -0
- package/src/domain/character/formTalentAuras.ts +171 -0
- package/src/domain/character/forms.ts +499 -0
- package/src/domain/character/races.ts +40 -0
- package/src/domain/character/resurrectionSickness.ts +49 -0
- package/src/domain/character/spellbook.ts +169 -0
- package/src/domain/character/stanceBar.ts +354 -0
- package/src/domain/character/stancePrereq.ts +70 -0
- package/src/domain/character/talentBuilds.ts +63 -0
- package/src/domain/character/talentImport.ts +130 -0
- package/src/domain/character/talentLink.ts +123 -0
- package/src/domain/character/talents.ts +201 -0
- package/src/domain/core/conditionSnapshot.ts +92 -0
- package/src/domain/core/hover.ts +14 -0
- package/src/domain/core/ids.ts +58 -0
- package/src/domain/core/internalNameTags.ts +5 -0
- package/src/domain/core/runtimeDataPatches.ts +357 -0
- package/src/domain/creature/aiTargeting.ts +588 -0
- package/src/domain/creature/combatCondition.ts +157 -0
- package/src/domain/creature/creatureAggro.ts +804 -0
- package/src/domain/creature/creatureAi.ts +206 -0
- package/src/domain/creature/creatureAiConstants.ts +661 -0
- package/src/domain/creature/creatureCastList.ts +1007 -0
- package/src/domain/creature/creatureEventAi.ts +1409 -0
- package/src/domain/creature/creatureGroups.ts +144 -0
- package/src/domain/creature/creatureLinking.ts +333 -0
- package/src/domain/creature/creatureScaling.ts +320 -0
- package/src/domain/creature/creatureTrigger.ts +53 -0
- package/src/domain/creature/creatureWander.ts +74 -0
- package/src/domain/creature/eventAiTexts.ts +145 -0
- package/src/domain/creature/faction.ts +287 -0
- package/src/domain/creature/forcedReaction.ts +48 -0
- package/src/domain/creature/outdoorHuntable.ts +74 -0
- package/src/domain/creature/patrolPacks.ts +210 -0
- package/src/domain/creature/reputation.ts +967 -0
- package/src/domain/creature/unitFlags.ts +74 -0
- package/src/domain/creature/worldBosses.ts +393 -0
- package/src/domain/dungeonMusic.mjs +48 -0
- package/src/domain/economy/auction.ts +282 -0
- package/src/domain/economy/auctionBot.ts +1060 -0
- package/src/domain/economy/honorPrice.ts +100 -0
- package/src/domain/economy/mail.ts +958 -0
- package/src/domain/economy/money.ts +42 -0
- package/src/domain/economy/syntheticClassTrainers.ts +62 -0
- package/src/domain/economy/trading.ts +296 -0
- package/src/domain/economy/vendorSellAll.ts +252 -0
- package/src/domain/economy/vendorStock.ts +146 -0
- package/src/domain/items/ammo.ts +817 -0
- package/src/domain/items/bank.ts +162 -0
- package/src/domain/items/consumableFamily.ts +205 -0
- package/src/domain/items/craftUpgrades.ts +83 -0
- package/src/domain/items/createItem.ts +93 -0
- package/src/domain/items/durability.ts +581 -0
- package/src/domain/items/equipment.ts +441 -0
- package/src/domain/items/equipmentHelper.ts +767 -0
- package/src/domain/items/inventory.ts +561 -0
- package/src/domain/items/itemBonding.ts +93 -0
- package/src/domain/items/itemContainer.ts +245 -0
- package/src/domain/items/itemDisplayName.ts +33 -0
- package/src/domain/items/itemInstance.ts +181 -0
- package/src/domain/items/itemRandomEnchantOutcomes.ts +315 -0
- package/src/domain/items/itemRandomProps.ts +77 -0
- package/src/domain/items/itemRestrictions.ts +329 -0
- package/src/domain/items/itemTeaches.ts +82 -0
- package/src/domain/items/itemUseCooldown.ts +52 -0
- package/src/domain/items/itemUseTarget.ts +90 -0
- package/src/domain/items/junkCategories.ts +174 -0
- package/src/domain/items/junkFilter.ts +368 -0
- package/src/domain/items/keyring.ts +131 -0
- package/src/domain/items/loot.ts +221 -0
- package/src/domain/items/mounts.ts +190 -0
- package/src/domain/items/onUsePayload.ts +102 -0
- package/src/domain/items/pendingLoot.ts +107 -0
- package/src/domain/items/relic.ts +105 -0
- package/src/domain/items/rocketBoots.ts +36 -0
- package/src/domain/items/sortContainer.ts +139 -0
- package/src/domain/items/spareContainers.ts +71 -0
- package/src/domain/items/spellReagentIndex.ts +131 -0
- package/src/domain/items/staleConsumables.ts +66 -0
- package/src/domain/items/weaponSpender.ts +110 -0
- package/src/domain/listDungeonIds.mjs +28 -0
- package/src/domain/macro/compose.ts +239 -0
- package/src/domain/macro/conditions.ts +260 -0
- package/src/domain/macro/creatureTypes.ts +40 -0
- package/src/domain/macro/describe.ts +169 -0
- package/src/domain/macro/icon.ts +60 -0
- package/src/domain/macro/index.ts +20 -0
- package/src/domain/macro/normalize.ts +112 -0
- package/src/domain/macro/parse.ts +316 -0
- package/src/domain/macro/plan.ts +288 -0
- package/src/domain/macro/rotation.ts +590 -0
- package/src/domain/macro/rotationEnv.ts +173 -0
- package/src/domain/macro/slots.ts +101 -0
- package/src/domain/macro/snapshot.ts +114 -0
- package/src/domain/macro/templates.ts +446 -0
- package/src/domain/macro/types.ts +70 -0
- package/src/domain/macro/units.ts +93 -0
- package/src/domain/macro/weaponTypes.ts +83 -0
- package/src/domain/nav/cityTeleports.ts +10 -0
- package/src/domain/nav/mounting.ts +113 -0
- package/src/domain/nav/nav.ts +43 -0
- package/src/domain/nav/navCollision.ts +606 -0
- package/src/domain/nav/navOutdoors.ts +91 -0
- package/src/domain/nav/navRegistry.ts +179 -0
- package/src/domain/nav/navRoomGraph.ts +302 -0
- package/src/domain/nav/navTypes.ts +43 -0
- package/src/domain/nav/navmesh.ts +1715 -0
- package/src/domain/nav/speed.ts +200 -0
- package/src/domain/nav/storey.ts +10 -0
- package/src/domain/nav/travel.ts +725 -0
- package/src/domain/nav/travelTypes.ts +75 -0
- package/src/domain/pet/pet.ts +1150 -0
- package/src/domain/pet/petActionBar.ts +180 -0
- package/src/domain/pet/petSpellbook.ts +39 -0
- package/src/domain/pet/petStable.ts +81 -0
- package/src/domain/professions/autoDisenchant.ts +236 -0
- package/src/domain/professions/characterEnchants.ts +176 -0
- package/src/domain/professions/craftCooldown.ts +52 -0
- package/src/domain/professions/enchantApply.ts +62 -0
- package/src/domain/professions/enchantTargets.ts +114 -0
- package/src/domain/professions/enchanting.ts +236 -0
- package/src/domain/professions/gatherFilter.ts +149 -0
- package/src/domain/professions/gearEnchant.ts +137 -0
- package/src/domain/professions/heldTools.ts +29 -0
- package/src/domain/professions/learnAllRecipes.ts +146 -0
- package/src/domain/professions/miningHarvest.ts +69 -0
- package/src/domain/professions/partyDisenchant.ts +294 -0
- package/src/domain/professions/partyWork.ts +83 -0
- package/src/domain/professions/pickpocket.ts +172 -0
- package/src/domain/professions/poisons.ts +240 -0
- package/src/domain/professions/professions.ts +677 -0
- package/src/domain/professions/rareNodes.ts +45 -0
- package/src/domain/professions/skinLoot.ts +27 -0
- package/src/domain/professions/vendorReagents.ts +152 -0
- package/src/domain/professions/weaponImbueHand.ts +171 -0
- package/src/domain/questFarmFilter.mjs +42 -0
- package/src/domain/quests/doQuest.ts +1204 -0
- package/src/domain/quests/gossip.ts +308 -0
- package/src/domain/quests/gossipTalk.ts +298 -0
- package/src/domain/quests/interpreter/canComplete.ts +150 -0
- package/src/domain/quests/interpreter/claim.ts +137 -0
- package/src/domain/quests/interpreter/dbscript/handlers.ts +1371 -0
- package/src/domain/quests/interpreter/dbscript/handlersBatch.ts +1251 -0
- package/src/domain/quests/interpreter/dbscript/hooks.ts +331 -0
- package/src/domain/quests/interpreter/dbscript/index.ts +212 -0
- package/src/domain/quests/interpreter/dbscript/opcodes.ts +257 -0
- package/src/domain/quests/interpreter/dbscript/registry.ts +1194 -0
- package/src/domain/quests/interpreter/dbscript/registryAreatriggerScripts.ts +539 -0
- package/src/domain/quests/interpreter/dbscript/registryAshenvale.ts +553 -0
- package/src/domain/quests/interpreter/dbscript/registryAzshara.ts +272 -0
- package/src/domain/quests/interpreter/dbscript/registryDarkshore.ts +637 -0
- package/src/domain/quests/interpreter/dbscript/registryDesolace.ts +501 -0
- package/src/domain/quests/interpreter/dbscript/registryDustwallowMarsh.ts +735 -0
- package/src/domain/quests/interpreter/dbscript/registryFelwood.ts +493 -0
- package/src/domain/quests/interpreter/dbscript/registryFeralas.ts +485 -0
- package/src/domain/quests/interpreter/dbscript/registryGoScripts.ts +203 -0
- package/src/domain/quests/interpreter/dbscript/registryMoonglade.ts +288 -0
- package/src/domain/quests/interpreter/dbscript/registryNpcProfessions.ts +230 -0
- package/src/domain/quests/interpreter/dbscript/registryNpcsSpecial.ts +545 -0
- package/src/domain/quests/interpreter/dbscript/registryRazorfenDowns.ts +293 -0
- package/src/domain/quests/interpreter/dbscript/registryRazorfenKraul.ts +221 -0
- package/src/domain/quests/interpreter/dbscript/registryRedridge.ts +112 -0
- package/src/domain/quests/interpreter/dbscript/registrySearingGorge.ts +125 -0
- package/src/domain/quests/interpreter/dbscript/registrySiblings.ts +51 -0
- package/src/domain/quests/interpreter/dbscript/registrySilverpineForest.ts +172 -0
- package/src/domain/quests/interpreter/dbscript/registryStonetalonMountains.ts +180 -0
- package/src/domain/quests/interpreter/dbscript/registryStormwindCity.ts +369 -0
- package/src/domain/quests/interpreter/dbscript/registryStranglethornVale.ts +115 -0
- package/src/domain/quests/interpreter/dbscript/registryStratholme.ts +146 -0
- package/src/domain/quests/interpreter/dbscript/registrySunkenTemple.ts +149 -0
- package/src/domain/quests/interpreter/dbscript/registrySwampOfSorrows.ts +163 -0
- package/src/domain/quests/interpreter/dbscript/registryTanaris.ts +318 -0
- package/src/domain/quests/interpreter/dbscript/registryTeldrassil.ts +120 -0
- package/src/domain/quests/interpreter/dbscript/registryTheBarrens.ts +603 -0
- package/src/domain/quests/interpreter/dbscript/registryThousandNeedles.ts +499 -0
- package/src/domain/quests/interpreter/dbscript/registryTirisfalGlades.ts +222 -0
- package/src/domain/quests/interpreter/dbscript/registryUngoroCrater.ts +291 -0
- package/src/domain/quests/interpreter/dbscript/registryWarEffort.ts +773 -0
- package/src/domain/quests/interpreter/dbscript/registryWesternPlaguelands.ts +206 -0
- package/src/domain/quests/interpreter/dbscript/registryWestfall.ts +190 -0
- package/src/domain/quests/interpreter/dbscript/registryWetlands.ts +186 -0
- package/src/domain/quests/interpreter/dbscript/registryWinterspring.ts +235 -0
- package/src/domain/quests/interpreter/dbscript/types.ts +817 -0
- package/src/domain/quests/interpreter/dbscript/vm.ts +171 -0
- package/src/domain/quests/interpreter/diff.ts +349 -0
- package/src/domain/quests/interpreter/fixtures/oracle-questlog-qi2.json +2417 -0
- package/src/domain/quests/interpreter/flags.ts +101 -0
- package/src/domain/quests/interpreter/index.ts +113 -0
- package/src/domain/quests/interpreter/live.ts +178 -0
- package/src/domain/quests/interpreter/types.ts +81 -0
- package/src/domain/quests/interpreter/updaters.ts +412 -0
- package/src/domain/quests/questCorpseUse.ts +155 -0
- package/src/domain/quests/questDialog.ts +43 -0
- package/src/domain/quests/questFight.ts +34 -0
- package/src/domain/quests/questObjectiveLabel.ts +39 -0
- package/src/domain/quests/questRewardChoice.ts +85 -0
- package/src/domain/quests/questScriptCredit.ts +26 -0
- package/src/domain/quests/questScriptItemUse.ts +74 -0
- package/src/domain/quests/questTameRods.ts +79 -0
- package/src/domain/quests/questlog.ts +1545 -0
- package/src/domain/quests/talkDeliver.ts +170 -0
- package/src/domain/raid/globalActionBar.ts +195 -0
- package/src/domain/raid/instanceRoster.ts +194 -0
- package/src/domain/raid/partySummon.ts +198 -0
- package/src/domain/raid/partyVault.ts +320 -0
- package/src/domain/raid/raidBossAbilities.ts +268 -0
- package/src/domain/raid/raidComp.ts +335 -0
- package/src/domain/raid/raidCustomRows.ts +453 -0
- package/src/domain/raid/raidEncounterGear.ts +298 -0
- package/src/domain/raid/raidEncounterTactics.ts +412 -0
- package/src/domain/raid/raidPlan.ts +509 -0
- package/src/domain/raid/raidPlanEdits.ts +98 -0
- package/src/domain/raid/raidPlanPacks.ts +480 -0
- package/src/domain/raid/raidRecovery.ts +183 -0
- package/src/domain/raid/raidRosterTemplate.ts +138 -0
- package/src/domain/rotation/auraSide.ts +68 -0
- package/src/domain/rotation/build.ts +3031 -0
- package/src/domain/rotation/classes/druid.ts +58 -0
- package/src/domain/rotation/classes/hooks.ts +59 -0
- package/src/domain/rotation/classes/hunter.ts +156 -0
- package/src/domain/rotation/classes/index.ts +104 -0
- package/src/domain/rotation/classes/mage.ts +99 -0
- package/src/domain/rotation/classes/paladin.ts +296 -0
- package/src/domain/rotation/classes/priest.ts +94 -0
- package/src/domain/rotation/classes/pullOpener.ts +92 -0
- package/src/domain/rotation/classes/rogue.ts +69 -0
- package/src/domain/rotation/classes/shaman.ts +29 -0
- package/src/domain/rotation/classes/supportSpec.ts +94 -0
- package/src/domain/rotation/classes/warlock.ts +81 -0
- package/src/domain/rotation/classes/warrior.ts +402 -0
- package/src/domain/rotation/classify.ts +1381 -0
- package/src/domain/rotation/condMetricPicker.ts +379 -0
- package/src/domain/rotation/consumables.ts +242 -0
- package/src/domain/rotation/evaluate.ts +1115 -0
- package/src/domain/rotation/finisherLadder.ts +145 -0
- package/src/domain/rotation/invent.ts +938 -0
- package/src/domain/rotation/killOrder.ts +30 -0
- package/src/domain/rotation/openerForm.ts +22 -0
- package/src/domain/rotation/paladinBuffs.ts +568 -0
- package/src/domain/rotation/prebuff.ts +283 -0
- package/src/domain/rotation/raidRowTuning.ts +34 -0
- package/src/domain/rotation/rotation.ts +11 -0
- package/src/domain/rotation/rotationProfiles.ts +180 -0
- package/src/domain/rotation/rotationSuggestions.ts +851 -0
- package/src/domain/rotation/ruleAfterChain.ts +49 -0
- package/src/domain/rotation/spec.ts +70 -0
- package/src/domain/rotation/spellPicker.ts +140 -0
- package/src/domain/rotation/stand.ts +143 -0
- package/src/domain/rotation/types.ts +718 -0
- package/src/domain/run/battlegroundRun.ts +111 -0
- package/src/domain/run/canEnterContext.ts +32 -0
- package/src/domain/run/death.ts +561 -0
- package/src/domain/run/deathRecovery.ts +289 -0
- package/src/domain/run/duel.ts +141 -0
- package/src/domain/run/dungeonRun.ts +344 -0
- package/src/domain/run/floorRest.ts +159 -0
- package/src/domain/run/honor.ts +557 -0
- package/src/domain/run/honorWeek.ts +180 -0
- package/src/domain/run/whenMap.ts +977 -0
- package/src/domain/scriptdev/arathiHighlands.ts +445 -0
- package/src/domain/scriptdev/ashenvale.ts +635 -0
- package/src/domain/scriptdev/ashenvaleHelpers.ts +1215 -0
- package/src/domain/scriptdev/azshara.ts +892 -0
- package/src/domain/scriptdev/blackfathomDeeps.ts +212 -0
- package/src/domain/scriptdev/blackrockDepths.ts +1434 -0
- package/src/domain/scriptdev/blackrockSpire.ts +730 -0
- package/src/domain/scriptdev/blackwingLair.ts +521 -0
- package/src/domain/scriptdev/burningSteppes.ts +62 -0
- package/src/domain/scriptdev/classFilter.ts +43 -0
- package/src/domain/scriptdev/darkshore.ts +817 -0
- package/src/domain/scriptdev/darkshoreHelpers.ts +853 -0
- package/src/domain/scriptdev/deadmines.ts +331 -0
- package/src/domain/scriptdev/desolace.ts +1239 -0
- package/src/domain/scriptdev/direMaul.ts +859 -0
- package/src/domain/scriptdev/durotar.ts +532 -0
- package/src/domain/scriptdev/dustwallowMarsh.ts +758 -0
- package/src/domain/scriptdev/dustwallowMarshHelpers.ts +994 -0
- package/src/domain/scriptdev/easternPlaguelands.ts +21 -0
- package/src/domain/scriptdev/felwood.ts +1055 -0
- package/src/domain/scriptdev/feralas.ts +571 -0
- package/src/domain/scriptdev/feralasHelpers.ts +838 -0
- package/src/domain/scriptdev/gnomeregan.ts +466 -0
- package/src/domain/scriptdev/guards.ts +616 -0
- package/src/domain/scriptdev/hinterlands.ts +693 -0
- package/src/domain/scriptdev/host.ts +1134 -0
- package/src/domain/scriptdev/index.ts +123 -0
- package/src/domain/scriptdev/instanceRaids.ts +136 -0
- package/src/domain/scriptdev/itemScripts.ts +37 -0
- package/src/domain/scriptdev/lochModan.ts +283 -0
- package/src/domain/scriptdev/maraudon.ts +232 -0
- package/src/domain/scriptdev/moltenCore.ts +831 -0
- package/src/domain/scriptdev/moonglade.ts +659 -0
- package/src/domain/scriptdev/moongladeHelpers.ts +933 -0
- package/src/domain/scriptdev/naxxramas.ts +577 -0
- package/src/domain/scriptdev/naxxramasConstruct.ts +364 -0
- package/src/domain/scriptdev/naxxramasFrostwyrm.ts +378 -0
- package/src/domain/scriptdev/naxxramasMilitary.ts +276 -0
- package/src/domain/scriptdev/npcProfessions.ts +1025 -0
- package/src/domain/scriptdev/npcsSpecial.ts +1379 -0
- package/src/domain/scriptdev/onyxiasLair.ts +313 -0
- package/src/domain/scriptdev/orgrimmar.ts +147 -0
- package/src/domain/scriptdev/professionSpecConsumer.ts +481 -0
- package/src/domain/scriptdev/razorfenDowns.ts +468 -0
- package/src/domain/scriptdev/razorfenKraul.ts +410 -0
- package/src/domain/scriptdev/redridgeMountains.ts +163 -0
- package/src/domain/scriptdev/registry.ts +945 -0
- package/src/domain/scriptdev/ruinsOfAhnqiraj.ts +356 -0
- package/src/domain/scriptdev/scarletMonastery.ts +713 -0
- package/src/domain/scriptdev/scholomance.ts +682 -0
- package/src/domain/scriptdev/searingGorge.ts +295 -0
- package/src/domain/scriptdev/shadowfangKeep.ts +181 -0
- package/src/domain/scriptdev/silithus.ts +71 -0
- package/src/domain/scriptdev/silverpineForest.ts +662 -0
- package/src/domain/scriptdev/stonetalonMountains.ts +493 -0
- package/src/domain/scriptdev/stormwindCity.ts +1192 -0
- package/src/domain/scriptdev/stranglethornVale.ts +402 -0
- package/src/domain/scriptdev/stratholme.ts +650 -0
- package/src/domain/scriptdev/stratholmeScripts.ts +505 -0
- package/src/domain/scriptdev/sunkenTemple.ts +736 -0
- package/src/domain/scriptdev/swampOfSorrows.ts +22 -0
- package/src/domain/scriptdev/tanaris.ts +391 -0
- package/src/domain/scriptdev/tanarisHelpers.ts +661 -0
- package/src/domain/scriptdev/teldrassil.ts +143 -0
- package/src/domain/scriptdev/templeOfAhnqiraj.ts +1013 -0
- package/src/domain/scriptdev/theBarrens.ts +709 -0
- package/src/domain/scriptdev/theBarrensHelpers.ts +1026 -0
- package/src/domain/scriptdev/thousandNeedles.ts +426 -0
- package/src/domain/scriptdev/thousandNeedlesHelpers.ts +566 -0
- package/src/domain/scriptdev/tirisfalGlades.ts +533 -0
- package/src/domain/scriptdev/types.ts +1026 -0
- package/src/domain/scriptdev/uldaman.ts +437 -0
- package/src/domain/scriptdev/ungoroCrater.ts +549 -0
- package/src/domain/scriptdev/ungoroCraterHelpers.ts +1006 -0
- package/src/domain/scriptdev/wailingCaverns.ts +700 -0
- package/src/domain/scriptdev/warEffort.ts +371 -0
- package/src/domain/scriptdev/westernPlaguelands.ts +952 -0
- package/src/domain/scriptdev/westfall.ts +767 -0
- package/src/domain/scriptdev/wetlands.ts +660 -0
- package/src/domain/scriptdev/winterspring.ts +482 -0
- package/src/domain/scriptdev/winterspringHelpers.ts +801 -0
- package/src/domain/scriptdev/worldBosses.ts +605 -0
- package/src/domain/scriptdev/zulfarrak.ts +478 -0
- package/src/domain/scriptdev/zulgurub.ts +1152 -0
- package/src/domain/sim/dummySim.ts +408 -0
- package/src/domain/sim/targetDummy.ts +29 -0
- package/src/domain/social/guild.ts +148 -0
- package/src/domain/spells/areaAuraMembership.ts +93 -0
- package/src/domain/spells/breakableStunCc.ts +166 -0
- package/src/domain/spells/cancelAura.ts +80 -0
- package/src/domain/spells/diminishing.ts +167 -0
- package/src/domain/spells/dispel.ts +149 -0
- package/src/domain/spells/dispelMechanic.ts +105 -0
- package/src/domain/spells/equipSpells.ts +247 -0
- package/src/domain/spells/learnOnTargets.ts +260 -0
- package/src/domain/spells/mergeSchoolImmunityFromRaw.ts +66 -0
- package/src/domain/spells/resurrectSpell.ts +124 -0
- package/src/domain/spells/spellPushback.ts +39 -0
- package/src/domain/spells/spellReflect.ts +43 -0
- package/src/domain/spells/stealth.ts +183 -0
- package/src/domain/spells/totem.ts +394 -0
- package/src/domain/spells/worldCast.ts +187 -0
- package/src/domain/world/areaTriggerLocks.ts +177 -0
- package/src/domain/world/dungeonMusic.ts +108 -0
- package/src/domain/world/gameEvents.ts +119 -0
- package/src/domain/world/gameObjects.ts +1030 -0
- package/src/domain/world/goEventFire.ts +234 -0
- package/src/domain/world/goMoTransport.ts +74 -0
- package/src/domain/world/goTransport.ts +81 -0
- package/src/domain/world/hazards.ts +459 -0
- package/src/domain/world/openLock.ts +398 -0
- package/src/domain/world/pools.ts +470 -0
- package/src/domain/world/restArea.ts +37 -0
- package/src/domain/world/weather.ts +201 -0
- package/src/domain/zoneAmbience.mjs +87 -0
- package/src/domain/zoneMusic.mjs +62 -0
- package/src/domain/zoneMusicQueue.mjs +7 -0
- package/src/engine/combat/activeSeal.ts +39 -0
- package/src/engine/combat/aiTargetResolve.ts +64 -0
- package/src/engine/combat/aoeCast.ts +289 -0
- package/src/engine/combat/applyConsumeAura.ts +74 -0
- package/src/engine/combat/applyDemonicSacrifice.ts +70 -0
- package/src/engine/combat/applyDispelMechanic.ts +30 -0
- package/src/engine/combat/applyHealEffect.ts +301 -0
- package/src/engine/combat/applySchoolAbsorb.ts +114 -0
- package/src/engine/combat/applySpell.ts +3169 -0
- package/src/engine/combat/applySpellLate.ts +2026 -0
- package/src/engine/combat/applySpellLateBag.ts +144 -0
- package/src/engine/combat/areaAura.ts +283 -0
- package/src/engine/combat/attackTable.ts +508 -0
- package/src/engine/combat/battleRez.ts +87 -0
- package/src/engine/combat/castAction.ts +36 -0
- package/src/engine/combat/castTargetGates.ts +75 -0
- package/src/engine/combat/castableKit.ts +68 -0
- package/src/engine/combat/ccMirrorUpgrade.ts +39 -0
- package/src/engine/combat/chainFan.ts +210 -0
- package/src/engine/combat/channel.ts +423 -0
- package/src/engine/combat/chaseCloseness.ts +95 -0
- package/src/engine/combat/cmangosCastGates.ts +129 -0
- package/src/engine/combat/combatFlag.ts +77 -0
- package/src/engine/combat/combatGeometry.ts +1329 -0
- package/src/engine/combat/combustion.ts +68 -0
- package/src/engine/combat/context.ts +730 -0
- package/src/engine/combat/createSession.ts +1165 -0
- package/src/engine/combat/creatureLeash.ts +38 -0
- package/src/engine/combat/crowdControl.ts +1259 -0
- package/src/engine/combat/dispelDuty.ts +54 -0
- package/src/engine/combat/dispelReflect.ts +609 -0
- package/src/engine/combat/displaceFloor.ts +113 -0
- package/src/engine/combat/duelBounds.ts +47 -0
- package/src/engine/combat/equippedItemGate.ts +297 -0
- package/src/engine/combat/evadeLifecycle.ts +63 -0
- package/src/engine/combat/eventAi.ts +2220 -0
- package/src/engine/combat/facing.ts +74 -0
- package/src/engine/combat/fearFlee.ts +137 -0
- package/src/engine/combat/fighterFactory.ts +1144 -0
- package/src/engine/combat/forceDespawnAction.ts +51 -0
- package/src/engine/combat/formulas.ts +807 -0
- package/src/engine/combat/friendlyFan.ts +55 -0
- package/src/engine/combat/groundPoolTick.ts +210 -0
- package/src/engine/combat/hazards.ts +114 -0
- package/src/engine/combat/healTargets.ts +226 -0
- package/src/engine/combat/heartbeatResist.ts +49 -0
- package/src/engine/combat/hostileItemUse.ts +75 -0
- package/src/engine/combat/interruptAuras.ts +66 -0
- package/src/engine/combat/itemRescue.ts +49 -0
- package/src/engine/combat/itemUse.ts +260 -0
- package/src/engine/combat/kitById.ts +23 -0
- package/src/engine/combat/knockBack.ts +201 -0
- package/src/engine/combat/leap.ts +89 -0
- package/src/engine/combat/mitigation.ts +587 -0
- package/src/engine/combat/modTaunt.ts +43 -0
- package/src/engine/combat/motionClaim.ts +268 -0
- package/src/engine/combat/motionGate.ts +150 -0
- package/src/engine/combat/motionGenerator.ts +27 -0
- package/src/engine/combat/navSight.ts +138 -0
- package/src/engine/combat/perfSink.ts +28 -0
- package/src/engine/combat/periodicDue.ts +55 -0
- package/src/engine/combat/petAuras.ts +86 -0
- package/src/engine/combat/pickAction.ts +3449 -0
- package/src/engine/combat/powerForm.ts +735 -0
- package/src/engine/combat/powerPools.ts +49 -0
- package/src/engine/combat/predicates.ts +538 -0
- package/src/engine/combat/procsDamage.ts +1823 -0
- package/src/engine/combat/reactiveGates.ts +135 -0
- package/src/engine/combat/roles.ts +389 -0
- package/src/engine/combat/rotationSubject.ts +78 -0
- package/src/engine/combat/ruleAfter.ts +106 -0
- package/src/engine/combat/seedDisplayAuras.ts +203 -0
- package/src/engine/combat/seedOocEffects.ts +317 -0
- package/src/engine/combat/seedSchoolImmunities.ts +45 -0
- package/src/engine/combat/session-types.ts +458 -0
- package/src/engine/combat/sessionApi.ts +1785 -0
- package/src/engine/combat/sessionCharge.ts +124 -0
- package/src/engine/combat/sessionChase.ts +869 -0
- package/src/engine/combat/sessionDeaths.ts +469 -0
- package/src/engine/combat/sessionFocus.ts +506 -0
- package/src/engine/combat/sessionFollow.ts +167 -0
- package/src/engine/combat/sessionGeometry.ts +188 -0
- package/src/engine/combat/sessionHome.ts +75 -0
- package/src/engine/combat/sessionInject.ts +719 -0
- package/src/engine/combat/sessionMechanicMove.ts +555 -0
- package/src/engine/combat/sessionMotion.ts +1553 -0
- package/src/engine/combat/sessionMotionKit.ts +247 -0
- package/src/engine/combat/sessionMoveIntent.ts +49 -0
- package/src/engine/combat/sessionMoveOrder.ts +200 -0
- package/src/engine/combat/sessionPathTo.ts +657 -0
- package/src/engine/combat/sessionPools.ts +688 -0
- package/src/engine/combat/sessionSteer.ts +144 -0
- package/src/engine/combat/sessionStepBack.ts +151 -0
- package/src/engine/combat/sessionStepWrap.ts +116 -0
- package/src/engine/combat/sessionView.ts +442 -0
- package/src/engine/combat/simEffects.ts +32 -0
- package/src/engine/combat/simFacade.ts +620 -0
- package/src/engine/combat/simPump.ts +264 -0
- package/src/engine/combat/simSnapshot.ts +165 -0
- package/src/engine/combat/simThreadPool.ts +35 -0
- package/src/engine/combat/skeramImages.ts +57 -0
- package/src/engine/combat/slowUpdate.ts +178 -0
- package/src/engine/combat/snapToFloor.ts +42 -0
- package/src/engine/combat/speedAura.ts +29 -0
- package/src/engine/combat/spellReagents.ts +71 -0
- package/src/engine/combat/stealthDetect.ts +93 -0
- package/src/engine/combat/stepBackMotion.ts +171 -0
- package/src/engine/combat/stepSession.ts +3102 -0
- package/src/engine/combat/summonCast.ts +243 -0
- package/src/engine/combat/thaddiusPolarity.ts +97 -0
- package/src/engine/combat/threat.ts +388 -0
- package/src/engine/combat/threatOps.ts +272 -0
- package/src/engine/combat/tickWorkerBody.ts +211 -0
- package/src/engine/combat/totemAuras.ts +122 -0
- package/src/engine/combat/triggerPayload.ts +42 -0
- package/src/engine/combat/triggeredCompanion.ts +63 -0
- package/src/engine/combat/types.ts +1897 -0
- package/src/engine/combat/useObjectTrap.ts +140 -0
- package/src/engine/combat/walkSurface.ts +54 -0
- package/src/engine/combat/weapon.ts +1248 -0
- package/src/engine/combat/worldFrame.ts +43 -0
- package/src/engine/combat.solo-golden.json +2242 -0
- package/src/engine/combat.ts +84 -0
- package/src/engine/core/detMath.ts +289 -0
- package/src/engine/core/events.ts +357 -0
- package/src/engine/core/rng.ts +36 -0
- package/src/engine/game/actionBars.ts +124 -0
- package/src/engine/game/activateObjectFromSpell.ts +320 -0
- package/src/engine/game/applyActiveObjectFromDbScript.ts +98 -0
- package/src/engine/game/applyDeathAndSpellScripts.ts +268 -0
- package/src/engine/game/applyDoorFromDbScript.ts +388 -0
- package/src/engine/game/applyEscortFromDbScript.ts +290 -0
- package/src/engine/game/applyGoSpellcasterCast.ts +40 -0
- package/src/engine/game/applyHoverFromDbScript.ts +110 -0
- package/src/engine/game/applyOocResurrect.ts +71 -0
- package/src/engine/game/applySpellDurability.ts +115 -0
- package/src/engine/game/applyTailFromDbScript.ts +478 -0
- package/src/engine/game/applyTerminateFromDbScript.ts +95 -0
- package/src/engine/game/auctionMail.ts +269 -0
- package/src/engine/game/bagOverflow.ts +130 -0
- package/src/engine/game/bank.ts +110 -0
- package/src/engine/game/barAutofill.ts +26 -0
- package/src/engine/game/battleground.ts +369 -0
- package/src/engine/game/combatEnemySpec.ts +216 -0
- package/src/engine/game/combatObjectTraps.ts +167 -0
- package/src/engine/game/combatSeed.ts +320 -0
- package/src/engine/game/combatSession.ts +3241 -0
- package/src/engine/game/combatSessionClock.ts +62 -0
- package/src/engine/game/companionQuestActions.ts +242 -0
- package/src/engine/game/companionTraining.ts +40 -0
- package/src/engine/game/consumables.ts +624 -0
- package/src/engine/game/context.ts +53 -0
- package/src/engine/game/createHealthstone.ts +45 -0
- package/src/engine/game/deathFlow.ts +127 -0
- package/src/engine/game/devGrant.ts +117 -0
- package/src/engine/game/dynamicObject.ts +365 -0
- package/src/engine/game/enchantItem.ts +177 -0
- package/src/engine/game/equipmentHelper.ts +806 -0
- package/src/engine/game/factionState.ts +108 -0
- package/src/engine/game/garmentsOoc.ts +69 -0
- package/src/engine/game/globalActionBar.ts +179 -0
- package/src/engine/game/goInteractQuestCredit.ts +51 -0
- package/src/engine/game/goMoTransportUse.ts +61 -0
- package/src/engine/game/goTransportUse.ts +60 -0
- package/src/engine/game/healthstoneUpkeep.ts +59 -0
- package/src/engine/game/killSkin.ts +124 -0
- package/src/engine/game/learnFromItem.ts +118 -0
- package/src/engine/game/loadReconcile.ts +551 -0
- package/src/engine/game/loot.ts +294 -0
- package/src/engine/game/macros.ts +125 -0
- package/src/engine/game/mail.ts +293 -0
- package/src/engine/game/meetingStoneQueue.ts +75 -0
- package/src/engine/game/meetingStoneUse.ts +54 -0
- package/src/engine/game/newGameState.ts +363 -0
- package/src/engine/game/oocAuraInterrupt.ts +92 -0
- package/src/engine/game/oocCast.ts +241 -0
- package/src/engine/game/oocCastEffects.ts +803 -0
- package/src/engine/game/oocCastMethods.ts +1077 -0
- package/src/engine/game/openLockFromSpell.ts +174 -0
- package/src/engine/game/outdoorPvp.ts +187 -0
- package/src/engine/game/outfitter.ts +644 -0
- package/src/engine/game/paladinBoard.ts +393 -0
- package/src/engine/game/partyAuras.ts +121 -0
- package/src/engine/game/persistCombatAuras.ts +91 -0
- package/src/engine/game/pets.ts +459 -0
- package/src/engine/game/prePullHealthstones.ts +154 -0
- package/src/engine/game/professions.ts +2227 -0
- package/src/engine/game/pruneStaleRuntime.ts +39 -0
- package/src/engine/game/questFightCredit.ts +36 -0
- package/src/engine/game/questItemOnCorpse.ts +166 -0
- package/src/engine/game/questItemOnCreature.ts +103 -0
- package/src/engine/game/questLog.ts +879 -0
- package/src/engine/game/questSpellComplete.ts +21 -0
- package/src/engine/game/questTurnIn.ts +598 -0
- package/src/engine/game/raidParty.ts +32 -0
- package/src/engine/game/reagentBuyIn.ts +39 -0
- package/src/engine/game/regenRest.ts +186 -0
- package/src/engine/game/rotation.ts +425 -0
- package/src/engine/game/runFullDungeon.ts +611 -0
- package/src/engine/game/runSupply.ts +80 -0
- package/src/engine/game/sanitizeDriftedItems.ts +135 -0
- package/src/engine/game/scriptDevGoUse.ts +397 -0
- package/src/engine/game/scriptDevInnkeeper.ts +150 -0
- package/src/engine/game/scriptDevProfessionSpec.ts +99 -0
- package/src/engine/game/sendEventFromSpell.ts +87 -0
- package/src/engine/game/sortContainers.ts +85 -0
- package/src/engine/game/spellReputation.ts +69 -0
- package/src/engine/game/squad/squadDungeonSeating.ts +197 -0
- package/src/engine/game/squadDungeon.ts +4222 -0
- package/src/engine/game/squadQuestShare.ts +260 -0
- package/src/engine/game/summonObjectSlot.ts +69 -0
- package/src/engine/game/summonObjectWild.ts +73 -0
- package/src/engine/game/summoningRitualUse.ts +141 -0
- package/src/engine/game/talents.ts +356 -0
- package/src/engine/game/tamingRodUse.ts +66 -0
- package/src/engine/game/tempSpawnCreature.ts +136 -0
- package/src/engine/game/tickDungeonPatrolScripts.ts +205 -0
- package/src/engine/game/tickTimedQuests.ts +28 -0
- package/src/engine/game/trade.ts +586 -0
- package/src/engine/game/travel.ts +1155 -0
- package/src/engine/game/types.ts +1855 -0
- package/src/engine/game/vendorOffers.ts +244 -0
- package/src/engine/game/weaponImbueItem.ts +281 -0
- package/src/engine/game/worldSession.ts +183 -0
- package/src/engine/game/worldState.ts +166 -0
- package/src/engine/game.ts +968 -0
- package/src/engine/spells/GAP.md +67 -0
- package/src/engine/spells/absorbs.ts +376 -0
- package/src/engine/spells/addTargetTrigger.ts +53 -0
- package/src/engine/spells/auraPerSecondCost.ts +50 -0
- package/src/engine/spells/auraStacking.ts +497 -0
- package/src/engine/spells/cmangos-special-cases-combustion.ts +13 -0
- package/src/engine/spells/cmangos-special-cases-s18.ts +445 -0
- package/src/engine/spells/cmangos-special-cases-s19.ts +324 -0
- package/src/engine/spells/cmangos-special-cases-s20.ts +385 -0
- package/src/engine/spells/cmangos-special-cases-s21.ts +116 -0
- package/src/engine/spells/cmangos-special-cases-s22.ts +24 -0
- package/src/engine/spells/cmangos-special-cases-s23.ts +52 -0
- package/src/engine/spells/cmangos-special-cases.ts +1570 -0
- package/src/engine/spells/conditions.ts +718 -0
- package/src/engine/spells/effects.ts +203 -0
- package/src/engine/spells/fixtures/oracle-effects-lock9a.json +1 -0
- package/src/engine/spells/immunity.ts +942 -0
- package/src/engine/spells/intentEffects.ts +351 -0
- package/src/engine/spells/interpreter/GAPS.md +21 -0
- package/src/engine/spells/interpreter/amount.ts +109 -0
- package/src/engine/spells/interpreter/class-scripts-kit.ts +478 -0
- package/src/engine/spells/interpreter/diff.ts +246 -0
- package/src/engine/spells/interpreter/duration.ts +90 -0
- package/src/engine/spells/interpreter/form-attrs.json +1 -0
- package/src/engine/spells/interpreter/form-attrs.ts +95 -0
- package/src/engine/spells/interpreter/handlers/aura-remove.ts +113 -0
- package/src/engine/spells/interpreter/handlers/auras-control.ts +1547 -0
- package/src/engine/spells/interpreter/handlers/auras-core.ts +1583 -0
- package/src/engine/spells/interpreter/handlers/auras-misc.ts +1774 -0
- package/src/engine/spells/interpreter/handlers/effect-disenchant.ts +17 -0
- package/src/engine/spells/interpreter/handlers/effect-skinning.ts +20 -0
- package/src/engine/spells/interpreter/handlers/effects-action-utility.ts +1455 -0
- package/src/engine/spells/interpreter/handlers/effects-core.ts +432 -0
- package/src/engine/spells/interpreter/handlers/effects-remainder.ts +105 -0
- package/src/engine/spells/interpreter/handlers/effects-summon-item.ts +687 -0
- package/src/engine/spells/interpreter/handlers/mod-increase-energy-percent.ts +36 -0
- package/src/engine/spells/interpreter/handlers/mod-increase-energy.ts +36 -0
- package/src/engine/spells/interpreter/handlers/periodic-trigger-zero.ts +327 -0
- package/src/engine/spells/interpreter/handlers/procs.ts +131 -0
- package/src/engine/spells/interpreter/handlers/registry.ts +975 -0
- package/src/engine/spells/interpreter/handlers/shared.ts +667 -0
- package/src/engine/spells/interpreter/handlers.ts +9 -0
- package/src/engine/spells/interpreter/hunter-kit.ts +202 -0
- package/src/engine/spells/interpreter/index.ts +525 -0
- package/src/engine/spells/interpreter/item-triggers.ts +1121 -0
- package/src/engine/spells/interpreter/lift-consume-energy.ts +37 -0
- package/src/engine/spells/interpreter/mage-portals.ts +37 -0
- package/src/engine/spells/interpreter/materialize-charm.ts +88 -0
- package/src/engine/spells/interpreter/materialize-dispel-mechanic.ts +25 -0
- package/src/engine/spells/interpreter/materialize-rating-buffs.ts +81 -0
- package/src/engine/spells/interpreter/materialize-speed.ts +109 -0
- package/src/engine/spells/interpreter/materialize-spell-magnet.ts +22 -0
- package/src/engine/spells/interpreter/opcodes.ts +789 -0
- package/src/engine/spells/interpreter/paladin-kit.ts +95 -0
- package/src/engine/spells/interpreter/paladin-seals.ts +499 -0
- package/src/engine/spells/interpreter/priest-warlock-kit.ts +135 -0
- package/src/engine/spells/interpreter/proc.ts +443 -0
- package/src/engine/spells/interpreter/registry/helpers.ts +16 -0
- package/src/engine/spells/interpreter/registry/index.ts +216 -0
- package/src/engine/spells/interpreter/registry/packs/class-abilities.ts +304 -0
- package/src/engine/spells/interpreter/registry/packs/dire-maul.ts +44 -0
- package/src/engine/spells/interpreter/registry/packs/dummy-script-tail.ts +1065 -0
- package/src/engine/spells/interpreter/registry/packs/ex-wave-m.ts +70 -0
- package/src/engine/spells/interpreter/registry/packs/life-tap.ts +107 -0
- package/src/engine/spells/interpreter/registry/packs/paladin.ts +246 -0
- package/src/engine/spells/interpreter/registry/packs/raid-script-summons.ts +26 -0
- package/src/engine/spells/interpreter/registry/packs/raid-threat-wipe.ts +22 -0
- package/src/engine/spells/interpreter/registry/packs/shaman.ts +214 -0
- package/src/engine/spells/interpreter/registry/packs/si4-dummy-generic-a.ts +269 -0
- package/src/engine/spells/interpreter/registry/packs/si4-dummy-generic-b.ts +370 -0
- package/src/engine/spells/interpreter/registry/packs/si4-dummy-generic-c.ts +172 -0
- package/src/engine/spells/interpreter/registry/packs/si4-dummy-hunter.ts +60 -0
- package/src/engine/spells/interpreter/registry/packs/si4-dummy-warrior.ts +31 -0
- package/src/engine/spells/interpreter/registry/packs/si4-script-generic-a.ts +114 -0
- package/src/engine/spells/interpreter/registry/packs/si4-script-generic-b.ts +249 -0
- package/src/engine/spells/interpreter/registry/packs/si4-script-generic-c.ts +233 -0
- package/src/engine/spells/interpreter/registry/types.ts +48 -0
- package/src/engine/spells/interpreter/talent-scripted.ts +329 -0
- package/src/engine/spells/interpreter/targeting.ts +90 -0
- package/src/engine/spells/interpreter/types-intent.ts +1501 -0
- package/src/engine/spells/interpreter/types.ts +419 -0
- package/src/engine/spells/interpreter/warrior-druid-kit.ts +314 -0
- package/src/engine/spells/interpreter/weapon-imbue-kit.ts +384 -0
- package/src/engine/spells/interrupt.ts +217 -0
- package/src/engine/spells/meleeAttackReset.ts +60 -0
- package/src/engine/spells/oocTriggerSpell.ts +49 -0
- package/src/engine/spells/overrideClassScripts.ts +319 -0
- package/src/engine/spells/parryHaste.ts +20 -0
- package/src/engine/spells/procs.ts +300 -0
- package/src/engine/spells/recovery.ts +203 -0
- package/src/engine/spells/scriptTargetDest.ts +76 -0
- package/src/engine/spells/scriptTargets.ts +85 -0
- package/src/engine/spells/singleTargetAura.ts +82 -0
- package/src/engine/spells/specialCasePayloads.ts +57 -0
- package/src/engine/spells/spellAttributes.ts +249 -0
- package/src/engine/spells/spellBonus.ts +143 -0
- package/src/engine/spells/spellModIntents.ts +419 -0
- package/src/engine/spells/spellMods.ts +283 -0
- package/src/engine/spells/spellRadius.ts +15 -0
- package/src/engine/spells/trapObjects.ts +26 -0
- package/src/json.d.ts +7 -0
- package/src/persistence/memoryRepository.ts +23 -0
- package/src/persistence/saveBackups.ts +162 -0
- package/src/persistence/saveFile.ts +68 -0
- package/src/persistence/saveRepository.ts +11 -0
- package/src/persistence/saveShape.ts +668 -0
- package/src/progression/leveling.ts +89 -0
- package/src/progression/rest.ts +1434 -0
- package/src/progression/training.ts +226 -0
- package/src/progression/xpGain.ts +369 -0
- package/src/quests/quests.ts +1580 -0
- package/src/world/aoePull.ts +198 -0
- package/src/world/areaTableDbc.ts +39 -0
- package/src/world/areaTableMapIdPairs.ts +141 -0
- package/src/world/bakeSlug.ts +43 -0
- package/src/world/continuousDungeon.ts +1250 -0
- package/src/world/dungeonBakeProjection.ts +190 -0
- package/src/world/dungeonMapDensity.ts +342 -0
- package/src/world/dungeonMapProjection.ts +135 -0
- package/src/world/dungeonNav.ts +904 -0
- package/src/world/dungeonNpcSim.ts +147 -0
- package/src/world/dungeonObjectives.ts +234 -0
- package/src/world/dungeonPreset.ts +35 -0
- package/src/world/dungeonRoute.ts +311 -0
- package/src/world/dungeonRouteGeometry.ts +732 -0
- package/src/world/dungeonRouteOrder.ts +1037 -0
- package/src/world/dungeonWingRooms.ts +426 -0
- package/src/world/escort.ts +296 -0
- package/src/world/goPoolRoll.ts +75 -0
- package/src/world/instanceObjects.ts +785 -0
- package/src/world/instances.ts +1572 -0
- package/src/world/meetingStones.ts +56 -0
- package/src/world/navmeshFloors.ts +145 -0
- package/src/world/nearGrid.ts +167 -0
- package/src/world/packBudget.ts +613 -0
- package/src/world/rareRoll.ts +335 -0
- package/src/world/routeAuthoring.ts +132 -0
- package/src/world/routeEdit.ts +209 -0
- package/src/world/world.ts +812 -0
- package/src/world/zoneHazards.ts +47 -0
|
@@ -0,0 +1,1037 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Geometric dungeon route order — entrance-first, nav-graph nearest-neighbour,
|
|
3
|
+
* hard gates and boss sequence preserved.
|
|
4
|
+
*
|
|
5
|
+
* Root cause this closes: `routeEncounterIdList` used InstanceDef declaration order,
|
|
6
|
+
* so "go here" fought every pack typed earlier in the file regardless of where it
|
|
7
|
+
* stood on the map (see mission brief; instances.ts routeEncounterIdList).
|
|
8
|
+
*
|
|
9
|
+
* ⚠️ TUNING, NOT FIDELITY. The *constraints* here are sourced — hard gates from
|
|
10
|
+
* key-sources.json, boss sequence from the authored routes, geometry from the nav grid.
|
|
11
|
+
* The *ordering policy* is not: the rank key at `fillRank` (segment, lastness, room,
|
|
12
|
+
* nearest-neighbour tour, euclid-from-entrance) and `UNREACHABLE_BASE` are pacing dials
|
|
13
|
+
* picked against route probes, and vanilla has no route table to match them against. Do
|
|
14
|
+
* not "correct" them from DBC/world-DB — there is nothing there. Change them only with a
|
|
15
|
+
* new probe-route-detour / probe-walk-follows-route measurement.
|
|
16
|
+
* Cite: FIDELITY-AUDIT.md §4 worklist row 8.
|
|
17
|
+
*
|
|
18
|
+
* Pure module — no DOM, no I/O, no Math.random.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import type { EncounterDef, EncounterEvent, InstanceDef } from '../data/schema.ts';
|
|
22
|
+
import { entranceForInstance } from '../data/dungeons/pack-from-clusters.ts';
|
|
23
|
+
import viewportsJson from '../data/maps/dungeon-viewports.json' with { type: 'json' };
|
|
24
|
+
import keySourcesJson from '../data/dungeons/key-sources.json' with { type: 'json' };
|
|
25
|
+
import authoredRoutesJson from '../data/tables/dungeonAuthoredRoutes.json' with { type: 'json' };
|
|
26
|
+
import {
|
|
27
|
+
projectWorld,
|
|
28
|
+
type MapPct,
|
|
29
|
+
type Viewport,
|
|
30
|
+
type WorldPos,
|
|
31
|
+
} from './dungeonMapProjection.ts';
|
|
32
|
+
import {
|
|
33
|
+
dijkstraCosts,
|
|
34
|
+
instanceNavGrid,
|
|
35
|
+
navGridFor,
|
|
36
|
+
snapToFloor,
|
|
37
|
+
type NavFloorDoc,
|
|
38
|
+
type NavGrid,
|
|
39
|
+
type NavWaypoint,
|
|
40
|
+
} from './dungeonNav.ts';
|
|
41
|
+
import {
|
|
42
|
+
stripAbsentPoolEncountersFromRoute,
|
|
43
|
+
type DungeonPoolRoll,
|
|
44
|
+
type PoolMapSpawn,
|
|
45
|
+
} from './rareRoll.ts';
|
|
46
|
+
import { STOREY_DZ_YD } from '../domain/nav/storey.ts';
|
|
47
|
+
|
|
48
|
+
// Viewport / projection (local — avoid importing dungeonRouteGeometry → instances)
|
|
49
|
+
|
|
50
|
+
function viewportFor(instanceId: string): Viewport | null {
|
|
51
|
+
const raw = (viewportsJson as Record<string, unknown>)[instanceId];
|
|
52
|
+
if (!raw || typeof raw !== 'object') return null;
|
|
53
|
+
const v = raw as { box?: number[] };
|
|
54
|
+
if (!Array.isArray(v.box) || v.box.length !== 4) return null;
|
|
55
|
+
const box = v.box.map(Number) as [number, number, number, number];
|
|
56
|
+
if (!box.every(Number.isFinite)) return null;
|
|
57
|
+
return { box };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function clampPct(p: MapPct): MapPct {
|
|
61
|
+
const FULL = 50 + 50;
|
|
62
|
+
return {
|
|
63
|
+
left: Math.max(0, Math.min(FULL, p.left)),
|
|
64
|
+
top: Math.max(0, Math.min(FULL, p.top)),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function worldOf(enc: EncounterDef): WorldPos | null {
|
|
69
|
+
const o = enc.origin;
|
|
70
|
+
if (!o || !Number.isFinite(o.x) || !Number.isFinite(o.y)) return null;
|
|
71
|
+
return { x: o.x, y: o.y, z: o.z };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function waypointOf(
|
|
75
|
+
instanceId: string,
|
|
76
|
+
world: WorldPos,
|
|
77
|
+
): NavWaypoint | null {
|
|
78
|
+
const vp = viewportFor(instanceId);
|
|
79
|
+
if (!vp) return null;
|
|
80
|
+
const pct = clampPct(projectWorld(vp, world));
|
|
81
|
+
const wp: NavWaypoint = { pct };
|
|
82
|
+
if (world.z != null && Number.isFinite(world.z)) wp.z = world.z;
|
|
83
|
+
return wp;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function entranceWorld(
|
|
87
|
+
instanceId: string,
|
|
88
|
+
route: readonly EncounterDef[],
|
|
89
|
+
triggerId?: number,
|
|
90
|
+
): WorldPos | null {
|
|
91
|
+
const ent = entranceForInstance(instanceId, triggerId);
|
|
92
|
+
if (ent) return { x: ent.x, y: ent.y, z: ent.z };
|
|
93
|
+
for (const e of route) {
|
|
94
|
+
const w = worldOf(e);
|
|
95
|
+
if (w) return w;
|
|
96
|
+
}
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Flag / boss constraints (sourced from EncounterDef.events — no new mechanism)
|
|
101
|
+
|
|
102
|
+
/** Flags this encounter requires before it can be engaged. */
|
|
103
|
+
export function routeRequiredFlags(enc: EncounterDef): string[] {
|
|
104
|
+
const out: string[] = [];
|
|
105
|
+
for (const ev of enc.events ?? []) {
|
|
106
|
+
if (ev.kind === 'requiresFlag') out.push(ev.flag);
|
|
107
|
+
}
|
|
108
|
+
return out;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Flags this encounter produces when cleared / gated (setsFlag + objectGate.setsFlag). Mirrors
|
|
112
|
+
// instances.flagsSetOnClear + objectGate hosted flags.
|
|
113
|
+
export function routeProducedFlags(enc: EncounterDef): string[] {
|
|
114
|
+
const out: string[] = [];
|
|
115
|
+
for (const ev of enc.events ?? []) {
|
|
116
|
+
if (ev.kind === 'setsFlag') {
|
|
117
|
+
if (!out.includes(ev.flag)) out.push(ev.flag);
|
|
118
|
+
} else if (ev.kind === 'objectGate') {
|
|
119
|
+
const g = ev as Extract<EncounterEvent, { kind: 'objectGate' }>;
|
|
120
|
+
if (!out.includes(g.setsFlag)) out.push(g.setsFlag);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return out;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Key-item constraints (`objectGate.requiredItem`)
|
|
127
|
+
|
|
128
|
+
type KeySourceRow = { encounters: string[]; outside: boolean; fromFloorObject?: boolean };
|
|
129
|
+
const KEY_SOURCES = keySourcesJson as Record<string, Record<string, KeySourceRow>>;
|
|
130
|
+
|
|
131
|
+
/** Gate items this encounter must hold before it can be engaged. */
|
|
132
|
+
export function routeRequiredItems(enc: EncounterDef): number[] {
|
|
133
|
+
const out: number[] = [];
|
|
134
|
+
for (const ev of enc.events ?? []) {
|
|
135
|
+
if (ev.kind !== 'objectGate') continue;
|
|
136
|
+
const g = ev as Extract<EncounterEvent, { kind: 'objectGate' }>;
|
|
137
|
+
if (g.requiredItem != null && !out.includes(g.requiredItem)) out.push(g.requiredItem);
|
|
138
|
+
}
|
|
139
|
+
return out;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Encounters on this instance whose loot can hand the party `itemEntry`
|
|
144
|
+
* (`src/data/dungeons/key-sources.json`, emitted by emit-dungeon-key-sources.mjs).
|
|
145
|
+
* Empty ⇒ outside key: the auto-run bypasses that gate, so it is not a route constraint.
|
|
146
|
+
*/
|
|
147
|
+
export function routeItemSources(instanceId: string, itemEntry: number): readonly string[] {
|
|
148
|
+
return KEY_SOURCES[instanceId]?.[String(itemEntry)]?.encounters ?? [];
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Authored itinerary anchors (`src/data/tables/dungeonAuthoredRoutes.json`)
|
|
152
|
+
|
|
153
|
+
type AuthoredNode = { kind?: string; name?: string };
|
|
154
|
+
const AUTHORED_ROUTES = (authoredRoutesJson as { routes?: Record<string, AuthoredNode[]> }).routes
|
|
155
|
+
?? {};
|
|
156
|
+
|
|
157
|
+
/** In-session Seed writes (dev route editor) so ordering does not wait on a JSON HMR. */
|
|
158
|
+
const authoredNameOverride: Record<string, string[]> = {};
|
|
159
|
+
|
|
160
|
+
/** Called by `routeEdit.rememberAuthoredRoute` — a Seeded route reorders the next run. */
|
|
161
|
+
export function setAuthoredRouteAnchors(instanceId: string, names: readonly string[]): void {
|
|
162
|
+
authoredNameOverride[instanceId] = [...names];
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/** Ordered anchor names for this instance (walk pins; `use` gates are not encounters). */
|
|
166
|
+
function authoredAnchorNames(instanceId: string): string[] {
|
|
167
|
+
const over = authoredNameOverride[instanceId];
|
|
168
|
+
if (over?.length) return over;
|
|
169
|
+
const raw = AUTHORED_ROUTES[instanceId];
|
|
170
|
+
if (!Array.isArray(raw)) return [];
|
|
171
|
+
const out: string[] = [];
|
|
172
|
+
for (const n of raw) {
|
|
173
|
+
if (n?.kind === 'use' || !n?.name) continue;
|
|
174
|
+
out.push(n.name);
|
|
175
|
+
}
|
|
176
|
+
return out;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Encounter ids the operator drew, in the order they drew them.
|
|
181
|
+
*
|
|
182
|
+
* `feat(route): authored itinerary, not the geometric pack tour` (0f65f584c) shipped this table
|
|
183
|
+
* and wired it to the editor and `nextAutoBossId`, but never to `run.routeEncounterIds` — which
|
|
184
|
+
* is what `routeEncounterList` / `minimapBosses` / `isUnlocked` actually read. So the HUD named a
|
|
185
|
+
* boss off the authored spine while the tour walked the geometric one.
|
|
186
|
+
*/
|
|
187
|
+
export function authoredAnchorIds(
|
|
188
|
+
instanceId: string,
|
|
189
|
+
route: readonly EncounterDef[],
|
|
190
|
+
): string[] {
|
|
191
|
+
const names = authoredAnchorNames(instanceId);
|
|
192
|
+
if (!names.length) return [];
|
|
193
|
+
const byKey = new Map<string, string>();
|
|
194
|
+
for (const e of route) {
|
|
195
|
+
if (!byKey.has(e.name)) byKey.set(e.name, e.id);
|
|
196
|
+
if (!byKey.has(e.id)) byKey.set(e.id, e.id);
|
|
197
|
+
}
|
|
198
|
+
const out: string[] = [];
|
|
199
|
+
const seen = new Set<string>();
|
|
200
|
+
for (const name of names) {
|
|
201
|
+
const id = byKey.get(name);
|
|
202
|
+
if (!id || seen.has(id)) continue;
|
|
203
|
+
seen.add(id);
|
|
204
|
+
out.push(id);
|
|
205
|
+
}
|
|
206
|
+
return out;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Tour distance between two stops. z is not optional here.
|
|
211
|
+
*
|
|
212
|
+
* A "room" is a WMO group, and 59 of the 126 multi-stop rooms span 20+ yd of z — lbrs
|
|
213
|
+
* `chamber-of-battle` spans 109 across 18 stops. With z dropped, two stops on opposite floors
|
|
214
|
+
* of one shaft read as neighbours: BRD's `230-a5b5fab6` pair is 23 yd apart in plan and 66 yd
|
|
215
|
+
* apart vertically, and the tour books them consecutively, so the party walks 2,203 yd of mesh
|
|
216
|
+
* route between them (probe-route-detour ×97.1). Its room ping-pongs down 57, up 66, down 67,
|
|
217
|
+
* up 45 for the same reason.
|
|
218
|
+
*
|
|
219
|
+
* Guarded rather than `?? 0`: a stop with no z must not be pulled toward the floor.
|
|
220
|
+
*
|
|
221
|
+
* `Math.sqrt` rather than `Math.hypot`: hypot's rescaling buys nothing at instance coordinates
|
|
222
|
+
* (a dungeon spans hundreds of yards, nowhere near the overflow it guards) and this runs once
|
|
223
|
+
* per candidate per pick at dungeon entry. sqrt is exactly rounded by IEEE754, so the only
|
|
224
|
+
* daylight between the two is hypot's extra accuracy in the last bit — no pair of stops in the
|
|
225
|
+
* tree re-orders on it (`src/dev/capabilities/routeOrder.mjs`).
|
|
226
|
+
*/
|
|
227
|
+
function euclid(a: WorldPos, b: WorldPos): number {
|
|
228
|
+
const dx = a.x - b.x;
|
|
229
|
+
const dy = a.y - b.y;
|
|
230
|
+
const dz = a.z != null && b.z != null ? a.z - b.z : 0;
|
|
231
|
+
return Math.sqrt(dx * dx + dy * dy + dz * dz);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/** Penalty band for packs A* cannot reach — still ordered, but after connected ones. */
|
|
235
|
+
const UNREACHABLE_BASE = 1e9;
|
|
236
|
+
|
|
237
|
+
export type OrderRouteOpts = {
|
|
238
|
+
/** Nav grid; when omitted, uses `instanceNavGrid(def.id)` then null. */
|
|
239
|
+
grid?: NavGrid | null;
|
|
240
|
+
/** Floorplan doc — builds a grid when `grid` is absent. */
|
|
241
|
+
floorplan?: NavFloorDoc | null;
|
|
242
|
+
// When set with `mapSpawns`, drop pool-lost rares and collapse multi-location rare candidates (same boss
|
|
243
|
+
// name) before ordering. Same roll as floor spawns.
|
|
244
|
+
poolRoll?: DungeonPoolRoll | null;
|
|
245
|
+
mapSpawns?: readonly PoolMapSpawn[];
|
|
246
|
+
// Party inventory at route-order time. A key already carried (Seal of Ascension brought in) lifts that
|
|
247
|
+
// gate's ordering constraint. Absent ⇒ nothing carried.
|
|
248
|
+
hasItem?: (itemEntry: number) => boolean;
|
|
249
|
+
/**
|
|
250
|
+
* True when a listed inside-key can drop for THIS party (loot conditionId).
|
|
251
|
+
* 13523 only drops after quest 5384 is rewarded. Absent ⇒ treat listed sources
|
|
252
|
+
* as farmable (legacy). False + not in bags ⇒ skip that gate (don't cheat-open it).
|
|
253
|
+
*/
|
|
254
|
+
itemObtainable?: (itemEntry: number) => boolean;
|
|
255
|
+
/** Alternate world door (areatrigger_teleport id) for entrance-first order. */
|
|
256
|
+
entranceTriggerId?: number;
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
// Apply pool strip + multi-slot rare collapse, preserving relative order. No-op when `poolRoll` /
|
|
260
|
+
// `mapSpawns` absent — callers that only order keep every authored candidate until a run rolls pools.
|
|
261
|
+
export function filterRouteByPoolRoll(
|
|
262
|
+
route: readonly EncounterDef[],
|
|
263
|
+
poolRoll: DungeonPoolRoll | null | undefined,
|
|
264
|
+
mapSpawns: readonly PoolMapSpawn[] | undefined,
|
|
265
|
+
): EncounterDef[] {
|
|
266
|
+
if (!mapSpawns) return [...route];
|
|
267
|
+
// Always run strip when mapSpawns provided: multi-slot collapse needs no pool
|
|
268
|
+
// members (Forresten has no bake pool but still ×4 candidates → one pin).
|
|
269
|
+
const ids = stripAbsentPoolEncountersFromRoute(
|
|
270
|
+
route.map((e) => e.id),
|
|
271
|
+
route,
|
|
272
|
+
poolRoll,
|
|
273
|
+
mapSpawns,
|
|
274
|
+
);
|
|
275
|
+
const byId = new Map(route.map((e) => [e.id, e]));
|
|
276
|
+
const out: EncounterDef[] = [];
|
|
277
|
+
for (const id of ids) {
|
|
278
|
+
const e = byId.get(id);
|
|
279
|
+
if (e) out.push(e);
|
|
280
|
+
}
|
|
281
|
+
return out;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Order a density-filtered (and optionally budget-split) encounter list for a run.
|
|
286
|
+
*
|
|
287
|
+
* - Starts at the instance entrance (`entranceForInstance`).
|
|
288
|
+
* - **Room first:** named WMO rooms are ordered by the min nav-graph distance of any
|
|
289
|
+
* pack in that room from the entrance. Within a room, packs sort by nav distance.
|
|
290
|
+
* That stops the zig-zag when two rooms interleave in raw distance (the live-play
|
|
291
|
+
* complaint: "routes then 2 3 5 6 7 8 all over the map").
|
|
292
|
+
* - Unassigned packs (no room / terrain maps) keep the previous per-pack nav rank —
|
|
293
|
+
* each is a singleton "room" keyed by encounter id so they still order by distance.
|
|
294
|
+
* - Hard gates: `requiresFlag` packs wait until a producer is already on the route
|
|
295
|
+
* (or no remaining producer exists — orphan flags auto-advance in the engine).
|
|
296
|
+
* - Inside-key trash sources: drain that source's room (Reliquary / Blood Steward)
|
|
297
|
+
* before a nearer named room (Chamber of Summoning), then walk the gate that
|
|
298
|
+
* wanted the key. Skip a source whose room holds a later boss (ST Bloodkeepers).
|
|
299
|
+
* - Bags / farmability: have the key → walk the gate; can loot it → source then
|
|
300
|
+
* gate; neither → mark the gate optional and do not detour (Scholo: Jandice).
|
|
301
|
+
* - Bosses keep their InstanceDef relative order (survey sequence).
|
|
302
|
+
* - Deterministic: ties break on Euclidean-to-entrance, then encounter id.
|
|
303
|
+
* - Optional `poolRoll` + `mapSpawns`: strip pool losers and multi-slot rare dups first.
|
|
304
|
+
*
|
|
305
|
+
* Defect (reintroduce to go red): replace roomKey with per-pack nav only
|
|
306
|
+
* (`return rankOfPack(enc)` without the roomOrder prefix) → room re-entries rise.
|
|
307
|
+
*/
|
|
308
|
+
export function orderRouteByGeometry(
|
|
309
|
+
def: InstanceDef,
|
|
310
|
+
route: readonly EncounterDef[],
|
|
311
|
+
opts?: OrderRouteOpts,
|
|
312
|
+
): EncounterDef[] {
|
|
313
|
+
if (opts?.mapSpawns) {
|
|
314
|
+
route = filterRouteByPoolRoll(route, opts.poolRoll, opts.mapSpawns);
|
|
315
|
+
}
|
|
316
|
+
if (route.length <= 1) return [...route];
|
|
317
|
+
|
|
318
|
+
const grid =
|
|
319
|
+
opts?.grid
|
|
320
|
+
?? (opts?.floorplan ? navGridFor(opts.floorplan) : null)
|
|
321
|
+
?? instanceNavGrid(def.id);
|
|
322
|
+
|
|
323
|
+
// Synthetic / fixture dungeons with no pack origins (testdelve event tests): keep density/def relative
|
|
324
|
+
// order. Reordering by id among origin-less packs was breaking hand-authored sequential unlock graphs.
|
|
325
|
+
const anyOrigin = route.some((e) => worldOf(e) != null);
|
|
326
|
+
if (!anyOrigin) return [...route];
|
|
327
|
+
|
|
328
|
+
const entrance = entranceWorld(def.id, route, opts?.entranceTriggerId);
|
|
329
|
+
const bossOrder = def.encounters.filter((e) => e.kind === 'boss').map((e) => e.id);
|
|
330
|
+
const bossRank = new Map(bossOrder.map((id, i) => [id, i]));
|
|
331
|
+
|
|
332
|
+
// Maps with zero named rooms (ragefire-chasm, shadowfang-keep, razorfen-kraul,
|
|
333
|
+
// zul-farrak) have every pack as a `~enc:` singleton, so the room-sticky greedy
|
|
334
|
+
// below degrades to a pure sort by nav-from-entrance. On a spread-out terrain map
|
|
335
|
+
// that ping-pongs: two packs at the same radius but opposite sides rank adjacent,
|
|
336
|
+
// so the tour bounces N↔S (zul-farrak walked 300-575 yd legs the stepper could not
|
|
337
|
+
// follow — 24 of 95 stalled legs, probe-walk-follows-route). For those maps order
|
|
338
|
+
// as a nearest-neighbour tour from the LAST placed pack instead.
|
|
339
|
+
const anyRoom = route.some((e) => e.roomId != null && e.roomId !== '');
|
|
340
|
+
const worldById = new Map<string, WorldPos>();
|
|
341
|
+
for (const e of route) {
|
|
342
|
+
const w = worldOf(e);
|
|
343
|
+
if (w) worldById.set(e.id, w);
|
|
344
|
+
}
|
|
345
|
+
let lastPlaced: WorldPos = entrance ?? { x: 0, y: 0 };
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* ANCHOR SEGMENTS — the operator's itinerary decides WHEN a boss is walked, geometry only
|
|
349
|
+
* decides what gets cleared on the way there.
|
|
350
|
+
*
|
|
351
|
+
* Without this the greedy is a pure trash drain: nothing pulls the tour toward a boss, so
|
|
352
|
+
* blackrock-depths (257 stops, 22 bosses) put Lord Roccor at stop 94 and then stranded the
|
|
353
|
+
* other 21 in one back-to-back block at stops 236-256 — `bossOk` released them in rank order
|
|
354
|
+
* only once no trash was left anywhere. The party cleared the instance, then ping-ponged it
|
|
355
|
+
* end to end boss to boss, while the HUD named a boss 140 stops ahead of the pack being walked.
|
|
356
|
+
*
|
|
357
|
+
* With no authored route, the InstanceDef boss sequence is the itinerary — the same order
|
|
358
|
+
* `bossOk` already enforces. The tour without any anchors ordered all 54 blackwing-lair
|
|
359
|
+
* packs (every one an upper storey, z 440-449) before Razorgore at z 413 (operator, 2026-08-29).
|
|
360
|
+
*
|
|
361
|
+
* Each non-anchor pack joins the leg of the anchor it stands nearest (ties to the earlier
|
|
362
|
+
* anchor), preferring anchors on its own storey: BWL's tower packs sit ~100 yd from Razorgore
|
|
363
|
+
* in plan but 27-36 yd overhead, and euclid alone booked all of them into his leg, so the tour
|
|
364
|
+
* climbed off the entrance floor before the first boss. `STOREY_DZ_YD` is the engine's own
|
|
365
|
+
* "another floor" line (domain/nav/storey.ts:6; the euclid doc above says why z 1:1 is not
|
|
366
|
+
* enough). An anchor sorts after its own leg's trash — fight through to the boss, then move on.
|
|
367
|
+
*/
|
|
368
|
+
let anchorIds = authoredAnchorIds(def.id, route);
|
|
369
|
+
if (anchorIds.length < 2) {
|
|
370
|
+
const inRoute = new Set(route.map((e) => e.id));
|
|
371
|
+
anchorIds = bossOrder.filter((id) => inRoute.has(id));
|
|
372
|
+
}
|
|
373
|
+
const anchorSeg = new Map<string, number>();
|
|
374
|
+
anchorIds.forEach((id, i) => anchorSeg.set(id, i));
|
|
375
|
+
const segOf = new Map<string, number>();
|
|
376
|
+
if (anchorIds.length >= 2) {
|
|
377
|
+
const nearestAnchor = (w: WorldPos, sameStorey: boolean): number | null => {
|
|
378
|
+
let best: number | null = null;
|
|
379
|
+
let bestD = Infinity;
|
|
380
|
+
for (let i = 0; i < anchorIds.length; i++) {
|
|
381
|
+
const aw = worldById.get(anchorIds[i]!);
|
|
382
|
+
if (!aw) continue;
|
|
383
|
+
if (sameStorey && (w.z == null || aw.z == null || Math.abs(w.z - aw.z) > STOREY_DZ_YD)) {
|
|
384
|
+
continue;
|
|
385
|
+
}
|
|
386
|
+
const d = euclid(w, aw);
|
|
387
|
+
if (d < bestD) {
|
|
388
|
+
bestD = d;
|
|
389
|
+
best = i;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
return best;
|
|
393
|
+
};
|
|
394
|
+
// A room moves as ONE leg (a split room is a re-entry per split): a room holding exactly one
|
|
395
|
+
// anchor is that leg, an anchor-less room joins by centroid. A room holding SEVERAL anchors
|
|
396
|
+
// (the-stockade is one room over the whole map) keeps per-pack assignment — binding it whole
|
|
397
|
+
// re-creates the trash drain.
|
|
398
|
+
const roomSeg = new Map<string, number>();
|
|
399
|
+
const multiAnchorRooms = new Set<string>();
|
|
400
|
+
const centroids = new Map<string, { x: number; y: number; z: number; nz: number; n: number }>();
|
|
401
|
+
for (const e of route) {
|
|
402
|
+
if (!e.roomId) continue;
|
|
403
|
+
const own = anchorSeg.get(e.id);
|
|
404
|
+
if (own != null) {
|
|
405
|
+
if (roomSeg.has(e.roomId)) {
|
|
406
|
+
multiAnchorRooms.add(e.roomId);
|
|
407
|
+
roomSeg.delete(e.roomId);
|
|
408
|
+
} else if (!multiAnchorRooms.has(e.roomId)) {
|
|
409
|
+
roomSeg.set(e.roomId, own);
|
|
410
|
+
}
|
|
411
|
+
continue;
|
|
412
|
+
}
|
|
413
|
+
const w = worldById.get(e.id);
|
|
414
|
+
if (!w) continue;
|
|
415
|
+
let c = centroids.get(e.roomId);
|
|
416
|
+
if (!c) {
|
|
417
|
+
c = { x: 0, y: 0, z: 0, nz: 0, n: 0 };
|
|
418
|
+
centroids.set(e.roomId, c);
|
|
419
|
+
}
|
|
420
|
+
c.x += w.x;
|
|
421
|
+
c.y += w.y;
|
|
422
|
+
c.n++;
|
|
423
|
+
if (w.z != null && Number.isFinite(w.z)) {
|
|
424
|
+
c.z += w.z;
|
|
425
|
+
c.nz++;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
for (const [roomId, c] of centroids) {
|
|
429
|
+
if (roomSeg.has(roomId) || multiAnchorRooms.has(roomId) || !c.n) continue;
|
|
430
|
+
const w: WorldPos = { x: c.x / c.n, y: c.y / c.n };
|
|
431
|
+
if (c.nz) w.z = c.z / c.nz;
|
|
432
|
+
const best = nearestAnchor(w, true) ?? nearestAnchor(w, false);
|
|
433
|
+
if (best != null) roomSeg.set(roomId, best);
|
|
434
|
+
}
|
|
435
|
+
for (const e of route) {
|
|
436
|
+
const own = anchorSeg.get(e.id);
|
|
437
|
+
if (own != null) {
|
|
438
|
+
segOf.set(e.id, own);
|
|
439
|
+
continue;
|
|
440
|
+
}
|
|
441
|
+
const room = e.roomId != null ? roomSeg.get(e.roomId) : undefined;
|
|
442
|
+
if (room != null) {
|
|
443
|
+
segOf.set(e.id, room);
|
|
444
|
+
continue;
|
|
445
|
+
}
|
|
446
|
+
const w = worldById.get(e.id);
|
|
447
|
+
const best = w ? nearestAnchor(w, true) ?? nearestAnchor(w, false) : null;
|
|
448
|
+
segOf.set(e.id, best ?? anchorIds.length - 1);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
const segKey = (enc: EncounterDef): number => segOf.get(enc.id) ?? 0;
|
|
452
|
+
/** Anchor walks last in its own leg, after the trash that leads to it. */
|
|
453
|
+
const anchorLast = (enc: EncounterDef): number => (anchorSeg.has(enc.id) ? 1 : 0);
|
|
454
|
+
|
|
455
|
+
// One Dijkstra from the entrance → nav distance for every pack (wall-aware).
|
|
456
|
+
const navFromEntrance = new Map<string, number>();
|
|
457
|
+
const euclidFromEntrance = new Map<string, number>();
|
|
458
|
+
if (entrance) {
|
|
459
|
+
for (const e of route) {
|
|
460
|
+
const w = worldOf(e);
|
|
461
|
+
if (w) euclidFromEntrance.set(e.id, euclid(entrance, w));
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
if (grid && entrance) {
|
|
465
|
+
const entWp = waypointOf(def.id, entrance);
|
|
466
|
+
const from = entWp ? snapToFloor(grid, entWp) : null;
|
|
467
|
+
if (from) {
|
|
468
|
+
const cellId = (band: number, x: number, y: number): number =>
|
|
469
|
+
band * grid.res * grid.res + y * grid.res + x;
|
|
470
|
+
const stops = new Set<number>();
|
|
471
|
+
const snapByEnc = new Map<string, number>();
|
|
472
|
+
for (const e of route) {
|
|
473
|
+
const w = worldOf(e);
|
|
474
|
+
const wp = w ? waypointOf(def.id, w) : null;
|
|
475
|
+
if (!wp) continue;
|
|
476
|
+
const s = snapToFloor(grid, wp);
|
|
477
|
+
if (!s) continue;
|
|
478
|
+
const id = cellId(s.band, s.x, s.y);
|
|
479
|
+
snapByEnc.set(e.id, id);
|
|
480
|
+
stops.add(id);
|
|
481
|
+
}
|
|
482
|
+
const costs = dijkstraCosts(grid, from, stops.size ? stops : undefined);
|
|
483
|
+
if (costs) {
|
|
484
|
+
for (const [encId, cid] of snapByEnc) {
|
|
485
|
+
const c = costs[cid]!;
|
|
486
|
+
if (Number.isFinite(c)) navFromEntrance.set(encId, c);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
const packPrimary = (enc: EncounterDef): number => {
|
|
493
|
+
const nav = navFromEntrance.get(enc.id);
|
|
494
|
+
const euc = euclidFromEntrance.get(enc.id) ?? UNREACHABLE_BASE;
|
|
495
|
+
return nav != null ? nav : UNREACHABLE_BASE + euc;
|
|
496
|
+
};
|
|
497
|
+
|
|
498
|
+
// Room key: named roomId, else singleton so unassigned packs keep pure pack order.
|
|
499
|
+
// Maps with zero named rooms → every pack is a singleton → identical to #599.
|
|
500
|
+
const roomKeyOf = (enc: EncounterDef): string => {
|
|
501
|
+
if (enc.roomId) return enc.roomId;
|
|
502
|
+
return `~enc:${enc.id}`;
|
|
503
|
+
};
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* Per-stop facts, walked once instead of once per candidate per pick.
|
|
507
|
+
*
|
|
508
|
+
* The rank key and all three gates below used to re-walk `enc.events` and rebuild a Set on
|
|
509
|
+
* every call, inside a pool scan inside the tour. Nothing here reads a field the
|
|
510
|
+
* `{ ...e, optional: true }` clone drops, so a stop's row is the same whether the greedy is
|
|
511
|
+
* holding the original or the clone.
|
|
512
|
+
*/
|
|
513
|
+
type StopFacts = {
|
|
514
|
+
world: WorldPos | null;
|
|
515
|
+
roomKey: string;
|
|
516
|
+
seg: number;
|
|
517
|
+
reqFlags: string[];
|
|
518
|
+
prodFlags: string[];
|
|
519
|
+
selfProduces: Set<string>;
|
|
520
|
+
reqItems: number[];
|
|
521
|
+
bossRank: number | undefined;
|
|
522
|
+
};
|
|
523
|
+
const factsById = new Map<string, StopFacts>();
|
|
524
|
+
const factsOf = (enc: EncounterDef): StopFacts => {
|
|
525
|
+
let f = factsById.get(enc.id);
|
|
526
|
+
if (!f) {
|
|
527
|
+
const prodFlags = routeProducedFlags(enc);
|
|
528
|
+
f = {
|
|
529
|
+
world: worldById.get(enc.id) ?? worldOf(enc),
|
|
530
|
+
roomKey: roomKeyOf(enc),
|
|
531
|
+
seg: segKey(enc),
|
|
532
|
+
reqFlags: routeRequiredFlags(enc),
|
|
533
|
+
prodFlags,
|
|
534
|
+
selfProduces: new Set(prodFlags),
|
|
535
|
+
reqItems: routeRequiredItems(enc),
|
|
536
|
+
bossRank: bossRank.get(enc.id),
|
|
537
|
+
};
|
|
538
|
+
factsById.set(enc.id, f);
|
|
539
|
+
}
|
|
540
|
+
return f;
|
|
541
|
+
};
|
|
542
|
+
for (const e of route) factsOf(e);
|
|
543
|
+
/** `objectGate.requiredItem` holders. Empty on most instances, and then the two key blocks in
|
|
544
|
+
* the tour have nothing to find, so they do not run at all. */
|
|
545
|
+
const gateStops = route.filter((e) => factsOf(e).reqItems.length > 0);
|
|
546
|
+
/** `routeItemSources` is a JSON lookup behind a `String(item)` key; one per item is enough. */
|
|
547
|
+
const sourcesCache = new Map<number, readonly string[]>();
|
|
548
|
+
const sourcesOf = (item: number): readonly string[] => {
|
|
549
|
+
let v = sourcesCache.get(item);
|
|
550
|
+
if (!v) {
|
|
551
|
+
v = routeItemSources(def.id, item);
|
|
552
|
+
sourcesCache.set(item, v);
|
|
553
|
+
}
|
|
554
|
+
return v;
|
|
555
|
+
};
|
|
556
|
+
|
|
557
|
+
// ---------------------------------------------------------------------------
|
|
558
|
+
// Room-sticky greedy (boss + flag constraints hard; rooms soft but sticky)
|
|
559
|
+
//
|
|
560
|
+
// Rank rooms by min nav-from-entrance of their members. Among currently legal
|
|
561
|
+
// packs (flags + boss sequence — same as #599), prefer the active room if it
|
|
562
|
+
// still has legal members; otherwise open the entrance-nearest legal room and
|
|
563
|
+
// stick to it until drained of legal packs. That stops Festival↔Gauntlet zig-zag
|
|
564
|
+
// from raw distance interleave while preserving InstanceDef boss order.
|
|
565
|
+
// ---------------------------------------------------------------------------
|
|
566
|
+
|
|
567
|
+
const remaining = new Map<string, EncounterDef>();
|
|
568
|
+
const itemFarmable = (item: number): boolean => {
|
|
569
|
+
if (opts?.hasItem?.(item)) return true;
|
|
570
|
+
if (opts?.itemObtainable) return opts.itemObtainable(item);
|
|
571
|
+
return true;
|
|
572
|
+
};
|
|
573
|
+
const gateUnsatisfiable = (enc: EncounterDef): boolean => {
|
|
574
|
+
for (const item of factsOf(enc).reqItems) {
|
|
575
|
+
if (opts?.hasItem?.(item)) continue;
|
|
576
|
+
if (!sourcesOf(item).length) continue;
|
|
577
|
+
if (!itemFarmable(item)) return true;
|
|
578
|
+
}
|
|
579
|
+
return false;
|
|
580
|
+
};
|
|
581
|
+
for (const e of route) {
|
|
582
|
+
remaining.set(
|
|
583
|
+
e.id,
|
|
584
|
+
gateUnsatisfiable(e) && !e.optional ? { ...e, optional: true } : e,
|
|
585
|
+
);
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
// Same min over the same members. The scan this replaces walked EVERY remaining stop and threw
|
|
589
|
+
// away the ones in another room, once per candidate per pick; these buckets hold the members it
|
|
590
|
+
// was looking for, so a query reads only the room it asked about. Membership is what changes as
|
|
591
|
+
// stops are placed, and that is one splice per placement. With no anchors `segKey` is 0 for
|
|
592
|
+
// every stop, so the leg half of the key is constant and the bucket is the room alone — which
|
|
593
|
+
// is what the scan did when `segOf.size` sent it past the leg filter.
|
|
594
|
+
//
|
|
595
|
+
// The memo, not the buckets, is what a new pick invalidates: `lastPlaced` moves, so every
|
|
596
|
+
// distance moves with it, but who stands in a room does not.
|
|
597
|
+
const roomCostKey = (rk: string, seg: number): string => `${seg}:${rk}`;
|
|
598
|
+
const roomMembers = new Map<string, { id: string; world: WorldPos }[]>();
|
|
599
|
+
const roomCostMemo = new Map<string, number>();
|
|
600
|
+
for (const e of remaining.values()) {
|
|
601
|
+
const f = factsOf(e);
|
|
602
|
+
if (!f.world) continue;
|
|
603
|
+
const key = roomCostKey(f.roomKey, f.seg);
|
|
604
|
+
let bucket = roomMembers.get(key);
|
|
605
|
+
if (!bucket) {
|
|
606
|
+
bucket = [];
|
|
607
|
+
roomMembers.set(key, bucket);
|
|
608
|
+
}
|
|
609
|
+
bucket.push({ id: e.id, world: f.world });
|
|
610
|
+
}
|
|
611
|
+
const dropRoomMember = (enc: EncounterDef): void => {
|
|
612
|
+
const f = factsOf(enc);
|
|
613
|
+
if (!f.world) return;
|
|
614
|
+
const bucket = roomMembers.get(roomCostKey(f.roomKey, f.seg));
|
|
615
|
+
if (!bucket) return;
|
|
616
|
+
const i = bucket.findIndex((m) => m.id === enc.id);
|
|
617
|
+
if (i < 0) return;
|
|
618
|
+
const last = bucket.pop();
|
|
619
|
+
if (last && i < bucket.length) bucket[i] = last;
|
|
620
|
+
};
|
|
621
|
+
|
|
622
|
+
/**
|
|
623
|
+
* How far the party is from a room, by its nearest stop still to clear.
|
|
624
|
+
*
|
|
625
|
+
* The room primary used to be min nav distance from the ENTRANCE, fixed for the whole
|
|
626
|
+
* run. That is a radial sweep: it clears everything at one radius before moving outward,
|
|
627
|
+
* which is why maraudon walked 66.5 km over 176 stops with 43 legs longer than 120 yd.
|
|
628
|
+
* Ranking a room by where the party actually stands turns this same pick-best-remaining
|
|
629
|
+
* loop into a tour over rooms, and keeps a room sticky once entered because its nearest
|
|
630
|
+
* uncleared stop is then a few yards away.
|
|
631
|
+
*
|
|
632
|
+
* Hard gates and boss sequence are unaffected — `flagsOk` and `bossOk` are checked per
|
|
633
|
+
* candidate below, not encoded in this rank.
|
|
634
|
+
*/
|
|
635
|
+
const roomTourCost = (rk: string, seg: number): number => {
|
|
636
|
+
const key = roomCostKey(rk, seg);
|
|
637
|
+
const memo = roomCostMemo.get(key);
|
|
638
|
+
if (memo !== undefined) return memo;
|
|
639
|
+
let best = Infinity;
|
|
640
|
+
for (const m of roomMembers.get(key) ?? []) {
|
|
641
|
+
const d = euclid(lastPlaced, m.world);
|
|
642
|
+
if (d < best) best = d;
|
|
643
|
+
}
|
|
644
|
+
const out = Number.isFinite(best) ? best : UNREACHABLE_BASE;
|
|
645
|
+
roomCostMemo.set(key, out);
|
|
646
|
+
return out;
|
|
647
|
+
};
|
|
648
|
+
const ordered: EncounterDef[] = [];
|
|
649
|
+
const produced = new Set<string>();
|
|
650
|
+
const placed = new Set<string>();
|
|
651
|
+
|
|
652
|
+
// Producers still in `remaining`, per flag, decremented as stops are placed. `producersLeft`
|
|
653
|
+
// scanned every remaining stop and re-walked its events for one boolean; every caller below
|
|
654
|
+
// has already established that the asking stop is not itself a producer of that flag, so the
|
|
655
|
+
// count needs no `exceptId`.
|
|
656
|
+
const producersByFlag = new Map<string, number>();
|
|
657
|
+
for (const e of remaining.values()) {
|
|
658
|
+
for (const f of factsOf(e).prodFlags) {
|
|
659
|
+
producersByFlag.set(f, (producersByFlag.get(f) ?? 0) + 1);
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
const dropProducer = (enc: EncounterDef): void => {
|
|
663
|
+
for (const f of factsOf(enc).prodFlags) {
|
|
664
|
+
const n = producersByFlag.get(f);
|
|
665
|
+
if (n != null) producersByFlag.set(f, n - 1);
|
|
666
|
+
}
|
|
667
|
+
};
|
|
668
|
+
|
|
669
|
+
const flagsOk = (enc: EncounterDef): boolean => {
|
|
670
|
+
const facts = factsOf(enc);
|
|
671
|
+
if (!facts.reqFlags.length) return true;
|
|
672
|
+
for (const f of facts.reqFlags) {
|
|
673
|
+
if (produced.has(f) || facts.selfProduces.has(f)) continue;
|
|
674
|
+
if (!(producersByFlag.get(f) ?? 0)) continue;
|
|
675
|
+
return false;
|
|
676
|
+
}
|
|
677
|
+
return true;
|
|
678
|
+
};
|
|
679
|
+
|
|
680
|
+
/**
|
|
681
|
+
* A gate wanting a key that DROPS INSIDE waits for a source to be placed first.
|
|
682
|
+
*
|
|
683
|
+
* `flagsOk` cannot see this: an encounter hosting its own `objectGate` also produces that
|
|
684
|
+
* gate's flag, so `selfProduces` makes it legal from stop 0. Measured 2026-08-14 with
|
|
685
|
+
* probe-dungeon-completion — scholomance ordered kirtonos-the-herald at stop 9 while every
|
|
686
|
+
* Blood of Innocents (13523) source sat at 17/48/58, `autoFireObjectGates` refused to bypass
|
|
687
|
+
* an inside key (`honest`), and `isUnlocked`'s sequential prefix then blocked all 49 stops
|
|
688
|
+
* behind it: 9/58 cleared, 0/13 bosses.
|
|
689
|
+
*
|
|
690
|
+
* Outside keys (empty source list) stay unconstrained — the auto-run bypasses those gates.
|
|
691
|
+
*/
|
|
692
|
+
const itemsOk = (enc: EncounterDef): boolean => {
|
|
693
|
+
const items = factsOf(enc).reqItems;
|
|
694
|
+
if (!items.length) return true;
|
|
695
|
+
for (const item of items) {
|
|
696
|
+
if (opts?.hasItem?.(item)) continue;
|
|
697
|
+
const sources = sourcesOf(item);
|
|
698
|
+
if (!sources.length) continue;
|
|
699
|
+
// A source already placed (or the encounter itself — sunken-temple's Bloodkeepers ride
|
|
700
|
+
// in avatar-of-hakkar's own pack) means the party holds the key on arrival.
|
|
701
|
+
if (sources.some((id) => id === enc.id || placed.has(id))) continue;
|
|
702
|
+
if (!sources.some((id) => remaining.has(id))) continue;
|
|
703
|
+
return false;
|
|
704
|
+
}
|
|
705
|
+
return true;
|
|
706
|
+
};
|
|
707
|
+
|
|
708
|
+
// The lowest-ranked boss still remaining and not skipped. `bossOk` walked the whole rank map
|
|
709
|
+
// per candidate to learn one number that is the same for every candidate in a pick, and
|
|
710
|
+
// `bossOrder` is already in ascending rank, so the first hit is the min.
|
|
711
|
+
const seqBossIds = bossOrder.filter((id) => remaining.has(id));
|
|
712
|
+
let firstBossRank = Infinity;
|
|
713
|
+
const rebuildBossGate = (): void => {
|
|
714
|
+
firstBossRank = Infinity;
|
|
715
|
+
for (const id of seqBossIds) {
|
|
716
|
+
const e = remaining.get(id);
|
|
717
|
+
// Skipped Kirtonos (no 13523) must not hold Jandice off the Chamber tour.
|
|
718
|
+
if (!e || e.optional) continue;
|
|
719
|
+
firstBossRank = bossRank.get(id) ?? Infinity;
|
|
720
|
+
break;
|
|
721
|
+
}
|
|
722
|
+
};
|
|
723
|
+
const bossOk = (enc: EncounterDef): boolean => {
|
|
724
|
+
if (enc.kind !== 'boss') return true;
|
|
725
|
+
const rank = factsOf(enc).bossRank;
|
|
726
|
+
if (rank == null) return true;
|
|
727
|
+
return firstBossRank >= rank;
|
|
728
|
+
};
|
|
729
|
+
|
|
730
|
+
// seg / anchorLast lead so the authored itinerary decides the leg; the rest is #883's tour.
|
|
731
|
+
type RankKey = [number, number, number, number, number, string];
|
|
732
|
+
// Written into a caller-owned tuple rather than returned: the pool scan runs once per stop
|
|
733
|
+
// placed, so a fresh 6-element array per candidate is a quadratic number of them, and this
|
|
734
|
+
// whole call is dungeon-entry latency (smoke section 7).
|
|
735
|
+
const fillRank = (enc: EncounterDef, out: RankKey): void => {
|
|
736
|
+
const facts = factsOf(enc);
|
|
737
|
+
const seg = facts.seg;
|
|
738
|
+
const tour = facts.world ? euclid(lastPlaced, facts.world) : UNREACHABLE_BASE;
|
|
739
|
+
out[0] = seg;
|
|
740
|
+
out[1] = anchorLast(enc);
|
|
741
|
+
if (!anyRoom) {
|
|
742
|
+
// Nearest-neighbour tour: primary = distance from the last placed pack.
|
|
743
|
+
out[2] = tour;
|
|
744
|
+
out[3] = packPrimary(enc);
|
|
745
|
+
} else {
|
|
746
|
+
// Room chosen by min nav-from-entrance (primary, identical for every member of a
|
|
747
|
+
// room). WITHIN a room, order as a nearest-neighbour tour from the last placed pack,
|
|
748
|
+
// not by nav-from-entrance — the latter ping-pongs when a room's packs sit at similar
|
|
749
|
+
// radii on opposite walls (stockade is one named room over the whole map, so the
|
|
750
|
+
// #883 !anyRoom tour never fired and it walked d=171/156/242 yd legs the stepper
|
|
751
|
+
// wedges on; probe-walk-follows-route). This is #883's tour applied inside a room.
|
|
752
|
+
out[2] = roomTourCost(facts.roomKey, seg);
|
|
753
|
+
out[3] = tour;
|
|
754
|
+
}
|
|
755
|
+
out[4] = euclidFromEntrance.get(enc.id) ?? UNREACHABLE_BASE;
|
|
756
|
+
out[5] = enc.id;
|
|
757
|
+
};
|
|
758
|
+
const better = (a: RankKey, b: RankKey): boolean => {
|
|
759
|
+
for (let i = 0; i < 5; i++) {
|
|
760
|
+
if (a[i]! < b[i]!) return true;
|
|
761
|
+
if (a[i]! > b[i]!) return false;
|
|
762
|
+
}
|
|
763
|
+
return a[5] < b[5];
|
|
764
|
+
};
|
|
765
|
+
const pickBest = (pool: EncounterDef[]): EncounterDef | null => {
|
|
766
|
+
let best: EncounterDef | null = null;
|
|
767
|
+
let cand: RankKey = [0, 0, 0, 0, 0, ''];
|
|
768
|
+
let bestKey: RankKey = [0, 0, 0, 0, 0, ''];
|
|
769
|
+
for (const e of pool) {
|
|
770
|
+
fillRank(e, cand);
|
|
771
|
+
if (!best || better(cand, bestKey)) {
|
|
772
|
+
best = e;
|
|
773
|
+
const swap = bestKey;
|
|
774
|
+
bestKey = cand;
|
|
775
|
+
cand = swap;
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
return best;
|
|
779
|
+
};
|
|
780
|
+
|
|
781
|
+
let activeRoom: string | null = null;
|
|
782
|
+
|
|
783
|
+
while (remaining.size > 0) {
|
|
784
|
+
// Everything a candidate could ask about `remaining` or `lastPlaced`, answered once. Both
|
|
785
|
+
// are fixed for the whole of this pick, so a candidate reading these gets the same number
|
|
786
|
+
// the per-candidate scans returned.
|
|
787
|
+
rebuildBossGate();
|
|
788
|
+
roomCostMemo.clear();
|
|
789
|
+
let pool: EncounterDef[] = [];
|
|
790
|
+
for (const e of remaining.values()) {
|
|
791
|
+
if (flagsOk(e) && bossOk(e) && itemsOk(e)) pool.push(e);
|
|
792
|
+
}
|
|
793
|
+
// Relax in the order the constraints matter: key items before flags before boss sequence.
|
|
794
|
+
// A pool that never empties is what keeps an unsatisfiable gate from wedging the tour.
|
|
795
|
+
if (!pool.length) {
|
|
796
|
+
for (const e of remaining.values()) {
|
|
797
|
+
if (flagsOk(e) && bossOk(e)) pool.push(e);
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
if (!pool.length) {
|
|
801
|
+
for (const e of remaining.values()) {
|
|
802
|
+
if (bossOk(e)) pool.push(e);
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
if (!pool.length) pool = [...remaining.values()];
|
|
806
|
+
|
|
807
|
+
// The authored leg is a hard partition: room-stick, key-focus and the tour all run INSIDE it.
|
|
808
|
+
// Without this the sticky-room filter can hold the tour in a room whose only legal members
|
|
809
|
+
// belong to a later boss's leg, which is the stranding this anchoring exists to stop.
|
|
810
|
+
if (anchorIds.length >= 2) {
|
|
811
|
+
let minSeg = Infinity;
|
|
812
|
+
for (const e of pool) minSeg = Math.min(minSeg, segKey(e));
|
|
813
|
+
if (Number.isFinite(minSeg)) {
|
|
814
|
+
const inSeg = pool.filter((e) => segKey(e) === minSeg);
|
|
815
|
+
if (inSeg.length) pool = inSeg;
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
// Trash sources of a still-needed inside key outrank room-stick and nearness.
|
|
820
|
+
// Scholomance: Chamber of Summoning is closer to the porch than Reliquary, so
|
|
821
|
+
// the tour drained Jandice's room (stops 7-15) before Blood Steward
|
|
822
|
+
// 289-dc725300 at stop 38. itemsOk only delays the GATE, not the room.
|
|
823
|
+
// Focus the nearest remaining trash source's room until that key is placed.
|
|
824
|
+
let keyFocusRoom: string | null = null;
|
|
825
|
+
if (gateStops.length) {
|
|
826
|
+
const sourceIds = new Set<string>();
|
|
827
|
+
for (const gate of gateStops) {
|
|
828
|
+
const e = remaining.get(gate.id);
|
|
829
|
+
if (!e) continue;
|
|
830
|
+
for (const item of factsOf(e).reqItems) {
|
|
831
|
+
if (opts?.hasItem?.(item)) continue;
|
|
832
|
+
if (!itemFarmable(item)) continue;
|
|
833
|
+
const sources = sourcesOf(item);
|
|
834
|
+
if (!sources.length) continue;
|
|
835
|
+
if (sources.some((id) => placed.has(id))) continue;
|
|
836
|
+
for (const id of sources) {
|
|
837
|
+
const src = remaining.get(id);
|
|
838
|
+
if (src && src.kind !== 'boss') sourceIds.add(id);
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
if (sourceIds.size) {
|
|
843
|
+
let nextBossRank = Infinity;
|
|
844
|
+
for (const e of remaining.values()) {
|
|
845
|
+
if (e.kind !== 'boss') continue;
|
|
846
|
+
const r = bossRank.get(e.id);
|
|
847
|
+
if (r != null && r < nextBossRank) nextBossRank = r;
|
|
848
|
+
}
|
|
849
|
+
const roomHasLaterBoss = (rk: string): boolean => {
|
|
850
|
+
if (rk.startsWith('~enc:')) return false;
|
|
851
|
+
for (const e of remaining.values()) {
|
|
852
|
+
if (e.kind !== 'boss' || factsOf(e).roomKey !== rk) continue;
|
|
853
|
+
const r = bossRank.get(e.id);
|
|
854
|
+
if (r != null && r > nextBossRank) return true;
|
|
855
|
+
}
|
|
856
|
+
return false;
|
|
857
|
+
};
|
|
858
|
+
const ranked = [...sourceIds].map((id) => {
|
|
859
|
+
const w = worldById.get(id);
|
|
860
|
+
return { id, d: w ? euclid(lastPlaced, w) : UNREACHABLE_BASE };
|
|
861
|
+
}).sort((a, b) => a.d - b.d || (a.id < b.id ? -1 : 1));
|
|
862
|
+
for (const { id: bestId } of ranked) {
|
|
863
|
+
const src = remaining.get(bestId);
|
|
864
|
+
const rk = src ? roomKeyOf(src) : `~enc:${bestId}`;
|
|
865
|
+
// ST Bloodkeepers live in Avatar's Sanctum — do not open a later
|
|
866
|
+
// boss room just because the key sits there.
|
|
867
|
+
if (roomHasLaterBoss(rk)) continue;
|
|
868
|
+
const focused = pool.filter(
|
|
869
|
+
(e) =>
|
|
870
|
+
e.id === bestId
|
|
871
|
+
|| e.id.startsWith(`${bestId}__budget`)
|
|
872
|
+
|| (!rk.startsWith('~enc:') && factsOf(e).roomKey === rk),
|
|
873
|
+
);
|
|
874
|
+
if (focused.length) {
|
|
875
|
+
pool = focused;
|
|
876
|
+
keyFocusRoom = rk.startsWith('~enc:') ? null : rk;
|
|
877
|
+
break;
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
// Drain the room we're in before spending the key. Reliquary still has
|
|
884
|
+
// neophytes after Blood Steward; jumping to Kirtonos left them for stop 49.
|
|
885
|
+
if (!keyFocusRoom && activeRoom) {
|
|
886
|
+
const inRoom = pool.filter((e) => factsOf(e).roomKey === activeRoom);
|
|
887
|
+
if (inRoom.length) pool = inRoom;
|
|
888
|
+
else activeRoom = null;
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
// Once an inside-key source is placed, its gate outranks nearness (Reliquary →
|
|
892
|
+
// Kirtonos). Carried hasItem must not — that yanked Deadmines to GO 16398 (KI-975).
|
|
893
|
+
if (!keyFocusRoom && !activeRoom && gateStops.length) {
|
|
894
|
+
const gated: EncounterDef[] = [];
|
|
895
|
+
for (const e of pool) {
|
|
896
|
+
const items = factsOf(e).reqItems;
|
|
897
|
+
if (!items.length) continue;
|
|
898
|
+
const ready = items.some((item) => {
|
|
899
|
+
const sources = sourcesOf(item);
|
|
900
|
+
if (!sources.length) return false;
|
|
901
|
+
return sources.some((id) => placed.has(id) || id === e.id);
|
|
902
|
+
});
|
|
903
|
+
if (ready) gated.push(e);
|
|
904
|
+
}
|
|
905
|
+
if (gated.length) {
|
|
906
|
+
let gate: EncounterDef | null = null;
|
|
907
|
+
let bestD = Infinity;
|
|
908
|
+
for (const e of gated) {
|
|
909
|
+
const w = factsOf(e).world;
|
|
910
|
+
const d = w ? euclid(lastPlaced, w) : UNREACHABLE_BASE;
|
|
911
|
+
if (d < bestD || (d === bestD && e.id < (gate?.id ?? ''))) {
|
|
912
|
+
bestD = d;
|
|
913
|
+
gate = e;
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
if (gate) {
|
|
917
|
+
const rk = factsOf(gate).roomKey;
|
|
918
|
+
const focused = pool.filter(
|
|
919
|
+
(e) => e.id === gate.id || (!rk.startsWith('~enc:') && factsOf(e).roomKey === rk),
|
|
920
|
+
);
|
|
921
|
+
if (focused.length) pool = focused;
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
const best = pickBest(pool);
|
|
927
|
+
if (!best) break;
|
|
928
|
+
ordered.push(best);
|
|
929
|
+
remaining.delete(best.id);
|
|
930
|
+
dropRoomMember(best);
|
|
931
|
+
dropProducer(best);
|
|
932
|
+
placed.add(best.id);
|
|
933
|
+
const bestFacts = factsOf(best);
|
|
934
|
+
if (bestFacts.world) lastPlaced = bestFacts.world;
|
|
935
|
+
for (const f of bestFacts.prodFlags) produced.add(f);
|
|
936
|
+
|
|
937
|
+
const rk = bestFacts.roomKey;
|
|
938
|
+
// Stay in this room if any remaining members (even gated) — only leave when
|
|
939
|
+
// the next legal pick is forced elsewhere by boss/flags. Soft stick: if the
|
|
940
|
+
// room still has *legal* members next iteration, inRoom filter keeps us here.
|
|
941
|
+
activeRoom = rk.startsWith('~enc:') ? null : rk;
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
return ordered;
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
// Count how many times the route leaves a named room and later returns to it. Singleton / null rooms
|
|
948
|
+
// (`roomId` null/undefined) do not count as re-entries.
|
|
949
|
+
export function countRoomReentries(route: readonly EncounterDef[]): number {
|
|
950
|
+
const seen = new Set<string>();
|
|
951
|
+
let prev: string | null = null;
|
|
952
|
+
let reentries = 0;
|
|
953
|
+
for (const e of route) {
|
|
954
|
+
const r = e.roomId ?? null;
|
|
955
|
+
if (!r) {
|
|
956
|
+
prev = null;
|
|
957
|
+
continue;
|
|
958
|
+
}
|
|
959
|
+
if (prev !== r) {
|
|
960
|
+
if (seen.has(r)) reentries++;
|
|
961
|
+
seen.add(r);
|
|
962
|
+
prev = r;
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
return reentries;
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
/** Distinct named-room sequence along a route (null rooms skipped). */
|
|
969
|
+
export function roomSequence(route: readonly EncounterDef[]): string[] {
|
|
970
|
+
const out: string[] = [];
|
|
971
|
+
for (const e of route) {
|
|
972
|
+
if (!e.roomId || !e.roomName) continue;
|
|
973
|
+
if (out[out.length - 1] === e.roomName) continue;
|
|
974
|
+
out.push(e.roomName);
|
|
975
|
+
}
|
|
976
|
+
return out;
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
/**
|
|
980
|
+
* Apply geometric order to a run's materialized route ids / budget atoms.
|
|
981
|
+
* Safe when `defeated` is empty (fresh enter / pace change / floorplan late-load).
|
|
982
|
+
* No-op when the run already has progress (would reshuffle cleared prefixes).
|
|
983
|
+
*/
|
|
984
|
+
export function applyGeometricRouteToRun(
|
|
985
|
+
def: InstanceDef,
|
|
986
|
+
run: {
|
|
987
|
+
dungeonId: string;
|
|
988
|
+
defeated: string[];
|
|
989
|
+
routeEncounterIds?: string[];
|
|
990
|
+
routeEncounters?: EncounterDef[];
|
|
991
|
+
},
|
|
992
|
+
opts?: OrderRouteOpts,
|
|
993
|
+
): {
|
|
994
|
+
routeEncounterIds: string[];
|
|
995
|
+
routeEncounters?: EncounterDef[];
|
|
996
|
+
} {
|
|
997
|
+
// Resolve the encounter list currently driving the run.
|
|
998
|
+
let route: EncounterDef[];
|
|
999
|
+
if (run.routeEncounters?.length) {
|
|
1000
|
+
const byId = new Map(run.routeEncounters.map((e) => [e.id, e]));
|
|
1001
|
+
const ids = run.routeEncounterIds?.length
|
|
1002
|
+
? run.routeEncounterIds
|
|
1003
|
+
: run.routeEncounters.map((e) => e.id);
|
|
1004
|
+
route = [];
|
|
1005
|
+
for (const id of ids) {
|
|
1006
|
+
const e = byId.get(id);
|
|
1007
|
+
if (e) route.push(e);
|
|
1008
|
+
}
|
|
1009
|
+
} else if (run.routeEncounterIds?.length) {
|
|
1010
|
+
const byId = new Map(def.encounters.map((e) => [e.id, e]));
|
|
1011
|
+
route = [];
|
|
1012
|
+
for (const id of run.routeEncounterIds) {
|
|
1013
|
+
const e = byId.get(id);
|
|
1014
|
+
if (e) route.push(e);
|
|
1015
|
+
}
|
|
1016
|
+
} else {
|
|
1017
|
+
route = [...def.encounters];
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
// Pool strip before geometry so multi-location rares never re-enter as dups.
|
|
1021
|
+
if (opts?.mapSpawns) {
|
|
1022
|
+
route = filterRouteByPoolRoll(route, opts.poolRoll, opts.mapSpawns);
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
const ordered = orderRouteByGeometry(def, route, {
|
|
1026
|
+
grid: opts?.grid,
|
|
1027
|
+
floorplan: opts?.floorplan,
|
|
1028
|
+
hasItem: opts?.hasItem,
|
|
1029
|
+
itemObtainable: opts?.itemObtainable,
|
|
1030
|
+
// Already filtered above — avoid double strip.
|
|
1031
|
+
});
|
|
1032
|
+
const routeEncounterIds = ordered.map((e) => e.id);
|
|
1033
|
+
if (run.routeEncounters?.length) {
|
|
1034
|
+
return { routeEncounterIds, routeEncounters: ordered };
|
|
1035
|
+
}
|
|
1036
|
+
return { routeEncounterIds };
|
|
1037
|
+
}
|