azeroth-shell 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +138 -2
- package/bin/azsh.mjs +19 -0
- package/docs/aifid/eventai-texts.json +4414 -0
- package/docs/dungeons/atlas/atlas-rows-dungeons.json +8989 -0
- package/docs/dungeons/survey/pack-id-migration.json +2418 -0
- package/package.json +26 -4
- package/src/autopilot/bossSkip.ts +26 -0
- package/src/autopilot/combatRotation.ts +824 -0
- package/src/autopilot/config.ts +533 -0
- package/src/autopilot/consumables.ts +58 -0
- package/src/autopilot/encounterStand.ts +121 -0
- package/src/autopilot/objectGatePlan.ts +53 -0
- package/src/autopilot/pathEngage.ts +229 -0
- package/src/autopilot/plan.ts +1643 -0
- package/src/autopilot/questPolicy.ts +274 -0
- package/src/autopilot/routeStep.ts +69 -0
- package/src/autopilot/sessionReport.ts +88 -0
- package/src/autopilot/topOffPower.ts +26 -0
- package/src/autopilot/view.ts +476 -0
- package/src/cli/bootCheck.mjs +34 -0
- package/src/cli/bootData.mjs +57 -0
- package/src/cli/combatLogFilter.ts +153 -0
- package/src/cli/combatLogFormat.ts +613 -0
- package/src/cli/fileRepository.mjs +105 -0
- package/src/cli/gzLoader.mjs +31 -0
- package/src/cli/liveGauntletEscort.ts +978 -0
- package/src/cli/liveStratUndead.ts +305 -0
- package/src/cli/macroHostCombat.ts +545 -0
- package/src/cli/macroHostWorld.ts +330 -0
- package/src/cli/prefs.mjs +31 -0
- package/src/cli/register.mjs +4 -0
- package/src/cli/repl.mjs +693 -0
- package/src/cli/rotationText.mjs +226 -0
- package/src/cli/squad.mjs +105 -0
- package/src/cli/term.mjs +126 -0
- package/src/cli/text.mjs +122 -0
- package/src/cli/tui.mjs +626 -0
- package/src/cli/tuiArena.mjs +109 -0
- package/src/cli/tuiAutopilotSettings.mjs +93 -0
- package/src/cli/tuiBags.mjs +180 -0
- package/src/cli/tuiBars.mjs +133 -0
- package/src/cli/tuiBg.mjs +243 -0
- package/src/cli/tuiBulk.mjs +162 -0
- package/src/cli/tuiCheck.mjs +1882 -0
- package/src/cli/tuiCity.mjs +396 -0
- package/src/cli/tuiCompanionKit.mjs +300 -0
- package/src/cli/tuiCompanions.mjs +471 -0
- package/src/cli/tuiDev.mjs +62 -0
- package/src/cli/tuiDungeon.mjs +606 -0
- package/src/cli/tuiEnchantTarget.mjs +120 -0
- package/src/cli/tuiFight.mjs +577 -0
- package/src/cli/tuiFightPanes.mjs +228 -0
- package/src/cli/tuiFloor.mjs +506 -0
- package/src/cli/tuiGear.mjs +158 -0
- package/src/cli/tuiHonor.mjs +74 -0
- package/src/cli/tuiLoot.mjs +97 -0
- package/src/cli/tuiMacros.mjs +144 -0
- package/src/cli/tuiOutfitter.mjs +115 -0
- package/src/cli/tuiPet.mjs +116 -0
- package/src/cli/tuiProfessions.mjs +800 -0
- package/src/cli/tuiRaid.mjs +127 -0
- package/src/cli/tuiRep.mjs +65 -0
- package/src/cli/tuiSaves.mjs +69 -0
- package/src/cli/tuiSearch.mjs +363 -0
- package/src/cli/tuiSettings.mjs +151 -0
- package/src/cli/tuiSimLab.mjs +80 -0
- package/src/cli/tuiTalents.mjs +60 -0
- package/src/cli/tuiViews.mjs +1178 -0
- package/src/cli/tuiWiki.mjs +181 -0
- package/src/cli/tuiWorld.mjs +361 -0
- package/src/cli/wikiIndex.ts +259 -0
- package/src/cli/wikiSources.ts +355 -0
- package/src/data/alteracValley.ts +176 -0
- package/src/data/appearanceOptions.ts +534 -0
- package/src/data/applyLootChanceSanity.ts +21 -0
- package/src/data/createFlavor.ts +96 -0
- package/src/data/customItems.ts +33 -0
- package/src/data/dataStore.ts +1904 -0
- package/src/data/dungeonGameObjects.ts +84 -0
- package/src/data/dungeonPlateCover.ts +66 -0
- package/src/data/dungeonSpawns.ts +294 -0
- package/src/data/dungeons/alterac-valley.ts +135 -0
- package/src/data/dungeons/apply-dungeon-rooms.ts +29 -0
- package/src/data/dungeons/apply-encounter-outdoors.ts +72 -0
- package/src/data/dungeons/apply-instance-geometry.ts +65 -0
- package/src/data/dungeons/apply-spawn-placement.ts +171 -0
- package/src/data/dungeons/blackfathom-deeps.ts +539 -0
- package/src/data/dungeons/blackrock-depths.ts +1410 -0
- package/src/data/dungeons/blackwing-lair.ts +129 -0
- package/src/data/dungeons/boss-eventai.ts +8 -0
- package/src/data/dungeons/deadmines.ts +410 -0
- package/src/data/dungeons/dire-maul-east.ts +980 -0
- package/src/data/dungeons/dire-maul-north.ts +661 -0
- package/src/data/dungeons/dire-maul-west.ts +785 -0
- package/src/data/dungeons/dungeon-rooms.json +5479 -0
- package/src/data/dungeons/encounter-outdoors.json +359 -0
- package/src/data/dungeons/eventai/blackfathom-deeps.ts +308 -0
- package/src/data/dungeons/eventai/blackrock-depths.ts +1257 -0
- package/src/data/dungeons/eventai/deadmines.ts +264 -0
- package/src/data/dungeons/eventai/dire-maul-east.ts +320 -0
- package/src/data/dungeons/eventai/gnomeregan.ts +213 -0
- package/src/data/dungeons/eventai/helpers.ts +79 -0
- package/src/data/dungeons/eventai/index.ts +133 -0
- package/src/data/dungeons/eventai/lbrs.ts +372 -0
- package/src/data/dungeons/eventai/maraudon.ts +323 -0
- package/src/data/dungeons/eventai/ragefire-chasm.ts +108 -0
- package/src/data/dungeons/eventai/razorfen-downs.ts +207 -0
- package/src/data/dungeons/eventai/razorfen-kraul.ts +326 -0
- package/src/data/dungeons/eventai/scholomance.ts +680 -0
- package/src/data/dungeons/eventai/shadowfang-keep.ts +230 -0
- package/src/data/dungeons/eventai/sm-cathedral.ts +75 -0
- package/src/data/dungeons/eventai/sm-graveyard.ts +150 -0
- package/src/data/dungeons/eventai/sm-library.ts +55 -0
- package/src/data/dungeons/eventai/stratholme-undead.ts +127 -0
- package/src/data/dungeons/eventai/sunken-temple.ts +582 -0
- package/src/data/dungeons/eventai/the-stockade.ts +228 -0
- package/src/data/dungeons/eventai/ubrs.ts +225 -0
- package/src/data/dungeons/eventai/uldaman.ts +355 -0
- package/src/data/dungeons/eventai/wailing-caverns.ts +396 -0
- package/src/data/dungeons/eventai/zul-farrak.ts +591 -0
- package/src/data/dungeons/gnomeregan.ts +909 -0
- package/src/data/dungeons/index.ts +126 -0
- package/src/data/dungeons/instance-geometry.json +5233 -0
- package/src/data/dungeons/key-sources.json +169 -0
- package/src/data/dungeons/lbrs.ts +996 -0
- package/src/data/dungeons/maraudon.ts +786 -0
- package/src/data/dungeons/molten-core.ts +194 -0
- package/src/data/dungeons/naxxramas.ts +173 -0
- package/src/data/dungeons/onyxias-lair.ts +71 -0
- package/src/data/dungeons/pack-from-clusters.ts +1256 -0
- package/src/data/dungeons/ragefire-chasm.ts +238 -0
- package/src/data/dungeons/razorfen-downs.ts +698 -0
- package/src/data/dungeons/razorfen-kraul.ts +766 -0
- package/src/data/dungeons/ruins-of-ahnqiraj.ts +121 -0
- package/src/data/dungeons/scholomance.ts +1076 -0
- package/src/data/dungeons/shadowfang-keep.ts +740 -0
- package/src/data/dungeons/sm-armory.ts +266 -0
- package/src/data/dungeons/sm-cathedral.ts +430 -0
- package/src/data/dungeons/sm-graveyard.ts +482 -0
- package/src/data/dungeons/sm-library.ts +337 -0
- package/src/data/dungeons/stratholme-live.ts +808 -0
- package/src/data/dungeons/stratholme-undead.ts +1164 -0
- package/src/data/dungeons/sunken-temple.ts +931 -0
- package/src/data/dungeons/temple-of-ahnqiraj.ts +119 -0
- package/src/data/dungeons/the-stockade.ts +282 -0
- package/src/data/dungeons/ubrs.ts +1286 -0
- package/src/data/dungeons/uldaman.ts +685 -0
- package/src/data/dungeons/wailing-caverns.ts +356 -0
- package/src/data/dungeons/zul-farrak.ts +686 -0
- package/src/data/dungeons/zul-gurub.ts +142 -0
- package/src/data/dungeons.ts +42 -0
- package/src/data/fixtures.ts +446 -0
- package/src/data/gameobject-data.json.gz +0 -0
- package/src/data/item-data.json.gz +0 -0
- package/src/data/loot-data.json.gz +0 -0
- package/src/data/maps/atlas-approach-maps.json +1425 -0
- package/src/data/maps/atlas-landmarks.json +1711 -0
- package/src/data/maps/dungeon-viewports.json +392 -0
- package/src/data/materializeModDamagePercentTaken.ts +70 -0
- package/src/data/materializeModHealthRegen.ts +89 -0
- package/src/data/materializeSpellReputation.ts +26 -0
- package/src/data/pageTexts.ts +19 -0
- package/src/data/profession-data.json.gz +0 -0
- package/src/data/raid-plans/blackfathom-deeps.json +86 -0
- package/src/data/raid-plans/blackrock-depths.json +419 -0
- package/src/data/raid-plans/blackwing-lair.json +328 -0
- package/src/data/raid-plans/deadmines.json +55 -0
- package/src/data/raid-plans/dire-maul-east.json +94 -0
- package/src/data/raid-plans/dire-maul-north.json +70 -0
- package/src/data/raid-plans/dire-maul-west.json +75 -0
- package/src/data/raid-plans/gnomeregan.json +131 -0
- package/src/data/raid-plans/lbrs.json +176 -0
- package/src/data/raid-plans/maraudon.json +102 -0
- package/src/data/raid-plans/molten-core.json +169 -0
- package/src/data/raid-plans/naxxramas.json +354 -0
- package/src/data/raid-plans/onyxias-lair.json +18 -0
- package/src/data/raid-plans/ragefire-chasm.json +30 -0
- package/src/data/raid-plans/razorfen-downs.json +75 -0
- package/src/data/raid-plans/razorfen-kraul.json +53 -0
- package/src/data/raid-plans/ruins-of-ahnqiraj.json +110 -0
- package/src/data/raid-plans/scholomance.json +170 -0
- package/src/data/raid-plans/shadowfang-keep.json +87 -0
- package/src/data/raid-plans/sm-armory.json +53 -0
- package/src/data/raid-plans/sm-cathedral.json +72 -0
- package/src/data/raid-plans/sm-graveyard.json +63 -0
- package/src/data/raid-plans/sm-library.json +23 -0
- package/src/data/raid-plans/stratholme-live.json +129 -0
- package/src/data/raid-plans/stratholme-undead.json +73 -0
- package/src/data/raid-plans/sunken-temple.json +159 -0
- package/src/data/raid-plans/temple-of-ahnqiraj.json +163 -0
- package/src/data/raid-plans/the-stockade.json +42 -0
- package/src/data/raid-plans/ubrs.json +53 -0
- package/src/data/raid-plans/uldaman.json +131 -0
- package/src/data/raid-plans/wailing-caverns.json +90 -0
- package/src/data/raid-plans/zul-farrak.json +156 -0
- package/src/data/raid-plans/zul-gurub.json +307 -0
- package/src/data/raidInstanceCreatures.mjs +473 -0
- package/src/data/rotations/index.ts +152 -0
- package/src/data/rotations/layout.mjs +72 -0
- package/src/data/rotations/pve/druid/balance.json +754 -0
- package/src/data/rotations/pve/druid/feral-tank.json +2448 -0
- package/src/data/rotations/pve/druid/feral.json +2660 -0
- package/src/data/rotations/pve/druid/restoration.json +424 -0
- package/src/data/rotations/pve/hunter/beast-mastery.json +1288 -0
- package/src/data/rotations/pve/hunter/marksmanship.json +1260 -0
- package/src/data/rotations/pve/hunter/survival.json +1373 -0
- package/src/data/rotations/pve/mage/arcane-leveling.json +1798 -0
- package/src/data/rotations/pve/mage/fire-leveling.json +2003 -0
- package/src/data/rotations/pve/mage/frost-leveling.json +1912 -0
- package/src/data/rotations/pve/paladin/holy.json +755 -0
- package/src/data/rotations/pve/paladin/protection.json +962 -0
- package/src/data/rotations/pve/paladin/retribution.json +924 -0
- package/src/data/rotations/pve/priest/discipline.json +772 -0
- package/src/data/rotations/pve/priest/holy.json +707 -0
- package/src/data/rotations/pve/priest/shadow.json +787 -0
- package/src/data/rotations/pve/rogue/assassination.json +2477 -0
- package/src/data/rotations/pve/rogue/combat.json +2483 -0
- package/src/data/rotations/pve/rogue/subtlety.json +2523 -0
- package/src/data/rotations/pve/shaman/elemental.json +1157 -0
- package/src/data/rotations/pve/shaman/enhancement.json +1085 -0
- package/src/data/rotations/pve/shaman/restoration.json +592 -0
- package/src/data/rotations/pve/warlock/affliction.json +1185 -0
- package/src/data/rotations/pve/warlock/demonology.json +1165 -0
- package/src/data/rotations/pve/warlock/destruction.json +1177 -0
- package/src/data/rotations/pve/warrior/arms.json +3020 -0
- package/src/data/rotations/pve/warrior/fury.json +2996 -0
- package/src/data/rotations/pve/warrior/protection.json +2509 -0
- package/src/data/rotations/pvp/druid/balance.json +593 -0
- package/src/data/rotations/pvp/druid/feral.json +3276 -0
- package/src/data/rotations/pvp/druid/restoration.json +662 -0
- package/src/data/rotations/pvp/hunter/beast-mastery.json +665 -0
- package/src/data/rotations/pvp/hunter/marksmanship.json +2406 -0
- package/src/data/rotations/pvp/hunter/survival.json +635 -0
- package/src/data/rotations/pvp/mage/arcane.json +574 -0
- package/src/data/rotations/pvp/mage/fire.json +570 -0
- package/src/data/rotations/pvp/mage/frost.json +4048 -0
- package/src/data/rotations/pvp/paladin/holy.json +1498 -0
- package/src/data/rotations/pvp/paladin/protection.json +567 -0
- package/src/data/rotations/pvp/paladin/retribution.json +1110 -0
- package/src/data/rotations/pvp/priest/discipline.json +1848 -0
- package/src/data/rotations/pvp/priest/holy.json +6340 -0
- package/src/data/rotations/pvp/priest/shadow.json +2329 -0
- package/src/data/rotations/pvp/rogue/assassination.json +1322 -0
- package/src/data/rotations/pvp/rogue/combat.json +5258 -0
- package/src/data/rotations/pvp/rogue/subtlety.json +586 -0
- package/src/data/rotations/pvp/shaman/elemental.json +1404 -0
- package/src/data/rotations/pvp/shaman/enhancement.json +1277 -0
- package/src/data/rotations/pvp/shaman/restoration.json +2490 -0
- package/src/data/rotations/pvp/warlock/affliction.json +1017 -0
- package/src/data/rotations/pvp/warlock/demonology.json +1503 -0
- package/src/data/rotations/pvp/warlock/destruction.json +638 -0
- package/src/data/rotations/pvp/warrior/arms.json +5665 -0
- package/src/data/rotations/pvp/warrior/fury.json +850 -0
- package/src/data/rotations/pvp/warrior/protection.json +777 -0
- package/src/data/schema/bundle.ts +511 -0
- package/src/data/schema/constants.ts +49 -0
- package/src/data/schema/creature.ts +454 -0
- package/src/data/schema/encounter.ts +155 -0
- package/src/data/schema/faction.ts +48 -0
- package/src/data/schema/item.ts +297 -0
- package/src/data/schema/itemEnchant.ts +105 -0
- package/src/data/schema/npc.ts +44 -0
- package/src/data/schema/primitives.ts +295 -0
- package/src/data/schema/profession.ts +226 -0
- package/src/data/schema/quest.ts +154 -0
- package/src/data/schema/spell.ts +1149 -0
- package/src/data/schema/talent.ts +74 -0
- package/src/data/schema/world.ts +124 -0
- package/src/data/schema.ts +19 -0
- package/src/data/skinNodeGapFill.ts +68 -0
- package/src/data/spell-data.json.gz +0 -0
- package/src/data/spell-script-targets.json +1 -0
- package/src/data/tables/ahItemValues.json.gz +0 -0
- package/src/data/tables/alteracValley.json.gz +0 -0
- package/src/data/tables/areatriggerTeleports.json.gz +0 -0
- package/src/data/tables/botNames.json.gz +0 -0
- package/src/data/tables/botPopulation.json.gz +0 -0
- package/src/data/tables/botSpecs.json.gz +0 -0
- package/src/data/tables/classLevelStats.json.gz +0 -0
- package/src/data/tables/combatConditions.json.gz +0 -0
- package/src/data/tables/combatSkillMeta.json.gz +0 -0
- package/src/data/tables/conditions.json.gz +0 -0
- package/src/data/tables/createInfo.json.gz +0 -0
- package/src/data/tables/creatureAggroGroups.json.gz +0 -0
- package/src/data/tables/creatureClassLevelStats.json.gz +0 -0
- package/src/data/tables/creaturePatrolPacks.json.gz +0 -0
- package/src/data/tables/creatureWeapons.json.gz +0 -0
- package/src/data/tables/creatures.json.gz +0 -0
- package/src/data/tables/dbScripts.json.gz +0 -0
- package/src/data/tables/dungeonAuthoredRoutes.json.gz +0 -0
- package/src/data/tables/dungeonCensus.json.gz +0 -0
- package/src/data/tables/dungeonGameObjects.json.gz +0 -0
- package/src/data/tables/dungeonPacks.json.gz +0 -0
- package/src/data/tables/dungeonPlateCover.json.gz +0 -0
- package/src/data/tables/dungeonSpawns.json.gz +0 -0
- package/src/data/tables/dungeonSurvey.json.gz +0 -0
- package/src/data/tables/durabilityCosts.json.gz +0 -0
- package/src/data/tables/durabilityQuality.json.gz +0 -0
- package/src/data/tables/enchantApplicators.json.gz +0 -0
- package/src/data/tables/entranceYards.json.gz +0 -0
- package/src/data/tables/explorationBasexp.json.gz +0 -0
- package/src/data/tables/factionTemplates.json.gz +0 -0
- package/src/data/tables/factions.json.gz +0 -0
- package/src/data/tables/gameEvents.json.gz +0 -0
- package/src/data/tables/gameObjects.json.gz +0 -0
- package/src/data/tables/gossipMenus.json.gz +0 -0
- package/src/data/tables/graveyards.json.gz +0 -0
- package/src/data/tables/instanceMaps.json.gz +0 -0
- package/src/data/tables/itemEnchantmentTemplates.json.gz +0 -0
- package/src/data/tables/itemEnchants.json.gz +0 -0
- package/src/data/tables/itemRandomProperties.json.gz +0 -0
- package/src/data/tables/itemSets.json.gz +0 -0
- package/src/data/tables/items.json.gz +0 -0
- package/src/data/tables/levelStats.json.gz +0 -0
- package/src/data/tables/locks.json.gz +0 -0
- package/src/data/tables/map-pin-overrides.json.gz +0 -0
- package/src/data/tables/nonCombatTrash.json.gz +0 -0
- package/src/data/tables/npcTexts.json.gz +0 -0
- package/src/data/tables/pageTexts.json.gz +0 -0
- package/src/data/tables/petAbilityTP.json.gz +0 -0
- package/src/data/tables/petGenericSpells.json.gz +0 -0
- package/src/data/tables/petLevelStats.json.gz +0 -0
- package/src/data/tables/playerbot-weightscales.json.gz +0 -0
- package/src/data/tables/pools.json.gz +0 -0
- package/src/data/tables/quests.json.gz +0 -0
- package/src/data/tables/races.json.gz +0 -0
- package/src/data/tables/reputationRanks.json.gz +0 -0
- package/src/data/tables/reputationRates.json.gz +0 -0
- package/src/data/tables/reputationSpillover.json.gz +0 -0
- package/src/data/tables/ridingSpells.json.gz +0 -0
- package/src/data/tables/scriptTexts.json.gz +0 -0
- package/src/data/tables/soundClipVolume.json.gz +0 -0
- package/src/data/tables/spellChain.json.gz +0 -0
- package/src/data/tables/spellTeleports.json.gz +0 -0
- package/src/data/tables/spells.json.gz +0 -0
- package/src/data/tables/talents.json.gz +0 -0
- package/src/data/tables/tavernTriggers.json.gz +0 -0
- package/src/data/tables/taxiNodes.json.gz +0 -0
- package/src/data/tables/taxiRoutes.json.gz +0 -0
- package/src/data/tables/trainers.json.gz +0 -0
- package/src/data/tables/transports.json.gz +0 -0
- package/src/data/tables/travelAnchors.json.gz +0 -0
- package/src/data/tables/travelEdges.json.gz +0 -0
- package/src/data/tables/unitConditions.json.gz +0 -0
- package/src/data/tables/vendors.json.gz +0 -0
- package/src/data/tables/weaponEnchantProcs.json.gz +0 -0
- package/src/data/tables/xp.json.gz +0 -0
- package/src/data/tables/zoneWeather.json.gz +0 -0
- package/src/data/tables/zones.json.gz +0 -0
- package/src/data/world-data.ts +56 -0
- package/src/data/zone-music-sets.json +2751 -0
- package/src/dev/etl/lib/loot-chance-sanity.mjs +180 -0
- package/src/domain/audioQuality.mjs +145 -0
- package/src/domain/bg/ab.ts +433 -0
- package/src/domain/bg/arenaComps.ts +644 -0
- package/src/domain/bg/arenaLab.ts +695 -0
- package/src/domain/bg/arenaPace.ts +31 -0
- package/src/domain/bg/arenaRating.ts +110 -0
- package/src/domain/bg/av.ts +758 -0
- package/src/domain/bg/avEnemyPush.ts +219 -0
- package/src/domain/bg/avField.ts +283 -0
- package/src/domain/bg/avInstanceNodes.ts +197 -0
- package/src/domain/bg/avInstanceScore.ts +123 -0
- package/src/domain/bg/avQuestItems.ts +55 -0
- package/src/domain/bg/avQuests.ts +210 -0
- package/src/domain/bg/avSession.ts +196 -0
- package/src/domain/bg/bgConsumes.ts +269 -0
- package/src/domain/bg/bgPrebuff.ts +153 -0
- package/src/domain/bg/bgRest.ts +91 -0
- package/src/domain/bg/bgRoster.ts +305 -0
- package/src/domain/bg/bgTier.ts +61 -0
- package/src/domain/bg/bgWorld.ts +862 -0
- package/src/domain/bg/match.ts +215 -0
- package/src/domain/bg/strategies.ts +552 -0
- package/src/domain/bg/types.ts +291 -0
- package/src/domain/bg/wsg.ts +264 -0
- package/src/domain/bots/actionProviderSpec.ts +38 -0
- package/src/domain/bots/autoConsumables.ts +285 -0
- package/src/domain/bots/betweenPullRest.ts +239 -0
- package/src/domain/bots/botGear.ts +248 -0
- package/src/domain/bots/botInventory.ts +486 -0
- package/src/domain/bots/botPet.ts +535 -0
- package/src/domain/bots/botPool.ts +291 -0
- package/src/domain/bots/botProfessions.ts +334 -0
- package/src/domain/bots/companionActionBar.ts +209 -0
- package/src/domain/bots/companionHireCost.ts +236 -0
- package/src/domain/bots/companionMissions.ts +582 -0
- package/src/domain/bots/companionPrep.ts +254 -0
- package/src/domain/bots/companionQuests.ts +270 -0
- package/src/domain/bots/companionRep.ts +75 -0
- package/src/domain/bots/companionRotation.ts +708 -0
- package/src/domain/bots/companionXp.ts +66 -0
- package/src/domain/bots/consumableShare.ts +811 -0
- package/src/domain/bots/dpsMate.ts +1072 -0
- package/src/domain/bots/groupCompReport.ts +132 -0
- package/src/domain/bots/healthstoneSupply.ts +198 -0
- package/src/domain/bots/outfitter.ts +1056 -0
- package/src/domain/bots/paladinPrePull.ts +231 -0
- package/src/domain/bots/partyRole.ts +40 -0
- package/src/domain/bots/playerbotEquipCache.ts +412 -0
- package/src/domain/bots/playerbotItemEligibility.ts +60 -0
- package/src/domain/bots/playerbotStatWeight.ts +853 -0
- package/src/domain/bots/playerbotUnavailableItems.ts +147 -0
- package/src/domain/bots/prePullBuffs.ts +219 -0
- package/src/domain/bots/prePullStealth.ts +82 -0
- package/src/domain/bots/runSupply.ts +251 -0
- package/src/domain/bots/runSupplyPrefs.ts +28 -0
- package/src/domain/bots/sendTradeGoods.ts +394 -0
- package/src/domain/bots/soulShardCap.ts +35 -0
- package/src/domain/bots/squad.ts +842 -0
- package/src/domain/bots/squadRun.ts +81 -0
- package/src/domain/character/actionbar.ts +346 -0
- package/src/domain/character/appearance.ts +165 -0
- package/src/domain/character/buildCombatant.ts +1726 -0
- package/src/domain/character/character.ts +356 -0
- package/src/domain/character/characterName.ts +43 -0
- package/src/domain/character/characterRatings.ts +424 -0
- package/src/domain/character/characterSpells.ts +282 -0
- package/src/domain/character/characterStats.ts +1559 -0
- package/src/domain/character/characterTalents.ts +398 -0
- package/src/domain/character/combatSkills.ts +994 -0
- package/src/domain/character/displayForm.ts +72 -0
- package/src/domain/character/formTalentAuras.ts +171 -0
- package/src/domain/character/forms.ts +499 -0
- package/src/domain/character/races.ts +40 -0
- package/src/domain/character/resurrectionSickness.ts +49 -0
- package/src/domain/character/spellbook.ts +169 -0
- package/src/domain/character/stanceBar.ts +354 -0
- package/src/domain/character/stancePrereq.ts +70 -0
- package/src/domain/character/talentBuilds.ts +63 -0
- package/src/domain/character/talentImport.ts +130 -0
- package/src/domain/character/talentLink.ts +123 -0
- package/src/domain/character/talents.ts +201 -0
- package/src/domain/core/conditionSnapshot.ts +92 -0
- package/src/domain/core/hover.ts +14 -0
- package/src/domain/core/ids.ts +58 -0
- package/src/domain/core/internalNameTags.ts +5 -0
- package/src/domain/core/runtimeDataPatches.ts +357 -0
- package/src/domain/creature/aiTargeting.ts +588 -0
- package/src/domain/creature/combatCondition.ts +157 -0
- package/src/domain/creature/creatureAggro.ts +804 -0
- package/src/domain/creature/creatureAi.ts +206 -0
- package/src/domain/creature/creatureAiConstants.ts +661 -0
- package/src/domain/creature/creatureCastList.ts +1007 -0
- package/src/domain/creature/creatureEventAi.ts +1409 -0
- package/src/domain/creature/creatureGroups.ts +144 -0
- package/src/domain/creature/creatureLinking.ts +333 -0
- package/src/domain/creature/creatureScaling.ts +320 -0
- package/src/domain/creature/creatureTrigger.ts +53 -0
- package/src/domain/creature/creatureWander.ts +74 -0
- package/src/domain/creature/eventAiTexts.ts +145 -0
- package/src/domain/creature/faction.ts +287 -0
- package/src/domain/creature/forcedReaction.ts +48 -0
- package/src/domain/creature/outdoorHuntable.ts +74 -0
- package/src/domain/creature/patrolPacks.ts +210 -0
- package/src/domain/creature/reputation.ts +967 -0
- package/src/domain/creature/unitFlags.ts +74 -0
- package/src/domain/creature/worldBosses.ts +393 -0
- package/src/domain/dungeonMusic.mjs +48 -0
- package/src/domain/economy/auction.ts +282 -0
- package/src/domain/economy/auctionBot.ts +1060 -0
- package/src/domain/economy/honorPrice.ts +100 -0
- package/src/domain/economy/mail.ts +958 -0
- package/src/domain/economy/money.ts +42 -0
- package/src/domain/economy/syntheticClassTrainers.ts +62 -0
- package/src/domain/economy/trading.ts +296 -0
- package/src/domain/economy/vendorSellAll.ts +252 -0
- package/src/domain/economy/vendorStock.ts +146 -0
- package/src/domain/items/ammo.ts +817 -0
- package/src/domain/items/bank.ts +162 -0
- package/src/domain/items/consumableFamily.ts +205 -0
- package/src/domain/items/craftUpgrades.ts +83 -0
- package/src/domain/items/createItem.ts +93 -0
- package/src/domain/items/durability.ts +581 -0
- package/src/domain/items/equipment.ts +441 -0
- package/src/domain/items/equipmentHelper.ts +767 -0
- package/src/domain/items/inventory.ts +561 -0
- package/src/domain/items/itemBonding.ts +93 -0
- package/src/domain/items/itemContainer.ts +245 -0
- package/src/domain/items/itemDisplayName.ts +33 -0
- package/src/domain/items/itemInstance.ts +181 -0
- package/src/domain/items/itemRandomEnchantOutcomes.ts +315 -0
- package/src/domain/items/itemRandomProps.ts +77 -0
- package/src/domain/items/itemRestrictions.ts +329 -0
- package/src/domain/items/itemTeaches.ts +82 -0
- package/src/domain/items/itemUseCooldown.ts +52 -0
- package/src/domain/items/itemUseTarget.ts +90 -0
- package/src/domain/items/junkCategories.ts +174 -0
- package/src/domain/items/junkFilter.ts +368 -0
- package/src/domain/items/keyring.ts +131 -0
- package/src/domain/items/loot.ts +221 -0
- package/src/domain/items/mounts.ts +190 -0
- package/src/domain/items/onUsePayload.ts +102 -0
- package/src/domain/items/pendingLoot.ts +107 -0
- package/src/domain/items/relic.ts +105 -0
- package/src/domain/items/rocketBoots.ts +36 -0
- package/src/domain/items/sortContainer.ts +139 -0
- package/src/domain/items/spareContainers.ts +71 -0
- package/src/domain/items/spellReagentIndex.ts +131 -0
- package/src/domain/items/staleConsumables.ts +66 -0
- package/src/domain/items/weaponSpender.ts +110 -0
- package/src/domain/listDungeonIds.mjs +28 -0
- package/src/domain/macro/compose.ts +239 -0
- package/src/domain/macro/conditions.ts +260 -0
- package/src/domain/macro/creatureTypes.ts +40 -0
- package/src/domain/macro/describe.ts +169 -0
- package/src/domain/macro/icon.ts +60 -0
- package/src/domain/macro/index.ts +20 -0
- package/src/domain/macro/normalize.ts +112 -0
- package/src/domain/macro/parse.ts +316 -0
- package/src/domain/macro/plan.ts +288 -0
- package/src/domain/macro/rotation.ts +590 -0
- package/src/domain/macro/rotationEnv.ts +173 -0
- package/src/domain/macro/slots.ts +101 -0
- package/src/domain/macro/snapshot.ts +114 -0
- package/src/domain/macro/templates.ts +446 -0
- package/src/domain/macro/types.ts +70 -0
- package/src/domain/macro/units.ts +93 -0
- package/src/domain/macro/weaponTypes.ts +83 -0
- package/src/domain/nav/cityTeleports.ts +10 -0
- package/src/domain/nav/mounting.ts +113 -0
- package/src/domain/nav/nav.ts +43 -0
- package/src/domain/nav/navCollision.ts +606 -0
- package/src/domain/nav/navOutdoors.ts +91 -0
- package/src/domain/nav/navRegistry.ts +179 -0
- package/src/domain/nav/navRoomGraph.ts +302 -0
- package/src/domain/nav/navTypes.ts +43 -0
- package/src/domain/nav/navmesh.ts +1715 -0
- package/src/domain/nav/speed.ts +200 -0
- package/src/domain/nav/storey.ts +10 -0
- package/src/domain/nav/travel.ts +725 -0
- package/src/domain/nav/travelTypes.ts +75 -0
- package/src/domain/pet/pet.ts +1150 -0
- package/src/domain/pet/petActionBar.ts +180 -0
- package/src/domain/pet/petSpellbook.ts +39 -0
- package/src/domain/pet/petStable.ts +81 -0
- package/src/domain/professions/autoDisenchant.ts +236 -0
- package/src/domain/professions/characterEnchants.ts +176 -0
- package/src/domain/professions/craftCooldown.ts +52 -0
- package/src/domain/professions/enchantApply.ts +62 -0
- package/src/domain/professions/enchantTargets.ts +114 -0
- package/src/domain/professions/enchanting.ts +236 -0
- package/src/domain/professions/gatherFilter.ts +149 -0
- package/src/domain/professions/gearEnchant.ts +137 -0
- package/src/domain/professions/heldTools.ts +29 -0
- package/src/domain/professions/learnAllRecipes.ts +146 -0
- package/src/domain/professions/miningHarvest.ts +69 -0
- package/src/domain/professions/partyDisenchant.ts +294 -0
- package/src/domain/professions/partyWork.ts +83 -0
- package/src/domain/professions/pickpocket.ts +172 -0
- package/src/domain/professions/poisons.ts +240 -0
- package/src/domain/professions/professions.ts +677 -0
- package/src/domain/professions/rareNodes.ts +45 -0
- package/src/domain/professions/skinLoot.ts +27 -0
- package/src/domain/professions/vendorReagents.ts +152 -0
- package/src/domain/professions/weaponImbueHand.ts +171 -0
- package/src/domain/questFarmFilter.mjs +42 -0
- package/src/domain/quests/doQuest.ts +1204 -0
- package/src/domain/quests/gossip.ts +308 -0
- package/src/domain/quests/gossipTalk.ts +298 -0
- package/src/domain/quests/interpreter/canComplete.ts +150 -0
- package/src/domain/quests/interpreter/claim.ts +137 -0
- package/src/domain/quests/interpreter/dbscript/handlers.ts +1371 -0
- package/src/domain/quests/interpreter/dbscript/handlersBatch.ts +1251 -0
- package/src/domain/quests/interpreter/dbscript/hooks.ts +331 -0
- package/src/domain/quests/interpreter/dbscript/index.ts +212 -0
- package/src/domain/quests/interpreter/dbscript/opcodes.ts +257 -0
- package/src/domain/quests/interpreter/dbscript/registry.ts +1194 -0
- package/src/domain/quests/interpreter/dbscript/registryAreatriggerScripts.ts +539 -0
- package/src/domain/quests/interpreter/dbscript/registryAshenvale.ts +553 -0
- package/src/domain/quests/interpreter/dbscript/registryAzshara.ts +272 -0
- package/src/domain/quests/interpreter/dbscript/registryDarkshore.ts +637 -0
- package/src/domain/quests/interpreter/dbscript/registryDesolace.ts +501 -0
- package/src/domain/quests/interpreter/dbscript/registryDustwallowMarsh.ts +735 -0
- package/src/domain/quests/interpreter/dbscript/registryFelwood.ts +493 -0
- package/src/domain/quests/interpreter/dbscript/registryFeralas.ts +485 -0
- package/src/domain/quests/interpreter/dbscript/registryGoScripts.ts +203 -0
- package/src/domain/quests/interpreter/dbscript/registryMoonglade.ts +288 -0
- package/src/domain/quests/interpreter/dbscript/registryNpcProfessions.ts +230 -0
- package/src/domain/quests/interpreter/dbscript/registryNpcsSpecial.ts +545 -0
- package/src/domain/quests/interpreter/dbscript/registryRazorfenDowns.ts +293 -0
- package/src/domain/quests/interpreter/dbscript/registryRazorfenKraul.ts +221 -0
- package/src/domain/quests/interpreter/dbscript/registryRedridge.ts +112 -0
- package/src/domain/quests/interpreter/dbscript/registrySearingGorge.ts +125 -0
- package/src/domain/quests/interpreter/dbscript/registrySiblings.ts +51 -0
- package/src/domain/quests/interpreter/dbscript/registrySilverpineForest.ts +172 -0
- package/src/domain/quests/interpreter/dbscript/registryStonetalonMountains.ts +180 -0
- package/src/domain/quests/interpreter/dbscript/registryStormwindCity.ts +369 -0
- package/src/domain/quests/interpreter/dbscript/registryStranglethornVale.ts +115 -0
- package/src/domain/quests/interpreter/dbscript/registryStratholme.ts +146 -0
- package/src/domain/quests/interpreter/dbscript/registrySunkenTemple.ts +149 -0
- package/src/domain/quests/interpreter/dbscript/registrySwampOfSorrows.ts +163 -0
- package/src/domain/quests/interpreter/dbscript/registryTanaris.ts +318 -0
- package/src/domain/quests/interpreter/dbscript/registryTeldrassil.ts +120 -0
- package/src/domain/quests/interpreter/dbscript/registryTheBarrens.ts +603 -0
- package/src/domain/quests/interpreter/dbscript/registryThousandNeedles.ts +499 -0
- package/src/domain/quests/interpreter/dbscript/registryTirisfalGlades.ts +222 -0
- package/src/domain/quests/interpreter/dbscript/registryUngoroCrater.ts +291 -0
- package/src/domain/quests/interpreter/dbscript/registryWarEffort.ts +773 -0
- package/src/domain/quests/interpreter/dbscript/registryWesternPlaguelands.ts +206 -0
- package/src/domain/quests/interpreter/dbscript/registryWestfall.ts +190 -0
- package/src/domain/quests/interpreter/dbscript/registryWetlands.ts +186 -0
- package/src/domain/quests/interpreter/dbscript/registryWinterspring.ts +235 -0
- package/src/domain/quests/interpreter/dbscript/types.ts +817 -0
- package/src/domain/quests/interpreter/dbscript/vm.ts +171 -0
- package/src/domain/quests/interpreter/diff.ts +349 -0
- package/src/domain/quests/interpreter/fixtures/oracle-questlog-qi2.json +2417 -0
- package/src/domain/quests/interpreter/flags.ts +101 -0
- package/src/domain/quests/interpreter/index.ts +113 -0
- package/src/domain/quests/interpreter/live.ts +178 -0
- package/src/domain/quests/interpreter/types.ts +81 -0
- package/src/domain/quests/interpreter/updaters.ts +412 -0
- package/src/domain/quests/questCorpseUse.ts +155 -0
- package/src/domain/quests/questDialog.ts +43 -0
- package/src/domain/quests/questFight.ts +34 -0
- package/src/domain/quests/questObjectiveLabel.ts +39 -0
- package/src/domain/quests/questRewardChoice.ts +85 -0
- package/src/domain/quests/questScriptCredit.ts +26 -0
- package/src/domain/quests/questScriptItemUse.ts +74 -0
- package/src/domain/quests/questTameRods.ts +79 -0
- package/src/domain/quests/questlog.ts +1545 -0
- package/src/domain/quests/talkDeliver.ts +170 -0
- package/src/domain/raid/globalActionBar.ts +195 -0
- package/src/domain/raid/instanceRoster.ts +194 -0
- package/src/domain/raid/partySummon.ts +198 -0
- package/src/domain/raid/partyVault.ts +320 -0
- package/src/domain/raid/raidBossAbilities.ts +268 -0
- package/src/domain/raid/raidComp.ts +335 -0
- package/src/domain/raid/raidCustomRows.ts +453 -0
- package/src/domain/raid/raidEncounterGear.ts +298 -0
- package/src/domain/raid/raidEncounterTactics.ts +412 -0
- package/src/domain/raid/raidPlan.ts +509 -0
- package/src/domain/raid/raidPlanEdits.ts +98 -0
- package/src/domain/raid/raidPlanPacks.ts +480 -0
- package/src/domain/raid/raidRecovery.ts +183 -0
- package/src/domain/raid/raidRosterTemplate.ts +138 -0
- package/src/domain/rotation/auraSide.ts +68 -0
- package/src/domain/rotation/build.ts +3031 -0
- package/src/domain/rotation/classes/druid.ts +58 -0
- package/src/domain/rotation/classes/hooks.ts +59 -0
- package/src/domain/rotation/classes/hunter.ts +156 -0
- package/src/domain/rotation/classes/index.ts +104 -0
- package/src/domain/rotation/classes/mage.ts +99 -0
- package/src/domain/rotation/classes/paladin.ts +296 -0
- package/src/domain/rotation/classes/priest.ts +94 -0
- package/src/domain/rotation/classes/pullOpener.ts +92 -0
- package/src/domain/rotation/classes/rogue.ts +69 -0
- package/src/domain/rotation/classes/shaman.ts +29 -0
- package/src/domain/rotation/classes/supportSpec.ts +94 -0
- package/src/domain/rotation/classes/warlock.ts +81 -0
- package/src/domain/rotation/classes/warrior.ts +402 -0
- package/src/domain/rotation/classify.ts +1381 -0
- package/src/domain/rotation/condMetricPicker.ts +379 -0
- package/src/domain/rotation/consumables.ts +242 -0
- package/src/domain/rotation/evaluate.ts +1115 -0
- package/src/domain/rotation/finisherLadder.ts +145 -0
- package/src/domain/rotation/invent.ts +938 -0
- package/src/domain/rotation/killOrder.ts +30 -0
- package/src/domain/rotation/openerForm.ts +22 -0
- package/src/domain/rotation/paladinBuffs.ts +568 -0
- package/src/domain/rotation/prebuff.ts +283 -0
- package/src/domain/rotation/raidRowTuning.ts +34 -0
- package/src/domain/rotation/rotation.ts +11 -0
- package/src/domain/rotation/rotationProfiles.ts +180 -0
- package/src/domain/rotation/rotationSuggestions.ts +851 -0
- package/src/domain/rotation/ruleAfterChain.ts +49 -0
- package/src/domain/rotation/spec.ts +70 -0
- package/src/domain/rotation/spellPicker.ts +140 -0
- package/src/domain/rotation/stand.ts +143 -0
- package/src/domain/rotation/types.ts +718 -0
- package/src/domain/run/battlegroundRun.ts +111 -0
- package/src/domain/run/canEnterContext.ts +32 -0
- package/src/domain/run/death.ts +561 -0
- package/src/domain/run/deathRecovery.ts +289 -0
- package/src/domain/run/duel.ts +141 -0
- package/src/domain/run/dungeonRun.ts +344 -0
- package/src/domain/run/floorRest.ts +159 -0
- package/src/domain/run/honor.ts +557 -0
- package/src/domain/run/honorWeek.ts +180 -0
- package/src/domain/run/whenMap.ts +977 -0
- package/src/domain/scriptdev/arathiHighlands.ts +445 -0
- package/src/domain/scriptdev/ashenvale.ts +635 -0
- package/src/domain/scriptdev/ashenvaleHelpers.ts +1215 -0
- package/src/domain/scriptdev/azshara.ts +892 -0
- package/src/domain/scriptdev/blackfathomDeeps.ts +212 -0
- package/src/domain/scriptdev/blackrockDepths.ts +1434 -0
- package/src/domain/scriptdev/blackrockSpire.ts +730 -0
- package/src/domain/scriptdev/blackwingLair.ts +521 -0
- package/src/domain/scriptdev/burningSteppes.ts +62 -0
- package/src/domain/scriptdev/classFilter.ts +43 -0
- package/src/domain/scriptdev/darkshore.ts +817 -0
- package/src/domain/scriptdev/darkshoreHelpers.ts +853 -0
- package/src/domain/scriptdev/deadmines.ts +331 -0
- package/src/domain/scriptdev/desolace.ts +1239 -0
- package/src/domain/scriptdev/direMaul.ts +859 -0
- package/src/domain/scriptdev/durotar.ts +532 -0
- package/src/domain/scriptdev/dustwallowMarsh.ts +758 -0
- package/src/domain/scriptdev/dustwallowMarshHelpers.ts +994 -0
- package/src/domain/scriptdev/easternPlaguelands.ts +21 -0
- package/src/domain/scriptdev/felwood.ts +1055 -0
- package/src/domain/scriptdev/feralas.ts +571 -0
- package/src/domain/scriptdev/feralasHelpers.ts +838 -0
- package/src/domain/scriptdev/gnomeregan.ts +466 -0
- package/src/domain/scriptdev/guards.ts +616 -0
- package/src/domain/scriptdev/hinterlands.ts +693 -0
- package/src/domain/scriptdev/host.ts +1134 -0
- package/src/domain/scriptdev/index.ts +123 -0
- package/src/domain/scriptdev/instanceRaids.ts +136 -0
- package/src/domain/scriptdev/itemScripts.ts +37 -0
- package/src/domain/scriptdev/lochModan.ts +283 -0
- package/src/domain/scriptdev/maraudon.ts +232 -0
- package/src/domain/scriptdev/moltenCore.ts +831 -0
- package/src/domain/scriptdev/moonglade.ts +659 -0
- package/src/domain/scriptdev/moongladeHelpers.ts +933 -0
- package/src/domain/scriptdev/naxxramas.ts +577 -0
- package/src/domain/scriptdev/naxxramasConstruct.ts +364 -0
- package/src/domain/scriptdev/naxxramasFrostwyrm.ts +378 -0
- package/src/domain/scriptdev/naxxramasMilitary.ts +276 -0
- package/src/domain/scriptdev/npcProfessions.ts +1025 -0
- package/src/domain/scriptdev/npcsSpecial.ts +1379 -0
- package/src/domain/scriptdev/onyxiasLair.ts +313 -0
- package/src/domain/scriptdev/orgrimmar.ts +147 -0
- package/src/domain/scriptdev/professionSpecConsumer.ts +481 -0
- package/src/domain/scriptdev/razorfenDowns.ts +468 -0
- package/src/domain/scriptdev/razorfenKraul.ts +410 -0
- package/src/domain/scriptdev/redridgeMountains.ts +163 -0
- package/src/domain/scriptdev/registry.ts +945 -0
- package/src/domain/scriptdev/ruinsOfAhnqiraj.ts +356 -0
- package/src/domain/scriptdev/scarletMonastery.ts +713 -0
- package/src/domain/scriptdev/scholomance.ts +682 -0
- package/src/domain/scriptdev/searingGorge.ts +295 -0
- package/src/domain/scriptdev/shadowfangKeep.ts +181 -0
- package/src/domain/scriptdev/silithus.ts +71 -0
- package/src/domain/scriptdev/silverpineForest.ts +662 -0
- package/src/domain/scriptdev/stonetalonMountains.ts +493 -0
- package/src/domain/scriptdev/stormwindCity.ts +1192 -0
- package/src/domain/scriptdev/stranglethornVale.ts +402 -0
- package/src/domain/scriptdev/stratholme.ts +650 -0
- package/src/domain/scriptdev/stratholmeScripts.ts +505 -0
- package/src/domain/scriptdev/sunkenTemple.ts +736 -0
- package/src/domain/scriptdev/swampOfSorrows.ts +22 -0
- package/src/domain/scriptdev/tanaris.ts +391 -0
- package/src/domain/scriptdev/tanarisHelpers.ts +661 -0
- package/src/domain/scriptdev/teldrassil.ts +143 -0
- package/src/domain/scriptdev/templeOfAhnqiraj.ts +1013 -0
- package/src/domain/scriptdev/theBarrens.ts +709 -0
- package/src/domain/scriptdev/theBarrensHelpers.ts +1026 -0
- package/src/domain/scriptdev/thousandNeedles.ts +426 -0
- package/src/domain/scriptdev/thousandNeedlesHelpers.ts +566 -0
- package/src/domain/scriptdev/tirisfalGlades.ts +533 -0
- package/src/domain/scriptdev/types.ts +1026 -0
- package/src/domain/scriptdev/uldaman.ts +437 -0
- package/src/domain/scriptdev/ungoroCrater.ts +549 -0
- package/src/domain/scriptdev/ungoroCraterHelpers.ts +1006 -0
- package/src/domain/scriptdev/wailingCaverns.ts +700 -0
- package/src/domain/scriptdev/warEffort.ts +371 -0
- package/src/domain/scriptdev/westernPlaguelands.ts +952 -0
- package/src/domain/scriptdev/westfall.ts +767 -0
- package/src/domain/scriptdev/wetlands.ts +660 -0
- package/src/domain/scriptdev/winterspring.ts +482 -0
- package/src/domain/scriptdev/winterspringHelpers.ts +801 -0
- package/src/domain/scriptdev/worldBosses.ts +605 -0
- package/src/domain/scriptdev/zulfarrak.ts +478 -0
- package/src/domain/scriptdev/zulgurub.ts +1152 -0
- package/src/domain/sim/dummySim.ts +408 -0
- package/src/domain/sim/targetDummy.ts +29 -0
- package/src/domain/social/guild.ts +148 -0
- package/src/domain/spells/areaAuraMembership.ts +93 -0
- package/src/domain/spells/breakableStunCc.ts +166 -0
- package/src/domain/spells/cancelAura.ts +80 -0
- package/src/domain/spells/diminishing.ts +167 -0
- package/src/domain/spells/dispel.ts +149 -0
- package/src/domain/spells/dispelMechanic.ts +105 -0
- package/src/domain/spells/equipSpells.ts +247 -0
- package/src/domain/spells/learnOnTargets.ts +260 -0
- package/src/domain/spells/mergeSchoolImmunityFromRaw.ts +66 -0
- package/src/domain/spells/resurrectSpell.ts +124 -0
- package/src/domain/spells/spellPushback.ts +39 -0
- package/src/domain/spells/spellReflect.ts +43 -0
- package/src/domain/spells/stealth.ts +183 -0
- package/src/domain/spells/totem.ts +394 -0
- package/src/domain/spells/worldCast.ts +187 -0
- package/src/domain/world/areaTriggerLocks.ts +177 -0
- package/src/domain/world/dungeonMusic.ts +108 -0
- package/src/domain/world/gameEvents.ts +119 -0
- package/src/domain/world/gameObjects.ts +1030 -0
- package/src/domain/world/goEventFire.ts +234 -0
- package/src/domain/world/goMoTransport.ts +74 -0
- package/src/domain/world/goTransport.ts +81 -0
- package/src/domain/world/hazards.ts +459 -0
- package/src/domain/world/openLock.ts +398 -0
- package/src/domain/world/pools.ts +470 -0
- package/src/domain/world/restArea.ts +37 -0
- package/src/domain/world/weather.ts +201 -0
- package/src/domain/zoneAmbience.mjs +87 -0
- package/src/domain/zoneMusic.mjs +62 -0
- package/src/domain/zoneMusicQueue.mjs +7 -0
- package/src/engine/combat/activeSeal.ts +39 -0
- package/src/engine/combat/aiTargetResolve.ts +64 -0
- package/src/engine/combat/aoeCast.ts +289 -0
- package/src/engine/combat/applyConsumeAura.ts +74 -0
- package/src/engine/combat/applyDemonicSacrifice.ts +70 -0
- package/src/engine/combat/applyDispelMechanic.ts +30 -0
- package/src/engine/combat/applyHealEffect.ts +301 -0
- package/src/engine/combat/applySchoolAbsorb.ts +114 -0
- package/src/engine/combat/applySpell.ts +3169 -0
- package/src/engine/combat/applySpellLate.ts +2026 -0
- package/src/engine/combat/applySpellLateBag.ts +144 -0
- package/src/engine/combat/areaAura.ts +283 -0
- package/src/engine/combat/attackTable.ts +508 -0
- package/src/engine/combat/battleRez.ts +87 -0
- package/src/engine/combat/castAction.ts +36 -0
- package/src/engine/combat/castTargetGates.ts +75 -0
- package/src/engine/combat/castableKit.ts +68 -0
- package/src/engine/combat/ccMirrorUpgrade.ts +39 -0
- package/src/engine/combat/chainFan.ts +210 -0
- package/src/engine/combat/channel.ts +423 -0
- package/src/engine/combat/chaseCloseness.ts +95 -0
- package/src/engine/combat/cmangosCastGates.ts +129 -0
- package/src/engine/combat/combatFlag.ts +77 -0
- package/src/engine/combat/combatGeometry.ts +1329 -0
- package/src/engine/combat/combustion.ts +68 -0
- package/src/engine/combat/context.ts +730 -0
- package/src/engine/combat/createSession.ts +1165 -0
- package/src/engine/combat/creatureLeash.ts +38 -0
- package/src/engine/combat/crowdControl.ts +1259 -0
- package/src/engine/combat/dispelDuty.ts +54 -0
- package/src/engine/combat/dispelReflect.ts +609 -0
- package/src/engine/combat/displaceFloor.ts +113 -0
- package/src/engine/combat/duelBounds.ts +47 -0
- package/src/engine/combat/equippedItemGate.ts +297 -0
- package/src/engine/combat/evadeLifecycle.ts +63 -0
- package/src/engine/combat/eventAi.ts +2220 -0
- package/src/engine/combat/facing.ts +74 -0
- package/src/engine/combat/fearFlee.ts +137 -0
- package/src/engine/combat/fighterFactory.ts +1144 -0
- package/src/engine/combat/forceDespawnAction.ts +51 -0
- package/src/engine/combat/formulas.ts +807 -0
- package/src/engine/combat/friendlyFan.ts +55 -0
- package/src/engine/combat/groundPoolTick.ts +210 -0
- package/src/engine/combat/hazards.ts +114 -0
- package/src/engine/combat/healTargets.ts +226 -0
- package/src/engine/combat/heartbeatResist.ts +49 -0
- package/src/engine/combat/hostileItemUse.ts +75 -0
- package/src/engine/combat/interruptAuras.ts +66 -0
- package/src/engine/combat/itemRescue.ts +49 -0
- package/src/engine/combat/itemUse.ts +260 -0
- package/src/engine/combat/kitById.ts +23 -0
- package/src/engine/combat/knockBack.ts +201 -0
- package/src/engine/combat/leap.ts +89 -0
- package/src/engine/combat/mitigation.ts +587 -0
- package/src/engine/combat/modTaunt.ts +43 -0
- package/src/engine/combat/motionClaim.ts +268 -0
- package/src/engine/combat/motionGate.ts +150 -0
- package/src/engine/combat/motionGenerator.ts +27 -0
- package/src/engine/combat/navSight.ts +138 -0
- package/src/engine/combat/perfSink.ts +28 -0
- package/src/engine/combat/periodicDue.ts +55 -0
- package/src/engine/combat/petAuras.ts +86 -0
- package/src/engine/combat/pickAction.ts +3449 -0
- package/src/engine/combat/powerForm.ts +735 -0
- package/src/engine/combat/powerPools.ts +49 -0
- package/src/engine/combat/predicates.ts +538 -0
- package/src/engine/combat/procsDamage.ts +1823 -0
- package/src/engine/combat/reactiveGates.ts +135 -0
- package/src/engine/combat/roles.ts +389 -0
- package/src/engine/combat/rotationSubject.ts +78 -0
- package/src/engine/combat/ruleAfter.ts +106 -0
- package/src/engine/combat/seedDisplayAuras.ts +203 -0
- package/src/engine/combat/seedOocEffects.ts +317 -0
- package/src/engine/combat/seedSchoolImmunities.ts +45 -0
- package/src/engine/combat/session-types.ts +458 -0
- package/src/engine/combat/sessionApi.ts +1785 -0
- package/src/engine/combat/sessionCharge.ts +124 -0
- package/src/engine/combat/sessionChase.ts +869 -0
- package/src/engine/combat/sessionDeaths.ts +469 -0
- package/src/engine/combat/sessionFocus.ts +506 -0
- package/src/engine/combat/sessionFollow.ts +167 -0
- package/src/engine/combat/sessionGeometry.ts +188 -0
- package/src/engine/combat/sessionHome.ts +75 -0
- package/src/engine/combat/sessionInject.ts +719 -0
- package/src/engine/combat/sessionMechanicMove.ts +555 -0
- package/src/engine/combat/sessionMotion.ts +1553 -0
- package/src/engine/combat/sessionMotionKit.ts +247 -0
- package/src/engine/combat/sessionMoveIntent.ts +49 -0
- package/src/engine/combat/sessionMoveOrder.ts +200 -0
- package/src/engine/combat/sessionPathTo.ts +657 -0
- package/src/engine/combat/sessionPools.ts +688 -0
- package/src/engine/combat/sessionSteer.ts +144 -0
- package/src/engine/combat/sessionStepBack.ts +151 -0
- package/src/engine/combat/sessionStepWrap.ts +116 -0
- package/src/engine/combat/sessionView.ts +442 -0
- package/src/engine/combat/simEffects.ts +32 -0
- package/src/engine/combat/simFacade.ts +620 -0
- package/src/engine/combat/simPump.ts +264 -0
- package/src/engine/combat/simSnapshot.ts +165 -0
- package/src/engine/combat/simThreadPool.ts +35 -0
- package/src/engine/combat/skeramImages.ts +57 -0
- package/src/engine/combat/slowUpdate.ts +178 -0
- package/src/engine/combat/snapToFloor.ts +42 -0
- package/src/engine/combat/speedAura.ts +29 -0
- package/src/engine/combat/spellReagents.ts +71 -0
- package/src/engine/combat/stealthDetect.ts +93 -0
- package/src/engine/combat/stepBackMotion.ts +171 -0
- package/src/engine/combat/stepSession.ts +3102 -0
- package/src/engine/combat/summonCast.ts +243 -0
- package/src/engine/combat/thaddiusPolarity.ts +97 -0
- package/src/engine/combat/threat.ts +388 -0
- package/src/engine/combat/threatOps.ts +272 -0
- package/src/engine/combat/tickWorkerBody.ts +211 -0
- package/src/engine/combat/totemAuras.ts +122 -0
- package/src/engine/combat/triggerPayload.ts +42 -0
- package/src/engine/combat/triggeredCompanion.ts +63 -0
- package/src/engine/combat/types.ts +1897 -0
- package/src/engine/combat/useObjectTrap.ts +140 -0
- package/src/engine/combat/walkSurface.ts +54 -0
- package/src/engine/combat/weapon.ts +1248 -0
- package/src/engine/combat/worldFrame.ts +43 -0
- package/src/engine/combat.solo-golden.json +2242 -0
- package/src/engine/combat.ts +84 -0
- package/src/engine/core/detMath.ts +289 -0
- package/src/engine/core/events.ts +357 -0
- package/src/engine/core/rng.ts +36 -0
- package/src/engine/game/actionBars.ts +124 -0
- package/src/engine/game/activateObjectFromSpell.ts +320 -0
- package/src/engine/game/applyActiveObjectFromDbScript.ts +98 -0
- package/src/engine/game/applyDeathAndSpellScripts.ts +268 -0
- package/src/engine/game/applyDoorFromDbScript.ts +388 -0
- package/src/engine/game/applyEscortFromDbScript.ts +290 -0
- package/src/engine/game/applyGoSpellcasterCast.ts +40 -0
- package/src/engine/game/applyHoverFromDbScript.ts +110 -0
- package/src/engine/game/applyOocResurrect.ts +71 -0
- package/src/engine/game/applySpellDurability.ts +115 -0
- package/src/engine/game/applyTailFromDbScript.ts +478 -0
- package/src/engine/game/applyTerminateFromDbScript.ts +95 -0
- package/src/engine/game/auctionMail.ts +269 -0
- package/src/engine/game/bagOverflow.ts +130 -0
- package/src/engine/game/bank.ts +110 -0
- package/src/engine/game/barAutofill.ts +26 -0
- package/src/engine/game/battleground.ts +369 -0
- package/src/engine/game/combatEnemySpec.ts +216 -0
- package/src/engine/game/combatObjectTraps.ts +167 -0
- package/src/engine/game/combatSeed.ts +320 -0
- package/src/engine/game/combatSession.ts +3241 -0
- package/src/engine/game/combatSessionClock.ts +62 -0
- package/src/engine/game/companionQuestActions.ts +242 -0
- package/src/engine/game/companionTraining.ts +40 -0
- package/src/engine/game/consumables.ts +624 -0
- package/src/engine/game/context.ts +53 -0
- package/src/engine/game/createHealthstone.ts +45 -0
- package/src/engine/game/deathFlow.ts +127 -0
- package/src/engine/game/devGrant.ts +117 -0
- package/src/engine/game/dynamicObject.ts +365 -0
- package/src/engine/game/enchantItem.ts +177 -0
- package/src/engine/game/equipmentHelper.ts +806 -0
- package/src/engine/game/factionState.ts +108 -0
- package/src/engine/game/garmentsOoc.ts +69 -0
- package/src/engine/game/globalActionBar.ts +179 -0
- package/src/engine/game/goInteractQuestCredit.ts +51 -0
- package/src/engine/game/goMoTransportUse.ts +61 -0
- package/src/engine/game/goTransportUse.ts +60 -0
- package/src/engine/game/healthstoneUpkeep.ts +59 -0
- package/src/engine/game/killSkin.ts +124 -0
- package/src/engine/game/learnFromItem.ts +118 -0
- package/src/engine/game/loadReconcile.ts +551 -0
- package/src/engine/game/loot.ts +294 -0
- package/src/engine/game/macros.ts +125 -0
- package/src/engine/game/mail.ts +293 -0
- package/src/engine/game/meetingStoneQueue.ts +75 -0
- package/src/engine/game/meetingStoneUse.ts +54 -0
- package/src/engine/game/newGameState.ts +363 -0
- package/src/engine/game/oocAuraInterrupt.ts +92 -0
- package/src/engine/game/oocCast.ts +241 -0
- package/src/engine/game/oocCastEffects.ts +803 -0
- package/src/engine/game/oocCastMethods.ts +1077 -0
- package/src/engine/game/openLockFromSpell.ts +174 -0
- package/src/engine/game/outdoorPvp.ts +187 -0
- package/src/engine/game/outfitter.ts +644 -0
- package/src/engine/game/paladinBoard.ts +393 -0
- package/src/engine/game/partyAuras.ts +121 -0
- package/src/engine/game/persistCombatAuras.ts +91 -0
- package/src/engine/game/pets.ts +459 -0
- package/src/engine/game/prePullHealthstones.ts +154 -0
- package/src/engine/game/professions.ts +2227 -0
- package/src/engine/game/pruneStaleRuntime.ts +39 -0
- package/src/engine/game/questFightCredit.ts +36 -0
- package/src/engine/game/questItemOnCorpse.ts +166 -0
- package/src/engine/game/questItemOnCreature.ts +103 -0
- package/src/engine/game/questLog.ts +879 -0
- package/src/engine/game/questSpellComplete.ts +21 -0
- package/src/engine/game/questTurnIn.ts +598 -0
- package/src/engine/game/raidParty.ts +32 -0
- package/src/engine/game/reagentBuyIn.ts +39 -0
- package/src/engine/game/regenRest.ts +186 -0
- package/src/engine/game/rotation.ts +425 -0
- package/src/engine/game/runFullDungeon.ts +611 -0
- package/src/engine/game/runSupply.ts +80 -0
- package/src/engine/game/sanitizeDriftedItems.ts +135 -0
- package/src/engine/game/scriptDevGoUse.ts +397 -0
- package/src/engine/game/scriptDevInnkeeper.ts +150 -0
- package/src/engine/game/scriptDevProfessionSpec.ts +99 -0
- package/src/engine/game/sendEventFromSpell.ts +87 -0
- package/src/engine/game/sortContainers.ts +85 -0
- package/src/engine/game/spellReputation.ts +69 -0
- package/src/engine/game/squad/squadDungeonSeating.ts +197 -0
- package/src/engine/game/squadDungeon.ts +4222 -0
- package/src/engine/game/squadQuestShare.ts +260 -0
- package/src/engine/game/summonObjectSlot.ts +69 -0
- package/src/engine/game/summonObjectWild.ts +73 -0
- package/src/engine/game/summoningRitualUse.ts +141 -0
- package/src/engine/game/talents.ts +356 -0
- package/src/engine/game/tamingRodUse.ts +66 -0
- package/src/engine/game/tempSpawnCreature.ts +136 -0
- package/src/engine/game/tickDungeonPatrolScripts.ts +205 -0
- package/src/engine/game/tickTimedQuests.ts +28 -0
- package/src/engine/game/trade.ts +586 -0
- package/src/engine/game/travel.ts +1155 -0
- package/src/engine/game/types.ts +1855 -0
- package/src/engine/game/vendorOffers.ts +244 -0
- package/src/engine/game/weaponImbueItem.ts +281 -0
- package/src/engine/game/worldSession.ts +183 -0
- package/src/engine/game/worldState.ts +166 -0
- package/src/engine/game.ts +968 -0
- package/src/engine/spells/GAP.md +67 -0
- package/src/engine/spells/absorbs.ts +376 -0
- package/src/engine/spells/addTargetTrigger.ts +53 -0
- package/src/engine/spells/auraPerSecondCost.ts +50 -0
- package/src/engine/spells/auraStacking.ts +497 -0
- package/src/engine/spells/cmangos-special-cases-combustion.ts +13 -0
- package/src/engine/spells/cmangos-special-cases-s18.ts +445 -0
- package/src/engine/spells/cmangos-special-cases-s19.ts +324 -0
- package/src/engine/spells/cmangos-special-cases-s20.ts +385 -0
- package/src/engine/spells/cmangos-special-cases-s21.ts +116 -0
- package/src/engine/spells/cmangos-special-cases-s22.ts +24 -0
- package/src/engine/spells/cmangos-special-cases-s23.ts +52 -0
- package/src/engine/spells/cmangos-special-cases.ts +1570 -0
- package/src/engine/spells/conditions.ts +718 -0
- package/src/engine/spells/effects.ts +203 -0
- package/src/engine/spells/fixtures/oracle-effects-lock9a.json +1 -0
- package/src/engine/spells/immunity.ts +942 -0
- package/src/engine/spells/intentEffects.ts +351 -0
- package/src/engine/spells/interpreter/GAPS.md +21 -0
- package/src/engine/spells/interpreter/amount.ts +109 -0
- package/src/engine/spells/interpreter/class-scripts-kit.ts +478 -0
- package/src/engine/spells/interpreter/diff.ts +246 -0
- package/src/engine/spells/interpreter/duration.ts +90 -0
- package/src/engine/spells/interpreter/form-attrs.json +1 -0
- package/src/engine/spells/interpreter/form-attrs.ts +95 -0
- package/src/engine/spells/interpreter/handlers/aura-remove.ts +113 -0
- package/src/engine/spells/interpreter/handlers/auras-control.ts +1547 -0
- package/src/engine/spells/interpreter/handlers/auras-core.ts +1583 -0
- package/src/engine/spells/interpreter/handlers/auras-misc.ts +1774 -0
- package/src/engine/spells/interpreter/handlers/effect-disenchant.ts +17 -0
- package/src/engine/spells/interpreter/handlers/effect-skinning.ts +20 -0
- package/src/engine/spells/interpreter/handlers/effects-action-utility.ts +1455 -0
- package/src/engine/spells/interpreter/handlers/effects-core.ts +432 -0
- package/src/engine/spells/interpreter/handlers/effects-remainder.ts +105 -0
- package/src/engine/spells/interpreter/handlers/effects-summon-item.ts +687 -0
- package/src/engine/spells/interpreter/handlers/mod-increase-energy-percent.ts +36 -0
- package/src/engine/spells/interpreter/handlers/mod-increase-energy.ts +36 -0
- package/src/engine/spells/interpreter/handlers/periodic-trigger-zero.ts +327 -0
- package/src/engine/spells/interpreter/handlers/procs.ts +131 -0
- package/src/engine/spells/interpreter/handlers/registry.ts +975 -0
- package/src/engine/spells/interpreter/handlers/shared.ts +667 -0
- package/src/engine/spells/interpreter/handlers.ts +9 -0
- package/src/engine/spells/interpreter/hunter-kit.ts +202 -0
- package/src/engine/spells/interpreter/index.ts +525 -0
- package/src/engine/spells/interpreter/item-triggers.ts +1121 -0
- package/src/engine/spells/interpreter/lift-consume-energy.ts +37 -0
- package/src/engine/spells/interpreter/mage-portals.ts +37 -0
- package/src/engine/spells/interpreter/materialize-charm.ts +88 -0
- package/src/engine/spells/interpreter/materialize-dispel-mechanic.ts +25 -0
- package/src/engine/spells/interpreter/materialize-rating-buffs.ts +81 -0
- package/src/engine/spells/interpreter/materialize-speed.ts +109 -0
- package/src/engine/spells/interpreter/materialize-spell-magnet.ts +22 -0
- package/src/engine/spells/interpreter/opcodes.ts +789 -0
- package/src/engine/spells/interpreter/paladin-kit.ts +95 -0
- package/src/engine/spells/interpreter/paladin-seals.ts +499 -0
- package/src/engine/spells/interpreter/priest-warlock-kit.ts +135 -0
- package/src/engine/spells/interpreter/proc.ts +443 -0
- package/src/engine/spells/interpreter/registry/helpers.ts +16 -0
- package/src/engine/spells/interpreter/registry/index.ts +216 -0
- package/src/engine/spells/interpreter/registry/packs/class-abilities.ts +304 -0
- package/src/engine/spells/interpreter/registry/packs/dire-maul.ts +44 -0
- package/src/engine/spells/interpreter/registry/packs/dummy-script-tail.ts +1065 -0
- package/src/engine/spells/interpreter/registry/packs/ex-wave-m.ts +70 -0
- package/src/engine/spells/interpreter/registry/packs/life-tap.ts +107 -0
- package/src/engine/spells/interpreter/registry/packs/paladin.ts +246 -0
- package/src/engine/spells/interpreter/registry/packs/raid-script-summons.ts +26 -0
- package/src/engine/spells/interpreter/registry/packs/raid-threat-wipe.ts +22 -0
- package/src/engine/spells/interpreter/registry/packs/shaman.ts +214 -0
- package/src/engine/spells/interpreter/registry/packs/si4-dummy-generic-a.ts +269 -0
- package/src/engine/spells/interpreter/registry/packs/si4-dummy-generic-b.ts +370 -0
- package/src/engine/spells/interpreter/registry/packs/si4-dummy-generic-c.ts +172 -0
- package/src/engine/spells/interpreter/registry/packs/si4-dummy-hunter.ts +60 -0
- package/src/engine/spells/interpreter/registry/packs/si4-dummy-warrior.ts +31 -0
- package/src/engine/spells/interpreter/registry/packs/si4-script-generic-a.ts +114 -0
- package/src/engine/spells/interpreter/registry/packs/si4-script-generic-b.ts +249 -0
- package/src/engine/spells/interpreter/registry/packs/si4-script-generic-c.ts +233 -0
- package/src/engine/spells/interpreter/registry/types.ts +48 -0
- package/src/engine/spells/interpreter/talent-scripted.ts +329 -0
- package/src/engine/spells/interpreter/targeting.ts +90 -0
- package/src/engine/spells/interpreter/types-intent.ts +1501 -0
- package/src/engine/spells/interpreter/types.ts +419 -0
- package/src/engine/spells/interpreter/warrior-druid-kit.ts +314 -0
- package/src/engine/spells/interpreter/weapon-imbue-kit.ts +384 -0
- package/src/engine/spells/interrupt.ts +217 -0
- package/src/engine/spells/meleeAttackReset.ts +60 -0
- package/src/engine/spells/oocTriggerSpell.ts +49 -0
- package/src/engine/spells/overrideClassScripts.ts +319 -0
- package/src/engine/spells/parryHaste.ts +20 -0
- package/src/engine/spells/procs.ts +300 -0
- package/src/engine/spells/recovery.ts +203 -0
- package/src/engine/spells/scriptTargetDest.ts +76 -0
- package/src/engine/spells/scriptTargets.ts +85 -0
- package/src/engine/spells/singleTargetAura.ts +82 -0
- package/src/engine/spells/specialCasePayloads.ts +57 -0
- package/src/engine/spells/spellAttributes.ts +249 -0
- package/src/engine/spells/spellBonus.ts +143 -0
- package/src/engine/spells/spellModIntents.ts +419 -0
- package/src/engine/spells/spellMods.ts +283 -0
- package/src/engine/spells/spellRadius.ts +15 -0
- package/src/engine/spells/trapObjects.ts +26 -0
- package/src/json.d.ts +7 -0
- package/src/persistence/memoryRepository.ts +23 -0
- package/src/persistence/saveBackups.ts +162 -0
- package/src/persistence/saveFile.ts +68 -0
- package/src/persistence/saveRepository.ts +11 -0
- package/src/persistence/saveShape.ts +668 -0
- package/src/progression/leveling.ts +89 -0
- package/src/progression/rest.ts +1434 -0
- package/src/progression/training.ts +226 -0
- package/src/progression/xpGain.ts +369 -0
- package/src/quests/quests.ts +1580 -0
- package/src/world/aoePull.ts +198 -0
- package/src/world/areaTableDbc.ts +39 -0
- package/src/world/areaTableMapIdPairs.ts +141 -0
- package/src/world/bakeSlug.ts +43 -0
- package/src/world/continuousDungeon.ts +1250 -0
- package/src/world/dungeonBakeProjection.ts +190 -0
- package/src/world/dungeonMapDensity.ts +342 -0
- package/src/world/dungeonMapProjection.ts +135 -0
- package/src/world/dungeonNav.ts +904 -0
- package/src/world/dungeonNpcSim.ts +147 -0
- package/src/world/dungeonObjectives.ts +234 -0
- package/src/world/dungeonPreset.ts +35 -0
- package/src/world/dungeonRoute.ts +311 -0
- package/src/world/dungeonRouteGeometry.ts +732 -0
- package/src/world/dungeonRouteOrder.ts +1037 -0
- package/src/world/dungeonWingRooms.ts +426 -0
- package/src/world/escort.ts +296 -0
- package/src/world/goPoolRoll.ts +75 -0
- package/src/world/instanceObjects.ts +785 -0
- package/src/world/instances.ts +1572 -0
- package/src/world/meetingStones.ts +56 -0
- package/src/world/navmeshFloors.ts +145 -0
- package/src/world/nearGrid.ts +167 -0
- package/src/world/packBudget.ts +613 -0
- package/src/world/rareRoll.ts +335 -0
- package/src/world/routeAuthoring.ts +132 -0
- package/src/world/routeEdit.ts +209 -0
- package/src/world/world.ts +812 -0
- package/src/world/zoneHazards.ts +47 -0
|
@@ -0,0 +1,1007 @@
|
|
|
1
|
+
// Creature cast-list scheduling and the EVENT_HANDLERS conditions. Pure domain; combat injects
|
|
2
|
+
// an Rng only when arming random timers. Cite: CreatureEventAI.cpp CheckEvent ~266–551.
|
|
3
|
+
|
|
4
|
+
import type {
|
|
5
|
+
CreatureCastEntry,
|
|
6
|
+
SpellDef,
|
|
7
|
+
} from '../../data/schema.ts';
|
|
8
|
+
import type { Rng } from '../../engine/core/rng.ts';
|
|
9
|
+
import { SPELL_LIST_TARGET, TARGET_T } from './aiTargeting.ts';
|
|
10
|
+
import {
|
|
11
|
+
CAST_FLAG,
|
|
12
|
+
EFLAG_RANDOM_ACTION,
|
|
13
|
+
EFLAG_REPEATABLE,
|
|
14
|
+
EVENT_T,
|
|
15
|
+
} from './creatureAiConstants.ts';
|
|
16
|
+
|
|
17
|
+
export type { CreatureCastEntry };
|
|
18
|
+
|
|
19
|
+
// UpdateSpellLists burns a 1200 ms timer at the top of every evaluation and stops at the first
|
|
20
|
+
// blocking row that casts, so one spell-list ability lands per poll. Cite: UnitAI.cpp:1187.
|
|
21
|
+
export const SPELL_LIST_POLL_MS = 1200;
|
|
22
|
+
|
|
23
|
+
// Cast scheduler (phase-1 EventAI cast + CD)
|
|
24
|
+
|
|
25
|
+
export interface CreatureCastSlot {
|
|
26
|
+
entry: CreatureCastEntry;
|
|
27
|
+
spell: SpellDef;
|
|
28
|
+
/** Session time when this cast becomes eligible again. */
|
|
29
|
+
readyAt: number;
|
|
30
|
+
/** AGGRO / non-repeatable one-shots. */
|
|
31
|
+
exhausted: boolean;
|
|
32
|
+
/** Stable list order index (tpl order or script order). */
|
|
33
|
+
listIndex: number;
|
|
34
|
+
/** CAST_OK fires this pull. Cite: boss_gothik.cpp:117 m_teleportCount. */
|
|
35
|
+
fires: number;
|
|
36
|
+
/** Unshuffled rotate kit. Cite: boss_fankriss.cpp:91 m_summonWormSpells. */
|
|
37
|
+
rotatePool?: SpellDef[];
|
|
38
|
+
/** Current shuffled order. Cite: boss_fankriss.cpp:99 std::shuffle. */
|
|
39
|
+
rotateOrder?: SpellDef[];
|
|
40
|
+
/** Remaining casts this wave. Cite: boss_fankriss.cpp:90 m_wormsSpawnPerWave. */
|
|
41
|
+
waveLeft?: number;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Friendly unit snapshot for FRIENDLY_HP / FRIENDLY_IS_CC / FRIENDLY_MISSING_BUFF. Cite:
|
|
45
|
+
// CreatureEventAI.cpp DoSelectLowestHpFriendly / DoFindFriendly* ~379–437.
|
|
46
|
+
export interface EventAiFriendlyUnit {
|
|
47
|
+
/** Current HP percent 0–100. */
|
|
48
|
+
hpPct: number;
|
|
49
|
+
/** Current HP (absolute); used when friendlyHpIsPercent is false. */
|
|
50
|
+
hp: number;
|
|
51
|
+
/** Max HP. */
|
|
52
|
+
maxHp: number;
|
|
53
|
+
/** Yards from the scanning creature; omit when distance not modeled → treated as in-range. */
|
|
54
|
+
distanceYd?: number;
|
|
55
|
+
/** True when unit has ≥ stacks of spellId. */
|
|
56
|
+
hasAura?: (spellId: number, stacks: number) => boolean;
|
|
57
|
+
/** True when unit is crowd-controlled (FRIENDLY_IS_CC). */
|
|
58
|
+
hasCc?: boolean;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface CastPickContext {
|
|
62
|
+
t: number;
|
|
63
|
+
/** Current HP percent 0–100 (self). */
|
|
64
|
+
hpPct: number;
|
|
65
|
+
/** Self mana percent 0–100; omit when creature has no mana pool. */
|
|
66
|
+
manaPct?: number;
|
|
67
|
+
/** Victim HP percent 0–100. */
|
|
68
|
+
targetHpPct?: number;
|
|
69
|
+
/** Victim mana percent 0–100; omit when victim has no mana. */
|
|
70
|
+
targetManaPct?: number;
|
|
71
|
+
// True when self has at least `stacks` of `spellId` (buffActive / stack model). Cite:
|
|
72
|
+
// EVENT_T_MISSING_AURA / AURA — CreatureEventAI.cpp ~472–495.
|
|
73
|
+
hasAura?: (spellId: number, stacks: number) => boolean;
|
|
74
|
+
/** Same for victim (TARGET_MISSING_AURA / TARGET_AURA). */
|
|
75
|
+
targetHasAura?: (spellId: number, stacks: number) => boolean;
|
|
76
|
+
/** Victim is mid non-melee cast. Cite: EVENT_T_TARGET_CASTING ~375–377. */
|
|
77
|
+
targetCasting?: boolean;
|
|
78
|
+
// Yards to victim. When undefined, RANGE events are skipped (no invented distance). Cite: EVENT_T_RANGE
|
|
79
|
+
// ~349–354.
|
|
80
|
+
distanceYd?: number;
|
|
81
|
+
// EventAI m_Phase (default 0). Used with entry.eventInversePhaseMask. Cite: CreatureEventAI.cpp
|
|
82
|
+
// CheckEvent ~266–270.
|
|
83
|
+
phase?: number;
|
|
84
|
+
// In-combat flag. Default true (combat schedulers). TIMER_OOC requires false. Cite: CreatureEventAI.cpp
|
|
85
|
+
// TIMER_OOC ~295–297, TIMER_IN_COMBAT ~291–293.
|
|
86
|
+
inCombat?: boolean;
|
|
87
|
+
// Ally scan list for FRIENDLY_* events. When absent, FRIENDLY_* conditions fail (do not invent multi-unit
|
|
88
|
+
// state). Cite: CheckEvent ~379–437.
|
|
89
|
+
friendlies?: readonly EventAiFriendlyUnit[];
|
|
90
|
+
// FACING_TARGET: 0 = creature is in victim's back (melee), 1 = in front. When undefined, FACING_TARGET
|
|
91
|
+
// fails closed. Cite: CheckEvent ~530–541.
|
|
92
|
+
facingTarget?: 0 | 1;
|
|
93
|
+
// TARGET_NOT_REACHABLE: victim present + combat movement + path not reachable. Cite: CheckEvent ~545–547.
|
|
94
|
+
targetNotReachable?: boolean;
|
|
95
|
+
// SELECT_ATTACKING_TARGET: true when a random threat target exists in [min,max] range. Cite: CheckEvent
|
|
96
|
+
// ~519–527.
|
|
97
|
+
hasAttackingTargetInRange?: boolean;
|
|
98
|
+
// Is ANY living hostile inside melee reach? SELECT_FLAG_IN_MELEE_RANGE used as an existence test
|
|
99
|
+
// before a self-centred AoE, not as a pick. Cite: shadowfang_keep.cpp:477; darkshore.cpp:805.
|
|
100
|
+
anyHostileInMelee?: boolean;
|
|
101
|
+
// Lifecycle pulses — true only when the matching C++ hook would enqueue the event. Cite:
|
|
102
|
+
// JustRespawned ~1401; EnterEvadeMode ~1478; JustReachedHome ~1462; ReceiveAIEvent ~1565.
|
|
103
|
+
spawned?: boolean;
|
|
104
|
+
evading?: boolean;
|
|
105
|
+
reachedHome?: boolean;
|
|
106
|
+
oocLos?: boolean;
|
|
107
|
+
/** RECEIVE_AI_EVENT pulse (matches event.receiveAIEvent). */
|
|
108
|
+
receivedAiEvent?: { eventType: number; senderEntry: number };
|
|
109
|
+
/** RECEIVE_EMOTE pulse. */
|
|
110
|
+
receivedEmoteId?: number;
|
|
111
|
+
/** SUMMONED_UNIT / SUMMONED_JUST_DIED / SUMMONED_JUST_DESPAWN invoker entry. */
|
|
112
|
+
summonedEntry?: number;
|
|
113
|
+
/** SPELLHIT / SPELLHIT_TARGET pulse. */
|
|
114
|
+
spellHitId?: number;
|
|
115
|
+
spellHitSchoolMask?: number;
|
|
116
|
+
/** SPELL_CAST: spell id just cast by self. */
|
|
117
|
+
spellCastId?: number;
|
|
118
|
+
// JustDied / JustKilledUnit pulse flags — DEATH / KILL holders must not fire on the living poll path (C++
|
|
119
|
+
// only enqueues from those hooks). Cite: CreatureEventAI.cpp JustDied / KilledUnit.
|
|
120
|
+
justDied?: boolean;
|
|
121
|
+
justKilled?: boolean;
|
|
122
|
+
// combat_condition gate for a spell-list row. Absent → ungated (the caller has no condition tables),
|
|
123
|
+
// which keeps a fixture bundle firing every row it fires today. Cite: UnitAI.cpp:1218-1224.
|
|
124
|
+
combatConditionOk?: (conditionId: number) => boolean;
|
|
125
|
+
// ScriptDev instance-data gate. Absent → ungated. Cite: boss_hakkar.cpp aspect CombatCondition 1005-1009
|
|
126
|
+
// / zulgurub.h TYPE_*.
|
|
127
|
+
instanceDataOk?: (field: number, equals?: number, notEquals?: number) => boolean;
|
|
128
|
+
// Current victim in melee. `false` = known out; `true`/absent = in melee or unknown. Cite: Unit.cpp
|
|
129
|
+
// CanReachWithMeleeAttack; boss_grobbulus.cpp:97-110.
|
|
130
|
+
victimInMelee?: boolean;
|
|
131
|
+
// False on a tick BETWEEN two spell-list evaluations: blocking rows are not eligible, while
|
|
132
|
+
// EventAI and ScriptDev slots keep their own timers. Absent is open. Cite: UnitAI.cpp:1187.
|
|
133
|
+
spellListPollOpen?: boolean;
|
|
134
|
+
// The creature already has a bar up or a channel running, so a ready timer WAITS rather than
|
|
135
|
+
// clipping: only CAST_TRIGGERED / CAST_INTERRUPT_PREVIOUS get through. Cite: UnitAI.cpp:178.
|
|
136
|
+
selfCasting?: boolean;
|
|
137
|
+
// Probability weight roll among the eligible spell-list rows. Absent is position priority,
|
|
138
|
+
// which is what C++ falls back to when every Probability is 0. Cite: UnitAI.cpp:1246-1291.
|
|
139
|
+
rng?: Rng;
|
|
140
|
+
/** Whether personal/category CD allows this spell id. */
|
|
141
|
+
spellReady: (spell: SpellDef) => boolean;
|
|
142
|
+
/** Power / silence / etc. */
|
|
143
|
+
canCast: (spell: SpellDef) => boolean;
|
|
144
|
+
// True when the intended cast target already has this spell's aura (any stacks). Used by
|
|
145
|
+
// CAST_AURA_NOT_PRESENT (0x20). Cite: CreatureEventAI.cpp ACTION_T_CAST ~792–793.
|
|
146
|
+
targetHasSpellAura?: (spellId: number) => boolean;
|
|
147
|
+
// Same, but for the CASTER — the row's target resolves to self/none, so C++ reads `caster->HasAura` and
|
|
148
|
+
// never the victim's bag. Cite: UnitAI.cpp:184-192 `if (!target) { if (caster->HasAura(spellId)) ... }`.
|
|
149
|
+
selfHasSpellAura?: (spellId: number) => boolean;
|
|
150
|
+
// CAST_PLAYER_ONLY — true when the resolved cast target is a player-controlled unit. Cite:
|
|
151
|
+
// CreatureEventAI.cpp ~790–791 SELECT_FLAG_PLAYER.
|
|
152
|
+
targetIsPlayer?: boolean;
|
|
153
|
+
// True when a living hostile player has this Unit::getClass() value, so a selectClass slot is
|
|
154
|
+
// skipped when none is on the threat list. Cite: boss_nefarian.cpp:266.
|
|
155
|
+
hostileHasClass?: (classId: number) => boolean;
|
|
156
|
+
// CAST_IGNORE_UNSELECTABLE — true when the target has UNIT_FLAG_NOT_SELECTABLE / UNINTERACTIBLE set.
|
|
157
|
+
// Absent → treat as selectable. Cite: UnitAI.cpp ~214 TRIGGERED_IGNORE_UNSELECTABLE_FLAG.
|
|
158
|
+
targetUnselectable?: boolean;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// A pollOnly row casts nothing, so it must arm even when it names no spell — Heigan's taunt and the
|
|
162
|
+
// Thaddius revive tick were both dropped here. Cite: boss_four_horsemen.cpp:169-174.
|
|
163
|
+
const POLL_ONLY_PLACEHOLDERS = new Map<number, SpellDef>();
|
|
164
|
+
|
|
165
|
+
function pollOnlyPlaceholder(spellId: number): SpellDef {
|
|
166
|
+
const id = spellId | 0;
|
|
167
|
+
const hit = POLL_ONLY_PLACEHOLDERS.get(id);
|
|
168
|
+
if (hit) return hit;
|
|
169
|
+
const made: SpellDef = {
|
|
170
|
+
id,
|
|
171
|
+
name: 'Scripted Action',
|
|
172
|
+
rank: 0,
|
|
173
|
+
school: 'physical',
|
|
174
|
+
powerType: 'mana',
|
|
175
|
+
cost: 0,
|
|
176
|
+
castTimeMs: 0,
|
|
177
|
+
cooldownMs: 0,
|
|
178
|
+
gcdMs: 0,
|
|
179
|
+
rangeMax: 0,
|
|
180
|
+
reqLevel: 0,
|
|
181
|
+
effects: [],
|
|
182
|
+
};
|
|
183
|
+
POLL_ONLY_PLACEHOLDERS.set(id, made);
|
|
184
|
+
return made;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Which spell this ROW casts for the spawn's life: a pickPerSpawn pool is drawn ONCE, and an
|
|
188
|
+
// all-absent pool falls back to the declared id. Cite: zulgurubScripts.cpp:61,110-115.
|
|
189
|
+
export function pickPerSpawnSpellId(
|
|
190
|
+
entry: CreatureCastEntry,
|
|
191
|
+
spellsById: ReadonlyMap<number, SpellDef>,
|
|
192
|
+
rng?: Rng,
|
|
193
|
+
): number {
|
|
194
|
+
const pool = (entry.pickPerSpawnSpellIds ?? []).map((id) => id | 0).filter((id) => spellsById.has(id));
|
|
195
|
+
if (pool.length === 0) return entry.spellId;
|
|
196
|
+
if (!rng || pool.length === 1) return pool[0]!;
|
|
197
|
+
return pool[rng.int(0, pool.length - 1)]!;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Arm slots at combat or OOC start: TIMER_* rows take their initial min/max (urand when an rng
|
|
201
|
+
// is given), and AGGRO or bare spells start readyAt 0. Cite: CreatureEventAI.cpp ~1607.
|
|
202
|
+
export function armCreatureCastSlots(
|
|
203
|
+
entries: readonly CreatureCastEntry[],
|
|
204
|
+
spellsById: ReadonlyMap<number, SpellDef>,
|
|
205
|
+
rng?: Rng,
|
|
206
|
+
): CreatureCastSlot[] {
|
|
207
|
+
const slots: CreatureCastSlot[] = [];
|
|
208
|
+
for (let i = 0; i < entries.length; i++) {
|
|
209
|
+
const entry = entries[i]!;
|
|
210
|
+
// One urand pick per SPAWN, held all fight — so it belongs here, not in the per-fire scheduler
|
|
211
|
+
// where rotateSpellIds lives. Cite: zulgurubScripts.cpp:61,110-115.
|
|
212
|
+
const spell = spellsById.get(pickPerSpawnSpellId(entry, spellsById, rng))
|
|
213
|
+
?? (entry.pollOnly === true ? pollOnlyPlaceholder(entry.spellId) : undefined);
|
|
214
|
+
if (!spell) continue;
|
|
215
|
+
let readyAt = 0;
|
|
216
|
+
const et = entry.eventType;
|
|
217
|
+
// TIMER_* events roll an initial CD, and bare creatureCasts that only set initialMinMs are
|
|
218
|
+
// schedule-shaped the same way, so honor the delay. Cite: UnitAI.cpp AddInitialCooldowns.
|
|
219
|
+
const timerEvent =
|
|
220
|
+
et === EVENT_T.TIMER_IN_COMBAT ||
|
|
221
|
+
et === EVENT_T.TIMER_OOC ||
|
|
222
|
+
et === EVENT_T.TIMER_GENERIC;
|
|
223
|
+
const bareInitial =
|
|
224
|
+
et == null && (entry.initialMinMs != null || entry.initialMaxMs != null);
|
|
225
|
+
if (timerEvent || bareInitial) {
|
|
226
|
+
readyAt = rollTimer(entry.initialMinMs ?? 0, entry.initialMaxMs ?? entry.initialMinMs ?? 0, rng);
|
|
227
|
+
}
|
|
228
|
+
// AddCombatAction(id, true) → disabled until ResetCombatAction / arm_timer.
|
|
229
|
+
// Cite: boss_marli.cpp:72; TimerAI.cpp disabled ctor → CreatureCastSlot.exhausted.
|
|
230
|
+
const slot: CreatureCastSlot = {
|
|
231
|
+
entry,
|
|
232
|
+
spell,
|
|
233
|
+
readyAt,
|
|
234
|
+
exhausted: entry.startDisabled === true,
|
|
235
|
+
listIndex: i,
|
|
236
|
+
fires: 0,
|
|
237
|
+
};
|
|
238
|
+
const rotateIds = entry.rotateSpellIds ?? [];
|
|
239
|
+
if (rotateIds.length > 0) {
|
|
240
|
+
const pool: SpellDef[] = [];
|
|
241
|
+
for (const id of rotateIds) {
|
|
242
|
+
const sp = spellsById.get(id | 0);
|
|
243
|
+
if (sp) pool.push(sp);
|
|
244
|
+
}
|
|
245
|
+
if (pool.length > 0) {
|
|
246
|
+
slot.rotatePool = pool;
|
|
247
|
+
initRotateWave(slot, rng);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
slots.push(slot);
|
|
251
|
+
}
|
|
252
|
+
return slots;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// AuraInterruptFlags bits any pull clears: the creature swings, takes damage, or eats a hostile
|
|
256
|
+
// spell early. Sneak 7939 carries 0x1803, so it must not be re-applied as spawn state.
|
|
257
|
+
const AURA_INTERRUPT_ON_PULL = 0x1 | 0x2 | 0x1000 | 0x01000000;
|
|
258
|
+
|
|
259
|
+
// The EVENT_T_TIMER_OOC self-casts a creature already wears when the party pulls it: an OOC row
|
|
260
|
+
// arms at spawn and can never fire in combat, so only its permanent auras carry (126 rows).
|
|
261
|
+
export function spawnOocSelfAuraRows(
|
|
262
|
+
entries: readonly CreatureCastEntry[],
|
|
263
|
+
spellsById: ReadonlyMap<number, SpellDef>,
|
|
264
|
+
): Array<{ entry: CreatureCastEntry; spell: SpellDef }> {
|
|
265
|
+
const out: Array<{ entry: CreatureCastEntry; spell: SpellDef }> = [];
|
|
266
|
+
for (const entry of entries) {
|
|
267
|
+
if (entry.eventType !== EVENT_T.TIMER_OOC) continue;
|
|
268
|
+
// action target 0 is dropped by the bake (lib-eventai-cast.mjs:63), so absent IS self.
|
|
269
|
+
const tgt = entry.target;
|
|
270
|
+
if (entry.targetSpace === 'spell_list') continue;
|
|
271
|
+
if (!(tgt == null || tgt === TARGET_T.SELF || tgt === TARGET_T.NONE)) continue;
|
|
272
|
+
const spell = spellsById.get(entry.spellId);
|
|
273
|
+
if (!spell) continue;
|
|
274
|
+
if (!spell.rawEffects?.some((r) => (r.effect | 0) === 6)) continue;
|
|
275
|
+
const fx = spell.effects ?? [];
|
|
276
|
+
if (fx.length === 0 || fx.some((f) => ('durationMs' in f ? (f.durationMs ?? 0) : 0) > 0)) continue;
|
|
277
|
+
if (((spell.auraInterruptFlags ?? 0) & AURA_INTERRUPT_ON_PULL) !== 0) continue;
|
|
278
|
+
out.push({ entry, spell });
|
|
279
|
+
}
|
|
280
|
+
return out;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// The rows above, minus the ones whose event_chance missed. A miss re-arms the repeat timer then
|
|
284
|
+
// disables the row, so it is one roll per spawn, not a retry loop. Cite: CreatureEventAI.cpp:606.
|
|
285
|
+
export function spawnOocSelfAuras(
|
|
286
|
+
entries: readonly CreatureCastEntry[],
|
|
287
|
+
spellsById: ReadonlyMap<number, SpellDef>,
|
|
288
|
+
rng?: Rng,
|
|
289
|
+
): SpellDef[] {
|
|
290
|
+
const out: SpellDef[] = [];
|
|
291
|
+
for (const row of spawnOocSelfAuraRows(entries, spellsById)) {
|
|
292
|
+
if (!rollCreatureCastChance(row.entry, rng)) continue;
|
|
293
|
+
out.push(row.spell);
|
|
294
|
+
}
|
|
295
|
+
return out;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function shuffleSpellDefs(pool: readonly SpellDef[], rng?: Rng): SpellDef[] {
|
|
299
|
+
const out = pool.slice();
|
|
300
|
+
for (let i = out.length - 1; i > 0; i--) {
|
|
301
|
+
const j = rng ? rng.int(0, i) : i;
|
|
302
|
+
const tmp = out[i]!;
|
|
303
|
+
out[i] = out[j]!;
|
|
304
|
+
out[j] = tmp;
|
|
305
|
+
}
|
|
306
|
+
return out;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/** Aggro shuffle + urand wave size. Cite: boss_fankriss.cpp:90-99,150. */
|
|
310
|
+
function initRotateWave(slot: CreatureCastSlot, rng?: Rng): void {
|
|
311
|
+
const pool = slot.rotatePool;
|
|
312
|
+
if (!pool?.length) return;
|
|
313
|
+
slot.rotateOrder = shuffleSpellDefs(pool, rng);
|
|
314
|
+
const lo = slot.entry.waveMinCount ?? 1;
|
|
315
|
+
const hi = slot.entry.waveMaxCount ?? lo;
|
|
316
|
+
const n = rng ? rng.int(lo, hi) : lo;
|
|
317
|
+
slot.waveLeft = Math.max(1, Math.min(n, pool.length));
|
|
318
|
+
slot.spell = slot.rotateOrder[slot.waveLeft - 1] ?? slot.spell;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// CAST_OK of a rotating-wave slot. Intra-wave 5-7s; else reshuffle and let repeat re-arm. Cite:
|
|
322
|
+
// boss_fankriss.cpp:141-157.
|
|
323
|
+
function advanceRotateWave(slot: CreatureCastSlot, t: number, rng?: Rng): boolean {
|
|
324
|
+
if (!slot.rotatePool?.length) return false;
|
|
325
|
+
const left = (slot.waveLeft ?? 1) - 1;
|
|
326
|
+
slot.waveLeft = left;
|
|
327
|
+
if (left > 0) {
|
|
328
|
+
slot.spell = slot.rotateOrder?.[left - 1] ?? slot.spell;
|
|
329
|
+
const lo = slot.entry.intraWaveMinMs ?? 0;
|
|
330
|
+
const hi = slot.entry.intraWaveMaxMs ?? lo;
|
|
331
|
+
if (lo > 0 || hi > 0) {
|
|
332
|
+
slot.readyAt = t + rollTimer(lo, hi, rng);
|
|
333
|
+
return true;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
initRotateWave(slot, rng);
|
|
337
|
+
return false;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Build cast entries from a bare spell id list. NOT a combat schedule source: specials must come
|
|
341
|
+
// from EventAI, spellList or ScriptDev timers. Kept for tests and explicit MAIN_SPELL wiring.
|
|
342
|
+
export function castEntriesFromSpellIds(spellIds: readonly number[]): CreatureCastEntry[] {
|
|
343
|
+
const out: CreatureCastEntry[] = [];
|
|
344
|
+
const seen = new Set<number>();
|
|
345
|
+
for (const id of spellIds) {
|
|
346
|
+
if (!id || seen.has(id)) continue;
|
|
347
|
+
seen.add(id);
|
|
348
|
+
out.push({ spellId: id });
|
|
349
|
+
}
|
|
350
|
+
return out;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// True when a cast entry has a real schedule — an EventAI event type, initial/repeat timers, or
|
|
354
|
+
// the weapon-paced CAST_MAIN_SPELL. Bare {spellId} rows are schedule-less specials.
|
|
355
|
+
export function hasCreatureCastSchedule(entry: CreatureCastEntry): boolean {
|
|
356
|
+
if (entry.eventType != null) return true;
|
|
357
|
+
if (entry.initialMinMs != null || entry.initialMaxMs != null) return true;
|
|
358
|
+
if (entry.repeatMinMs != null || entry.repeatMaxMs != null) return true;
|
|
359
|
+
if (((entry.castFlags ?? 0) & CAST_FLAG.MAIN_SPELL) !== 0) return true;
|
|
360
|
+
return false;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/** Dev-only: schedule-less special kits are bugs, never silent weapon-pace spam. */
|
|
364
|
+
const _bareSpecialWarned = new Set<string>();
|
|
365
|
+
export function warnScheduleLessCreatureSpecial(
|
|
366
|
+
creatureName: string,
|
|
367
|
+
spellIds: readonly number[],
|
|
368
|
+
): void {
|
|
369
|
+
const key = `${creatureName}:${spellIds.join(',')}`;
|
|
370
|
+
if (_bareSpecialWarned.has(key)) return;
|
|
371
|
+
_bareSpecialWarned.add(key);
|
|
372
|
+
const msg =
|
|
373
|
+
`[aifid D4] schedule-less creature special kit on "${creatureName}" ` +
|
|
374
|
+
`spells=[${spellIds.join(',')}] - specials need EventAI/spellList/ScriptDev schedule; ` +
|
|
375
|
+
`only melee auto uses weapon pace (GAPS D4). Template spells alone do not cast.`;
|
|
376
|
+
// Loud in dev / tests; never throw (determinism + production fights stay melee-only).
|
|
377
|
+
// eslint-disable-next-line no-console
|
|
378
|
+
console.warn(msg);
|
|
379
|
+
if (typeof process !== 'undefined' && process.env?.NODE_ENV !== 'production') {
|
|
380
|
+
// Prefer assert.ok style surface when `assert` is available via tests.
|
|
381
|
+
if (typeof globalThis !== 'undefined' && (globalThis as { __AIFID_D4_ASSERT?: boolean }).__AIFID_D4_ASSERT) {
|
|
382
|
+
throw new Error(msg);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/** Test helper — clear once-per-name warn cache. */
|
|
388
|
+
export function resetScheduleLessWarnCacheForTests(): void {
|
|
389
|
+
_bareSpecialWarned.clear();
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// Does this row's target resolve to the caster itself? EventAI SELF 0 / NONE 15, spell-list
|
|
393
|
+
// NONE 0 / SELF 2 — the 0–7 ids overlap, so the space decides. Cite: UnitAI.cpp:186-192.
|
|
394
|
+
function castTargetIsCaster(entry: CreatureCastEntry): boolean {
|
|
395
|
+
const mode = entry.target;
|
|
396
|
+
if (mode == null) return false;
|
|
397
|
+
return entry.targetSpace === 'spell_list'
|
|
398
|
+
? mode === SPELL_LIST_TARGET.NONE || mode === SPELL_LIST_TARGET.SELF
|
|
399
|
+
: mode === TARGET_T.SELF || mode === TARGET_T.NONE;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// Pick the next ready cast deterministically: eligible rows first, then lowest readyAt, listIndex,
|
|
403
|
+
// spellId. Null when none. The caller rolls event_chance and re-arms via markCreatureCastUsed.
|
|
404
|
+
export function pickCreatureCast(slots: readonly CreatureCastSlot[], ctx: CastPickContext): CreatureCastSlot | null {
|
|
405
|
+
let best: CreatureCastSlot | null = null;
|
|
406
|
+
let bestFromSpellList = false;
|
|
407
|
+
/** Eligible blocking spell-list rows this evaluation — the pool the Probability roll draws from. */
|
|
408
|
+
let listEligible: CreatureCastSlot[] | null = null;
|
|
409
|
+
const phase = ctx.phase ?? 0;
|
|
410
|
+
for (const slot of slots) {
|
|
411
|
+
if (slot.exhausted) continue;
|
|
412
|
+
const fireCap = slot.entry.maxFires ?? 0;
|
|
413
|
+
if (fireCap > 0 && slot.fires >= fireCap) continue;
|
|
414
|
+
if (ctx.t < slot.readyAt) continue;
|
|
415
|
+
// A spell-list row only fires on an evaluation tick; NON_BLOCKING rows are the exception and
|
|
416
|
+
// never compete for the slot. Cite: UnitAI.cpp:1187-1191 the 1200 ms timer; :1265 nonBlocking.
|
|
417
|
+
const fromSpellList =
|
|
418
|
+
(slot.entry.targetSpace === 'spell_list' || slot.entry.spellListPolled === true)
|
|
419
|
+
&& slot.entry.nonBlocking !== true;
|
|
420
|
+
if (fromSpellList && ctx.spellListPollOpen === false) continue;
|
|
421
|
+
// Mid-cast: hold the row instead of clipping the caster's own bar. Cite: UnitAI.cpp:178,:280.
|
|
422
|
+
if (ctx.selfCasting) {
|
|
423
|
+
const midCastOk = (slot.entry.castFlags ?? 0)
|
|
424
|
+
& (CAST_FLAG.TRIGGERED | CAST_FLAG.INTERRUPT_PREVIOUS);
|
|
425
|
+
if (midCastOk === 0) continue;
|
|
426
|
+
}
|
|
427
|
+
// Phase gate — CreatureEventAI.cpp CheckEvent ~266–270
|
|
428
|
+
if (eventPhaseBlocked(slot.entry.eventInversePhaseMask, phase)) continue;
|
|
429
|
+
if (!eventConditionsOk(slot.entry, ctx)) continue;
|
|
430
|
+
// creature_spell_list CombatCondition — re-checked every pick, like UpdateSpellLists.
|
|
431
|
+
// Cite: UnitAI.cpp:1218-1224.
|
|
432
|
+
const condId = slot.entry.combatCondition ?? 0;
|
|
433
|
+
if (condId > 0 && ctx.combatConditionOk && !ctx.combatConditionOk(condId)) continue;
|
|
434
|
+
if (slot.entry.instanceDataField != null && ctx.instanceDataOk) {
|
|
435
|
+
if (!ctx.instanceDataOk(
|
|
436
|
+
slot.entry.instanceDataField | 0,
|
|
437
|
+
slot.entry.instanceDataEquals,
|
|
438
|
+
slot.entry.instanceDataNotEquals,
|
|
439
|
+
)) continue;
|
|
440
|
+
}
|
|
441
|
+
if (slot.entry.requireVictimNotInMelee && ctx.victimInMelee !== false) continue;
|
|
442
|
+
// `if (Unit* target = SelectAttackingTarget(..., IN_MELEE_RANGE))` guarding a nullptr cast: the
|
|
443
|
+
// flag is an EXISTENCE test here. Absent knowledge is treated as unknown, so the row still fires.
|
|
444
|
+
if (slot.entry.requireMeleeTargetExists && ctx.anyHostileInMelee === false) continue;
|
|
445
|
+
if (slot.entry.pollOnly) {
|
|
446
|
+
if (!best || compareSlots(slot, best) < 0) best = slot;
|
|
447
|
+
continue;
|
|
448
|
+
}
|
|
449
|
+
// Cast-flag gates at pick time (DoCastSpellIfCan pre-checks + GetTargetByType selectFlags).
|
|
450
|
+
// Cite: CreatureEventAI.cpp ACTION_T_CAST ~784–802; UnitAI.cpp DoCastSpellIfCan ~160–223.
|
|
451
|
+
const flags = slot.entry.castFlags ?? 0;
|
|
452
|
+
// CAST_AURA_NOT_PRESENT — the aura is read off the SPELL'S target, so a self-cast reads the
|
|
453
|
+
// caster. Reading the victim made the gate never true and Venoxis re-cast Frenzy 171 times.
|
|
454
|
+
if ((flags & CAST_FLAG.AURA_NOT_PRESENT) !== 0) {
|
|
455
|
+
const holder = castTargetIsCaster(slot.entry) ? ctx.selfHasSpellAura : ctx.targetHasSpellAura;
|
|
456
|
+
if (holder?.(slot.spell.id)) continue;
|
|
457
|
+
}
|
|
458
|
+
// CAST_PLAYER_ONLY — ~790–791 SELECT_FLAG_PLAYER.
|
|
459
|
+
if ((flags & CAST_FLAG.PLAYER_ONLY) !== 0) {
|
|
460
|
+
if (ctx.targetIsPlayer === false) continue;
|
|
461
|
+
}
|
|
462
|
+
// getClass() existence — skip the arm when no player of that class is hostile.
|
|
463
|
+
// Cite: boss_nefarian.cpp:266-280; SharedDefines.h:65-79.
|
|
464
|
+
const needClass = slot.entry.selectClass ?? 0;
|
|
465
|
+
if (needClass > 0 && ctx.hostileHasClass && !ctx.hostileHasClass(needClass)) continue;
|
|
466
|
+
// CAST_TARGET_CASTING — ~794–795 SELECT_FLAG_CASTING.
|
|
467
|
+
if ((flags & CAST_FLAG.TARGET_CASTING) !== 0) {
|
|
468
|
+
if (!ctx.targetCasting) continue;
|
|
469
|
+
}
|
|
470
|
+
// CAST_IGNORE_UNSELECTABLE — without the bit, unselectable targets are invalid.
|
|
471
|
+
if ((flags & CAST_FLAG.IGNORE_UNSELECTABLE) === 0 && ctx.targetUnselectable === true) {
|
|
472
|
+
continue;
|
|
473
|
+
}
|
|
474
|
+
if (!ctx.spellReady(slot.spell)) continue;
|
|
475
|
+
// FORCE_CAST / FORCE_TARGET_SELF skip CanCastSpell (mana/range) — UnitAI.cpp ~196–202.
|
|
476
|
+
const forceCast =
|
|
477
|
+
(flags & (CAST_FLAG.FORCE_CAST | CAST_FLAG.FORCE_TARGET_SELF)) !== 0;
|
|
478
|
+
if (!forceCast && !ctx.canCast(slot.spell)) continue;
|
|
479
|
+
if (fromSpellList) (listEligible ??= []).push(slot);
|
|
480
|
+
if (!best || compareSlots(slot, best) < 0) { best = slot; bestFromSpellList = fromSpellList; }
|
|
481
|
+
}
|
|
482
|
+
// The spell list is weight-rolled, not ordered: C++ shuffles and rolls irand(0, sum-1), and sum 0
|
|
483
|
+
// is position priority. A ScriptDev-ported row carries no weight, so the FIFO pick stands.
|
|
484
|
+
if (bestFromSpellList && listEligible && listEligible.length > 1) {
|
|
485
|
+
return rollSpellListSlot(listEligible, ctx.rng) ?? best;
|
|
486
|
+
}
|
|
487
|
+
return best;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// One weighted draw over the eligible spell-list rows. Null when there is no Rng or every
|
|
491
|
+
// Probability is 0, and the caller then keeps its position-priority pick. Cite: UnitAI.cpp:1246.
|
|
492
|
+
function rollSpellListSlot(
|
|
493
|
+
eligible: readonly CreatureCastSlot[],
|
|
494
|
+
rng?: Rng,
|
|
495
|
+
): CreatureCastSlot | null {
|
|
496
|
+
if (!rng) return null;
|
|
497
|
+
let sum = 0;
|
|
498
|
+
for (const s of eligible) sum += Math.max(0, (s.entry.spellListProbability ?? 0) | 0);
|
|
499
|
+
if (sum <= 0) return null;
|
|
500
|
+
const order = eligible.slice();
|
|
501
|
+
for (let i = order.length - 1; i > 0; i--) {
|
|
502
|
+
const j = rng.int(0, i);
|
|
503
|
+
const tmp = order[i]!;
|
|
504
|
+
order[i] = order[j]!;
|
|
505
|
+
order[j] = tmp;
|
|
506
|
+
}
|
|
507
|
+
let roll = rng.int(0, sum - 1);
|
|
508
|
+
for (const s of order) {
|
|
509
|
+
const p = Math.max(0, (s.entry.spellListProbability ?? 0) | 0);
|
|
510
|
+
if (roll < p) return s;
|
|
511
|
+
roll -= p;
|
|
512
|
+
}
|
|
513
|
+
return null;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// One CombatAI getClass() switch: pick a random hostile player and fire the sibling whose
|
|
517
|
+
// selectClass matches. Null means no matching arm. Cite: boss_nefarian.cpp:266-287.
|
|
518
|
+
export function resolveClassCallCastSlot(
|
|
519
|
+
slots: readonly CreatureCastSlot[],
|
|
520
|
+
picked: CreatureCastSlot,
|
|
521
|
+
players: readonly { classId?: number; spec?: { classId?: number } }[],
|
|
522
|
+
rng?: Rng,
|
|
523
|
+
): CreatureCastSlot | null {
|
|
524
|
+
const e = picked.entry;
|
|
525
|
+
if (e.scriptId == null || (e.selectClass ?? 0) <= 0) return picked;
|
|
526
|
+
const siblings = slots.filter(
|
|
527
|
+
(s) => !s.exhausted && s.entry.scriptId === e.scriptId && (s.entry.selectClass ?? 0) > 0,
|
|
528
|
+
);
|
|
529
|
+
if (siblings.length === 0) return picked;
|
|
530
|
+
const classOf = (p: { classId?: number; spec?: { classId?: number } }) =>
|
|
531
|
+
((p.classId ?? p.spec?.classId ?? 0) | 0);
|
|
532
|
+
const pool = players.filter((p) => classOf(p) > 0);
|
|
533
|
+
if (pool.length === 0) return null;
|
|
534
|
+
let chosen = pool[0]!;
|
|
535
|
+
if (pool.length > 1 && rng) {
|
|
536
|
+
chosen = pool[rng.int(0, pool.length - 1)] ?? chosen;
|
|
537
|
+
}
|
|
538
|
+
const cls = classOf(chosen);
|
|
539
|
+
return siblings.find((s) => ((s.entry.selectClass ?? 0) | 0) === cls) ?? null;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
export function resolveRandomActionCastSlot(
|
|
543
|
+
slots: readonly CreatureCastSlot[],
|
|
544
|
+
picked: CreatureCastSlot,
|
|
545
|
+
rng?: Rng,
|
|
546
|
+
): CreatureCastSlot {
|
|
547
|
+
const e = picked.entry;
|
|
548
|
+
if (e.scriptId == null || ((e.eventFlags ?? 0) & EFLAG_RANDOM_ACTION) === 0) return picked;
|
|
549
|
+
const siblings = slots.filter((s) => !s.exhausted && s.entry.scriptId === e.scriptId);
|
|
550
|
+
if (siblings.length <= 1 || !rng) return picked;
|
|
551
|
+
return siblings[rng.int(0, siblings.length - 1)] ?? picked;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
// Re-arm the whole script row after one of its RANDOM_ACTION slots fired: the row is one event
|
|
555
|
+
// with one timer, so every sibling mirrors it. Rows without the flag keep per-slot re-arm.
|
|
556
|
+
export function markCreatureCastRowUsed(
|
|
557
|
+
slots: readonly CreatureCastSlot[],
|
|
558
|
+
fired: CreatureCastSlot,
|
|
559
|
+
t: number,
|
|
560
|
+
rng?: Rng,
|
|
561
|
+
): void {
|
|
562
|
+
markCreatureCastUsed(fired, t, rng);
|
|
563
|
+
const e = fired.entry;
|
|
564
|
+
if (e.scriptId == null || ((e.eventFlags ?? 0) & EFLAG_RANDOM_ACTION) === 0) return;
|
|
565
|
+
for (const s of slots) {
|
|
566
|
+
if (s === fired || s.entry.scriptId !== e.scriptId) continue;
|
|
567
|
+
s.readyAt = fired.readyAt;
|
|
568
|
+
s.exhausted = fired.exhausted;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
// event_chance gate — CreatureEventAI.cpp ProcessEvent ~607–612. `event_chance <= rnd % 100` fails. Absent
|
|
573
|
+
// / 100 → always true. Without rng, only chancePct ≥ 100 (or absent) succeeds (deterministic).
|
|
574
|
+
export function rollCreatureCastChance(entry: CreatureCastEntry, rng?: Rng): boolean {
|
|
575
|
+
const chance = entry.chancePct ?? 100;
|
|
576
|
+
if (chance >= 100) return true;
|
|
577
|
+
if (chance <= 0) return false;
|
|
578
|
+
if (!rng) return false;
|
|
579
|
+
// rnd % 100 in [0, 99]; success when chance > rnd%100 ⇔ rnd%100 < chance
|
|
580
|
+
return rng.int(0, 99) < chance;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
// After a cast attempt, success or chance fail, arm the next timer and/or exhaust. One-shots
|
|
584
|
+
// (AGGRO/DEATH/KILL) and timer rows without EFLAG_REPEATABLE exhaust. Cite: ResetEvent ~573.
|
|
585
|
+
export function markCreatureCastUsed(slot: CreatureCastSlot, t: number, rng?: Rng): void {
|
|
586
|
+
const e = slot.entry;
|
|
587
|
+
const et = e.eventType;
|
|
588
|
+
|
|
589
|
+
// IsRepeatableEvent-false types plus the KILL one-shot path. KILL is repeatable in C++ but
|
|
590
|
+
// legacy Phonecraft always exhausted it — kept, for byte-identical fights.
|
|
591
|
+
if (isAlwaysExhaustEventType(et)) {
|
|
592
|
+
slot.exhausted = true;
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
const hasRepeat =
|
|
597
|
+
(e.repeatMinMs != null && e.repeatMinMs > 0) ||
|
|
598
|
+
(e.repeatMaxMs != null && e.repeatMaxMs > 0);
|
|
599
|
+
|
|
600
|
+
// Without EFLAG_REPEATABLE, fire once and skip the timer re-arm. Bare tpl entries with no
|
|
601
|
+
// eventFlags field keep phase-1 CD/repeat behaviour. Cite: ResetEvent enabled=false.
|
|
602
|
+
if (
|
|
603
|
+
isRepeatableEventType(et) &&
|
|
604
|
+
e.eventFlags != null &&
|
|
605
|
+
(e.eventFlags & EFLAG_REPEATABLE) === 0
|
|
606
|
+
) {
|
|
607
|
+
slot.exhausted = true;
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
if (advanceRotateWave(slot, t, rng)) return;
|
|
612
|
+
|
|
613
|
+
if (hasRepeat) {
|
|
614
|
+
const lo = e.repeatMinMs ?? e.repeatMaxMs ?? 0;
|
|
615
|
+
const hi = e.repeatMaxMs ?? e.repeatMinMs ?? 0;
|
|
616
|
+
slot.readyAt = t + rollTimer(lo, hi, rng);
|
|
617
|
+
} else {
|
|
618
|
+
// Rely on spell/category CD and keep the slot eligible for re-check. Zero-CD main or ranged
|
|
619
|
+
// filler would re-fire every tick, so the caller may push readyAt further after the cast.
|
|
620
|
+
slot.readyAt = t;
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
// CreatureAI::ResetTimersOnEvade — an evading creature re-rolls every in-combat timer off its
|
|
625
|
+
// INITIAL band, so a re-pull opens on the first pull's schedule. Cite: CreatureAI.cpp:66-71.
|
|
626
|
+
export function resetCreatureCastSlotsOnEvade(
|
|
627
|
+
slots: readonly CreatureCastSlot[],
|
|
628
|
+
t: number,
|
|
629
|
+
rng?: Rng,
|
|
630
|
+
): void {
|
|
631
|
+
for (const slot of slots) {
|
|
632
|
+
const e = slot.entry;
|
|
633
|
+
const et = e.eventType;
|
|
634
|
+
const timerEvent = et === EVENT_T.TIMER_IN_COMBAT
|
|
635
|
+
|| et === EVENT_T.TIMER_OOC
|
|
636
|
+
|| et === EVENT_T.TIMER_GENERIC;
|
|
637
|
+
const bareInitial = et == null && (e.initialMinMs != null || e.initialMaxMs != null);
|
|
638
|
+
// A row disabled at arm time (AddCombatAction(id, true)) stays disabled — only the script
|
|
639
|
+
// re-arms it. Cite: TimerAI.cpp disabled ctor.
|
|
640
|
+
if (!timerEvent && !bareInitial) continue;
|
|
641
|
+
slot.exhausted = false;
|
|
642
|
+
slot.fires = 0;
|
|
643
|
+
slot.readyAt = t + rollTimer(e.initialMinMs ?? 0, e.initialMaxMs ?? e.initialMinMs ?? 0, rng);
|
|
644
|
+
if (slot.rotatePool?.length) initRotateWave(slot, rng);
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
/** CAST_OK of a maxFires slot. Cite: boss_gothik.cpp:318 ++m_teleportCount; :305 >= 4. */
|
|
649
|
+
export function countCreatureCastFire(slot: CreatureCastSlot): void {
|
|
650
|
+
const cap = slot.entry.maxFires ?? 0;
|
|
651
|
+
if (cap <= 0) return;
|
|
652
|
+
slot.fires += 1;
|
|
653
|
+
if (slot.fires >= cap) slot.exhausted = true;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
// After a successful CAST_MAIN_SPELL, pace the slot by `paceMs` so a no-CD spell cannot re-fire
|
|
657
|
+
// every tick. Only CAST_MAIN_SPELL uses weapon pacing; bare template specials never do.
|
|
658
|
+
export function paceMainSpellSlot(slot: CreatureCastSlot, t: number, paceMs: number): void {
|
|
659
|
+
if (paceMs <= 0) return;
|
|
660
|
+
const flags = slot.entry.castFlags ?? 0;
|
|
661
|
+
const isMain = (flags & CAST_FLAG.MAIN_SPELL) !== 0;
|
|
662
|
+
if (!isMain) return;
|
|
663
|
+
slot.readyAt = Math.max(slot.readyAt, t + paceMs);
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
// IsRepeatableEvent — true for types that honor EFLAG_REPEATABLE. Cite: CreatureEventAI.cpp
|
|
667
|
+
// IsRepeatableEvent ~196–208.
|
|
668
|
+
function isRepeatableEventType(et: number | undefined): boolean {
|
|
669
|
+
if (et == null) return true;
|
|
670
|
+
if (
|
|
671
|
+
et === EVENT_T.SPAWNED ||
|
|
672
|
+
et === EVENT_T.DEATH ||
|
|
673
|
+
et === EVENT_T.AGGRO ||
|
|
674
|
+
et === EVENT_T.EVADE ||
|
|
675
|
+
et === EVENT_T.REACHED_HOME
|
|
676
|
+
) {
|
|
677
|
+
return false;
|
|
678
|
+
}
|
|
679
|
+
return true;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
/** One-shot lifecycle types that always exhaust after fire (IsRepeatableEvent false). */
|
|
683
|
+
function isAlwaysExhaustEventType(et: number | undefined): boolean {
|
|
684
|
+
if (et == null) return false;
|
|
685
|
+
return (
|
|
686
|
+
et === EVENT_T.SPAWNED ||
|
|
687
|
+
et === EVENT_T.DEATH ||
|
|
688
|
+
et === EVENT_T.AGGRO ||
|
|
689
|
+
et === EVENT_T.EVADE ||
|
|
690
|
+
et === EVENT_T.REACHED_HOME ||
|
|
691
|
+
et === EVENT_T.KILL
|
|
692
|
+
);
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
// EVENT_HANDLERS — CheckEvent body per type (CreatureEventAI.cpp ~291–551) Literal keys so
|
|
696
|
+
// scripts/aifid/census.mjs parseHandlerTable can enumerate them.
|
|
697
|
+
|
|
698
|
+
export type EventConditionHandler = (entry: CreatureCastEntry, ctx: CastPickContext) => boolean;
|
|
699
|
+
|
|
700
|
+
function inCombatFlag(ctx: CastPickContext): boolean {
|
|
701
|
+
// Combat schedulers omit the flag → treat as in combat (legacy).
|
|
702
|
+
return ctx.inCombat !== false;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
function friendlyInRadius(
|
|
706
|
+
f: EventAiFriendlyUnit,
|
|
707
|
+
radiusYd: number,
|
|
708
|
+
): boolean {
|
|
709
|
+
if (f.distanceYd == null) return true;
|
|
710
|
+
return f.distanceYd <= radiusYd;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
/** DoSelectLowestHpFriendly predicate — CreatureEventAI.cpp ~379–390. */
|
|
714
|
+
function friendlyHpMatch(
|
|
715
|
+
entry: CreatureCastEntry,
|
|
716
|
+
friendlies: readonly EventAiFriendlyUnit[],
|
|
717
|
+
): boolean {
|
|
718
|
+
const deficit = entry.friendlyHpDeficit;
|
|
719
|
+
const radius = entry.friendlyRadiusYd ?? 0;
|
|
720
|
+
const isPct = entry.friendlyHpIsPercent === true;
|
|
721
|
+
for (const f of friendlies) {
|
|
722
|
+
if (!friendlyInRadius(f, radius)) continue;
|
|
723
|
+
// Incomplete cast row (no deficit) — do not invent a threshold; skip unit.
|
|
724
|
+
// Prefer enrichCastEntriesFromScripts so baked creatureCasts gain real params.
|
|
725
|
+
if (deficit == null) continue;
|
|
726
|
+
if (isPct) {
|
|
727
|
+
// deficit is HP% missing required (e.g. 30 → fire when hpPct <= 70)
|
|
728
|
+
if (f.hpPct <= 100 - deficit) return true;
|
|
729
|
+
} else {
|
|
730
|
+
// absolute missing HP: maxHp - hp >= deficit
|
|
731
|
+
if (f.maxHp - f.hp >= deficit) return true;
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
return false;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
function friendlyMissingBuffMatch(
|
|
738
|
+
entry: CreatureCastEntry,
|
|
739
|
+
friendlies: readonly EventAiFriendlyUnit[],
|
|
740
|
+
): boolean {
|
|
741
|
+
// Cast-bake rows often omit event params; the CAST spell is the buff being applied.
|
|
742
|
+
const sid = entry.friendlyBuffSpellId ?? entry.auraSpellId ?? (entry.spellId || 0);
|
|
743
|
+
if (!sid) return false;
|
|
744
|
+
const radius = entry.friendlyRadiusYd ?? 0;
|
|
745
|
+
for (const f of friendlies) {
|
|
746
|
+
if (!friendlyInRadius(f, radius)) continue;
|
|
747
|
+
if (f.hasAura?.(sid, 1)) continue;
|
|
748
|
+
return true;
|
|
749
|
+
}
|
|
750
|
+
return false;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
function friendlyIsCcMatch(
|
|
754
|
+
entry: CreatureCastEntry,
|
|
755
|
+
friendlies: readonly EventAiFriendlyUnit[],
|
|
756
|
+
): boolean {
|
|
757
|
+
const radius = entry.friendlyRadiusYd ?? 0;
|
|
758
|
+
for (const f of friendlies) {
|
|
759
|
+
if (!friendlyInRadius(f, radius)) continue;
|
|
760
|
+
if (f.hasCc) return true;
|
|
761
|
+
}
|
|
762
|
+
return false;
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
function spellHitMatches(entry: CreatureCastEntry, ctx: CastPickContext): boolean {
|
|
766
|
+
// Pulse required — C++ only enqueues on SpellHit. Cite: SpellHit path + CheckEvent ~345–347.
|
|
767
|
+
if (ctx.spellHitId == null) return false;
|
|
768
|
+
const want = entry.spellHitSpellId ?? entry.auraSpellId ?? 0;
|
|
769
|
+
if (want && ctx.spellHitId !== want) return false;
|
|
770
|
+
const school = entry.spellHitSchoolMask;
|
|
771
|
+
// -1 / 0xffffffff = any school; 0 often means "unset / any" in dumps
|
|
772
|
+
if (school != null && school !== -1 && school !== 0xffffffff && school !== 0) {
|
|
773
|
+
if (ctx.spellHitSchoolMask == null) return false;
|
|
774
|
+
if ((ctx.spellHitSchoolMask & school) === 0) return false;
|
|
775
|
+
}
|
|
776
|
+
return true;
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
// Implemented EVENT_T condition table. Presence = census "implemented". Cite: CreatureEventAI.cpp
|
|
780
|
+
// CheckEvent ~291–551 per case.
|
|
781
|
+
export const EVENT_HANDLERS: Record<number, EventConditionHandler> = {
|
|
782
|
+
// TIMER_IN_COMBAT — ~291–293 Optional hpPct band (ScriptDev: Gandling portal only while HP > 3%). Cite:
|
|
783
|
+
// boss_darkmaster_gandling.cpp:94; CreatureEventAI.cpp percent_range ~301–310.
|
|
784
|
+
[EVENT_T.TIMER_IN_COMBAT]: (entry, ctx) => {
|
|
785
|
+
if (!inCombatFlag(ctx)) return false;
|
|
786
|
+
if (entry.hpPctMax != null || entry.hpPctMin != null) {
|
|
787
|
+
return percentBandOk(ctx.hpPct, entry.hpPctMax, entry.hpPctMin);
|
|
788
|
+
}
|
|
789
|
+
return true;
|
|
790
|
+
},
|
|
791
|
+
// TIMER_OOC — ~295–297
|
|
792
|
+
[EVENT_T.TIMER_OOC]: (_e, ctx) => !inCombatFlag(ctx),
|
|
793
|
+
// TIMER_GENERIC — ~299–300 (any combat state)
|
|
794
|
+
[EVENT_T.TIMER_GENERIC]: () => true,
|
|
795
|
+
// HP — ~301–310
|
|
796
|
+
[EVENT_T.HP]: (entry, ctx) => percentBandOk(ctx.hpPct, entry.hpPctMax, entry.hpPctMin),
|
|
797
|
+
// MANA — ~312–321
|
|
798
|
+
[EVENT_T.MANA]: (entry, ctx) => {
|
|
799
|
+
if (ctx.manaPct == null) return false;
|
|
800
|
+
return percentBandOk(ctx.manaPct, entry.manaPctMax, entry.manaPctMin);
|
|
801
|
+
},
|
|
802
|
+
// AGGRO — ~323–324 (hook-gated; poll path treats ready slot as eligible like legacy)
|
|
803
|
+
[EVENT_T.AGGRO]: () => true,
|
|
804
|
+
// KILL — ~325–327 (JustKilledUnit hook only — never poll while living)
|
|
805
|
+
[EVENT_T.KILL]: (_e, ctx) => ctx.justKilled === true,
|
|
806
|
+
// DEATH — ~329–341 (JustDied hook only — never poll while living; combat uses runEnemyDeathEventAi)
|
|
807
|
+
[EVENT_T.DEATH]: (_e, ctx) => ctx.justDied === true,
|
|
808
|
+
// EVADE — ~343–344; requires evade pulse (EnterEvadeMode ~1478)
|
|
809
|
+
[EVENT_T.EVADE]: (_e, ctx) => ctx.evading === true,
|
|
810
|
+
// SPELLHIT — enqueued from the SpellHit hook only, filtered by spell id and school mask. Polling
|
|
811
|
+
// it as () => true fired the row every tick. Cite: CreatureEventAI.cpp:1666; Spell.cpp:1345.
|
|
812
|
+
[EVENT_T.SPELLHIT]: (entry, ctx) => spellHitMatches(entry, ctx),
|
|
813
|
+
// RANGE — ~349–354
|
|
814
|
+
[EVENT_T.RANGE]: (entry, ctx) => {
|
|
815
|
+
if (ctx.distanceYd == null) return true;
|
|
816
|
+
const minD = entry.rangeMinYd ?? 0;
|
|
817
|
+
const maxD = entry.rangeMaxYd ?? Number.POSITIVE_INFINITY;
|
|
818
|
+
return ctx.distanceYd >= minD && ctx.distanceYd <= maxD;
|
|
819
|
+
},
|
|
820
|
+
// OOC_LOS — ~356–361; requires oocLos pulse (MoveInLineOfSight OOC)
|
|
821
|
+
[EVENT_T.OOC_LOS]: (_e, ctx) => ctx.oocLos === true && !inCombatFlag(ctx),
|
|
822
|
+
// SPAWNED — ~362–363; JustRespawned pulse or combat-start arm (readyAt=0 one-shot)
|
|
823
|
+
// When spawned flag omitted, allow once like AGGRO (fight arm ≈ spawn into session).
|
|
824
|
+
[EVENT_T.SPAWNED]: (_e, ctx) => ctx.spawned !== false,
|
|
825
|
+
// TARGET_HP — ~364–373
|
|
826
|
+
[EVENT_T.TARGET_HP]: (entry, ctx) => {
|
|
827
|
+
if (ctx.targetHpPct == null) return false;
|
|
828
|
+
return percentBandOk(ctx.targetHpPct, entry.hpPctMax, entry.hpPctMin);
|
|
829
|
+
},
|
|
830
|
+
// TARGET_CASTING — ~375–377
|
|
831
|
+
[EVENT_T.TARGET_CASTING]: (_e, ctx) => ctx.targetCasting === true,
|
|
832
|
+
// FRIENDLY_HP — ~379–390
|
|
833
|
+
[EVENT_T.FRIENDLY_HP]: (entry, ctx) => {
|
|
834
|
+
if (!inCombatFlag(ctx)) return false;
|
|
835
|
+
if (!ctx.friendlies || ctx.friendlies.length === 0) return false;
|
|
836
|
+
return friendlyHpMatch(entry, ctx.friendlies);
|
|
837
|
+
},
|
|
838
|
+
// FRIENDLY_IS_CC — ~392–405
|
|
839
|
+
[EVENT_T.FRIENDLY_IS_CC]: (entry, ctx) => {
|
|
840
|
+
if (!inCombatFlag(ctx)) return false;
|
|
841
|
+
if (!ctx.friendlies || ctx.friendlies.length === 0) return false;
|
|
842
|
+
return friendlyIsCcMatch(entry, ctx.friendlies);
|
|
843
|
+
},
|
|
844
|
+
// FRIENDLY_MISSING_BUFF — ~407–437
|
|
845
|
+
[EVENT_T.FRIENDLY_MISSING_BUFF]: (entry, ctx) => {
|
|
846
|
+
const mode = entry.friendlyBuffInCombat ?? 0;
|
|
847
|
+
const combat = inCombatFlag(ctx);
|
|
848
|
+
if (mode === 0 && !combat) return false;
|
|
849
|
+
if (mode === 2 && combat) return false;
|
|
850
|
+
if (!ctx.friendlies || ctx.friendlies.length === 0) return false;
|
|
851
|
+
return friendlyMissingBuffMatch(entry, ctx.friendlies);
|
|
852
|
+
},
|
|
853
|
+
// SUMMONED_UNIT — ~439–450
|
|
854
|
+
[EVENT_T.SUMMONED_UNIT]: (entry, ctx) => {
|
|
855
|
+
if (ctx.summonedEntry == null) return false;
|
|
856
|
+
const want = entry.summonedCreatureId ?? 0;
|
|
857
|
+
return !want || ctx.summonedEntry === want;
|
|
858
|
+
},
|
|
859
|
+
// TARGET_MANA — ~452–461
|
|
860
|
+
[EVENT_T.TARGET_MANA]: (entry, ctx) => {
|
|
861
|
+
if (ctx.targetManaPct == null) return false;
|
|
862
|
+
return percentBandOk(ctx.targetManaPct, entry.manaPctMax, entry.manaPctMin);
|
|
863
|
+
},
|
|
864
|
+
// REACHED_HOME — ~463–464
|
|
865
|
+
[EVENT_T.REACHED_HOME]: (_e, ctx) => ctx.reachedHome === true,
|
|
866
|
+
// RECEIVE_EMOTE — ~465–471 (product has no /emote surface → disposition in census;
|
|
867
|
+
// handler still present so domain pulses work if a surface is added later)
|
|
868
|
+
[EVENT_T.RECEIVE_EMOTE]: (entry, ctx) => {
|
|
869
|
+
if (ctx.receivedEmoteId == null) return false;
|
|
870
|
+
const want = entry.receiveEmoteId ?? 0;
|
|
871
|
+
return !want || ctx.receivedEmoteId === want;
|
|
872
|
+
},
|
|
873
|
+
// AURA — ~472–477 (self has enough stacks)
|
|
874
|
+
[EVENT_T.AURA]: (entry, ctx) => {
|
|
875
|
+
const sid = entry.auraSpellId ?? 0;
|
|
876
|
+
if (!sid) return false;
|
|
877
|
+
const stacks = entry.auraStacks ?? 1;
|
|
878
|
+
if (!ctx.hasAura) return false;
|
|
879
|
+
return ctx.hasAura(sid, stacks);
|
|
880
|
+
},
|
|
881
|
+
// TARGET_AURA — ~479–487
|
|
882
|
+
[EVENT_T.TARGET_AURA]: (entry, ctx) => {
|
|
883
|
+
const sid = entry.auraSpellId ?? 0;
|
|
884
|
+
if (!sid) return false;
|
|
885
|
+
const stacks = entry.auraStacks ?? 1;
|
|
886
|
+
if (!ctx.targetHasAura) return false;
|
|
887
|
+
return ctx.targetHasAura(sid, stacks);
|
|
888
|
+
},
|
|
889
|
+
// SUMMONED_JUST_DIED — ~439–450 (same filter as SUMMONED_UNIT)
|
|
890
|
+
[EVENT_T.SUMMONED_JUST_DIED]: (entry, ctx) => {
|
|
891
|
+
if (ctx.summonedEntry == null) return false;
|
|
892
|
+
const want = entry.summonedCreatureId ?? 0;
|
|
893
|
+
return !want || ctx.summonedEntry === want;
|
|
894
|
+
},
|
|
895
|
+
// SUMMONED_JUST_DESPAWN — same
|
|
896
|
+
[EVENT_T.SUMMONED_JUST_DESPAWN]: (entry, ctx) => {
|
|
897
|
+
if (ctx.summonedEntry == null) return false;
|
|
898
|
+
const want = entry.summonedCreatureId ?? 0;
|
|
899
|
+
return !want || ctx.summonedEntry === want;
|
|
900
|
+
},
|
|
901
|
+
// MISSING_AURA — ~489–494
|
|
902
|
+
[EVENT_T.MISSING_AURA]: (entry, ctx) => {
|
|
903
|
+
const sid = entry.auraSpellId ?? 0;
|
|
904
|
+
if (!sid) return true;
|
|
905
|
+
const stacks = entry.auraStacks ?? 1;
|
|
906
|
+
if (!ctx.hasAura) return true;
|
|
907
|
+
return !ctx.hasAura(sid, stacks);
|
|
908
|
+
},
|
|
909
|
+
// TARGET_MISSING_AURA — ~496–504
|
|
910
|
+
[EVENT_T.TARGET_MISSING_AURA]: (entry, ctx) => {
|
|
911
|
+
const sid = entry.auraSpellId ?? 0;
|
|
912
|
+
if (!sid) return true;
|
|
913
|
+
const stacks = entry.auraStacks ?? 1;
|
|
914
|
+
if (!ctx.targetHasAura) return true;
|
|
915
|
+
return !ctx.targetHasAura(sid, stacks);
|
|
916
|
+
},
|
|
917
|
+
// RECEIVE_AI_EVENT — ~506–507 + ReceiveAIEvent filter ~1565–1567
|
|
918
|
+
[EVENT_T.RECEIVE_AI_EVENT]: (entry, ctx) => {
|
|
919
|
+
if (!ctx.receivedAiEvent) return false;
|
|
920
|
+
const wantType = entry.receiveAiEventType ?? 0;
|
|
921
|
+
if (wantType && ctx.receivedAiEvent.eventType !== wantType) return false;
|
|
922
|
+
const wantSender = entry.receiveAiSenderEntry ?? 0;
|
|
923
|
+
if (wantSender && ctx.receivedAiEvent.senderEntry !== wantSender) return false;
|
|
924
|
+
return true;
|
|
925
|
+
},
|
|
926
|
+
// SELECT_ATTACKING_TARGET — ~519–527
|
|
927
|
+
[EVENT_T.SELECT_ATTACKING_TARGET]: (_e, ctx) => ctx.hasAttackingTargetInRange === true,
|
|
928
|
+
// FACING_TARGET — ~530–541
|
|
929
|
+
[EVENT_T.FACING_TARGET]: (entry, ctx) => {
|
|
930
|
+
if (!inCombatFlag(ctx)) return false;
|
|
931
|
+
if (ctx.facingTarget == null) return false;
|
|
932
|
+
const want = entry.facingBackOrFront ?? 0;
|
|
933
|
+
return ctx.facingTarget === want;
|
|
934
|
+
},
|
|
935
|
+
// SPELLHIT_TARGET — ~345–347 (pulse-gated; unlike SPELLHIT legacy)
|
|
936
|
+
[EVENT_T.SPELLHIT_TARGET]: (entry, ctx) => spellHitMatches(entry, ctx),
|
|
937
|
+
// TARGET_NOT_REACHABLE — ~545–547
|
|
938
|
+
[EVENT_T.TARGET_NOT_REACHABLE]: (_e, ctx) => ctx.targetNotReachable === true,
|
|
939
|
+
// SPELL_CAST — ~1584–1587
|
|
940
|
+
[EVENT_T.SPELL_CAST]: (entry, ctx) => {
|
|
941
|
+
if (ctx.spellCastId == null) return false;
|
|
942
|
+
const want = entry.spellHitSpellId ?? entry.auraSpellId ?? 0;
|
|
943
|
+
return !want || ctx.spellCastId === want;
|
|
944
|
+
},
|
|
945
|
+
};
|
|
946
|
+
|
|
947
|
+
// Event conditions from CheckEvent via EVENT_HANDLERS. Bare entries (no eventType) always pass. Unknown
|
|
948
|
+
// types fail closed.
|
|
949
|
+
export function eventConditionsOk(entry: CreatureCastEntry, ctx: CastPickContext): boolean {
|
|
950
|
+
const et = entry.eventType;
|
|
951
|
+
if (et == null) return true;
|
|
952
|
+
const handler = EVENT_HANDLERS[et];
|
|
953
|
+
if (!handler) return false;
|
|
954
|
+
return handler(entry, ctx);
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
/** perc > percentMax || perc < percentMin → fail (EVENT_T_HP/MANA). */
|
|
958
|
+
function percentBandOk(pct: number, max?: number, min?: number): boolean {
|
|
959
|
+
const hi = max ?? 100;
|
|
960
|
+
const lo = min ?? 0;
|
|
961
|
+
return pct <= hi && pct >= lo;
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
function compareSlots(a: CreatureCastSlot, b: CreatureCastSlot): number {
|
|
965
|
+
if (a.readyAt !== b.readyAt) return a.readyAt - b.readyAt;
|
|
966
|
+
if (a.listIndex !== b.listIndex) return a.listIndex - b.listIndex;
|
|
967
|
+
return a.spell.id - b.spell.id;
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
/** Re-arm a timer that just became unblocked. Cite: boss_onyxia.cpp:368-378 ResetCombatAction. */
|
|
971
|
+
export function rearmCreatureCastSlotsOnPhase(
|
|
972
|
+
slots: readonly CreatureCastSlot[],
|
|
973
|
+
prevPhase: number,
|
|
974
|
+
nextPhase: number,
|
|
975
|
+
t: number,
|
|
976
|
+
rng?: Rng,
|
|
977
|
+
): void {
|
|
978
|
+
if ((prevPhase | 0) === (nextPhase | 0)) return;
|
|
979
|
+
for (const slot of slots) {
|
|
980
|
+
const mask = slot.entry.eventInversePhaseMask;
|
|
981
|
+
if (!mask) continue;
|
|
982
|
+
if (!eventPhaseBlocked(mask, prevPhase) || eventPhaseBlocked(mask, nextPhase)) continue;
|
|
983
|
+
const e = slot.entry;
|
|
984
|
+
slot.exhausted = false;
|
|
985
|
+
slot.readyAt = t + rollTimer(e.initialMinMs ?? 0, e.initialMaxMs ?? e.initialMinMs ?? 0, rng);
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
export function rollTimer(minMs: number, maxMs: number, rng?: Rng): number {
|
|
990
|
+
const lo = Math.max(0, minMs | 0);
|
|
991
|
+
const hi = Math.max(lo, maxMs | 0);
|
|
992
|
+
if (hi <= lo || !rng) return lo;
|
|
993
|
+
return rng.int(lo, hi);
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
// Skip the event when the bit for the current phase is SET in event_inverse_phase_mask.
|
|
997
|
+
// Cite: CreatureEventAI.cpp CheckEvent ~266–270.
|
|
998
|
+
export function eventPhaseBlocked(
|
|
999
|
+
eventInversePhaseMask: number | undefined | null,
|
|
1000
|
+
phase: number,
|
|
1001
|
+
): boolean {
|
|
1002
|
+
const mask = (eventInversePhaseMask ?? 0) | 0;
|
|
1003
|
+
if (!mask) return false;
|
|
1004
|
+
const p = phase | 0;
|
|
1005
|
+
if (p < 0 || p > 31) return false;
|
|
1006
|
+
return (mask & (1 << p)) !== 0;
|
|
1007
|
+
}
|