azeroth-shell 0.0.0-stage → 0.1.1
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 +35 -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 +144 -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,1165 @@
|
|
|
1
|
+
// createSession and simulate — deterministic, seeded, fixed-tick combat. Two consumers advance the
|
|
2
|
+
// same session: simulate runs it to the end with the AI, and the UI feeds action-bar taps in.
|
|
3
|
+
|
|
4
|
+
import type { SpellDef } from '../../data/schema.ts';
|
|
5
|
+
import type { Rng } from '../core/rng.ts';
|
|
6
|
+
import { channelInterruptCtx, interruptChannel, maybeCancelChannelOnMove } from './channel.ts';
|
|
7
|
+
export { MAX_SESSION_EVENTS } from './sessionStepWrap.ts';
|
|
8
|
+
// Rows kept in the trim-proof death roll. A boss fight never reaches this; a continuous world session
|
|
9
|
+
// can, and the total stays exact. Oldest rows are the graded ones, so the cap drops later deaths.
|
|
10
|
+
const MAX_TRACKED_DEATHS = 512;
|
|
11
|
+
// Bucket for damage whose row carried no `target`. Named rather than folded into a guess: a non-zero
|
|
12
|
+
// reading here means the attribution is lossy and the per-role columns undercount.
|
|
13
|
+
export const UNATTRIBUTED_VICTIM = '_unattributed';
|
|
14
|
+
import type { CombatEvent, Side, TotemSide, EnemySide } from '../core/events.ts';
|
|
15
|
+
import { isEnemySide, enemySideAt, allySideAt, MAX_ALLIES, MECHANIC_EVENT_KINDS } from '../core/events.ts';
|
|
16
|
+
import { TUNING } from './formulas.ts';
|
|
17
|
+
import {
|
|
18
|
+
isDetectedAtDistance,
|
|
19
|
+
} from '../../domain/spells/stealth.ts';
|
|
20
|
+
import { combatPerf } from './perfSink.ts';
|
|
21
|
+
import { type GroundPool } from '../game/dynamicObject.ts';
|
|
22
|
+
import { type CombatPosYd } from './combatGeometry.ts';
|
|
23
|
+
import { stripExclusiveSiblingSources, type AreaAuraSource } from './areaAura.ts';
|
|
24
|
+
import { resolveCombatSpell, isInterpreterEnabled, durMs } from '../spells/interpreter/index.ts';
|
|
25
|
+
import { ThreatManager, type CombatantId } from './threat.ts';
|
|
26
|
+
|
|
27
|
+
// ---- Wave 4 S24–S27: types / predicates / roles / fighter factory ----
|
|
28
|
+
import type {
|
|
29
|
+
WeaponProfile,
|
|
30
|
+
AllyActionProvider,
|
|
31
|
+
CombatantSpec,
|
|
32
|
+
CombatItem,
|
|
33
|
+
CombatConfig,
|
|
34
|
+
CombatResult,
|
|
35
|
+
ActiveEffect,
|
|
36
|
+
CombatAction,
|
|
37
|
+
SimTickInputs,
|
|
38
|
+
Fighter,
|
|
39
|
+
CombatSession,
|
|
40
|
+
} from './types.ts';
|
|
41
|
+
import type { QueuedCast } from './session-types.ts';
|
|
42
|
+
// P12: types / predicates / roles re-export only from combat.ts barrel (not here).
|
|
43
|
+
import { makeFighter } from './fighterFactory.ts';
|
|
44
|
+
import type { CombatContext, PendingMissile } from './context.ts';
|
|
45
|
+
import type { ScriptDevAction } from '../../domain/scriptdev/types.ts';
|
|
46
|
+
import { installDispelReflect } from './dispelReflect.ts';
|
|
47
|
+
import { armEnemyCastState, installSessionInject } from './sessionInject.ts';
|
|
48
|
+
import { installSessionGeometry } from './sessionGeometry.ts';
|
|
49
|
+
import { installSessionPools } from './sessionPools.ts';
|
|
50
|
+
import { installSessionFocus } from './sessionFocus.ts';
|
|
51
|
+
import { installSessionDeaths } from './sessionDeaths.ts';
|
|
52
|
+
import { installPowerForm } from './powerForm.ts';
|
|
53
|
+
import { installInterruptAuras } from './interruptAuras.ts';
|
|
54
|
+
import { DAZED_SPELL_ID } from './facing.ts';
|
|
55
|
+
import { installCrowdControl } from './crowdControl.ts';
|
|
56
|
+
import { seedFighterSchoolImmunities } from './seedSchoolImmunities.ts';
|
|
57
|
+
import { installThreatOps } from './threatOps.ts';
|
|
58
|
+
import { installEventAi } from './eventAi.ts';
|
|
59
|
+
import { installProcsDamage } from './procsDamage.ts';
|
|
60
|
+
import { installApplySpell } from './applySpell.ts';
|
|
61
|
+
import { installAoeCast } from './aoeCast.ts';
|
|
62
|
+
import { installItemUse } from './itemUse.ts';
|
|
63
|
+
import { installWeapon } from './weapon.ts';
|
|
64
|
+
import { installPickAction } from './pickAction.ts';
|
|
65
|
+
import { installStepSession } from './stepSession.ts';
|
|
66
|
+
import { buildSessionView } from './sessionView.ts';
|
|
67
|
+
import { buildCombatSessionApi } from './sessionApi.ts';
|
|
68
|
+
import {
|
|
69
|
+
seedAllOocEffects,
|
|
70
|
+
seedDamageShields as seedDamageShieldsInto,
|
|
71
|
+
seedProcTriggers as seedProcTriggersInto,
|
|
72
|
+
seedSchoolAbsorbs as seedSchoolAbsorbsInto,
|
|
73
|
+
seedManaShields as seedManaShieldsInto,
|
|
74
|
+
seedRestoreHealth as seedRestoreHealthInto,
|
|
75
|
+
} from './seedOocEffects.ts';
|
|
76
|
+
import { seedDisplayAurasInto } from './seedDisplayAuras.ts';
|
|
77
|
+
import { totalStealthStrength } from '../../domain/spells/stealth.ts';
|
|
78
|
+
import {
|
|
79
|
+
dueInstantOfEffect,
|
|
80
|
+
dueInstantOfPool,
|
|
81
|
+
lowerPeriodicDue,
|
|
82
|
+
PERIODIC_DUE_NONE,
|
|
83
|
+
recomputePeriodicDue as recomputePeriodicDueFrom,
|
|
84
|
+
} from './periodicDue.ts';
|
|
85
|
+
|
|
86
|
+
// SoR swing formula lives on combat.ts barrel; local copy for install deps (same coeffs).
|
|
87
|
+
// Cite: combat.ts sealRighteousnessSwing; UnitAuraProcHandler.cpp:1116-1134.
|
|
88
|
+
function sealRighteousnessSwing(coeff: number, weapon: WeaponProfile): number {
|
|
89
|
+
const speed = weapon.speedMs / 1000;
|
|
90
|
+
const scaled = coeff * 1.2 * 1.03 * speed / 100;
|
|
91
|
+
const base = weapon.twoHand ? (1.20 * scaled + 1) : (0.85 * Math.ceil(scaled) - 1);
|
|
92
|
+
return Math.max(1, Math.floor(base + 0.03 * (weapon.dmgMin + weapon.dmgMax) / 2) + 1);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** SI5: pre-resolve kit spells so AI roleOf / ability lists see interpreter effects. */
|
|
96
|
+
function materializeSpecForCombat(spec: CombatantSpec): CombatantSpec {
|
|
97
|
+
if (!isInterpreterEnabled()) return spec;
|
|
98
|
+
return {
|
|
99
|
+
...spec,
|
|
100
|
+
spells: spec.spells.map((s) => resolveCombatSpell(s)),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function createSession(
|
|
105
|
+
playerSpec: CombatantSpec,
|
|
106
|
+
enemySpec: CombatantSpec | CombatantSpec[],
|
|
107
|
+
rng: Rng,
|
|
108
|
+
cfg: CombatConfig = {},
|
|
109
|
+
petSpec: CombatantSpec | null = null,
|
|
110
|
+
allySpecs: CombatantSpec[] | null = null,
|
|
111
|
+
): CombatSession {
|
|
112
|
+
const tickMs = cfg.tickMs ?? TUNING.tickMs;
|
|
113
|
+
const maxMs = cfg.maxMs ?? TUNING.maxMs;
|
|
114
|
+
|
|
115
|
+
// May be EMPTY: a continuous floor session opens with no enemy and every body arrives by inject.
|
|
116
|
+
// Nothing in the engine needs a primary — the two readers are 1v1-shaped reporting.
|
|
117
|
+
const enemySpecs = Array.isArray(enemySpec) ? enemySpec : [enemySpec];
|
|
118
|
+
|
|
119
|
+
const player = makeFighter('player', materializeSpecForCombat(playerSpec));
|
|
120
|
+
player.sitting = cfg.sitting ?? false;
|
|
121
|
+
// Wave 4A: N enemies. Index 0 keeps side `'enemy'` for 1v1 event/UI back-compat.
|
|
122
|
+
const enemies: Fighter[] = enemySpecs.map((spec, i) => makeFighter(enemySideAt(i), materializeSpecForCombat(spec)));
|
|
123
|
+
// The primary enemy, absent on a floor session. Written as a ternary, NOT an annotated const: with
|
|
124
|
+
// unchecked indexed access off, control-flow narrows a const straight back past the annotation.
|
|
125
|
+
const enemy: Fighter | undefined = enemies.length > 0 ? enemies[0] : undefined;
|
|
126
|
+
// Optional player-side pet. Everything pet-related is guarded by `pet != null`
|
|
127
|
+
// so a fight without a pet behaves EXACTLY as the 1v1 engine always has (no extra rng draws).
|
|
128
|
+
const pet = petSpec ? makeFighter('pet', materializeSpecForCombat(petSpec)) : null;
|
|
129
|
+
// Friendly companions as real combatants. Every ally touchpoint iterates this array, so zero allies
|
|
130
|
+
// keeps a solo session byte-identical — no extra rng draws and no extra events.
|
|
131
|
+
const allyList = allySpecs ?? [];
|
|
132
|
+
if (allyList.length > MAX_ALLIES) {
|
|
133
|
+
throw new Error(`createSession allows at most ${MAX_ALLIES} allies (got ${allyList.length})`);
|
|
134
|
+
}
|
|
135
|
+
const allies: Fighter[] = allyList.map((spec, i) => makeFighter(allySideAt(i), materializeSpecForCombat(spec)));
|
|
136
|
+
// The companion -> pet edge, off the seat-indexed config and onto the units, once (E1.1). Every
|
|
137
|
+
// reader asks the unit from here on; cfg.allyPetIndex stays the game layer's input contract.
|
|
138
|
+
for (let i = 0; i < allies.length; i++) {
|
|
139
|
+
const pIdx = cfg.allyPetIndex?.[i];
|
|
140
|
+
if (pIdx == null || pIdx < 0 || pIdx >= allies.length) continue;
|
|
141
|
+
allies[i]!.spec.petSide = allies[pIdx]!.side;
|
|
142
|
+
}
|
|
143
|
+
// The player holds seat 0 of subgroup 0 and their pet rides with them; the ally seats are dealt by
|
|
144
|
+
// whoever built the party. Stamped here so no caller can leave the player ungrouped.
|
|
145
|
+
player.spec.subgroup ??= 0;
|
|
146
|
+
if (pet) pet.spec.subgroup ??= 0;
|
|
147
|
+
|
|
148
|
+
// Per-enemy creature cast scheduler — the EventAI subset plus the spell list, with full EventAI
|
|
149
|
+
// state beside it. Pets and the player keep the class-heuristic picker.
|
|
150
|
+
const enemyCastBySide = new Map<EnemySide, import('./context.ts').EnemyCastState>();
|
|
151
|
+
const initialResetActions = new Map<EnemySide, ScriptDevAction[]>();
|
|
152
|
+
for (const ef of enemies) {
|
|
153
|
+
const acts: ScriptDevAction[] = [];
|
|
154
|
+
enemyCastBySide.set(ef.side as EnemySide, armEnemyCastState(ef, rng, acts));
|
|
155
|
+
if (acts.length > 0) initialResetActions.set(ef.side as EnemySide, acts);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Per-dungeon-run InstanceData store (ACTION_T_SET_INST_DATA). Cite: CreatureEventAI.cpp ~1044–1054;
|
|
159
|
+
// ScriptDev instance scripts consume in S9/S10.
|
|
160
|
+
const instanceData = new Map<number, number>(
|
|
161
|
+
Object.entries(cfg.instanceData ?? {}).map(([k, v]) => [Number(k), v | 0]),
|
|
162
|
+
);
|
|
163
|
+
// Guardian ownership for DESPAWN_GUARDIANS — summoner side → set of enemy sides. Cite:
|
|
164
|
+
// CreatureEventAI.cpp ACTION_T_DESPAWN_GUARDIANS ~1283–1295.
|
|
165
|
+
const guardianSidesBySummoner = new Map<Side, Set<Side>>();
|
|
166
|
+
|
|
167
|
+
// Up to 4 totems PER SHAMAN (Unit.h:2625 is a per-unit slot array), keyed by the totem's side.
|
|
168
|
+
// Empty until someone casts summon_totem — zero totems means no extra RNG.
|
|
169
|
+
const totems = new Map<TotemSide, Fighter>();
|
|
170
|
+
/** Totem side → owner/name — survives UnSummon so DPSMate can merge guardian damage. */
|
|
171
|
+
const totemOwnerMeta = new Map<TotemSide, { ownerSide: Side; name: string }>();
|
|
172
|
+
|
|
173
|
+
// Wave N1 — session-owned DynamicObject ground pools (Consecration / Blizzard / RoF).
|
|
174
|
+
// Empty until a PERSISTENT_AREA_AURA (or aoe+dot bake) is cast — zero pools ⇒ no extra RNG.
|
|
175
|
+
const groundPools: GroundPool[] = [];
|
|
176
|
+
/** SpellEntry.speed missiles — empty until a geometry session launches one. */
|
|
177
|
+
const pendingMissiles: PendingMissile[] = [];
|
|
178
|
+
let nextGroundPoolId = 1; // bumped via ctx.nextGroundPoolId (installed below, after ctx exists)
|
|
179
|
+
|
|
180
|
+
// Live class-A area-aura holders. Empty until one is applied, so a fight without party auras does
|
|
181
|
+
// zero extra work. Cite: SpellAuras.cpp AreaAura::Update; RANGE-ARCHITECTURE §4.8.
|
|
182
|
+
const areaAuraSources: AreaAuraSource[] = [];
|
|
183
|
+
// SPELL_ATTR_DO_NOT_DISPLAY ids seen this session (sessionView drops their aura chips).
|
|
184
|
+
const hiddenAuraSpellIds = new Set<number>();
|
|
185
|
+
|
|
186
|
+
// Per-unit combat yards relative to the pull origin, mutable when seeded and absent on the legacy
|
|
187
|
+
// no-geometry path. The z is carried, not stripped: it is the only trustworthy storey on a stack.
|
|
188
|
+
let unitPositionsMutable: Record<string, CombatPosYd> | undefined = cfg.unitPositionsYd
|
|
189
|
+
? Object.fromEntries(
|
|
190
|
+
Object.entries(cfg.unitPositionsYd).map(([k, v]) => [k, {
|
|
191
|
+
x: v.x,
|
|
192
|
+
y: v.y,
|
|
193
|
+
...(v.z != null && Number.isFinite(v.z)
|
|
194
|
+
? { z: v.z }
|
|
195
|
+
: (!isEnemySide(k) && cfg.unitPositionsYd?.player?.z != null
|
|
196
|
+
? { z: cfg.unitPositionsYd.player.z }
|
|
197
|
+
: {})),
|
|
198
|
+
}]),
|
|
199
|
+
)
|
|
200
|
+
: undefined;
|
|
201
|
+
// Geometry helpers (posOf/setPos/placeEnemyAt/…) installed via installSessionGeometry after ctx.
|
|
202
|
+
|
|
203
|
+
// Each creature that can hold a threat list owns its own manager. The player, pets and totems are
|
|
204
|
+
// list ENTRIES and own none. Cite: ThreatManager.cpp; Unit.cpp AddThreat / DeleteThreatList.
|
|
205
|
+
const threatByOwner = new Map<CombatantId, ThreatManager>();
|
|
206
|
+
for (const ef of enemies) {
|
|
207
|
+
threatByOwner.set(ef.side, new ThreatManager(ef.side));
|
|
208
|
+
}
|
|
209
|
+
// AttackStart seed — a multi-enemy pack co-pulls as already engaged, while a solo mob waits for the
|
|
210
|
+
// first threat unless the caller asks otherwise.
|
|
211
|
+
{
|
|
212
|
+
const seed =
|
|
213
|
+
cfg.seedAttackStart === true
|
|
214
|
+
|| (cfg.seedAttackStart !== false && enemies.length > 1);
|
|
215
|
+
if (seed) {
|
|
216
|
+
// It opens on the party's tank, which is the PLAYER when the player is the one tanking.
|
|
217
|
+
// Preferring a companion over a tanking human treated the human as a separate unit.
|
|
218
|
+
let seedId: CombatantId = 'player';
|
|
219
|
+
if (player.spec.groupRole !== 'tank') {
|
|
220
|
+
for (const al of allies) {
|
|
221
|
+
if (al.hp > 0 && al.spec.groupRole === 'tank') {
|
|
222
|
+
seedId = al.side;
|
|
223
|
+
break;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
for (const ef of enemies) {
|
|
228
|
+
threatByOwner.get(ef.side)!.addThreatDirectly(seedId, 1, false);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
const threatOf = (owner: CombatantId | Fighter): ThreatManager | undefined => {
|
|
233
|
+
const id = typeof owner === 'string' ? owner : owner.side;
|
|
234
|
+
return threatByOwner.get(id);
|
|
235
|
+
};
|
|
236
|
+
// Seed pre-fight cooldowns (remainings from world state or prior fight) as relative readyAt from t=0.
|
|
237
|
+
if (cfg.initialSpellCooldowns) {
|
|
238
|
+
for (const [idStr, rem] of Object.entries(cfg.initialSpellCooldowns)) {
|
|
239
|
+
const r = Number(rem);
|
|
240
|
+
if (r > 0) player.cooldowns.set(Number(idStr), r);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
if (cfg.initialCategoryCooldowns) {
|
|
244
|
+
for (const [cStr, rem] of Object.entries(cfg.initialCategoryCooldowns)) {
|
|
245
|
+
const r = Number(rem);
|
|
246
|
+
if (r > 0) player.categoryCooldowns.set(Number(cStr), r);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
const effects: ActiveEffect[] = [];
|
|
250
|
+
// A structural version of `effects`, bumped from the array itself rather than from ~400 call sites,
|
|
251
|
+
// so a new push cannot skip it. In-place field edits need no bump: the index holds the same objects.
|
|
252
|
+
let effectsVersion = 0;
|
|
253
|
+
let nextPeriodicDue = PERIODIC_DUE_NONE;
|
|
254
|
+
const touchPeriodicDue = (at: number): void => {
|
|
255
|
+
nextPeriodicDue = lowerPeriodicDue(nextPeriodicDue, at);
|
|
256
|
+
};
|
|
257
|
+
const touchEffect = (e: ActiveEffect): void => {
|
|
258
|
+
touchPeriodicDue(dueInstantOfEffect(e));
|
|
259
|
+
};
|
|
260
|
+
const touchGroundPool = (pool: GroundPool): void => {
|
|
261
|
+
touchPeriodicDue(dueInstantOfPool(pool));
|
|
262
|
+
};
|
|
263
|
+
const recomputePeriodicDue = (): void => {
|
|
264
|
+
nextPeriodicDue = recomputePeriodicDueFrom(effects, totems.values(), groundPools);
|
|
265
|
+
};
|
|
266
|
+
// Assigned after bySide exists — seed OOC speed auras leave fighters speedDirty=true.
|
|
267
|
+
let noteSpeedAura: ((e: ActiveEffect) => void) | null = null;
|
|
268
|
+
let noteDecideAura: ((e: ActiveEffect) => void) | null = null;
|
|
269
|
+
// Effects bucketed by the unit they sit on. The view used to walk the GLOBAL array twice per unit,
|
|
270
|
+
// 8.3% of tick self time at 40 units. Rebuilt on the version, so it is exact, not eventual.
|
|
271
|
+
const effectsByTarget = new Map<Side, ActiveEffect[]>();
|
|
272
|
+
const effectsBySource = new Map<Side, ActiveEffect[]>();
|
|
273
|
+
let effectsIndexVersion = -1;
|
|
274
|
+
const NO_EFFECTS: ActiveEffect[] = [];
|
|
275
|
+
const indexAdd = (e: ActiveEffect): void => {
|
|
276
|
+
const on = effectsByTarget.get(e.target);
|
|
277
|
+
if (on === undefined) effectsByTarget.set(e.target, [e]);
|
|
278
|
+
else on.push(e);
|
|
279
|
+
const from = effectsBySource.get(e.source);
|
|
280
|
+
if (from === undefined) effectsBySource.set(e.source, [e]);
|
|
281
|
+
else from.push(e);
|
|
282
|
+
};
|
|
283
|
+
const indexDrop = (e: ActiveEffect): void => {
|
|
284
|
+
const on = effectsByTarget.get(e.target);
|
|
285
|
+
if (on) {
|
|
286
|
+
const i = on.lastIndexOf(e);
|
|
287
|
+
if (i >= 0) on.splice(i, 1);
|
|
288
|
+
if (on.length === 0) effectsByTarget.delete(e.target);
|
|
289
|
+
}
|
|
290
|
+
const from = effectsBySource.get(e.source);
|
|
291
|
+
if (from) {
|
|
292
|
+
const i = from.lastIndexOf(e);
|
|
293
|
+
if (i >= 0) from.splice(i, 1);
|
|
294
|
+
if (from.length === 0) effectsBySource.delete(e.source);
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
const rebuildEffectIndex = (): void => {
|
|
298
|
+
if (effectsIndexVersion === effectsVersion) return;
|
|
299
|
+
effectsByTarget.clear();
|
|
300
|
+
effectsBySource.clear();
|
|
301
|
+
for (const e of effects) indexAdd(e);
|
|
302
|
+
effectsIndexVersion = effectsVersion;
|
|
303
|
+
};
|
|
304
|
+
const rawPush = Array.prototype.push;
|
|
305
|
+
const rawSplice = Array.prototype.splice;
|
|
306
|
+
effects.push = function push(...items: ActiveEffect[]): number {
|
|
307
|
+
const indexLive = effectsIndexVersion === effectsVersion;
|
|
308
|
+
effectsVersion++;
|
|
309
|
+
if (noteSpeedAura) for (const e of items) noteSpeedAura(e);
|
|
310
|
+
if (noteDecideAura) for (const e of items) noteDecideAura(e);
|
|
311
|
+
for (const e of items) touchEffect(e);
|
|
312
|
+
const n = rawPush.apply(this, items);
|
|
313
|
+
if (indexLive) {
|
|
314
|
+
for (const e of items) indexAdd(e);
|
|
315
|
+
effectsIndexVersion = effectsVersion;
|
|
316
|
+
}
|
|
317
|
+
return n;
|
|
318
|
+
};
|
|
319
|
+
effects.splice = function splice(...args: [number, number?, ...ActiveEffect[]]): ActiveEffect[] {
|
|
320
|
+
const indexLive = effectsIndexVersion === effectsVersion;
|
|
321
|
+
const removed = rawSplice.apply(this, args as unknown as [number, number]);
|
|
322
|
+
effectsVersion++;
|
|
323
|
+
if (indexLive) {
|
|
324
|
+
for (const e of removed) indexDrop(e);
|
|
325
|
+
for (let i = 2; i < args.length; i++) if (args[i]) indexAdd(args[i] as ActiveEffect);
|
|
326
|
+
effectsIndexVersion = effectsVersion;
|
|
327
|
+
}
|
|
328
|
+
if (noteSpeedAura) {
|
|
329
|
+
for (const e of removed) noteSpeedAura(e);
|
|
330
|
+
for (let i = 2; i < args.length; i++) if (args[i]) noteSpeedAura(args[i] as ActiveEffect);
|
|
331
|
+
}
|
|
332
|
+
if (noteDecideAura) {
|
|
333
|
+
for (const e of removed) noteDecideAura(e);
|
|
334
|
+
for (let i = 2; i < args.length; i++) if (args[i]) noteDecideAura(args[i] as ActiveEffect);
|
|
335
|
+
}
|
|
336
|
+
for (let i = 2; i < args.length; i++) if (args[i]) touchEffect(args[i] as ActiveEffect);
|
|
337
|
+
return removed;
|
|
338
|
+
};
|
|
339
|
+
// A refresh that re-sources a live row moves it between by-source buckets; writing the field in
|
|
340
|
+
// place left it parked under the old caster and a paladin aura swap then stripped nothing.
|
|
341
|
+
const setEffectSource = (e: ActiveEffect, side: Side): void => {
|
|
342
|
+
if (e.source === side) return;
|
|
343
|
+
const indexLive = effectsIndexVersion === effectsVersion;
|
|
344
|
+
if (indexLive) indexDrop(e);
|
|
345
|
+
e.source = side;
|
|
346
|
+
if (indexLive) indexAdd(e);
|
|
347
|
+
};
|
|
348
|
+
// Version-keyed skips (heartbeat resists) read fields a refresh edits in place; bump for them.
|
|
349
|
+
const bumpEffectsVersion = (): void => {
|
|
350
|
+
const indexLive = effectsIndexVersion === effectsVersion;
|
|
351
|
+
effectsVersion++;
|
|
352
|
+
if (indexLive) effectsIndexVersion = effectsVersion;
|
|
353
|
+
};
|
|
354
|
+
const effectsOn = (side: Side): ActiveEffect[] => {
|
|
355
|
+
rebuildEffectIndex();
|
|
356
|
+
return effectsByTarget.get(side) ?? NO_EFFECTS;
|
|
357
|
+
};
|
|
358
|
+
const effectsFrom = (side: Side): ActiveEffect[] => {
|
|
359
|
+
rebuildEffectIndex();
|
|
360
|
+
return effectsBySource.get(side) ?? NO_EFFECTS;
|
|
361
|
+
};
|
|
362
|
+
const events: CombatEvent[] = [];
|
|
363
|
+
|
|
364
|
+
// Pre-pull out-of-combat shields, absorbs, HoTs and display auras. The party in turn order is the
|
|
365
|
+
// shape the store serves once pools install, built by hand here only because this runs first.
|
|
366
|
+
seedAllOocEffects({
|
|
367
|
+
party: [player, ...(pet ? [pet] : []), ...allies],
|
|
368
|
+
player, effects, rng,
|
|
369
|
+
posOf: (side) => unitPositionsMutable?.[side],
|
|
370
|
+
});
|
|
371
|
+
// Single-arg wrappers for installApplySpell dep bag (closed over session effects/rng).
|
|
372
|
+
const seedDamageShields = (f: Fighter): void => seedDamageShieldsInto(f, effects);
|
|
373
|
+
const seedProcTriggers = (f: Fighter): void =>
|
|
374
|
+
seedProcTriggersInto(f, effects, (side) => unitPositionsMutable?.[side]);
|
|
375
|
+
const seedSchoolAbsorbs = (f: Fighter): void => seedSchoolAbsorbsInto(f, effects, rng);
|
|
376
|
+
const seedManaShields = (f: Fighter): void => seedManaShieldsInto(f, effects, rng);
|
|
377
|
+
const seedRestoreHealth = (f: Fighter): void => seedRestoreHealthInto(f, effects);
|
|
378
|
+
const seedDisplayAuras = (f: Fighter): void => seedDisplayAurasInto(f, effects);
|
|
379
|
+
|
|
380
|
+
// Pull detect: only when cfg.encounterDistanceYd is set (opt-in). Product unitPositions alone
|
|
381
|
+
// must not strip the opener window (KI-872). breakStealth is defined later; applied at return.
|
|
382
|
+
|
|
383
|
+
let t = 0;
|
|
384
|
+
let done = false;
|
|
385
|
+
let winner: Side | 'timeout' | null = null;
|
|
386
|
+
let aiPlayer = false;
|
|
387
|
+
// Last non-aura/resource event time. Push still re-arms it; fights no longer abort on it.
|
|
388
|
+
let lastActionT = 0;
|
|
389
|
+
let eventsDropped = 0;
|
|
390
|
+
/** Trim-proof count of scripted-mechanic events (see push + CombatResult.mechanics). */
|
|
391
|
+
const mechanicTally = new Map<CombatEvent['kind'], { count: number; firstT: number; lastT: number }>();
|
|
392
|
+
/** Trim-proof damage landed per victim side (see push + CombatResult.damageTakenBySide). */
|
|
393
|
+
const damageTakenBySide = new Map<string, number>();
|
|
394
|
+
/** Most recent damaging row per victim — the killing blow the next `death` row reads. */
|
|
395
|
+
const lastHitBySide = new Map<string, { by: Side; name: string }>();
|
|
396
|
+
/** Trim-proof death roll in order (see push + CombatResult.deaths). Capped; deathsTotal counts all. */
|
|
397
|
+
const deathRoll: { side: Side; t: number; by?: Side; cause?: string }[] = [];
|
|
398
|
+
let deathsTotal = 0;
|
|
399
|
+
const deathEmitted = new Set<string>();
|
|
400
|
+
// TargetCreatureType cast refusals already logged this fight (one line per spell).
|
|
401
|
+
const invalidTargetReported = new Set<number>();
|
|
402
|
+
let soulShardsGained = 0;
|
|
403
|
+
|
|
404
|
+
// Player and pet focus among the pack, defaulting to the primary and re-targeting when it dies. No
|
|
405
|
+
// primary starts on the PLAYER, which is what the focus stands down to when nothing bears threat.
|
|
406
|
+
let playerFocus: Fighter = enemy ?? player;
|
|
407
|
+
|
|
408
|
+
// Soul Shard on kill: a target that dies under a DoT/aura with grantsSoulShardOnKill (or from a
|
|
409
|
+
// killing spell with that flag) yields item 6265 in resolveFight. Wave D bake; not an id-Set.
|
|
410
|
+
|
|
411
|
+
// In-combat consumables. Counts and cooldowns are local copies so the caller's bags are not mutated
|
|
412
|
+
// mid-fight; the spent totals reconcile at the end. Each unit has an independent kit.
|
|
413
|
+
const playerItems: CombatItem[] = (playerSpec.items ?? []).map((it) => ({ ...it, effect: { ...it.effect } }));
|
|
414
|
+
const itemCd = new Map<number, number>();
|
|
415
|
+
const itemsSpent = new Map<number, number>();
|
|
416
|
+
const itemsGained = new Map<number, number>();
|
|
417
|
+
// One side-keyed store for every non-player bag. Seat order still decides who gets one; it is
|
|
418
|
+
// no longer how anyone finds it.
|
|
419
|
+
const unitItems = new Map<Side, CombatItem[]>();
|
|
420
|
+
const unitItemCd = new Map<Side, Map<number, number>>();
|
|
421
|
+
const unitItemsSpent = new Map<Side, Map<number, number>>();
|
|
422
|
+
const unitItemsGained = new Map<Side, Map<number, number>>();
|
|
423
|
+
for (const al of allies) {
|
|
424
|
+
unitItems.set(al.side, (al.spec.items ?? []).map((it) => ({ ...it, effect: { ...it.effect } })));
|
|
425
|
+
unitItemCd.set(al.side, new Map());
|
|
426
|
+
unitItemsSpent.set(al.side, new Map());
|
|
427
|
+
}
|
|
428
|
+
// BG enemy bots carry the same PvP kits (companionSpec fills spec.items for them too), into
|
|
429
|
+
// the same store. Their spent totals reconcile nowhere — enemies own no persistent inventory.
|
|
430
|
+
for (const ef of enemies) {
|
|
431
|
+
unitItems.set(ef.side, (ef.spec.items ?? []).map((it) => ({ ...it, effect: { ...it.effect } })));
|
|
432
|
+
unitItemCd.set(ef.side, new Map());
|
|
433
|
+
unitItemsSpent.set(ef.side, new Map());
|
|
434
|
+
}
|
|
435
|
+
// Rotation providers off the two seat-indexed config arrays and onto the seats, once. The same move
|
|
436
|
+
// as the bags above: seat order still decides who gets one, it is no longer how anyone finds it.
|
|
437
|
+
const unitActionProvider = new Map<Side, AllyActionProvider>();
|
|
438
|
+
for (let i = 0; i < allies.length; i++) {
|
|
439
|
+
const p = cfg.allyActionProviders?.[i];
|
|
440
|
+
if (p) unitActionProvider.set(allies[i]!.side, p);
|
|
441
|
+
}
|
|
442
|
+
for (let i = 0; i < enemies.length; i++) {
|
|
443
|
+
const p = cfg.enemyActionProviders?.[i];
|
|
444
|
+
if (p) unitActionProvider.set(enemies[i]!.side, p);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// Pools/faction helpers installed via installSessionPools after ctx + geometry.
|
|
448
|
+
// Focus / manual maps — on ctx for sessionPools free-fire + inject recycle.
|
|
449
|
+
let explicitEnemyFocus: EnemySide | null = null;
|
|
450
|
+
const allyManualTarget = new Map<Side, EnemySide>();
|
|
451
|
+
// Pet bar state. The spec carries the player's persisted choice; a fresh pet is follow and defensive
|
|
452
|
+
// like a newly summoned one. Cite: Pet.cpp SetReactState; CharmInfo::SetCommandState.
|
|
453
|
+
let petCommand: 'attack' | 'follow' | 'stay' = pet?.spec.petCommand ?? 'follow';
|
|
454
|
+
let petReact: 'aggressive' | 'defensive' | 'passive' = pet?.spec.petReact ?? 'defensive';
|
|
455
|
+
let petManualTarget: EnemySide | null = null;
|
|
456
|
+
let petStayPos: CombatPosYd | null = null;
|
|
457
|
+
// A pet that starts a fight already at heel is not retreating. Cite: PetHandler.cpp:182.
|
|
458
|
+
let petRetreating = false;
|
|
459
|
+
|
|
460
|
+
// ---- Wave 4 S28/S29: CombatContext bag + dispel/reflect peel ----
|
|
461
|
+
const ctx = {
|
|
462
|
+
get t() { return t; },
|
|
463
|
+
set t(v: number) { t = v; },
|
|
464
|
+
get ended() { return done; },
|
|
465
|
+
set ended(v: boolean) { done = v; },
|
|
466
|
+
get winner() { return winner; },
|
|
467
|
+
set winner(v: Side | 'timeout' | null) { winner = v; },
|
|
468
|
+
get aiPlayer() { return aiPlayer; },
|
|
469
|
+
set aiPlayer(v: boolean) { aiPlayer = v; },
|
|
470
|
+
tickMs,
|
|
471
|
+
maxMs,
|
|
472
|
+
rng,
|
|
473
|
+
cfg,
|
|
474
|
+
// creature_template_classlevelstats BaseDamage, for SCALES_WITH_CREATURE_LEVEL only.
|
|
475
|
+
creatureBaseDamageAt: cfg.creatureBaseDamageAt,
|
|
476
|
+
// The rank ladder, for aura identity.
|
|
477
|
+
spellChainOf: cfg.spellChainOf,
|
|
478
|
+
// Both fall back to the global spell store when a caller wired only `spellOf`. Every PvP session
|
|
479
|
+
// did: no creature could daze, and Feign Death never dropped on a move (interruptAuras.ts:18).
|
|
480
|
+
dazeSpell: cfg.dazeSpell ?? cfg.spellOf?.(DAZED_SPELL_ID),
|
|
481
|
+
auraInterruptFlagsOf: cfg.auraInterruptFlagsOf
|
|
482
|
+
?? (cfg.spellOf ? (id: number) => (cfg.spellOf!(id)?.auraInterruptFlags ?? 0) | 0 : undefined),
|
|
483
|
+
// The global spell store, for triggered payloads that sit in no kit.
|
|
484
|
+
spellOf: cfg.spellOf,
|
|
485
|
+
player,
|
|
486
|
+
// `enemies` and `allies` are NOT on the context: the by-side map is the storage and the block
|
|
487
|
+
// views are the ordered readers. The locals exist only to seed it and finish construction.
|
|
488
|
+
enemy,
|
|
489
|
+
pet,
|
|
490
|
+
petUnsummonedHp: null,
|
|
491
|
+
totems,
|
|
492
|
+
effects,
|
|
493
|
+
effectsOn,
|
|
494
|
+
effectsFrom,
|
|
495
|
+
setEffectSource,
|
|
496
|
+
bumpEffectsVersion,
|
|
497
|
+
effectsVersion: () => effectsVersion,
|
|
498
|
+
get nextPeriodicDue() { return nextPeriodicDue; },
|
|
499
|
+
touchEffect,
|
|
500
|
+
touchPeriodicDue,
|
|
501
|
+
touchGroundPool,
|
|
502
|
+
recomputePeriodicDue,
|
|
503
|
+
events,
|
|
504
|
+
threatByOwner,
|
|
505
|
+
enemyCastBySide,
|
|
506
|
+
groundPools,
|
|
507
|
+
areaAuraSources,
|
|
508
|
+
hiddenAuraSpellIds,
|
|
509
|
+
pendingMissiles,
|
|
510
|
+
get unitPositionsMutable() { return unitPositionsMutable; },
|
|
511
|
+
set unitPositionsMutable(v: any) { unitPositionsMutable = v; },
|
|
512
|
+
posGen: 0,
|
|
513
|
+
get explicitEnemyFocus() { return explicitEnemyFocus; },
|
|
514
|
+
set explicitEnemyFocus(v: EnemySide | null) { explicitEnemyFocus = v; },
|
|
515
|
+
allyManualTarget,
|
|
516
|
+
get petCommand() { return petCommand; },
|
|
517
|
+
set petCommand(v: 'attack' | 'follow' | 'stay') { petCommand = v; },
|
|
518
|
+
get petReact() { return petReact; },
|
|
519
|
+
set petReact(v: 'aggressive' | 'defensive' | 'passive') { petReact = v; },
|
|
520
|
+
get petManualTarget() { return petManualTarget; },
|
|
521
|
+
set petManualTarget(v: EnemySide | null) { petManualTarget = v; },
|
|
522
|
+
get petStayPos() { return petStayPos; },
|
|
523
|
+
set petStayPos(v: CombatPosYd | null) { petStayPos = v; },
|
|
524
|
+
get petRetreating() { return petRetreating; },
|
|
525
|
+
set petRetreating(v: boolean) { petRetreating = v; },
|
|
526
|
+
instanceData,
|
|
527
|
+
guardianSidesBySummoner,
|
|
528
|
+
playerItems,
|
|
529
|
+
itemCd,
|
|
530
|
+
itemsSpent,
|
|
531
|
+
itemsGained,
|
|
532
|
+
unitItems,
|
|
533
|
+
unitItemCd,
|
|
534
|
+
unitItemsSpent,
|
|
535
|
+
unitItemsGained,
|
|
536
|
+
unitActionProvider,
|
|
537
|
+
deathEmitted,
|
|
538
|
+
invalidTargetReported,
|
|
539
|
+
interruptClaim: new Map<CombatantId, CombatantId>(),
|
|
540
|
+
dispelClaim: new Map<string, CombatantId>(),
|
|
541
|
+
rezClaim: new Map<CombatantId, CombatantId>(),
|
|
542
|
+
get playerFocus() { return playerFocus.side; },
|
|
543
|
+
get playerFocusFighter() { return playerFocus; },
|
|
544
|
+
set playerFocusFighter(v: Fighter) { playerFocus = v; },
|
|
545
|
+
get soulShardsGained() { return soulShardsGained; },
|
|
546
|
+
set soulShardsGained(v: number) { soulShardsGained = v; },
|
|
547
|
+
get lastActionT() { return lastActionT; },
|
|
548
|
+
set lastActionT(v: number) { lastActionT = v; },
|
|
549
|
+
get eventsDropped() { return eventsDropped; },
|
|
550
|
+
set eventsDropped(v: number) { eventsDropped = v; },
|
|
551
|
+
push: null as unknown as CombatContext['push'],
|
|
552
|
+
fightersOrder: null as unknown as CombatContext['fightersOrder'],
|
|
553
|
+
partyUnits: null as unknown as CombatContext['partyUnits'],
|
|
554
|
+
bySide: null as unknown as CombatContext['bySide'],
|
|
555
|
+
factionOf: null as unknown as CombatContext['factionOf'],
|
|
556
|
+
factionOfFighter: null as unknown as CombatContext['factionOfFighter'],
|
|
557
|
+
livingHostilesFor: null as unknown as CombatContext['livingHostilesFor'],
|
|
558
|
+
livingFriendlies: null as unknown as CombatContext['livingFriendlies'],
|
|
559
|
+
friendlyPoolOf: null as unknown as CombatContext['friendlyPoolOf'],
|
|
560
|
+
lowestHpFriendlyOf: null as unknown as CombatContext['lowestHpFriendlyOf'],
|
|
561
|
+
foePoolOf: null as unknown as CombatContext['foePoolOf'],
|
|
562
|
+
livingEnemies: null as unknown as CombatContext['livingEnemies'],
|
|
563
|
+
livingCombatEnemies: null as unknown as CombatContext['livingCombatEnemies'],
|
|
564
|
+
isCharmed: null as unknown as CombatContext['isCharmed'],
|
|
565
|
+
isUnattackable: null as unknown as CombatContext['isUnattackable'],
|
|
566
|
+
threatOf: null as unknown as CombatContext['threatOf'],
|
|
567
|
+
distanceBetween: null as unknown as CombatContext['distanceBetween'],
|
|
568
|
+
posOf: null as unknown as CombatContext['posOf'],
|
|
569
|
+
setPos: null as unknown as CombatContext['setPos'],
|
|
570
|
+
isMoving: null as unknown as CombatContext['isMoving'],
|
|
571
|
+
} as CombatContext;
|
|
572
|
+
|
|
573
|
+
// The ground-pool case bumps the session counter through the context. The peel left a bare local
|
|
574
|
+
// increment behind, which was a ReferenceError on every persistent-area cast.
|
|
575
|
+
ctx.nextGroundPoolId = () => nextGroundPoolId++;
|
|
576
|
+
ctx.threatOf = threatOf;
|
|
577
|
+
|
|
578
|
+
// P2 geometry → P3 pools (pools read posOf/distanceBetween/posGen).
|
|
579
|
+
installSessionGeometry(ctx);
|
|
580
|
+
// Liveness: non-finite HP, from bad damage math, and non-positive are both dead. The negated
|
|
581
|
+
// comparison is intentional — NaN <= 0 is false in JS, which left mobs immortal after a NaN.
|
|
582
|
+
const dead = (f: Fighter): boolean => !(f.hp > 0);
|
|
583
|
+
(ctx as any).dead = dead;
|
|
584
|
+
installSessionPools(ctx, { dead, seedEnemies: enemies, seedAllies: allies });
|
|
585
|
+
|
|
586
|
+
// Local aliases for remaining createSession bodies (bound once after install).
|
|
587
|
+
const posOf = ctx.posOf;
|
|
588
|
+
const setPos = ctx.setPos;
|
|
589
|
+
const distanceBetween = ctx.distanceBetween;
|
|
590
|
+
const placeEnemyAt = ctx.placeEnemyAt!;
|
|
591
|
+
const snapToMesh = ctx.snapToMesh!;
|
|
592
|
+
const fightersOrder = ctx.fightersOrder;
|
|
593
|
+
const bySide = ctx.bySide;
|
|
594
|
+
noteSpeedAura = (e: ActiveEffect): void => {
|
|
595
|
+
if (e.kind !== 'speed') return;
|
|
596
|
+
const f = bySide(e.target);
|
|
597
|
+
if (f.side === e.target) f.speedDirty = true;
|
|
598
|
+
};
|
|
599
|
+
noteDecideAura = (e: ActiveEffect): void => {
|
|
600
|
+
const f = bySide(e.target);
|
|
601
|
+
if (f.side === e.target) f.decideDirty = true;
|
|
602
|
+
};
|
|
603
|
+
const bumpRoster = ctx.bumpRoster!;
|
|
604
|
+
const bumpPool = ctx.bumpPool!;
|
|
605
|
+
const livingEnemies = ctx.livingEnemies;
|
|
606
|
+
const livingCombatEnemies = ctx.livingCombatEnemies;
|
|
607
|
+
const livingHostilesFor = ctx.livingHostilesFor;
|
|
608
|
+
const livingFriendlies = ctx.livingFriendlies;
|
|
609
|
+
const friendlyPoolOf = ctx.friendlyPoolOf;
|
|
610
|
+
const foePoolOf = ctx.foePoolOf;
|
|
611
|
+
const lowestHpFriendlyOf = ctx.lowestHpFriendlyOf;
|
|
612
|
+
const lowestHpFriendly = ctx.lowestHpFriendly!;
|
|
613
|
+
const isCharmed = ctx.isCharmed;
|
|
614
|
+
const isUnattackable = ctx.isUnattackable;
|
|
615
|
+
const factionOf = ctx.factionOf;
|
|
616
|
+
const factionOfFighter = ctx.factionOfFighter;
|
|
617
|
+
const nativeFactionOf = ctx.nativeFactionOf!;
|
|
618
|
+
const enemyHasThreat = ctx.enemyHasThreat!;
|
|
619
|
+
const allEnemiesDead = ctx.allEnemiesDead!;
|
|
620
|
+
const partyWiped = ctx.partyWiped!;
|
|
621
|
+
const preferFocusHostile = ctx.preferFocusHostile!;
|
|
622
|
+
const freeFireHostilesFor = ctx.freeFireHostilesFor!;
|
|
623
|
+
|
|
624
|
+
installDispelReflect(ctx);
|
|
625
|
+
|
|
626
|
+
// Register or refresh a class-A area-aura holder. Re-casting the same aura from the same holder
|
|
627
|
+
// replaces the entry rather than stacking maintain passes — the C++ holder is one per caster.
|
|
628
|
+
const registerAreaAuraSource = (source: AreaAuraSource): void => {
|
|
629
|
+
source.radiusPending = true;
|
|
630
|
+
// A paladin swapping auras keeps one holder row per spell_group; the old aura leaves every carrier.
|
|
631
|
+
stripExclusiveSiblingSources(areaAuraSources, source, ctx.bySide);
|
|
632
|
+
const i = areaAuraSources.findIndex(
|
|
633
|
+
(s) => s.holderSide === source.holderSide && s.spellId === source.spellId,
|
|
634
|
+
);
|
|
635
|
+
if (i >= 0) areaAuraSources[i] = source;
|
|
636
|
+
else areaAuraSources.push(source);
|
|
637
|
+
};
|
|
638
|
+
ctx.registerAreaAuraSource = registerAreaAuraSource;
|
|
639
|
+
|
|
640
|
+
// Party takeover: the rotation KEEPS running while a companion is controlled, so a queued cast wins
|
|
641
|
+
// exactly one pick and a controlled healer never stops healing. The aim rides INSIDE each step.
|
|
642
|
+
const unitCastOrders = new Map<Side, QueuedCast[]>();
|
|
643
|
+
(ctx as { unitCastOrders?: Map<Side, QueuedCast[]> }).unitCastOrders = unitCastOrders;
|
|
644
|
+
// The target of an IN-FLIGHT cast that was aimed explicitly. A finished cast otherwise re-resolves,
|
|
645
|
+
// so a Shackle aimed at a skeleton landed on whatever the priest was fighting. Keyed by spell id.
|
|
646
|
+
const castTargetOverride = new Map<Side, { spellId: number; side: EnemySide }>();
|
|
647
|
+
(ctx as { castTargetOverride?: Map<Side, { spellId: number; side: EnemySide }> })
|
|
648
|
+
.castTargetOverride = castTargetOverride;
|
|
649
|
+
const castDestOverride = new Map<Side, { spellId: number; dest: CombatPosYd }>();
|
|
650
|
+
(ctx as { castDestOverride?: Map<Side, { spellId: number; dest: CombatPosYd }> })
|
|
651
|
+
.castDestOverride = castDestOverride;
|
|
652
|
+
/** Rotation `target: 'other'` aim, set and consumed within one tick (CC on a non-focus mob). */
|
|
653
|
+
const rotationOffTarget = new Map<Side, EnemySide>();
|
|
654
|
+
(ctx as { rotationOffTarget?: Map<Side, EnemySide> }).rotationOffTarget = rotationOffTarget;
|
|
655
|
+
/** Click-to-move / RTS group orders — WAYPOINT QUEUES honored in sessionMotion
|
|
656
|
+
* (shift-click appends; a plain order replaces the whole queue). */
|
|
657
|
+
const unitMoveOrders = new Map<string, CombatPosYd[]>();
|
|
658
|
+
(ctx as { unitMoveOrders?: Map<string, CombatPosYd[]> }).unitMoveOrders = unitMoveOrders;
|
|
659
|
+
/** Held-direction locomotion (WASD / stick), unit vectors. Stepped in sessionMotion. */
|
|
660
|
+
const unitMoveIntents = new Map<string, CombatPosYd>();
|
|
661
|
+
(ctx as { unitMoveIntents?: Map<string, CombatPosYd> }).unitMoveIntents = unitMoveIntents;
|
|
662
|
+
/** Sides whose motion the player owns — AI gap-close is off for them until released. */
|
|
663
|
+
const unitManualMotion = new Set<string>();
|
|
664
|
+
(ctx as { unitManualMotion?: Set<string> }).unitManualMotion = unitManualMotion;
|
|
665
|
+
/** Player "go here" dests: walk the order; do not stop to pre-cast. */
|
|
666
|
+
const unitHoldCourse = new Set<string>();
|
|
667
|
+
(ctx as { unitHoldCourse?: Set<string> }).unitHoldCourse = unitHoldCourse;
|
|
668
|
+
/** Split-now slots: each unit walks its own dest, not the player's corridor. */
|
|
669
|
+
const unitDirectMove = new Set<string>();
|
|
670
|
+
(ctx as { unitDirectMove?: Set<string> }).unitDirectMove = unitDirectMove;
|
|
671
|
+
/** RTS stance: gap-close policy after move / on stand. Default retaliate (absent = retaliate). */
|
|
672
|
+
type UnitStance = 'retaliate' | 'passive' | 'hold' | 'patrol';
|
|
673
|
+
const unitStance = new Map<string, UnitStance>();
|
|
674
|
+
(ctx as { unitStance?: Map<string, UnitStance> }).unitStance = unitStance;
|
|
675
|
+
/** Full patrol loop when stance is patrol (cycled when the move queue empties). */
|
|
676
|
+
const unitPatrolLoop = new Map<string, CombatPosYd[]>();
|
|
677
|
+
(ctx as { unitPatrolLoop?: Map<string, CombatPosYd[]> }).unitPatrolLoop = unitPatrolLoop;
|
|
678
|
+
/** Drop every manual-motion claim on a side (death, despawn, fight end). */
|
|
679
|
+
const clearManualMotion = (side: string): void => {
|
|
680
|
+
unitMoveOrders.delete(side);
|
|
681
|
+
unitMoveIntents.delete(side);
|
|
682
|
+
unitManualMotion.delete(side);
|
|
683
|
+
unitHoldCourse.delete(side);
|
|
684
|
+
unitDirectMove.delete(side);
|
|
685
|
+
unitStance.delete(side);
|
|
686
|
+
unitPatrolLoop.delete(side);
|
|
687
|
+
};
|
|
688
|
+
// A plain move outranks a cast: cancel the hardcast and the channel so the unit walks now. A
|
|
689
|
+
// shift-queued order leaves the cast alone, since motion waits behind it.
|
|
690
|
+
const cancelCastForMove = (unit: Fighter): void => {
|
|
691
|
+
if (unit.castingSpell) {
|
|
692
|
+
unit.castingSpell = null;
|
|
693
|
+
unit.castStartedAt = 0;
|
|
694
|
+
unit.castFinishAt = 0;
|
|
695
|
+
unit.castPushbacks = 0;
|
|
696
|
+
unit.castCost = 0;
|
|
697
|
+
unit.castCostPower = undefined;
|
|
698
|
+
unit.castCritAdd = 0;
|
|
699
|
+
unit.castEffectPct = 0;
|
|
700
|
+
}
|
|
701
|
+
// A channel is a cast bar, not a fire-and-forget aura: dropping the state alone left Blizzard and
|
|
702
|
+
// Drain Life ticking off a caster who had walked away. The teardown takes its auras with it.
|
|
703
|
+
if (unit.channeling) maybeCancelChannelOnMove(unit, channelInterruptCtx(ctx));
|
|
704
|
+
unit.pendingCharge = null;
|
|
705
|
+
};
|
|
706
|
+
/** Hold-ground anchors (cfg.friendlyHoldYd): initial spot / move-order dest / resync. */
|
|
707
|
+
const unitAnchors = new Map<string, CombatPosYd>();
|
|
708
|
+
if (cfg.friendlyHoldYd != null && unitPositionsMutable) {
|
|
709
|
+
for (const [side, p] of Object.entries(unitPositionsMutable)) {
|
|
710
|
+
if (!isEnemySide(side)) unitAnchors.set(side, { x: p.x, y: p.y });
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
(ctx as { unitAnchors?: Map<string, CombatPosYd> }).unitAnchors = unitAnchors;
|
|
714
|
+
// Evade home per enemy side, seeded here for the pack the session was CREATED with. An inject stamps
|
|
715
|
+
// every streamed or summoned body as it places it. Cite: HomeMovementGenerator.cpp:51.
|
|
716
|
+
const enemyHomeYd = new Map<string, CombatPosYd>();
|
|
717
|
+
if (unitPositionsMutable) {
|
|
718
|
+
for (const [side, p] of Object.entries(unitPositionsMutable)) {
|
|
719
|
+
if (isEnemySide(side)) enemyHomeYd.set(side, { ...p });
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
(ctx as { enemyHomeYd?: Map<string, CombatPosYd> }).enemyHomeYd = enemyHomeYd;
|
|
723
|
+
// Pull-sequence spent sets, maintained per event instead of rescanned. Walking the whole log cost
|
|
724
|
+
// O(run length) per unit per tick, and after the first pull every opener stayed spent forever.
|
|
725
|
+
const pullSpentSpells = new Map<Side, Set<number>>();
|
|
726
|
+
const pullSpentItems = new Map<Side, Set<number>>();
|
|
727
|
+
const markPullSpent = (map: Map<Side, Set<number>>, side: Side, id: number): void => {
|
|
728
|
+
let s = map.get(side);
|
|
729
|
+
if (!s) { s = new Set(); map.set(side, s); }
|
|
730
|
+
s.add(id);
|
|
731
|
+
};
|
|
732
|
+
const EMPTY_SPENT: ReadonlySet<number> = new Set();
|
|
733
|
+
const pullSpentSpellsFor = (side: Side): ReadonlySet<number> => pullSpentSpells.get(side) ?? EMPTY_SPENT;
|
|
734
|
+
const pullSpentItemsFor = (side: Side): ReadonlySet<number> => pullSpentItems.get(side) ?? EMPTY_SPENT;
|
|
735
|
+
const clearPullSpent = (): void => { pullSpentSpells.clear(); pullSpentItems.clear(); };
|
|
736
|
+
const clearPullSpentIfIdle = (): void => {
|
|
737
|
+
if (pullSpentSpells.size && livingCombatEnemies().length === 0) clearPullSpent();
|
|
738
|
+
};
|
|
739
|
+
const clearPullSpentFor = (side: Side): void => {
|
|
740
|
+
pullSpentSpells.delete(side);
|
|
741
|
+
pullSpentItems.delete(side);
|
|
742
|
+
};
|
|
743
|
+
|
|
744
|
+
const push = (e: CombatEvent): void => {
|
|
745
|
+
// Anything but a passive tick is combat progress and re-arms the evade timer. `aura` and
|
|
746
|
+
// `resource` are exactly what a stalled fight keeps emitting (measured on Onyxia).
|
|
747
|
+
if (e.kind !== 'aura' && e.kind !== 'resource') lastActionT = e.t ?? t;
|
|
748
|
+
// The scripted-mechanic tally, counted BEFORE the ring can trim. A raid emits ~250 events a second,
|
|
749
|
+
// so a 100 s boss keeps only its last ~30 s and every phase transition was pushed then dropped.
|
|
750
|
+
if (MECHANIC_EVENT_KINDS.has(e.kind)) {
|
|
751
|
+
const slot = mechanicTally.get(e.kind);
|
|
752
|
+
const at = e.t ?? t;
|
|
753
|
+
if (slot) {
|
|
754
|
+
slot.count += 1;
|
|
755
|
+
slot.lastT = at;
|
|
756
|
+
} else {
|
|
757
|
+
mechanicTally.set(e.kind, { count: 1, firstT: at, lastT: at });
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
// Damage taken and the death roll, tallied here for the same reason: `events` is a tail, so a long
|
|
761
|
+
// fight's columns cover only its end. Every damage row the engine pushes sets a target.
|
|
762
|
+
if (e.kind === 'melee' || e.kind === 'spell_hit' || e.kind === 'dot_tick') {
|
|
763
|
+
if (Number.isFinite(e.amount) && e.amount > 0) {
|
|
764
|
+
const victim: string = e.target ?? UNATTRIBUTED_VICTIM;
|
|
765
|
+
damageTakenBySide.set(victim, (damageTakenBySide.get(victim) ?? 0) + e.amount);
|
|
766
|
+
if (e.target !== undefined) {
|
|
767
|
+
// Mutated in place, not re-allocated: this runs on every damage row and the map is
|
|
768
|
+
// bounded by the side count, not by the fight's length.
|
|
769
|
+
const blowName = e.kind === 'melee' ? (e.name ?? 'melee') : e.name;
|
|
770
|
+
const prev = lastHitBySide.get(victim);
|
|
771
|
+
if (prev) { prev.by = e.actor; prev.name = blowName; }
|
|
772
|
+
else lastHitBySide.set(victim, { by: e.actor, name: blowName });
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
} else if (e.kind === 'death') {
|
|
776
|
+
deathsTotal += 1;
|
|
777
|
+
if (deathRoll.length < MAX_TRACKED_DEATHS) {
|
|
778
|
+
const blow = lastHitBySide.get(e.actor);
|
|
779
|
+
deathRoll.push({
|
|
780
|
+
side: e.actor,
|
|
781
|
+
t: e.t ?? t,
|
|
782
|
+
...(blow ? { by: blow.by, cause: blow.name } : {}),
|
|
783
|
+
});
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
// Same kinds the two rescans matched. The aura row is the ally contract (KI-631):
|
|
787
|
+
// self-buff openers emit only `aura`, and without it the opener waits on a spent cast.
|
|
788
|
+
if (e.kind === 'cast_start' || e.kind === 'cast_finish' || e.kind === 'spell_hit') {
|
|
789
|
+
markPullSpent(pullSpentSpells, e.actor, e.spellId);
|
|
790
|
+
} else if (e.kind === 'aura' && e.applied !== false && e.spellId != null) {
|
|
791
|
+
markPullSpent(pullSpentSpells, e.actor, e.spellId);
|
|
792
|
+
} else if (e.kind === 'item_use') {
|
|
793
|
+
markPullSpent(pullSpentItems, e.actor, e.entry);
|
|
794
|
+
}
|
|
795
|
+
events.push(e);
|
|
796
|
+
};
|
|
797
|
+
ctx.pullSpentSpellsFor = pullSpentSpellsFor;
|
|
798
|
+
ctx.pullSpentItemsFor = pullSpentItemsFor;
|
|
799
|
+
ctx.clearPullSpentIfIdle = clearPullSpentIfIdle;
|
|
800
|
+
ctx.clearPullSpentFor = clearPullSpentFor;
|
|
801
|
+
ctx.push = push;
|
|
802
|
+
ctx.mechanicTally = mechanicTally;
|
|
803
|
+
ctx.damageTakenBySide = damageTakenBySide;
|
|
804
|
+
ctx.deathRoll = deathRoll;
|
|
805
|
+
ctx.deathsTotalOf = () => deathsTotal;
|
|
806
|
+
ctx.noteTotemOwner = (side, owner, name) => {
|
|
807
|
+
totemOwnerMeta.set(side, { ownerSide: owner, name });
|
|
808
|
+
};
|
|
809
|
+
/** Clamp HP to a finite non-negative integer (keeps display and death checks aligned). */
|
|
810
|
+
const sanitizeHp = (f: Fighter): void => {
|
|
811
|
+
if (!Number.isFinite(f.hp) || f.hp <= 0) f.hp = 0;
|
|
812
|
+
else f.hp = Math.round(f.hp);
|
|
813
|
+
};
|
|
814
|
+
|
|
815
|
+
// ---- sessionFocus (P4) — after pools; inject/eventAi consume focus helpers ----
|
|
816
|
+
installSessionFocus(ctx, { dead });
|
|
817
|
+
const ensurePlayerFocus = ctx.ensurePlayerFocus!;
|
|
818
|
+
const foeOf = ctx.foeOf!;
|
|
819
|
+
const hostileTargetOf = ctx.hostileTargetOf!;
|
|
820
|
+
const nearestPartyInAcquire = ctx.nearestPartyInAcquire!;
|
|
821
|
+
const fighterByThreatId = ctx.fighterByThreatId!;
|
|
822
|
+
const resolveEventAiTargetFighter = ctx.resolveEventAiTargetFighter!;
|
|
823
|
+
|
|
824
|
+
// ---- sessionInject (P1) — before eventAi (spawn/summon deps) --------------
|
|
825
|
+
installSessionInject(ctx, {
|
|
826
|
+
dead,
|
|
827
|
+
nearestPartyInAcquire,
|
|
828
|
+
hostileTargetOf,
|
|
829
|
+
materializeSpecForCombat,
|
|
830
|
+
});
|
|
831
|
+
const injectEnemySpec = ctx.injectEnemySpec!;
|
|
832
|
+
const injectEnemySummon = ctx.injectEnemySummon!;
|
|
833
|
+
const forceDespawnEnemy = ctx.forceDespawnEnemy!;
|
|
834
|
+
const runScriptDevAggroLifecycle = ctx.runScriptDevAggroLifecycle!;
|
|
835
|
+
|
|
836
|
+
// ---- eventAi hub (S32 peel → eventAi.ts) ----------------------------------
|
|
837
|
+
installEventAi(ctx, {
|
|
838
|
+
injectEnemySummon,
|
|
839
|
+
forceDespawnEnemy,
|
|
840
|
+
resolveEventAiTargetFighter,
|
|
841
|
+
removeAurasDueToSpell: (unit, spellId) => ctx.removeAurasDueToSpell!(unit, spellId),
|
|
842
|
+
dead,
|
|
843
|
+
hostileTargetOf,
|
|
844
|
+
breakCharm: (f) => ctx.breakCharm!(f),
|
|
845
|
+
});
|
|
846
|
+
// resolveEventAiCastTarget already hung by installSessionFocus.
|
|
847
|
+
|
|
848
|
+
for (const ef of enemies) {
|
|
849
|
+
const acts = initialResetActions.get(ef.side as EnemySide);
|
|
850
|
+
if (acts && acts.length > 0) {
|
|
851
|
+
ctx.applyScriptDevCombatActions?.(ef, acts);
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
// Damage dealt TO an enemy generates threat for the dealer on THAT enemy's list, DoTs included.
|
|
856
|
+
// Cite: Unit.cpp:1207–1208 threat = damage * GetSpellThreatMultiplier; victim->AddThreat.
|
|
857
|
+
installThreatOps(ctx);
|
|
858
|
+
|
|
859
|
+
const finalize = (): void => {
|
|
860
|
+
if (winner === null) {
|
|
861
|
+
if (partyWiped()) winner = 'enemy';
|
|
862
|
+
else if (allEnemiesDead()) winner = 'player';
|
|
863
|
+
else winner = 'timeout';
|
|
864
|
+
}
|
|
865
|
+
// The clock stops when the fight ends, so a channel armed on the killing blow read as running
|
|
866
|
+
// forever and the frame kept drawing its bar. Its auras come off with it, as on any cancel.
|
|
867
|
+
for (const f of ctx.allSeats()) {
|
|
868
|
+
if (f.channeling) interruptChannel(f, channelInterruptCtx(ctx), { quiet: true });
|
|
869
|
+
}
|
|
870
|
+
if (cfg.duel) duelComplete();
|
|
871
|
+
};
|
|
872
|
+
|
|
873
|
+
// Player::DuelComplete — each duelist loses every NEGATIVE aura the other applied, and combo points
|
|
874
|
+
// on the opponent clear. A cross-side aura IS a negative one, since a duelist cannot help the other.
|
|
875
|
+
const duelComplete = (): void => {
|
|
876
|
+
const strip: Array<{ side: Side; spellId: number }> = [];
|
|
877
|
+
for (const e of ctx.effects) {
|
|
878
|
+
if (e.spellId == null) continue;
|
|
879
|
+
if (isEnemySide(e.source) === isEnemySide(e.target)) continue;
|
|
880
|
+
strip.push({ side: e.target, spellId: e.spellId | 0 });
|
|
881
|
+
}
|
|
882
|
+
for (const s of strip) {
|
|
883
|
+
const f = ctx.sideFighter?.(s.side);
|
|
884
|
+
if (f) ctx.removeAurasDueToSpell!(f, s.spellId);
|
|
885
|
+
}
|
|
886
|
+
// Cite: Player.cpp:6979-6989 — points held on the opponent (or their pet) are cleared.
|
|
887
|
+
for (const f of ctx.allSeats()) {
|
|
888
|
+
if (f.comboTarget == null) continue;
|
|
889
|
+
if (isEnemySide(f.comboTarget) === isEnemySide(f.side)) continue;
|
|
890
|
+
f.comboTarget = null;
|
|
891
|
+
f.comboPoints = 0;
|
|
892
|
+
}
|
|
893
|
+
};
|
|
894
|
+
|
|
895
|
+
const removeFromAllThreat = (id: CombatantId): void => ctx.removeFromAllThreat!(id);
|
|
896
|
+
const clearAllThreat = (): void => ctx.clearAllThreat!();
|
|
897
|
+
|
|
898
|
+
// ---- sessionDeaths (P5) — after inject / eventAi / threatOps ----
|
|
899
|
+
installSessionDeaths(ctx, { dead, sanitizeHp, clearManualMotion });
|
|
900
|
+
const unsummonTotem = ctx.unsummonTotem!;
|
|
901
|
+
const checkDeaths = ctx.checkDeaths!;
|
|
902
|
+
|
|
903
|
+
// ---- powerForm (P6) — hang spend/form/stealth/regen/combo/rollHeal before applySpell ----
|
|
904
|
+
installPowerForm(ctx);
|
|
905
|
+
const rollHeal = ctx.rollHeal!;
|
|
906
|
+
const spend = ctx.spend!;
|
|
907
|
+
const spellCost = ctx.spellCost!;
|
|
908
|
+
const canAffordSpell = ctx.canAffordSpell!;
|
|
909
|
+
const leaveForm = ctx.leaveForm!;
|
|
910
|
+
const enterForm = ctx.enterForm!;
|
|
911
|
+
const applyStealth = ctx.applyStealth!;
|
|
912
|
+
const breakStealth = ctx.breakStealth!;
|
|
913
|
+
const maybeBreakStealthOn = ctx.maybeBreakStealthOn!;
|
|
914
|
+
const behindOk = ctx.behindOk!;
|
|
915
|
+
// Generic RemoveAurasWithInterruptFlags — needs powerForm's stealth break, so it installs here.
|
|
916
|
+
installInterruptAuras(ctx, { maybeBreakStealthOn });
|
|
917
|
+
const gainRage = ctx.gainRage!;
|
|
918
|
+
const regenRage = ctx.regenRage!;
|
|
919
|
+
const regenEnergy = ctx.regenEnergy!;
|
|
920
|
+
const regenMana = ctx.regenMana!;
|
|
921
|
+
const regenFocus = ctx.regenFocus!;
|
|
922
|
+
const regenHealth = ctx.regenHealth!;
|
|
923
|
+
const addComboPoints = ctx.addComboPoints!;
|
|
924
|
+
const clearComboPoints = ctx.clearComboPoints!;
|
|
925
|
+
const comboOn = ctx.comboOn!;
|
|
926
|
+
const scaleDurationByCp = ctx.scaleDurationByCp!;
|
|
927
|
+
|
|
928
|
+
// S30 — CC + DR + onDamaged + grantImmunityAura; KI-086 OOC school_immunity seed.
|
|
929
|
+
installCrowdControl(ctx);
|
|
930
|
+
// installCrowdControl just assigned grantImmunityAura on ctx.
|
|
931
|
+
const grantImm = ctx.grantImmunityAura;
|
|
932
|
+
if (grantImm) {
|
|
933
|
+
// The party, from the store - the same list, memoized, and no `if (f)` for a pet that
|
|
934
|
+
// `partyUnits()` only includes when there is one. Cite: ENGINE-REBUILD E1.1.
|
|
935
|
+
for (const f of ctx.partyUnits()) seedFighterSchoolImmunities(f, grantImm);
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
// ---- procsDamage hub (Wave 4 peel → procsDamage.ts) ----
|
|
939
|
+
installProcsDamage(ctx, {
|
|
940
|
+
dead,
|
|
941
|
+
gainRage,
|
|
942
|
+
});
|
|
943
|
+
// ---- applySpell hub (Wave 4 peel → applySpell.ts) ----
|
|
944
|
+
installApplySpell(ctx, {
|
|
945
|
+
dead,
|
|
946
|
+
sanitizeHp,
|
|
947
|
+
spend,
|
|
948
|
+
gainRage,
|
|
949
|
+
addComboPoints,
|
|
950
|
+
clearComboPoints,
|
|
951
|
+
comboOn,
|
|
952
|
+
scaleDurationByCp,
|
|
953
|
+
spellCost,
|
|
954
|
+
canAffordSpell,
|
|
955
|
+
leaveForm,
|
|
956
|
+
enterForm,
|
|
957
|
+
applyStealth,
|
|
958
|
+
breakStealth,
|
|
959
|
+
maybeBreakStealthOn,
|
|
960
|
+
behindOk,
|
|
961
|
+
foeOf,
|
|
962
|
+
ensurePlayerFocus,
|
|
963
|
+
lowestHpFriendly,
|
|
964
|
+
isControlled: (f) => (ctx.isControlled as (f: Fighter) => boolean)(f),
|
|
965
|
+
isControlledHard: (f) => (ctx.isControlledHard as (f: Fighter) => boolean)(f),
|
|
966
|
+
isWeaponAction: (s) => (ctx.isWeaponAction as (s: SpellDef) => boolean)(s),
|
|
967
|
+
isBreaker: (s, f) => (ctx.isBreaker as (s: SpellDef, f?: Fighter) => boolean)(s, f),
|
|
968
|
+
spellTemplateImmune: (tgt, spell) => (ctx.spellTemplateImmune as (tgt: Fighter, spell: SpellDef) => boolean)(tgt, spell),
|
|
969
|
+
ccImmune: (tgt, mechanic, onlyTypes) => ctx.ccImmune!(tgt, mechanic, onlyTypes),
|
|
970
|
+
applyDr: (src, tgt, mechanic, baseMs) => ctx.applyDr!(src, tgt, mechanic, baseMs),
|
|
971
|
+
applyControl: (src, tgt, spell, eff, durMs, heartbeat) => ctx.applyControl!(src, tgt, spell, eff, durMs, heartbeat),
|
|
972
|
+
breakControl: (f, control) => ctx.breakControl!(f, control),
|
|
973
|
+
onDamaged: (f, amount, damageType, fromSpellId, info) => ctx.onDamaged!(f, amount, damageType, fromSpellId, info),
|
|
974
|
+
rollHeal,
|
|
975
|
+
unsummonTotem,
|
|
976
|
+
checkDeaths,
|
|
977
|
+
finalize,
|
|
978
|
+
seedDamageShields,
|
|
979
|
+
seedProcTriggers,
|
|
980
|
+
seedSchoolAbsorbs,
|
|
981
|
+
seedManaShields,
|
|
982
|
+
seedRestoreHealth,
|
|
983
|
+
seedDisplayAuras,
|
|
984
|
+
sealRighteousnessSwing,
|
|
985
|
+
materializeSpecForCombat,
|
|
986
|
+
});
|
|
987
|
+
// ---- aoeCast (P7) / itemUse (P8) — after applySpell ----
|
|
988
|
+
installAoeCast(ctx);
|
|
989
|
+
installItemUse(ctx);
|
|
990
|
+
const castOnTargets = ctx.castOnTargets!;
|
|
991
|
+
const useItem = ctx.useItem!;
|
|
992
|
+
|
|
993
|
+
// Weapon temp-enchant combat procs — Windfury, Flametongue, poisons — and Rockbiter's flat damage.
|
|
994
|
+
// RNG is drawn only when a chance or PPM imbue is present, so a no-imbue fight stays deterministic.
|
|
995
|
+
installWeapon(ctx, {
|
|
996
|
+
dead,
|
|
997
|
+
sanitizeHp,
|
|
998
|
+
spend,
|
|
999
|
+
gainRage,
|
|
1000
|
+
addComboPoints,
|
|
1001
|
+
clearComboPoints,
|
|
1002
|
+
comboOn,
|
|
1003
|
+
onDamaged: (f, amount, damageType, fromSpellId, info) => ctx.onDamaged!(f, amount, damageType, fromSpellId, info),
|
|
1004
|
+
dealDamage: (...args: any[]) => (ctx.dealDamage as Function)(...args),
|
|
1005
|
+
applySpell: (...args: any[]) => (ctx.applySpell as Function)(...args),
|
|
1006
|
+
triggerProcs: (...args: any[]) => (ctx.triggerProcs as Function)(...args),
|
|
1007
|
+
triggerTakenProcs: (...args: any[]) => (ctx.triggerTakenProcs as Function)(...args),
|
|
1008
|
+
rollGearProcs: (...args: any[]) => (ctx.rollGearProcs as Function)(...args),
|
|
1009
|
+
fireDamageShields: (...args: any[]) => (ctx.fireDamageShields as Function)(...args),
|
|
1010
|
+
fireDealMeleeAura42: (...args: any[]) => (ctx.fireDealMeleeAura42 as Function)(...args),
|
|
1011
|
+
breakStealth,
|
|
1012
|
+
maybeBreakStealthOn,
|
|
1013
|
+
sealRighteousnessSwing,
|
|
1014
|
+
});
|
|
1015
|
+
// ---- pickAction hub (Wave 4 peel → pickAction.ts) ----
|
|
1016
|
+
installPickAction(ctx, {
|
|
1017
|
+
dead,
|
|
1018
|
+
isControlled: (f) => (ctx.isControlled as (f: Fighter) => boolean)(f),
|
|
1019
|
+
isControlledHard: (f) => (ctx.isControlledHard as (f: Fighter) => boolean)(f),
|
|
1020
|
+
isWeaponAction: (s) => (ctx.isWeaponAction as (s: SpellDef) => boolean)(s),
|
|
1021
|
+
isBreaker: (s, f) => (ctx.isBreaker as (s: SpellDef, f?: Fighter) => boolean)(s, f),
|
|
1022
|
+
ccImmune: (tgt, mechanic, onlyTypes) => ctx.ccImmune!(tgt, mechanic, onlyTypes),
|
|
1023
|
+
spellTemplateImmune: (tgt, spell) => (ctx.spellTemplateImmune as (tgt: Fighter, spell: SpellDef) => boolean)(tgt, spell),
|
|
1024
|
+
canAffordSpell,
|
|
1025
|
+
spellCost,
|
|
1026
|
+
comboOn,
|
|
1027
|
+
foeOf,
|
|
1028
|
+
ensurePlayerFocus,
|
|
1029
|
+
lowestHpFriendly,
|
|
1030
|
+
});
|
|
1031
|
+
// Probe tap (D): record the pick without a second call — pickAction spends items / EventAI.
|
|
1032
|
+
{
|
|
1033
|
+
const rawPick = ctx.pickAction;
|
|
1034
|
+
if (rawPick) {
|
|
1035
|
+
ctx.pickAction = (f, foe) => {
|
|
1036
|
+
const r = rawPick(f, foe);
|
|
1037
|
+
const tap = (globalThis as { __pcPickTap?: (side: string, id: number) => void }).__pcPickTap;
|
|
1038
|
+
if (tap) tap(f.side, r?.id ?? 0);
|
|
1039
|
+
return r;
|
|
1040
|
+
};
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
// sim.pickAction: 37.7% of JS on the 2026-08-18 MC capture and invisible inside combat.sim.
|
|
1044
|
+
if (ctx.pickAction) {
|
|
1045
|
+
const innerPick = ctx.pickAction;
|
|
1046
|
+
ctx.pickAction = (f, foe) => combatPerf('sim.pickAction', () => innerPick(f, foe));
|
|
1047
|
+
}
|
|
1048
|
+
// ---- stepSession hub (Wave 4 peel → stepSession.ts; P11 ring+evade wrap inside) ----
|
|
1049
|
+
installStepSession(ctx, {
|
|
1050
|
+
dead,
|
|
1051
|
+
sanitizeHp,
|
|
1052
|
+
spend,
|
|
1053
|
+
gainRage,
|
|
1054
|
+
addComboPoints,
|
|
1055
|
+
clearComboPoints,
|
|
1056
|
+
comboOn,
|
|
1057
|
+
foeOf,
|
|
1058
|
+
ensurePlayerFocus,
|
|
1059
|
+
hostileTargetOf,
|
|
1060
|
+
isWeaponAction: (s) => (ctx.isWeaponAction as (s: SpellDef) => boolean)(s),
|
|
1061
|
+
isBreaker: (s, f) => (ctx.isBreaker as (s: SpellDef, f?: Fighter) => boolean)(s, f),
|
|
1062
|
+
spellCost,
|
|
1063
|
+
allySnapshot: (...args: any[]) => (ctx.allySnapshot as Function)(...args),
|
|
1064
|
+
castOnTargets,
|
|
1065
|
+
useItem,
|
|
1066
|
+
enterForm,
|
|
1067
|
+
applyStealth,
|
|
1068
|
+
maybeBreakStealthOn,
|
|
1069
|
+
checkDeaths,
|
|
1070
|
+
finalize,
|
|
1071
|
+
unsummonTotem,
|
|
1072
|
+
preferFocusHostile,
|
|
1073
|
+
allEnemiesDead,
|
|
1074
|
+
regenRage,
|
|
1075
|
+
regenEnergy,
|
|
1076
|
+
regenMana,
|
|
1077
|
+
regenFocus,
|
|
1078
|
+
regenHealth,
|
|
1079
|
+
});
|
|
1080
|
+
// Typed rather than spread through `any`: `tick(action, inputs)` carries the per-tick game-layer
|
|
1081
|
+
// values (S6b mechanism 2), and an `any[]` forwarder let a caller drop them with no complaint.
|
|
1082
|
+
const step = (action?: CombatAction, inputs?: SimTickInputs): CombatEvent[] => {
|
|
1083
|
+
const run = ctx.step;
|
|
1084
|
+
if (!run) throw new Error('createSession: step wrapper not installed');
|
|
1085
|
+
return run(action, inputs);
|
|
1086
|
+
};
|
|
1087
|
+
|
|
1088
|
+
// ---- sessionView (P9) ----
|
|
1089
|
+
const sessionView = buildSessionView(ctx);
|
|
1090
|
+
const { memoView, invalidateViewMemo, result, enemiesView, alliesView } = sessionView;
|
|
1091
|
+
|
|
1092
|
+
// Wave 4 peel fix-up: peels reference these via ctx.
|
|
1093
|
+
ctx.behindOk = behindOk;
|
|
1094
|
+
ctx.fighterByThreatId = fighterByThreatId;
|
|
1095
|
+
|
|
1096
|
+
// Fire on-equip procs (trigger=1) once hub methods (incl. rollGearProcs) are on ctx.
|
|
1097
|
+
(ctx.rollGearProcs as Function)(player, enemy, 'equip');
|
|
1098
|
+
for (const ef of enemies) (ctx.rollGearProcs as Function)(ef, player, 'equip');
|
|
1099
|
+
if (pet) (ctx.rollGearProcs as Function)(pet, enemy, 'equip');
|
|
1100
|
+
for (const al of allies) (ctx.rollGearProcs as Function)(al, enemy, 'equip');
|
|
1101
|
+
|
|
1102
|
+
// Every enemy walks in wearing its out-of-combat self-casts: those rows fire on the first idle tick
|
|
1103
|
+
// after spawn and are refused once combat is open, so a session can only ever inherit them.
|
|
1104
|
+
for (const ef of enemies) {
|
|
1105
|
+
for (const sp of enemyCastBySide.get(ef.side as EnemySide)?.spawnAuras ?? []) {
|
|
1106
|
+
ctx.applySpell?.(ef, ef, sp);
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
// Detect-on-pull only when the encounter distance is set; absent is the opener window. EVERY
|
|
1111
|
+
// stealthed body is checked, not only the player — party rogues now arrive stealthed too.
|
|
1112
|
+
if (cfg.encounterDistanceYd != null) {
|
|
1113
|
+
const stealthStrengthOf = (f: Fighter) =>
|
|
1114
|
+
totalStealthStrength(f.stealthAuraStrength, f.stealthLevelBonus);
|
|
1115
|
+
for (const sneaker of [player, ...allies]) {
|
|
1116
|
+
if (!sneaker.stealthed) continue;
|
|
1117
|
+
for (const ef of enemies) {
|
|
1118
|
+
const dist = distanceBetween(sneaker, ef) ?? cfg.encounterDistanceYd;
|
|
1119
|
+
if (dist == null) continue;
|
|
1120
|
+
if (isDetectedAtDistance(
|
|
1121
|
+
dist,
|
|
1122
|
+
ef.spec.level,
|
|
1123
|
+
stealthStrengthOf(sneaker),
|
|
1124
|
+
ef.stealthDetectStrength,
|
|
1125
|
+
false,
|
|
1126
|
+
)) {
|
|
1127
|
+
breakStealth(sneaker, 'detected');
|
|
1128
|
+
break;
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
// ---- sessionApi (P10) ----
|
|
1135
|
+
return buildCombatSessionApi(ctx, {
|
|
1136
|
+
dead,
|
|
1137
|
+
step: step as any,
|
|
1138
|
+
result,
|
|
1139
|
+
memoView,
|
|
1140
|
+
invalidateViewMemo,
|
|
1141
|
+
enemiesView,
|
|
1142
|
+
alliesView,
|
|
1143
|
+
finalize,
|
|
1144
|
+
totemOwnerMeta,
|
|
1145
|
+
unitMoveOrders,
|
|
1146
|
+
unitMoveIntents,
|
|
1147
|
+
unitManualMotion,
|
|
1148
|
+
unitStance,
|
|
1149
|
+
unitPatrolLoop,
|
|
1150
|
+
unitAnchors,
|
|
1151
|
+
unitCastOrders,
|
|
1152
|
+
clearManualMotion,
|
|
1153
|
+
cancelCastForMove,
|
|
1154
|
+
});
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
export function simulate(
|
|
1158
|
+
playerSpec: CombatantSpec,
|
|
1159
|
+
enemySpec: CombatantSpec | CombatantSpec[],
|
|
1160
|
+
rng: Rng,
|
|
1161
|
+
cfg: CombatConfig = {},
|
|
1162
|
+
): CombatResult {
|
|
1163
|
+
return createSession(playerSpec, enemySpec, rng, cfg).runToEnd();
|
|
1164
|
+
}
|
|
1165
|
+
|