azeroth-shell 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +138 -2
- package/bin/azsh.mjs +19 -0
- package/docs/aifid/eventai-texts.json +4414 -0
- package/docs/dungeons/atlas/atlas-rows-dungeons.json +8989 -0
- package/docs/dungeons/survey/pack-id-migration.json +2418 -0
- package/package.json +35 -4
- package/src/autopilot/bossSkip.ts +26 -0
- package/src/autopilot/combatRotation.ts +824 -0
- package/src/autopilot/config.ts +533 -0
- package/src/autopilot/consumables.ts +58 -0
- package/src/autopilot/encounterStand.ts +121 -0
- package/src/autopilot/objectGatePlan.ts +53 -0
- package/src/autopilot/pathEngage.ts +229 -0
- package/src/autopilot/plan.ts +1643 -0
- package/src/autopilot/questPolicy.ts +274 -0
- package/src/autopilot/routeStep.ts +69 -0
- package/src/autopilot/sessionReport.ts +88 -0
- package/src/autopilot/topOffPower.ts +26 -0
- package/src/autopilot/view.ts +476 -0
- package/src/cli/bootCheck.mjs +34 -0
- package/src/cli/bootData.mjs +57 -0
- package/src/cli/combatLogFilter.ts +153 -0
- package/src/cli/combatLogFormat.ts +613 -0
- package/src/cli/fileRepository.mjs +105 -0
- package/src/cli/gzLoader.mjs +31 -0
- package/src/cli/liveGauntletEscort.ts +978 -0
- package/src/cli/liveStratUndead.ts +305 -0
- package/src/cli/macroHostCombat.ts +545 -0
- package/src/cli/macroHostWorld.ts +330 -0
- package/src/cli/prefs.mjs +31 -0
- package/src/cli/register.mjs +4 -0
- package/src/cli/repl.mjs +693 -0
- package/src/cli/rotationText.mjs +226 -0
- package/src/cli/squad.mjs +105 -0
- package/src/cli/term.mjs +144 -0
- package/src/cli/text.mjs +122 -0
- package/src/cli/tui.mjs +626 -0
- package/src/cli/tuiArena.mjs +109 -0
- package/src/cli/tuiAutopilotSettings.mjs +93 -0
- package/src/cli/tuiBags.mjs +180 -0
- package/src/cli/tuiBars.mjs +133 -0
- package/src/cli/tuiBg.mjs +243 -0
- package/src/cli/tuiBulk.mjs +162 -0
- package/src/cli/tuiCheck.mjs +1882 -0
- package/src/cli/tuiCity.mjs +396 -0
- package/src/cli/tuiCompanionKit.mjs +300 -0
- package/src/cli/tuiCompanions.mjs +471 -0
- package/src/cli/tuiDev.mjs +62 -0
- package/src/cli/tuiDungeon.mjs +606 -0
- package/src/cli/tuiEnchantTarget.mjs +120 -0
- package/src/cli/tuiFight.mjs +577 -0
- package/src/cli/tuiFightPanes.mjs +228 -0
- package/src/cli/tuiFloor.mjs +506 -0
- package/src/cli/tuiGear.mjs +158 -0
- package/src/cli/tuiHonor.mjs +74 -0
- package/src/cli/tuiLoot.mjs +97 -0
- package/src/cli/tuiMacros.mjs +144 -0
- package/src/cli/tuiOutfitter.mjs +115 -0
- package/src/cli/tuiPet.mjs +116 -0
- package/src/cli/tuiProfessions.mjs +800 -0
- package/src/cli/tuiRaid.mjs +127 -0
- package/src/cli/tuiRep.mjs +65 -0
- package/src/cli/tuiSaves.mjs +69 -0
- package/src/cli/tuiSearch.mjs +363 -0
- package/src/cli/tuiSettings.mjs +151 -0
- package/src/cli/tuiSimLab.mjs +80 -0
- package/src/cli/tuiTalents.mjs +60 -0
- package/src/cli/tuiViews.mjs +1178 -0
- package/src/cli/tuiWiki.mjs +181 -0
- package/src/cli/tuiWorld.mjs +361 -0
- package/src/cli/wikiIndex.ts +259 -0
- package/src/cli/wikiSources.ts +355 -0
- package/src/data/alteracValley.ts +176 -0
- package/src/data/appearanceOptions.ts +534 -0
- package/src/data/applyLootChanceSanity.ts +21 -0
- package/src/data/createFlavor.ts +96 -0
- package/src/data/customItems.ts +33 -0
- package/src/data/dataStore.ts +1904 -0
- package/src/data/dungeonGameObjects.ts +84 -0
- package/src/data/dungeonPlateCover.ts +66 -0
- package/src/data/dungeonSpawns.ts +294 -0
- package/src/data/dungeons/alterac-valley.ts +135 -0
- package/src/data/dungeons/apply-dungeon-rooms.ts +29 -0
- package/src/data/dungeons/apply-encounter-outdoors.ts +72 -0
- package/src/data/dungeons/apply-instance-geometry.ts +65 -0
- package/src/data/dungeons/apply-spawn-placement.ts +171 -0
- package/src/data/dungeons/blackfathom-deeps.ts +539 -0
- package/src/data/dungeons/blackrock-depths.ts +1410 -0
- package/src/data/dungeons/blackwing-lair.ts +129 -0
- package/src/data/dungeons/boss-eventai.ts +8 -0
- package/src/data/dungeons/deadmines.ts +410 -0
- package/src/data/dungeons/dire-maul-east.ts +980 -0
- package/src/data/dungeons/dire-maul-north.ts +661 -0
- package/src/data/dungeons/dire-maul-west.ts +785 -0
- package/src/data/dungeons/dungeon-rooms.json +5479 -0
- package/src/data/dungeons/encounter-outdoors.json +359 -0
- package/src/data/dungeons/eventai/blackfathom-deeps.ts +308 -0
- package/src/data/dungeons/eventai/blackrock-depths.ts +1257 -0
- package/src/data/dungeons/eventai/deadmines.ts +264 -0
- package/src/data/dungeons/eventai/dire-maul-east.ts +320 -0
- package/src/data/dungeons/eventai/gnomeregan.ts +213 -0
- package/src/data/dungeons/eventai/helpers.ts +79 -0
- package/src/data/dungeons/eventai/index.ts +133 -0
- package/src/data/dungeons/eventai/lbrs.ts +372 -0
- package/src/data/dungeons/eventai/maraudon.ts +323 -0
- package/src/data/dungeons/eventai/ragefire-chasm.ts +108 -0
- package/src/data/dungeons/eventai/razorfen-downs.ts +207 -0
- package/src/data/dungeons/eventai/razorfen-kraul.ts +326 -0
- package/src/data/dungeons/eventai/scholomance.ts +680 -0
- package/src/data/dungeons/eventai/shadowfang-keep.ts +230 -0
- package/src/data/dungeons/eventai/sm-cathedral.ts +75 -0
- package/src/data/dungeons/eventai/sm-graveyard.ts +150 -0
- package/src/data/dungeons/eventai/sm-library.ts +55 -0
- package/src/data/dungeons/eventai/stratholme-undead.ts +127 -0
- package/src/data/dungeons/eventai/sunken-temple.ts +582 -0
- package/src/data/dungeons/eventai/the-stockade.ts +228 -0
- package/src/data/dungeons/eventai/ubrs.ts +225 -0
- package/src/data/dungeons/eventai/uldaman.ts +355 -0
- package/src/data/dungeons/eventai/wailing-caverns.ts +396 -0
- package/src/data/dungeons/eventai/zul-farrak.ts +591 -0
- package/src/data/dungeons/gnomeregan.ts +909 -0
- package/src/data/dungeons/index.ts +126 -0
- package/src/data/dungeons/instance-geometry.json +5233 -0
- package/src/data/dungeons/key-sources.json +169 -0
- package/src/data/dungeons/lbrs.ts +996 -0
- package/src/data/dungeons/maraudon.ts +786 -0
- package/src/data/dungeons/molten-core.ts +194 -0
- package/src/data/dungeons/naxxramas.ts +173 -0
- package/src/data/dungeons/onyxias-lair.ts +71 -0
- package/src/data/dungeons/pack-from-clusters.ts +1256 -0
- package/src/data/dungeons/ragefire-chasm.ts +238 -0
- package/src/data/dungeons/razorfen-downs.ts +698 -0
- package/src/data/dungeons/razorfen-kraul.ts +766 -0
- package/src/data/dungeons/ruins-of-ahnqiraj.ts +121 -0
- package/src/data/dungeons/scholomance.ts +1076 -0
- package/src/data/dungeons/shadowfang-keep.ts +740 -0
- package/src/data/dungeons/sm-armory.ts +266 -0
- package/src/data/dungeons/sm-cathedral.ts +430 -0
- package/src/data/dungeons/sm-graveyard.ts +482 -0
- package/src/data/dungeons/sm-library.ts +337 -0
- package/src/data/dungeons/stratholme-live.ts +808 -0
- package/src/data/dungeons/stratholme-undead.ts +1164 -0
- package/src/data/dungeons/sunken-temple.ts +931 -0
- package/src/data/dungeons/temple-of-ahnqiraj.ts +119 -0
- package/src/data/dungeons/the-stockade.ts +282 -0
- package/src/data/dungeons/ubrs.ts +1286 -0
- package/src/data/dungeons/uldaman.ts +685 -0
- package/src/data/dungeons/wailing-caverns.ts +356 -0
- package/src/data/dungeons/zul-farrak.ts +686 -0
- package/src/data/dungeons/zul-gurub.ts +142 -0
- package/src/data/dungeons.ts +42 -0
- package/src/data/fixtures.ts +446 -0
- package/src/data/gameobject-data.json.gz +0 -0
- package/src/data/item-data.json.gz +0 -0
- package/src/data/loot-data.json.gz +0 -0
- package/src/data/maps/atlas-approach-maps.json +1425 -0
- package/src/data/maps/atlas-landmarks.json +1711 -0
- package/src/data/maps/dungeon-viewports.json +392 -0
- package/src/data/materializeModDamagePercentTaken.ts +70 -0
- package/src/data/materializeModHealthRegen.ts +89 -0
- package/src/data/materializeSpellReputation.ts +26 -0
- package/src/data/pageTexts.ts +19 -0
- package/src/data/profession-data.json.gz +0 -0
- package/src/data/raid-plans/blackfathom-deeps.json +86 -0
- package/src/data/raid-plans/blackrock-depths.json +419 -0
- package/src/data/raid-plans/blackwing-lair.json +328 -0
- package/src/data/raid-plans/deadmines.json +55 -0
- package/src/data/raid-plans/dire-maul-east.json +94 -0
- package/src/data/raid-plans/dire-maul-north.json +70 -0
- package/src/data/raid-plans/dire-maul-west.json +75 -0
- package/src/data/raid-plans/gnomeregan.json +131 -0
- package/src/data/raid-plans/lbrs.json +176 -0
- package/src/data/raid-plans/maraudon.json +102 -0
- package/src/data/raid-plans/molten-core.json +169 -0
- package/src/data/raid-plans/naxxramas.json +354 -0
- package/src/data/raid-plans/onyxias-lair.json +18 -0
- package/src/data/raid-plans/ragefire-chasm.json +30 -0
- package/src/data/raid-plans/razorfen-downs.json +75 -0
- package/src/data/raid-plans/razorfen-kraul.json +53 -0
- package/src/data/raid-plans/ruins-of-ahnqiraj.json +110 -0
- package/src/data/raid-plans/scholomance.json +170 -0
- package/src/data/raid-plans/shadowfang-keep.json +87 -0
- package/src/data/raid-plans/sm-armory.json +53 -0
- package/src/data/raid-plans/sm-cathedral.json +72 -0
- package/src/data/raid-plans/sm-graveyard.json +63 -0
- package/src/data/raid-plans/sm-library.json +23 -0
- package/src/data/raid-plans/stratholme-live.json +129 -0
- package/src/data/raid-plans/stratholme-undead.json +73 -0
- package/src/data/raid-plans/sunken-temple.json +159 -0
- package/src/data/raid-plans/temple-of-ahnqiraj.json +163 -0
- package/src/data/raid-plans/the-stockade.json +42 -0
- package/src/data/raid-plans/ubrs.json +53 -0
- package/src/data/raid-plans/uldaman.json +131 -0
- package/src/data/raid-plans/wailing-caverns.json +90 -0
- package/src/data/raid-plans/zul-farrak.json +156 -0
- package/src/data/raid-plans/zul-gurub.json +307 -0
- package/src/data/raidInstanceCreatures.mjs +473 -0
- package/src/data/rotations/index.ts +152 -0
- package/src/data/rotations/layout.mjs +72 -0
- package/src/data/rotations/pve/druid/balance.json +754 -0
- package/src/data/rotations/pve/druid/feral-tank.json +2448 -0
- package/src/data/rotations/pve/druid/feral.json +2660 -0
- package/src/data/rotations/pve/druid/restoration.json +424 -0
- package/src/data/rotations/pve/hunter/beast-mastery.json +1288 -0
- package/src/data/rotations/pve/hunter/marksmanship.json +1260 -0
- package/src/data/rotations/pve/hunter/survival.json +1373 -0
- package/src/data/rotations/pve/mage/arcane-leveling.json +1798 -0
- package/src/data/rotations/pve/mage/fire-leveling.json +2003 -0
- package/src/data/rotations/pve/mage/frost-leveling.json +1912 -0
- package/src/data/rotations/pve/paladin/holy.json +755 -0
- package/src/data/rotations/pve/paladin/protection.json +962 -0
- package/src/data/rotations/pve/paladin/retribution.json +924 -0
- package/src/data/rotations/pve/priest/discipline.json +772 -0
- package/src/data/rotations/pve/priest/holy.json +707 -0
- package/src/data/rotations/pve/priest/shadow.json +787 -0
- package/src/data/rotations/pve/rogue/assassination.json +2477 -0
- package/src/data/rotations/pve/rogue/combat.json +2483 -0
- package/src/data/rotations/pve/rogue/subtlety.json +2523 -0
- package/src/data/rotations/pve/shaman/elemental.json +1157 -0
- package/src/data/rotations/pve/shaman/enhancement.json +1085 -0
- package/src/data/rotations/pve/shaman/restoration.json +592 -0
- package/src/data/rotations/pve/warlock/affliction.json +1185 -0
- package/src/data/rotations/pve/warlock/demonology.json +1165 -0
- package/src/data/rotations/pve/warlock/destruction.json +1177 -0
- package/src/data/rotations/pve/warrior/arms.json +3020 -0
- package/src/data/rotations/pve/warrior/fury.json +2996 -0
- package/src/data/rotations/pve/warrior/protection.json +2509 -0
- package/src/data/rotations/pvp/druid/balance.json +593 -0
- package/src/data/rotations/pvp/druid/feral.json +3276 -0
- package/src/data/rotations/pvp/druid/restoration.json +662 -0
- package/src/data/rotations/pvp/hunter/beast-mastery.json +665 -0
- package/src/data/rotations/pvp/hunter/marksmanship.json +2406 -0
- package/src/data/rotations/pvp/hunter/survival.json +635 -0
- package/src/data/rotations/pvp/mage/arcane.json +574 -0
- package/src/data/rotations/pvp/mage/fire.json +570 -0
- package/src/data/rotations/pvp/mage/frost.json +4048 -0
- package/src/data/rotations/pvp/paladin/holy.json +1498 -0
- package/src/data/rotations/pvp/paladin/protection.json +567 -0
- package/src/data/rotations/pvp/paladin/retribution.json +1110 -0
- package/src/data/rotations/pvp/priest/discipline.json +1848 -0
- package/src/data/rotations/pvp/priest/holy.json +6340 -0
- package/src/data/rotations/pvp/priest/shadow.json +2329 -0
- package/src/data/rotations/pvp/rogue/assassination.json +1322 -0
- package/src/data/rotations/pvp/rogue/combat.json +5258 -0
- package/src/data/rotations/pvp/rogue/subtlety.json +586 -0
- package/src/data/rotations/pvp/shaman/elemental.json +1404 -0
- package/src/data/rotations/pvp/shaman/enhancement.json +1277 -0
- package/src/data/rotations/pvp/shaman/restoration.json +2490 -0
- package/src/data/rotations/pvp/warlock/affliction.json +1017 -0
- package/src/data/rotations/pvp/warlock/demonology.json +1503 -0
- package/src/data/rotations/pvp/warlock/destruction.json +638 -0
- package/src/data/rotations/pvp/warrior/arms.json +5665 -0
- package/src/data/rotations/pvp/warrior/fury.json +850 -0
- package/src/data/rotations/pvp/warrior/protection.json +777 -0
- package/src/data/schema/bundle.ts +511 -0
- package/src/data/schema/constants.ts +49 -0
- package/src/data/schema/creature.ts +454 -0
- package/src/data/schema/encounter.ts +155 -0
- package/src/data/schema/faction.ts +48 -0
- package/src/data/schema/item.ts +297 -0
- package/src/data/schema/itemEnchant.ts +105 -0
- package/src/data/schema/npc.ts +44 -0
- package/src/data/schema/primitives.ts +295 -0
- package/src/data/schema/profession.ts +226 -0
- package/src/data/schema/quest.ts +154 -0
- package/src/data/schema/spell.ts +1149 -0
- package/src/data/schema/talent.ts +74 -0
- package/src/data/schema/world.ts +124 -0
- package/src/data/schema.ts +19 -0
- package/src/data/skinNodeGapFill.ts +68 -0
- package/src/data/spell-data.json.gz +0 -0
- package/src/data/spell-script-targets.json +1 -0
- package/src/data/tables/ahItemValues.json.gz +0 -0
- package/src/data/tables/alteracValley.json.gz +0 -0
- package/src/data/tables/areatriggerTeleports.json.gz +0 -0
- package/src/data/tables/botNames.json.gz +0 -0
- package/src/data/tables/botPopulation.json.gz +0 -0
- package/src/data/tables/botSpecs.json.gz +0 -0
- package/src/data/tables/classLevelStats.json.gz +0 -0
- package/src/data/tables/combatConditions.json.gz +0 -0
- package/src/data/tables/combatSkillMeta.json.gz +0 -0
- package/src/data/tables/conditions.json.gz +0 -0
- package/src/data/tables/createInfo.json.gz +0 -0
- package/src/data/tables/creatureAggroGroups.json.gz +0 -0
- package/src/data/tables/creatureClassLevelStats.json.gz +0 -0
- package/src/data/tables/creaturePatrolPacks.json.gz +0 -0
- package/src/data/tables/creatureWeapons.json.gz +0 -0
- package/src/data/tables/creatures.json.gz +0 -0
- package/src/data/tables/dbScripts.json.gz +0 -0
- package/src/data/tables/dungeonAuthoredRoutes.json.gz +0 -0
- package/src/data/tables/dungeonCensus.json.gz +0 -0
- package/src/data/tables/dungeonGameObjects.json.gz +0 -0
- package/src/data/tables/dungeonPacks.json.gz +0 -0
- package/src/data/tables/dungeonPlateCover.json.gz +0 -0
- package/src/data/tables/dungeonSpawns.json.gz +0 -0
- package/src/data/tables/dungeonSurvey.json.gz +0 -0
- package/src/data/tables/durabilityCosts.json.gz +0 -0
- package/src/data/tables/durabilityQuality.json.gz +0 -0
- package/src/data/tables/enchantApplicators.json.gz +0 -0
- package/src/data/tables/entranceYards.json.gz +0 -0
- package/src/data/tables/explorationBasexp.json.gz +0 -0
- package/src/data/tables/factionTemplates.json.gz +0 -0
- package/src/data/tables/factions.json.gz +0 -0
- package/src/data/tables/gameEvents.json.gz +0 -0
- package/src/data/tables/gameObjects.json.gz +0 -0
- package/src/data/tables/gossipMenus.json.gz +0 -0
- package/src/data/tables/graveyards.json.gz +0 -0
- package/src/data/tables/instanceMaps.json.gz +0 -0
- package/src/data/tables/itemEnchantmentTemplates.json.gz +0 -0
- package/src/data/tables/itemEnchants.json.gz +0 -0
- package/src/data/tables/itemRandomProperties.json.gz +0 -0
- package/src/data/tables/itemSets.json.gz +0 -0
- package/src/data/tables/items.json.gz +0 -0
- package/src/data/tables/levelStats.json.gz +0 -0
- package/src/data/tables/locks.json.gz +0 -0
- package/src/data/tables/map-pin-overrides.json.gz +0 -0
- package/src/data/tables/nonCombatTrash.json.gz +0 -0
- package/src/data/tables/npcTexts.json.gz +0 -0
- package/src/data/tables/pageTexts.json.gz +0 -0
- package/src/data/tables/petAbilityTP.json.gz +0 -0
- package/src/data/tables/petGenericSpells.json.gz +0 -0
- package/src/data/tables/petLevelStats.json.gz +0 -0
- package/src/data/tables/playerbot-weightscales.json.gz +0 -0
- package/src/data/tables/pools.json.gz +0 -0
- package/src/data/tables/quests.json.gz +0 -0
- package/src/data/tables/races.json.gz +0 -0
- package/src/data/tables/reputationRanks.json.gz +0 -0
- package/src/data/tables/reputationRates.json.gz +0 -0
- package/src/data/tables/reputationSpillover.json.gz +0 -0
- package/src/data/tables/ridingSpells.json.gz +0 -0
- package/src/data/tables/scriptTexts.json.gz +0 -0
- package/src/data/tables/soundClipVolume.json.gz +0 -0
- package/src/data/tables/spellChain.json.gz +0 -0
- package/src/data/tables/spellTeleports.json.gz +0 -0
- package/src/data/tables/spells.json.gz +0 -0
- package/src/data/tables/talents.json.gz +0 -0
- package/src/data/tables/tavernTriggers.json.gz +0 -0
- package/src/data/tables/taxiNodes.json.gz +0 -0
- package/src/data/tables/taxiRoutes.json.gz +0 -0
- package/src/data/tables/trainers.json.gz +0 -0
- package/src/data/tables/transports.json.gz +0 -0
- package/src/data/tables/travelAnchors.json.gz +0 -0
- package/src/data/tables/travelEdges.json.gz +0 -0
- package/src/data/tables/unitConditions.json.gz +0 -0
- package/src/data/tables/vendors.json.gz +0 -0
- package/src/data/tables/weaponEnchantProcs.json.gz +0 -0
- package/src/data/tables/xp.json.gz +0 -0
- package/src/data/tables/zoneWeather.json.gz +0 -0
- package/src/data/tables/zones.json.gz +0 -0
- package/src/data/world-data.ts +56 -0
- package/src/data/zone-music-sets.json +2751 -0
- package/src/dev/etl/lib/loot-chance-sanity.mjs +180 -0
- package/src/domain/audioQuality.mjs +145 -0
- package/src/domain/bg/ab.ts +433 -0
- package/src/domain/bg/arenaComps.ts +644 -0
- package/src/domain/bg/arenaLab.ts +695 -0
- package/src/domain/bg/arenaPace.ts +31 -0
- package/src/domain/bg/arenaRating.ts +110 -0
- package/src/domain/bg/av.ts +758 -0
- package/src/domain/bg/avEnemyPush.ts +219 -0
- package/src/domain/bg/avField.ts +283 -0
- package/src/domain/bg/avInstanceNodes.ts +197 -0
- package/src/domain/bg/avInstanceScore.ts +123 -0
- package/src/domain/bg/avQuestItems.ts +55 -0
- package/src/domain/bg/avQuests.ts +210 -0
- package/src/domain/bg/avSession.ts +196 -0
- package/src/domain/bg/bgConsumes.ts +269 -0
- package/src/domain/bg/bgPrebuff.ts +153 -0
- package/src/domain/bg/bgRest.ts +91 -0
- package/src/domain/bg/bgRoster.ts +305 -0
- package/src/domain/bg/bgTier.ts +61 -0
- package/src/domain/bg/bgWorld.ts +862 -0
- package/src/domain/bg/match.ts +215 -0
- package/src/domain/bg/strategies.ts +552 -0
- package/src/domain/bg/types.ts +291 -0
- package/src/domain/bg/wsg.ts +264 -0
- package/src/domain/bots/actionProviderSpec.ts +38 -0
- package/src/domain/bots/autoConsumables.ts +285 -0
- package/src/domain/bots/betweenPullRest.ts +239 -0
- package/src/domain/bots/botGear.ts +248 -0
- package/src/domain/bots/botInventory.ts +486 -0
- package/src/domain/bots/botPet.ts +535 -0
- package/src/domain/bots/botPool.ts +291 -0
- package/src/domain/bots/botProfessions.ts +334 -0
- package/src/domain/bots/companionActionBar.ts +209 -0
- package/src/domain/bots/companionHireCost.ts +236 -0
- package/src/domain/bots/companionMissions.ts +582 -0
- package/src/domain/bots/companionPrep.ts +254 -0
- package/src/domain/bots/companionQuests.ts +270 -0
- package/src/domain/bots/companionRep.ts +75 -0
- package/src/domain/bots/companionRotation.ts +708 -0
- package/src/domain/bots/companionXp.ts +66 -0
- package/src/domain/bots/consumableShare.ts +811 -0
- package/src/domain/bots/dpsMate.ts +1072 -0
- package/src/domain/bots/groupCompReport.ts +132 -0
- package/src/domain/bots/healthstoneSupply.ts +198 -0
- package/src/domain/bots/outfitter.ts +1056 -0
- package/src/domain/bots/paladinPrePull.ts +231 -0
- package/src/domain/bots/partyRole.ts +40 -0
- package/src/domain/bots/playerbotEquipCache.ts +412 -0
- package/src/domain/bots/playerbotItemEligibility.ts +60 -0
- package/src/domain/bots/playerbotStatWeight.ts +853 -0
- package/src/domain/bots/playerbotUnavailableItems.ts +147 -0
- package/src/domain/bots/prePullBuffs.ts +219 -0
- package/src/domain/bots/prePullStealth.ts +82 -0
- package/src/domain/bots/runSupply.ts +251 -0
- package/src/domain/bots/runSupplyPrefs.ts +28 -0
- package/src/domain/bots/sendTradeGoods.ts +394 -0
- package/src/domain/bots/soulShardCap.ts +35 -0
- package/src/domain/bots/squad.ts +842 -0
- package/src/domain/bots/squadRun.ts +81 -0
- package/src/domain/character/actionbar.ts +346 -0
- package/src/domain/character/appearance.ts +165 -0
- package/src/domain/character/buildCombatant.ts +1726 -0
- package/src/domain/character/character.ts +356 -0
- package/src/domain/character/characterName.ts +43 -0
- package/src/domain/character/characterRatings.ts +424 -0
- package/src/domain/character/characterSpells.ts +282 -0
- package/src/domain/character/characterStats.ts +1559 -0
- package/src/domain/character/characterTalents.ts +398 -0
- package/src/domain/character/combatSkills.ts +994 -0
- package/src/domain/character/displayForm.ts +72 -0
- package/src/domain/character/formTalentAuras.ts +171 -0
- package/src/domain/character/forms.ts +499 -0
- package/src/domain/character/races.ts +40 -0
- package/src/domain/character/resurrectionSickness.ts +49 -0
- package/src/domain/character/spellbook.ts +169 -0
- package/src/domain/character/stanceBar.ts +354 -0
- package/src/domain/character/stancePrereq.ts +70 -0
- package/src/domain/character/talentBuilds.ts +63 -0
- package/src/domain/character/talentImport.ts +130 -0
- package/src/domain/character/talentLink.ts +123 -0
- package/src/domain/character/talents.ts +201 -0
- package/src/domain/core/conditionSnapshot.ts +92 -0
- package/src/domain/core/hover.ts +14 -0
- package/src/domain/core/ids.ts +58 -0
- package/src/domain/core/internalNameTags.ts +5 -0
- package/src/domain/core/runtimeDataPatches.ts +357 -0
- package/src/domain/creature/aiTargeting.ts +588 -0
- package/src/domain/creature/combatCondition.ts +157 -0
- package/src/domain/creature/creatureAggro.ts +804 -0
- package/src/domain/creature/creatureAi.ts +206 -0
- package/src/domain/creature/creatureAiConstants.ts +661 -0
- package/src/domain/creature/creatureCastList.ts +1007 -0
- package/src/domain/creature/creatureEventAi.ts +1409 -0
- package/src/domain/creature/creatureGroups.ts +144 -0
- package/src/domain/creature/creatureLinking.ts +333 -0
- package/src/domain/creature/creatureScaling.ts +320 -0
- package/src/domain/creature/creatureTrigger.ts +53 -0
- package/src/domain/creature/creatureWander.ts +74 -0
- package/src/domain/creature/eventAiTexts.ts +145 -0
- package/src/domain/creature/faction.ts +287 -0
- package/src/domain/creature/forcedReaction.ts +48 -0
- package/src/domain/creature/outdoorHuntable.ts +74 -0
- package/src/domain/creature/patrolPacks.ts +210 -0
- package/src/domain/creature/reputation.ts +967 -0
- package/src/domain/creature/unitFlags.ts +74 -0
- package/src/domain/creature/worldBosses.ts +393 -0
- package/src/domain/dungeonMusic.mjs +48 -0
- package/src/domain/economy/auction.ts +282 -0
- package/src/domain/economy/auctionBot.ts +1060 -0
- package/src/domain/economy/honorPrice.ts +100 -0
- package/src/domain/economy/mail.ts +958 -0
- package/src/domain/economy/money.ts +42 -0
- package/src/domain/economy/syntheticClassTrainers.ts +62 -0
- package/src/domain/economy/trading.ts +296 -0
- package/src/domain/economy/vendorSellAll.ts +252 -0
- package/src/domain/economy/vendorStock.ts +146 -0
- package/src/domain/items/ammo.ts +817 -0
- package/src/domain/items/bank.ts +162 -0
- package/src/domain/items/consumableFamily.ts +205 -0
- package/src/domain/items/craftUpgrades.ts +83 -0
- package/src/domain/items/createItem.ts +93 -0
- package/src/domain/items/durability.ts +581 -0
- package/src/domain/items/equipment.ts +441 -0
- package/src/domain/items/equipmentHelper.ts +767 -0
- package/src/domain/items/inventory.ts +561 -0
- package/src/domain/items/itemBonding.ts +93 -0
- package/src/domain/items/itemContainer.ts +245 -0
- package/src/domain/items/itemDisplayName.ts +33 -0
- package/src/domain/items/itemInstance.ts +181 -0
- package/src/domain/items/itemRandomEnchantOutcomes.ts +315 -0
- package/src/domain/items/itemRandomProps.ts +77 -0
- package/src/domain/items/itemRestrictions.ts +329 -0
- package/src/domain/items/itemTeaches.ts +82 -0
- package/src/domain/items/itemUseCooldown.ts +52 -0
- package/src/domain/items/itemUseTarget.ts +90 -0
- package/src/domain/items/junkCategories.ts +174 -0
- package/src/domain/items/junkFilter.ts +368 -0
- package/src/domain/items/keyring.ts +131 -0
- package/src/domain/items/loot.ts +221 -0
- package/src/domain/items/mounts.ts +190 -0
- package/src/domain/items/onUsePayload.ts +102 -0
- package/src/domain/items/pendingLoot.ts +107 -0
- package/src/domain/items/relic.ts +105 -0
- package/src/domain/items/rocketBoots.ts +36 -0
- package/src/domain/items/sortContainer.ts +139 -0
- package/src/domain/items/spareContainers.ts +71 -0
- package/src/domain/items/spellReagentIndex.ts +131 -0
- package/src/domain/items/staleConsumables.ts +66 -0
- package/src/domain/items/weaponSpender.ts +110 -0
- package/src/domain/listDungeonIds.mjs +28 -0
- package/src/domain/macro/compose.ts +239 -0
- package/src/domain/macro/conditions.ts +260 -0
- package/src/domain/macro/creatureTypes.ts +40 -0
- package/src/domain/macro/describe.ts +169 -0
- package/src/domain/macro/icon.ts +60 -0
- package/src/domain/macro/index.ts +20 -0
- package/src/domain/macro/normalize.ts +112 -0
- package/src/domain/macro/parse.ts +316 -0
- package/src/domain/macro/plan.ts +288 -0
- package/src/domain/macro/rotation.ts +590 -0
- package/src/domain/macro/rotationEnv.ts +173 -0
- package/src/domain/macro/slots.ts +101 -0
- package/src/domain/macro/snapshot.ts +114 -0
- package/src/domain/macro/templates.ts +446 -0
- package/src/domain/macro/types.ts +70 -0
- package/src/domain/macro/units.ts +93 -0
- package/src/domain/macro/weaponTypes.ts +83 -0
- package/src/domain/nav/cityTeleports.ts +10 -0
- package/src/domain/nav/mounting.ts +113 -0
- package/src/domain/nav/nav.ts +43 -0
- package/src/domain/nav/navCollision.ts +606 -0
- package/src/domain/nav/navOutdoors.ts +91 -0
- package/src/domain/nav/navRegistry.ts +179 -0
- package/src/domain/nav/navRoomGraph.ts +302 -0
- package/src/domain/nav/navTypes.ts +43 -0
- package/src/domain/nav/navmesh.ts +1715 -0
- package/src/domain/nav/speed.ts +200 -0
- package/src/domain/nav/storey.ts +10 -0
- package/src/domain/nav/travel.ts +725 -0
- package/src/domain/nav/travelTypes.ts +75 -0
- package/src/domain/pet/pet.ts +1150 -0
- package/src/domain/pet/petActionBar.ts +180 -0
- package/src/domain/pet/petSpellbook.ts +39 -0
- package/src/domain/pet/petStable.ts +81 -0
- package/src/domain/professions/autoDisenchant.ts +236 -0
- package/src/domain/professions/characterEnchants.ts +176 -0
- package/src/domain/professions/craftCooldown.ts +52 -0
- package/src/domain/professions/enchantApply.ts +62 -0
- package/src/domain/professions/enchantTargets.ts +114 -0
- package/src/domain/professions/enchanting.ts +236 -0
- package/src/domain/professions/gatherFilter.ts +149 -0
- package/src/domain/professions/gearEnchant.ts +137 -0
- package/src/domain/professions/heldTools.ts +29 -0
- package/src/domain/professions/learnAllRecipes.ts +146 -0
- package/src/domain/professions/miningHarvest.ts +69 -0
- package/src/domain/professions/partyDisenchant.ts +294 -0
- package/src/domain/professions/partyWork.ts +83 -0
- package/src/domain/professions/pickpocket.ts +172 -0
- package/src/domain/professions/poisons.ts +240 -0
- package/src/domain/professions/professions.ts +677 -0
- package/src/domain/professions/rareNodes.ts +45 -0
- package/src/domain/professions/skinLoot.ts +27 -0
- package/src/domain/professions/vendorReagents.ts +152 -0
- package/src/domain/professions/weaponImbueHand.ts +171 -0
- package/src/domain/questFarmFilter.mjs +42 -0
- package/src/domain/quests/doQuest.ts +1204 -0
- package/src/domain/quests/gossip.ts +308 -0
- package/src/domain/quests/gossipTalk.ts +298 -0
- package/src/domain/quests/interpreter/canComplete.ts +150 -0
- package/src/domain/quests/interpreter/claim.ts +137 -0
- package/src/domain/quests/interpreter/dbscript/handlers.ts +1371 -0
- package/src/domain/quests/interpreter/dbscript/handlersBatch.ts +1251 -0
- package/src/domain/quests/interpreter/dbscript/hooks.ts +331 -0
- package/src/domain/quests/interpreter/dbscript/index.ts +212 -0
- package/src/domain/quests/interpreter/dbscript/opcodes.ts +257 -0
- package/src/domain/quests/interpreter/dbscript/registry.ts +1194 -0
- package/src/domain/quests/interpreter/dbscript/registryAreatriggerScripts.ts +539 -0
- package/src/domain/quests/interpreter/dbscript/registryAshenvale.ts +553 -0
- package/src/domain/quests/interpreter/dbscript/registryAzshara.ts +272 -0
- package/src/domain/quests/interpreter/dbscript/registryDarkshore.ts +637 -0
- package/src/domain/quests/interpreter/dbscript/registryDesolace.ts +501 -0
- package/src/domain/quests/interpreter/dbscript/registryDustwallowMarsh.ts +735 -0
- package/src/domain/quests/interpreter/dbscript/registryFelwood.ts +493 -0
- package/src/domain/quests/interpreter/dbscript/registryFeralas.ts +485 -0
- package/src/domain/quests/interpreter/dbscript/registryGoScripts.ts +203 -0
- package/src/domain/quests/interpreter/dbscript/registryMoonglade.ts +288 -0
- package/src/domain/quests/interpreter/dbscript/registryNpcProfessions.ts +230 -0
- package/src/domain/quests/interpreter/dbscript/registryNpcsSpecial.ts +545 -0
- package/src/domain/quests/interpreter/dbscript/registryRazorfenDowns.ts +293 -0
- package/src/domain/quests/interpreter/dbscript/registryRazorfenKraul.ts +221 -0
- package/src/domain/quests/interpreter/dbscript/registryRedridge.ts +112 -0
- package/src/domain/quests/interpreter/dbscript/registrySearingGorge.ts +125 -0
- package/src/domain/quests/interpreter/dbscript/registrySiblings.ts +51 -0
- package/src/domain/quests/interpreter/dbscript/registrySilverpineForest.ts +172 -0
- package/src/domain/quests/interpreter/dbscript/registryStonetalonMountains.ts +180 -0
- package/src/domain/quests/interpreter/dbscript/registryStormwindCity.ts +369 -0
- package/src/domain/quests/interpreter/dbscript/registryStranglethornVale.ts +115 -0
- package/src/domain/quests/interpreter/dbscript/registryStratholme.ts +146 -0
- package/src/domain/quests/interpreter/dbscript/registrySunkenTemple.ts +149 -0
- package/src/domain/quests/interpreter/dbscript/registrySwampOfSorrows.ts +163 -0
- package/src/domain/quests/interpreter/dbscript/registryTanaris.ts +318 -0
- package/src/domain/quests/interpreter/dbscript/registryTeldrassil.ts +120 -0
- package/src/domain/quests/interpreter/dbscript/registryTheBarrens.ts +603 -0
- package/src/domain/quests/interpreter/dbscript/registryThousandNeedles.ts +499 -0
- package/src/domain/quests/interpreter/dbscript/registryTirisfalGlades.ts +222 -0
- package/src/domain/quests/interpreter/dbscript/registryUngoroCrater.ts +291 -0
- package/src/domain/quests/interpreter/dbscript/registryWarEffort.ts +773 -0
- package/src/domain/quests/interpreter/dbscript/registryWesternPlaguelands.ts +206 -0
- package/src/domain/quests/interpreter/dbscript/registryWestfall.ts +190 -0
- package/src/domain/quests/interpreter/dbscript/registryWetlands.ts +186 -0
- package/src/domain/quests/interpreter/dbscript/registryWinterspring.ts +235 -0
- package/src/domain/quests/interpreter/dbscript/types.ts +817 -0
- package/src/domain/quests/interpreter/dbscript/vm.ts +171 -0
- package/src/domain/quests/interpreter/diff.ts +349 -0
- package/src/domain/quests/interpreter/fixtures/oracle-questlog-qi2.json +2417 -0
- package/src/domain/quests/interpreter/flags.ts +101 -0
- package/src/domain/quests/interpreter/index.ts +113 -0
- package/src/domain/quests/interpreter/live.ts +178 -0
- package/src/domain/quests/interpreter/types.ts +81 -0
- package/src/domain/quests/interpreter/updaters.ts +412 -0
- package/src/domain/quests/questCorpseUse.ts +155 -0
- package/src/domain/quests/questDialog.ts +43 -0
- package/src/domain/quests/questFight.ts +34 -0
- package/src/domain/quests/questObjectiveLabel.ts +39 -0
- package/src/domain/quests/questRewardChoice.ts +85 -0
- package/src/domain/quests/questScriptCredit.ts +26 -0
- package/src/domain/quests/questScriptItemUse.ts +74 -0
- package/src/domain/quests/questTameRods.ts +79 -0
- package/src/domain/quests/questlog.ts +1545 -0
- package/src/domain/quests/talkDeliver.ts +170 -0
- package/src/domain/raid/globalActionBar.ts +195 -0
- package/src/domain/raid/instanceRoster.ts +194 -0
- package/src/domain/raid/partySummon.ts +198 -0
- package/src/domain/raid/partyVault.ts +320 -0
- package/src/domain/raid/raidBossAbilities.ts +268 -0
- package/src/domain/raid/raidComp.ts +335 -0
- package/src/domain/raid/raidCustomRows.ts +453 -0
- package/src/domain/raid/raidEncounterGear.ts +298 -0
- package/src/domain/raid/raidEncounterTactics.ts +412 -0
- package/src/domain/raid/raidPlan.ts +509 -0
- package/src/domain/raid/raidPlanEdits.ts +98 -0
- package/src/domain/raid/raidPlanPacks.ts +480 -0
- package/src/domain/raid/raidRecovery.ts +183 -0
- package/src/domain/raid/raidRosterTemplate.ts +138 -0
- package/src/domain/rotation/auraSide.ts +68 -0
- package/src/domain/rotation/build.ts +3031 -0
- package/src/domain/rotation/classes/druid.ts +58 -0
- package/src/domain/rotation/classes/hooks.ts +59 -0
- package/src/domain/rotation/classes/hunter.ts +156 -0
- package/src/domain/rotation/classes/index.ts +104 -0
- package/src/domain/rotation/classes/mage.ts +99 -0
- package/src/domain/rotation/classes/paladin.ts +296 -0
- package/src/domain/rotation/classes/priest.ts +94 -0
- package/src/domain/rotation/classes/pullOpener.ts +92 -0
- package/src/domain/rotation/classes/rogue.ts +69 -0
- package/src/domain/rotation/classes/shaman.ts +29 -0
- package/src/domain/rotation/classes/supportSpec.ts +94 -0
- package/src/domain/rotation/classes/warlock.ts +81 -0
- package/src/domain/rotation/classes/warrior.ts +402 -0
- package/src/domain/rotation/classify.ts +1381 -0
- package/src/domain/rotation/condMetricPicker.ts +379 -0
- package/src/domain/rotation/consumables.ts +242 -0
- package/src/domain/rotation/evaluate.ts +1115 -0
- package/src/domain/rotation/finisherLadder.ts +145 -0
- package/src/domain/rotation/invent.ts +938 -0
- package/src/domain/rotation/killOrder.ts +30 -0
- package/src/domain/rotation/openerForm.ts +22 -0
- package/src/domain/rotation/paladinBuffs.ts +568 -0
- package/src/domain/rotation/prebuff.ts +283 -0
- package/src/domain/rotation/raidRowTuning.ts +34 -0
- package/src/domain/rotation/rotation.ts +11 -0
- package/src/domain/rotation/rotationProfiles.ts +180 -0
- package/src/domain/rotation/rotationSuggestions.ts +851 -0
- package/src/domain/rotation/ruleAfterChain.ts +49 -0
- package/src/domain/rotation/spec.ts +70 -0
- package/src/domain/rotation/spellPicker.ts +140 -0
- package/src/domain/rotation/stand.ts +143 -0
- package/src/domain/rotation/types.ts +718 -0
- package/src/domain/run/battlegroundRun.ts +111 -0
- package/src/domain/run/canEnterContext.ts +32 -0
- package/src/domain/run/death.ts +561 -0
- package/src/domain/run/deathRecovery.ts +289 -0
- package/src/domain/run/duel.ts +141 -0
- package/src/domain/run/dungeonRun.ts +344 -0
- package/src/domain/run/floorRest.ts +159 -0
- package/src/domain/run/honor.ts +557 -0
- package/src/domain/run/honorWeek.ts +180 -0
- package/src/domain/run/whenMap.ts +977 -0
- package/src/domain/scriptdev/arathiHighlands.ts +445 -0
- package/src/domain/scriptdev/ashenvale.ts +635 -0
- package/src/domain/scriptdev/ashenvaleHelpers.ts +1215 -0
- package/src/domain/scriptdev/azshara.ts +892 -0
- package/src/domain/scriptdev/blackfathomDeeps.ts +212 -0
- package/src/domain/scriptdev/blackrockDepths.ts +1434 -0
- package/src/domain/scriptdev/blackrockSpire.ts +730 -0
- package/src/domain/scriptdev/blackwingLair.ts +521 -0
- package/src/domain/scriptdev/burningSteppes.ts +62 -0
- package/src/domain/scriptdev/classFilter.ts +43 -0
- package/src/domain/scriptdev/darkshore.ts +817 -0
- package/src/domain/scriptdev/darkshoreHelpers.ts +853 -0
- package/src/domain/scriptdev/deadmines.ts +331 -0
- package/src/domain/scriptdev/desolace.ts +1239 -0
- package/src/domain/scriptdev/direMaul.ts +859 -0
- package/src/domain/scriptdev/durotar.ts +532 -0
- package/src/domain/scriptdev/dustwallowMarsh.ts +758 -0
- package/src/domain/scriptdev/dustwallowMarshHelpers.ts +994 -0
- package/src/domain/scriptdev/easternPlaguelands.ts +21 -0
- package/src/domain/scriptdev/felwood.ts +1055 -0
- package/src/domain/scriptdev/feralas.ts +571 -0
- package/src/domain/scriptdev/feralasHelpers.ts +838 -0
- package/src/domain/scriptdev/gnomeregan.ts +466 -0
- package/src/domain/scriptdev/guards.ts +616 -0
- package/src/domain/scriptdev/hinterlands.ts +693 -0
- package/src/domain/scriptdev/host.ts +1134 -0
- package/src/domain/scriptdev/index.ts +123 -0
- package/src/domain/scriptdev/instanceRaids.ts +136 -0
- package/src/domain/scriptdev/itemScripts.ts +37 -0
- package/src/domain/scriptdev/lochModan.ts +283 -0
- package/src/domain/scriptdev/maraudon.ts +232 -0
- package/src/domain/scriptdev/moltenCore.ts +831 -0
- package/src/domain/scriptdev/moonglade.ts +659 -0
- package/src/domain/scriptdev/moongladeHelpers.ts +933 -0
- package/src/domain/scriptdev/naxxramas.ts +577 -0
- package/src/domain/scriptdev/naxxramasConstruct.ts +364 -0
- package/src/domain/scriptdev/naxxramasFrostwyrm.ts +378 -0
- package/src/domain/scriptdev/naxxramasMilitary.ts +276 -0
- package/src/domain/scriptdev/npcProfessions.ts +1025 -0
- package/src/domain/scriptdev/npcsSpecial.ts +1379 -0
- package/src/domain/scriptdev/onyxiasLair.ts +313 -0
- package/src/domain/scriptdev/orgrimmar.ts +147 -0
- package/src/domain/scriptdev/professionSpecConsumer.ts +481 -0
- package/src/domain/scriptdev/razorfenDowns.ts +468 -0
- package/src/domain/scriptdev/razorfenKraul.ts +410 -0
- package/src/domain/scriptdev/redridgeMountains.ts +163 -0
- package/src/domain/scriptdev/registry.ts +945 -0
- package/src/domain/scriptdev/ruinsOfAhnqiraj.ts +356 -0
- package/src/domain/scriptdev/scarletMonastery.ts +713 -0
- package/src/domain/scriptdev/scholomance.ts +682 -0
- package/src/domain/scriptdev/searingGorge.ts +295 -0
- package/src/domain/scriptdev/shadowfangKeep.ts +181 -0
- package/src/domain/scriptdev/silithus.ts +71 -0
- package/src/domain/scriptdev/silverpineForest.ts +662 -0
- package/src/domain/scriptdev/stonetalonMountains.ts +493 -0
- package/src/domain/scriptdev/stormwindCity.ts +1192 -0
- package/src/domain/scriptdev/stranglethornVale.ts +402 -0
- package/src/domain/scriptdev/stratholme.ts +650 -0
- package/src/domain/scriptdev/stratholmeScripts.ts +505 -0
- package/src/domain/scriptdev/sunkenTemple.ts +736 -0
- package/src/domain/scriptdev/swampOfSorrows.ts +22 -0
- package/src/domain/scriptdev/tanaris.ts +391 -0
- package/src/domain/scriptdev/tanarisHelpers.ts +661 -0
- package/src/domain/scriptdev/teldrassil.ts +143 -0
- package/src/domain/scriptdev/templeOfAhnqiraj.ts +1013 -0
- package/src/domain/scriptdev/theBarrens.ts +709 -0
- package/src/domain/scriptdev/theBarrensHelpers.ts +1026 -0
- package/src/domain/scriptdev/thousandNeedles.ts +426 -0
- package/src/domain/scriptdev/thousandNeedlesHelpers.ts +566 -0
- package/src/domain/scriptdev/tirisfalGlades.ts +533 -0
- package/src/domain/scriptdev/types.ts +1026 -0
- package/src/domain/scriptdev/uldaman.ts +437 -0
- package/src/domain/scriptdev/ungoroCrater.ts +549 -0
- package/src/domain/scriptdev/ungoroCraterHelpers.ts +1006 -0
- package/src/domain/scriptdev/wailingCaverns.ts +700 -0
- package/src/domain/scriptdev/warEffort.ts +371 -0
- package/src/domain/scriptdev/westernPlaguelands.ts +952 -0
- package/src/domain/scriptdev/westfall.ts +767 -0
- package/src/domain/scriptdev/wetlands.ts +660 -0
- package/src/domain/scriptdev/winterspring.ts +482 -0
- package/src/domain/scriptdev/winterspringHelpers.ts +801 -0
- package/src/domain/scriptdev/worldBosses.ts +605 -0
- package/src/domain/scriptdev/zulfarrak.ts +478 -0
- package/src/domain/scriptdev/zulgurub.ts +1152 -0
- package/src/domain/sim/dummySim.ts +408 -0
- package/src/domain/sim/targetDummy.ts +29 -0
- package/src/domain/social/guild.ts +148 -0
- package/src/domain/spells/areaAuraMembership.ts +93 -0
- package/src/domain/spells/breakableStunCc.ts +166 -0
- package/src/domain/spells/cancelAura.ts +80 -0
- package/src/domain/spells/diminishing.ts +167 -0
- package/src/domain/spells/dispel.ts +149 -0
- package/src/domain/spells/dispelMechanic.ts +105 -0
- package/src/domain/spells/equipSpells.ts +247 -0
- package/src/domain/spells/learnOnTargets.ts +260 -0
- package/src/domain/spells/mergeSchoolImmunityFromRaw.ts +66 -0
- package/src/domain/spells/resurrectSpell.ts +124 -0
- package/src/domain/spells/spellPushback.ts +39 -0
- package/src/domain/spells/spellReflect.ts +43 -0
- package/src/domain/spells/stealth.ts +183 -0
- package/src/domain/spells/totem.ts +394 -0
- package/src/domain/spells/worldCast.ts +187 -0
- package/src/domain/world/areaTriggerLocks.ts +177 -0
- package/src/domain/world/dungeonMusic.ts +108 -0
- package/src/domain/world/gameEvents.ts +119 -0
- package/src/domain/world/gameObjects.ts +1030 -0
- package/src/domain/world/goEventFire.ts +234 -0
- package/src/domain/world/goMoTransport.ts +74 -0
- package/src/domain/world/goTransport.ts +81 -0
- package/src/domain/world/hazards.ts +459 -0
- package/src/domain/world/openLock.ts +398 -0
- package/src/domain/world/pools.ts +470 -0
- package/src/domain/world/restArea.ts +37 -0
- package/src/domain/world/weather.ts +201 -0
- package/src/domain/zoneAmbience.mjs +87 -0
- package/src/domain/zoneMusic.mjs +62 -0
- package/src/domain/zoneMusicQueue.mjs +7 -0
- package/src/engine/combat/activeSeal.ts +39 -0
- package/src/engine/combat/aiTargetResolve.ts +64 -0
- package/src/engine/combat/aoeCast.ts +289 -0
- package/src/engine/combat/applyConsumeAura.ts +74 -0
- package/src/engine/combat/applyDemonicSacrifice.ts +70 -0
- package/src/engine/combat/applyDispelMechanic.ts +30 -0
- package/src/engine/combat/applyHealEffect.ts +301 -0
- package/src/engine/combat/applySchoolAbsorb.ts +114 -0
- package/src/engine/combat/applySpell.ts +3169 -0
- package/src/engine/combat/applySpellLate.ts +2026 -0
- package/src/engine/combat/applySpellLateBag.ts +144 -0
- package/src/engine/combat/areaAura.ts +283 -0
- package/src/engine/combat/attackTable.ts +508 -0
- package/src/engine/combat/battleRez.ts +87 -0
- package/src/engine/combat/castAction.ts +36 -0
- package/src/engine/combat/castTargetGates.ts +75 -0
- package/src/engine/combat/castableKit.ts +68 -0
- package/src/engine/combat/ccMirrorUpgrade.ts +39 -0
- package/src/engine/combat/chainFan.ts +210 -0
- package/src/engine/combat/channel.ts +423 -0
- package/src/engine/combat/chaseCloseness.ts +95 -0
- package/src/engine/combat/cmangosCastGates.ts +129 -0
- package/src/engine/combat/combatFlag.ts +77 -0
- package/src/engine/combat/combatGeometry.ts +1329 -0
- package/src/engine/combat/combustion.ts +68 -0
- package/src/engine/combat/context.ts +730 -0
- package/src/engine/combat/createSession.ts +1165 -0
- package/src/engine/combat/creatureLeash.ts +38 -0
- package/src/engine/combat/crowdControl.ts +1259 -0
- package/src/engine/combat/dispelDuty.ts +54 -0
- package/src/engine/combat/dispelReflect.ts +609 -0
- package/src/engine/combat/displaceFloor.ts +113 -0
- package/src/engine/combat/duelBounds.ts +47 -0
- package/src/engine/combat/equippedItemGate.ts +297 -0
- package/src/engine/combat/evadeLifecycle.ts +63 -0
- package/src/engine/combat/eventAi.ts +2220 -0
- package/src/engine/combat/facing.ts +74 -0
- package/src/engine/combat/fearFlee.ts +137 -0
- package/src/engine/combat/fighterFactory.ts +1144 -0
- package/src/engine/combat/forceDespawnAction.ts +51 -0
- package/src/engine/combat/formulas.ts +807 -0
- package/src/engine/combat/friendlyFan.ts +55 -0
- package/src/engine/combat/groundPoolTick.ts +210 -0
- package/src/engine/combat/hazards.ts +114 -0
- package/src/engine/combat/healTargets.ts +226 -0
- package/src/engine/combat/heartbeatResist.ts +49 -0
- package/src/engine/combat/hostileItemUse.ts +75 -0
- package/src/engine/combat/interruptAuras.ts +66 -0
- package/src/engine/combat/itemRescue.ts +49 -0
- package/src/engine/combat/itemUse.ts +260 -0
- package/src/engine/combat/kitById.ts +23 -0
- package/src/engine/combat/knockBack.ts +201 -0
- package/src/engine/combat/leap.ts +89 -0
- package/src/engine/combat/mitigation.ts +587 -0
- package/src/engine/combat/modTaunt.ts +43 -0
- package/src/engine/combat/motionClaim.ts +268 -0
- package/src/engine/combat/motionGate.ts +150 -0
- package/src/engine/combat/motionGenerator.ts +27 -0
- package/src/engine/combat/navSight.ts +138 -0
- package/src/engine/combat/perfSink.ts +28 -0
- package/src/engine/combat/periodicDue.ts +55 -0
- package/src/engine/combat/petAuras.ts +86 -0
- package/src/engine/combat/pickAction.ts +3449 -0
- package/src/engine/combat/powerForm.ts +735 -0
- package/src/engine/combat/powerPools.ts +49 -0
- package/src/engine/combat/predicates.ts +538 -0
- package/src/engine/combat/procsDamage.ts +1823 -0
- package/src/engine/combat/reactiveGates.ts +135 -0
- package/src/engine/combat/roles.ts +389 -0
- package/src/engine/combat/rotationSubject.ts +78 -0
- package/src/engine/combat/ruleAfter.ts +106 -0
- package/src/engine/combat/seedDisplayAuras.ts +203 -0
- package/src/engine/combat/seedOocEffects.ts +317 -0
- package/src/engine/combat/seedSchoolImmunities.ts +45 -0
- package/src/engine/combat/session-types.ts +458 -0
- package/src/engine/combat/sessionApi.ts +1785 -0
- package/src/engine/combat/sessionCharge.ts +124 -0
- package/src/engine/combat/sessionChase.ts +869 -0
- package/src/engine/combat/sessionDeaths.ts +469 -0
- package/src/engine/combat/sessionFocus.ts +506 -0
- package/src/engine/combat/sessionFollow.ts +167 -0
- package/src/engine/combat/sessionGeometry.ts +188 -0
- package/src/engine/combat/sessionHome.ts +75 -0
- package/src/engine/combat/sessionInject.ts +719 -0
- package/src/engine/combat/sessionMechanicMove.ts +555 -0
- package/src/engine/combat/sessionMotion.ts +1553 -0
- package/src/engine/combat/sessionMotionKit.ts +247 -0
- package/src/engine/combat/sessionMoveIntent.ts +49 -0
- package/src/engine/combat/sessionMoveOrder.ts +200 -0
- package/src/engine/combat/sessionPathTo.ts +657 -0
- package/src/engine/combat/sessionPools.ts +688 -0
- package/src/engine/combat/sessionSteer.ts +144 -0
- package/src/engine/combat/sessionStepBack.ts +151 -0
- package/src/engine/combat/sessionStepWrap.ts +116 -0
- package/src/engine/combat/sessionView.ts +442 -0
- package/src/engine/combat/simEffects.ts +32 -0
- package/src/engine/combat/simFacade.ts +620 -0
- package/src/engine/combat/simPump.ts +264 -0
- package/src/engine/combat/simSnapshot.ts +165 -0
- package/src/engine/combat/simThreadPool.ts +35 -0
- package/src/engine/combat/skeramImages.ts +57 -0
- package/src/engine/combat/slowUpdate.ts +178 -0
- package/src/engine/combat/snapToFloor.ts +42 -0
- package/src/engine/combat/speedAura.ts +29 -0
- package/src/engine/combat/spellReagents.ts +71 -0
- package/src/engine/combat/stealthDetect.ts +93 -0
- package/src/engine/combat/stepBackMotion.ts +171 -0
- package/src/engine/combat/stepSession.ts +3102 -0
- package/src/engine/combat/summonCast.ts +243 -0
- package/src/engine/combat/thaddiusPolarity.ts +97 -0
- package/src/engine/combat/threat.ts +388 -0
- package/src/engine/combat/threatOps.ts +272 -0
- package/src/engine/combat/tickWorkerBody.ts +211 -0
- package/src/engine/combat/totemAuras.ts +122 -0
- package/src/engine/combat/triggerPayload.ts +42 -0
- package/src/engine/combat/triggeredCompanion.ts +63 -0
- package/src/engine/combat/types.ts +1897 -0
- package/src/engine/combat/useObjectTrap.ts +140 -0
- package/src/engine/combat/walkSurface.ts +54 -0
- package/src/engine/combat/weapon.ts +1248 -0
- package/src/engine/combat/worldFrame.ts +43 -0
- package/src/engine/combat.solo-golden.json +2242 -0
- package/src/engine/combat.ts +84 -0
- package/src/engine/core/detMath.ts +289 -0
- package/src/engine/core/events.ts +357 -0
- package/src/engine/core/rng.ts +36 -0
- package/src/engine/game/actionBars.ts +124 -0
- package/src/engine/game/activateObjectFromSpell.ts +320 -0
- package/src/engine/game/applyActiveObjectFromDbScript.ts +98 -0
- package/src/engine/game/applyDeathAndSpellScripts.ts +268 -0
- package/src/engine/game/applyDoorFromDbScript.ts +388 -0
- package/src/engine/game/applyEscortFromDbScript.ts +290 -0
- package/src/engine/game/applyGoSpellcasterCast.ts +40 -0
- package/src/engine/game/applyHoverFromDbScript.ts +110 -0
- package/src/engine/game/applyOocResurrect.ts +71 -0
- package/src/engine/game/applySpellDurability.ts +115 -0
- package/src/engine/game/applyTailFromDbScript.ts +478 -0
- package/src/engine/game/applyTerminateFromDbScript.ts +95 -0
- package/src/engine/game/auctionMail.ts +269 -0
- package/src/engine/game/bagOverflow.ts +130 -0
- package/src/engine/game/bank.ts +110 -0
- package/src/engine/game/barAutofill.ts +26 -0
- package/src/engine/game/battleground.ts +369 -0
- package/src/engine/game/combatEnemySpec.ts +216 -0
- package/src/engine/game/combatObjectTraps.ts +167 -0
- package/src/engine/game/combatSeed.ts +320 -0
- package/src/engine/game/combatSession.ts +3241 -0
- package/src/engine/game/combatSessionClock.ts +62 -0
- package/src/engine/game/companionQuestActions.ts +242 -0
- package/src/engine/game/companionTraining.ts +40 -0
- package/src/engine/game/consumables.ts +624 -0
- package/src/engine/game/context.ts +53 -0
- package/src/engine/game/createHealthstone.ts +45 -0
- package/src/engine/game/deathFlow.ts +127 -0
- package/src/engine/game/devGrant.ts +117 -0
- package/src/engine/game/dynamicObject.ts +365 -0
- package/src/engine/game/enchantItem.ts +177 -0
- package/src/engine/game/equipmentHelper.ts +806 -0
- package/src/engine/game/factionState.ts +108 -0
- package/src/engine/game/garmentsOoc.ts +69 -0
- package/src/engine/game/globalActionBar.ts +179 -0
- package/src/engine/game/goInteractQuestCredit.ts +51 -0
- package/src/engine/game/goMoTransportUse.ts +61 -0
- package/src/engine/game/goTransportUse.ts +60 -0
- package/src/engine/game/healthstoneUpkeep.ts +59 -0
- package/src/engine/game/killSkin.ts +124 -0
- package/src/engine/game/learnFromItem.ts +118 -0
- package/src/engine/game/loadReconcile.ts +551 -0
- package/src/engine/game/loot.ts +294 -0
- package/src/engine/game/macros.ts +125 -0
- package/src/engine/game/mail.ts +293 -0
- package/src/engine/game/meetingStoneQueue.ts +75 -0
- package/src/engine/game/meetingStoneUse.ts +54 -0
- package/src/engine/game/newGameState.ts +363 -0
- package/src/engine/game/oocAuraInterrupt.ts +92 -0
- package/src/engine/game/oocCast.ts +241 -0
- package/src/engine/game/oocCastEffects.ts +803 -0
- package/src/engine/game/oocCastMethods.ts +1077 -0
- package/src/engine/game/openLockFromSpell.ts +174 -0
- package/src/engine/game/outdoorPvp.ts +187 -0
- package/src/engine/game/outfitter.ts +644 -0
- package/src/engine/game/paladinBoard.ts +393 -0
- package/src/engine/game/partyAuras.ts +121 -0
- package/src/engine/game/persistCombatAuras.ts +91 -0
- package/src/engine/game/pets.ts +459 -0
- package/src/engine/game/prePullHealthstones.ts +154 -0
- package/src/engine/game/professions.ts +2227 -0
- package/src/engine/game/pruneStaleRuntime.ts +39 -0
- package/src/engine/game/questFightCredit.ts +36 -0
- package/src/engine/game/questItemOnCorpse.ts +166 -0
- package/src/engine/game/questItemOnCreature.ts +103 -0
- package/src/engine/game/questLog.ts +879 -0
- package/src/engine/game/questSpellComplete.ts +21 -0
- package/src/engine/game/questTurnIn.ts +598 -0
- package/src/engine/game/raidParty.ts +32 -0
- package/src/engine/game/reagentBuyIn.ts +39 -0
- package/src/engine/game/regenRest.ts +186 -0
- package/src/engine/game/rotation.ts +425 -0
- package/src/engine/game/runFullDungeon.ts +611 -0
- package/src/engine/game/runSupply.ts +80 -0
- package/src/engine/game/sanitizeDriftedItems.ts +135 -0
- package/src/engine/game/scriptDevGoUse.ts +397 -0
- package/src/engine/game/scriptDevInnkeeper.ts +150 -0
- package/src/engine/game/scriptDevProfessionSpec.ts +99 -0
- package/src/engine/game/sendEventFromSpell.ts +87 -0
- package/src/engine/game/sortContainers.ts +85 -0
- package/src/engine/game/spellReputation.ts +69 -0
- package/src/engine/game/squad/squadDungeonSeating.ts +197 -0
- package/src/engine/game/squadDungeon.ts +4222 -0
- package/src/engine/game/squadQuestShare.ts +260 -0
- package/src/engine/game/summonObjectSlot.ts +69 -0
- package/src/engine/game/summonObjectWild.ts +73 -0
- package/src/engine/game/summoningRitualUse.ts +141 -0
- package/src/engine/game/talents.ts +356 -0
- package/src/engine/game/tamingRodUse.ts +66 -0
- package/src/engine/game/tempSpawnCreature.ts +136 -0
- package/src/engine/game/tickDungeonPatrolScripts.ts +205 -0
- package/src/engine/game/tickTimedQuests.ts +28 -0
- package/src/engine/game/trade.ts +586 -0
- package/src/engine/game/travel.ts +1155 -0
- package/src/engine/game/types.ts +1855 -0
- package/src/engine/game/vendorOffers.ts +244 -0
- package/src/engine/game/weaponImbueItem.ts +281 -0
- package/src/engine/game/worldSession.ts +183 -0
- package/src/engine/game/worldState.ts +166 -0
- package/src/engine/game.ts +968 -0
- package/src/engine/spells/GAP.md +67 -0
- package/src/engine/spells/absorbs.ts +376 -0
- package/src/engine/spells/addTargetTrigger.ts +53 -0
- package/src/engine/spells/auraPerSecondCost.ts +50 -0
- package/src/engine/spells/auraStacking.ts +497 -0
- package/src/engine/spells/cmangos-special-cases-combustion.ts +13 -0
- package/src/engine/spells/cmangos-special-cases-s18.ts +445 -0
- package/src/engine/spells/cmangos-special-cases-s19.ts +324 -0
- package/src/engine/spells/cmangos-special-cases-s20.ts +385 -0
- package/src/engine/spells/cmangos-special-cases-s21.ts +116 -0
- package/src/engine/spells/cmangos-special-cases-s22.ts +24 -0
- package/src/engine/spells/cmangos-special-cases-s23.ts +52 -0
- package/src/engine/spells/cmangos-special-cases.ts +1570 -0
- package/src/engine/spells/conditions.ts +718 -0
- package/src/engine/spells/effects.ts +203 -0
- package/src/engine/spells/fixtures/oracle-effects-lock9a.json +1 -0
- package/src/engine/spells/immunity.ts +942 -0
- package/src/engine/spells/intentEffects.ts +351 -0
- package/src/engine/spells/interpreter/GAPS.md +21 -0
- package/src/engine/spells/interpreter/amount.ts +109 -0
- package/src/engine/spells/interpreter/class-scripts-kit.ts +478 -0
- package/src/engine/spells/interpreter/diff.ts +246 -0
- package/src/engine/spells/interpreter/duration.ts +90 -0
- package/src/engine/spells/interpreter/form-attrs.json +1 -0
- package/src/engine/spells/interpreter/form-attrs.ts +95 -0
- package/src/engine/spells/interpreter/handlers/aura-remove.ts +113 -0
- package/src/engine/spells/interpreter/handlers/auras-control.ts +1547 -0
- package/src/engine/spells/interpreter/handlers/auras-core.ts +1583 -0
- package/src/engine/spells/interpreter/handlers/auras-misc.ts +1774 -0
- package/src/engine/spells/interpreter/handlers/effect-disenchant.ts +17 -0
- package/src/engine/spells/interpreter/handlers/effect-skinning.ts +20 -0
- package/src/engine/spells/interpreter/handlers/effects-action-utility.ts +1455 -0
- package/src/engine/spells/interpreter/handlers/effects-core.ts +432 -0
- package/src/engine/spells/interpreter/handlers/effects-remainder.ts +105 -0
- package/src/engine/spells/interpreter/handlers/effects-summon-item.ts +687 -0
- package/src/engine/spells/interpreter/handlers/mod-increase-energy-percent.ts +36 -0
- package/src/engine/spells/interpreter/handlers/mod-increase-energy.ts +36 -0
- package/src/engine/spells/interpreter/handlers/periodic-trigger-zero.ts +327 -0
- package/src/engine/spells/interpreter/handlers/procs.ts +131 -0
- package/src/engine/spells/interpreter/handlers/registry.ts +975 -0
- package/src/engine/spells/interpreter/handlers/shared.ts +667 -0
- package/src/engine/spells/interpreter/handlers.ts +9 -0
- package/src/engine/spells/interpreter/hunter-kit.ts +202 -0
- package/src/engine/spells/interpreter/index.ts +525 -0
- package/src/engine/spells/interpreter/item-triggers.ts +1121 -0
- package/src/engine/spells/interpreter/lift-consume-energy.ts +37 -0
- package/src/engine/spells/interpreter/mage-portals.ts +37 -0
- package/src/engine/spells/interpreter/materialize-charm.ts +88 -0
- package/src/engine/spells/interpreter/materialize-dispel-mechanic.ts +25 -0
- package/src/engine/spells/interpreter/materialize-rating-buffs.ts +81 -0
- package/src/engine/spells/interpreter/materialize-speed.ts +109 -0
- package/src/engine/spells/interpreter/materialize-spell-magnet.ts +22 -0
- package/src/engine/spells/interpreter/opcodes.ts +789 -0
- package/src/engine/spells/interpreter/paladin-kit.ts +95 -0
- package/src/engine/spells/interpreter/paladin-seals.ts +499 -0
- package/src/engine/spells/interpreter/priest-warlock-kit.ts +135 -0
- package/src/engine/spells/interpreter/proc.ts +443 -0
- package/src/engine/spells/interpreter/registry/helpers.ts +16 -0
- package/src/engine/spells/interpreter/registry/index.ts +216 -0
- package/src/engine/spells/interpreter/registry/packs/class-abilities.ts +304 -0
- package/src/engine/spells/interpreter/registry/packs/dire-maul.ts +44 -0
- package/src/engine/spells/interpreter/registry/packs/dummy-script-tail.ts +1065 -0
- package/src/engine/spells/interpreter/registry/packs/ex-wave-m.ts +70 -0
- package/src/engine/spells/interpreter/registry/packs/life-tap.ts +107 -0
- package/src/engine/spells/interpreter/registry/packs/paladin.ts +246 -0
- package/src/engine/spells/interpreter/registry/packs/raid-script-summons.ts +26 -0
- package/src/engine/spells/interpreter/registry/packs/raid-threat-wipe.ts +22 -0
- package/src/engine/spells/interpreter/registry/packs/shaman.ts +214 -0
- package/src/engine/spells/interpreter/registry/packs/si4-dummy-generic-a.ts +269 -0
- package/src/engine/spells/interpreter/registry/packs/si4-dummy-generic-b.ts +370 -0
- package/src/engine/spells/interpreter/registry/packs/si4-dummy-generic-c.ts +172 -0
- package/src/engine/spells/interpreter/registry/packs/si4-dummy-hunter.ts +60 -0
- package/src/engine/spells/interpreter/registry/packs/si4-dummy-warrior.ts +31 -0
- package/src/engine/spells/interpreter/registry/packs/si4-script-generic-a.ts +114 -0
- package/src/engine/spells/interpreter/registry/packs/si4-script-generic-b.ts +249 -0
- package/src/engine/spells/interpreter/registry/packs/si4-script-generic-c.ts +233 -0
- package/src/engine/spells/interpreter/registry/types.ts +48 -0
- package/src/engine/spells/interpreter/talent-scripted.ts +329 -0
- package/src/engine/spells/interpreter/targeting.ts +90 -0
- package/src/engine/spells/interpreter/types-intent.ts +1501 -0
- package/src/engine/spells/interpreter/types.ts +419 -0
- package/src/engine/spells/interpreter/warrior-druid-kit.ts +314 -0
- package/src/engine/spells/interpreter/weapon-imbue-kit.ts +384 -0
- package/src/engine/spells/interrupt.ts +217 -0
- package/src/engine/spells/meleeAttackReset.ts +60 -0
- package/src/engine/spells/oocTriggerSpell.ts +49 -0
- package/src/engine/spells/overrideClassScripts.ts +319 -0
- package/src/engine/spells/parryHaste.ts +20 -0
- package/src/engine/spells/procs.ts +300 -0
- package/src/engine/spells/recovery.ts +203 -0
- package/src/engine/spells/scriptTargetDest.ts +76 -0
- package/src/engine/spells/scriptTargets.ts +85 -0
- package/src/engine/spells/singleTargetAura.ts +82 -0
- package/src/engine/spells/specialCasePayloads.ts +57 -0
- package/src/engine/spells/spellAttributes.ts +249 -0
- package/src/engine/spells/spellBonus.ts +143 -0
- package/src/engine/spells/spellModIntents.ts +419 -0
- package/src/engine/spells/spellMods.ts +283 -0
- package/src/engine/spells/spellRadius.ts +15 -0
- package/src/engine/spells/trapObjects.ts +26 -0
- package/src/json.d.ts +7 -0
- package/src/persistence/memoryRepository.ts +23 -0
- package/src/persistence/saveBackups.ts +162 -0
- package/src/persistence/saveFile.ts +68 -0
- package/src/persistence/saveRepository.ts +11 -0
- package/src/persistence/saveShape.ts +668 -0
- package/src/progression/leveling.ts +89 -0
- package/src/progression/rest.ts +1434 -0
- package/src/progression/training.ts +226 -0
- package/src/progression/xpGain.ts +369 -0
- package/src/quests/quests.ts +1580 -0
- package/src/world/aoePull.ts +198 -0
- package/src/world/areaTableDbc.ts +39 -0
- package/src/world/areaTableMapIdPairs.ts +141 -0
- package/src/world/bakeSlug.ts +43 -0
- package/src/world/continuousDungeon.ts +1250 -0
- package/src/world/dungeonBakeProjection.ts +190 -0
- package/src/world/dungeonMapDensity.ts +342 -0
- package/src/world/dungeonMapProjection.ts +135 -0
- package/src/world/dungeonNav.ts +904 -0
- package/src/world/dungeonNpcSim.ts +147 -0
- package/src/world/dungeonObjectives.ts +234 -0
- package/src/world/dungeonPreset.ts +35 -0
- package/src/world/dungeonRoute.ts +311 -0
- package/src/world/dungeonRouteGeometry.ts +732 -0
- package/src/world/dungeonRouteOrder.ts +1037 -0
- package/src/world/dungeonWingRooms.ts +426 -0
- package/src/world/escort.ts +296 -0
- package/src/world/goPoolRoll.ts +75 -0
- package/src/world/instanceObjects.ts +785 -0
- package/src/world/instances.ts +1572 -0
- package/src/world/meetingStones.ts +56 -0
- package/src/world/navmeshFloors.ts +145 -0
- package/src/world/nearGrid.ts +167 -0
- package/src/world/packBudget.ts +613 -0
- package/src/world/rareRoll.ts +335 -0
- package/src/world/routeAuthoring.ts +132 -0
- package/src/world/routeEdit.ts +209 -0
- package/src/world/world.ts +812 -0
- package/src/world/zoneHazards.ts +47 -0
|
@@ -0,0 +1,1455 @@
|
|
|
1
|
+
/** SI2.5 / SI3 handlers — effects-action-utility (split from handlers.ts, pure move). */
|
|
2
|
+
|
|
3
|
+
import type { RawSpellEffect } from '../../../../data/schema.ts';
|
|
4
|
+
import { BASE_CHARGE_SPEED } from '../../../../domain/nav/speed.ts';
|
|
5
|
+
import {
|
|
6
|
+
EVERLOOK_TRANSPORTER_SPELL_ID,
|
|
7
|
+
everlookTransporterOutcomeSpellId,
|
|
8
|
+
} from '../../cmangos-special-cases.ts';
|
|
9
|
+
import { effectAmountRange, clampPositiveRange } from '../amount.ts';
|
|
10
|
+
import type {
|
|
11
|
+
InterpretIntent,
|
|
12
|
+
} from '../types.ts';
|
|
13
|
+
import { dispatchDummy, dispatchScript } from '../registry/index.ts';
|
|
14
|
+
import { isMagePortalGo, isMagePortalSpell } from '../mage-portals.ts';
|
|
15
|
+
import {
|
|
16
|
+
SPELL_EFFECT_OPEN_LOCK_ITEM,
|
|
17
|
+
} from '../opcodes.ts';
|
|
18
|
+
import type {
|
|
19
|
+
HandlerState,
|
|
20
|
+
} from './shared.ts';
|
|
21
|
+
import {
|
|
22
|
+
SPELLFAMILY_WARRIOR,
|
|
23
|
+
SPELLFAMILY_WARLOCK,
|
|
24
|
+
SPELLFAMILY_ROGUE,
|
|
25
|
+
ITEM_CLASS_WEAPON,
|
|
26
|
+
ITEM_CLASS_ARMOR,
|
|
27
|
+
TARGET_LOCATION_DATABASE,
|
|
28
|
+
IN_MILLISECONDS,
|
|
29
|
+
STUCK_HEARTHSTONE_SPELL,
|
|
30
|
+
STUCK_NUDGE_DISTANCE,
|
|
31
|
+
SHIELD_SLAM_IDS,
|
|
32
|
+
MAX_POWERS,
|
|
33
|
+
POWER_HAPPINESS,
|
|
34
|
+
MAX_EXTRA_ATTACKS,
|
|
35
|
+
MOVEMENT_GRAVITY,
|
|
36
|
+
DYNAMIC_OBJECT_FARSIGHT_FOCUS,
|
|
37
|
+
VANISH_SANCTUARY_FAMILY_FLAGS,
|
|
38
|
+
VANISH_CANNOT_BE_DETECTED_MS,
|
|
39
|
+
SHIELD_SLAM_DISPEL_FLAG,
|
|
40
|
+
SPELLCATEGORY_DEVOUR_MAGIC,
|
|
41
|
+
DEVOUR_MAGIC_HEAL,
|
|
42
|
+
GO_ACTION,
|
|
43
|
+
GO_TYPE_FISHINGNODE,
|
|
44
|
+
FISHING_BOBBER_READY_TIME,
|
|
45
|
+
TWILIGHT_TEMPLARS,
|
|
46
|
+
TWILIGHT_DUKES,
|
|
47
|
+
TWILIGHT_ROYALS,
|
|
48
|
+
TWILIGHT_FIXED_NPC,
|
|
49
|
+
schoolOf,
|
|
50
|
+
familyOf,
|
|
51
|
+
flagsOf,
|
|
52
|
+
durationMsResolved,
|
|
53
|
+
trunc,
|
|
54
|
+
calculateSimpleValue,
|
|
55
|
+
} from './shared.ts';
|
|
56
|
+
// ── SI3 batch 1 ─────────────────────────────────────────────────────────────
|
|
57
|
+
|
|
58
|
+
// SPELL_EFFECT_SEND_EVENT emits a real EFFECT, not just an intent: intent-only left pure send-event
|
|
59
|
+
// rows with an empty effect list, so consumers reading effects saw nothing. Misc may legitimately be 0.
|
|
60
|
+
export function handleSendEvent(state: HandlerState, raw: RawSpellEffect, _slot: number): void {
|
|
61
|
+
const eventId = raw.miscValue | 0;
|
|
62
|
+
// Combat / resolveCombatSpell / OOC surface — map event id for dbscripts_on_event.
|
|
63
|
+
state.effects.push({ kind: 'send_event', eventId });
|
|
64
|
+
state.intents.push({ kind: 'send_event', eventId });
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// SPELL_EFFECT_OPEN_LOCK and its item form, player caster only, where the amount is the LOCK SKILL and
|
|
68
|
+
// never damage. A real effect, since intent-only left multi-slot rows missing the open-lock rule.
|
|
69
|
+
export function handleOpenLock(state: HandlerState, raw: RawSpellEffect, _slot: number): void {
|
|
70
|
+
// C++: !player caster → return. Cite: :2206–2210.
|
|
71
|
+
if (state.ctx.casterIsPlayer === false) {
|
|
72
|
+
state.intents.push({ kind: 'skip_effect', reason: 'OPEN_LOCK requires player caster' });
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const { min } = effectAmountRange(raw, state.ctx);
|
|
76
|
+
const lockType = raw.miscValue | 0;
|
|
77
|
+
const eff = raw.effect | 0;
|
|
78
|
+
// Item vs GO: opcode 59 is item-cast open; 33 is GO/item target open.
|
|
79
|
+
// Cite: :2217–2228; dispatch 33+59 → EffectOpenLock.
|
|
80
|
+
let fromItem: boolean;
|
|
81
|
+
if (state.ctx.openLockTargetIsItem != null) {
|
|
82
|
+
fromItem = state.ctx.openLockTargetIsItem;
|
|
83
|
+
} else {
|
|
84
|
+
fromItem = eff === SPELL_EFFECT_OPEN_LOCK_ITEM;
|
|
85
|
+
}
|
|
86
|
+
const intent: InterpretIntent = {
|
|
87
|
+
kind: 'open_lock',
|
|
88
|
+
skillBonus: min,
|
|
89
|
+
fromItem,
|
|
90
|
+
};
|
|
91
|
+
if (lockType) intent.lockType = lockType;
|
|
92
|
+
// Combat / resolveCombatSpell / OOC surface — payload for openLockFromSpell.
|
|
93
|
+
state.effects.push({
|
|
94
|
+
kind: 'open_lock',
|
|
95
|
+
skillBonus: min,
|
|
96
|
+
fromItem,
|
|
97
|
+
...(lockType ? { lockType } : {}),
|
|
98
|
+
});
|
|
99
|
+
state.intents.push(intent);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// SPELL_EFFECT_LEARN_SPELL (36). Player unitTarget → learn EffectTriggerSpell; non-player + player caster →
|
|
103
|
+
// pet path. Cite: SpellEffects.cpp:2445–2465; EffectLearnPetSpell :3386–3411.
|
|
104
|
+
export function handleLearnSpell(state: HandlerState, raw: RawSpellEffect, _slot: number): void {
|
|
105
|
+
const spellToLearn = raw.triggerSpell | 0;
|
|
106
|
+
// No unitTarget at interpret time — bake-thin defaults to player path when trigger set.
|
|
107
|
+
const unitIsPlayer = state.ctx.unitTargetIsPlayer !== false;
|
|
108
|
+
const casterIsPlayer = state.ctx.casterIsPlayer !== false;
|
|
109
|
+
|
|
110
|
+
if (!unitIsPlayer) {
|
|
111
|
+
// EffectLearnSpell :2450–2455 → EffectLearnPetSpell when caster is player.
|
|
112
|
+
if (!casterIsPlayer) return;
|
|
113
|
+
if (!spellToLearn) {
|
|
114
|
+
state.intents.push({ kind: 'skip_effect', reason: 'LEARN_SPELL pet path empty triggerSpell' });
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
state.intents.push({ kind: 'learn_spell', spellId: spellToLearn, target: 'pet' });
|
|
118
|
+
state.effects.push({ kind: 'learn_pet_spell', spellId: spellToLearn });
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Player path always calls learnSpell(trigger) even if 0 in C++ — skip empty for honesty.
|
|
123
|
+
if (!spellToLearn) {
|
|
124
|
+
state.intents.push({ kind: 'skip_effect', reason: 'LEARN_SPELL empty EffectTriggerSpell' });
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
state.intents.push({ kind: 'learn_spell', spellId: spellToLearn, target: 'player' });
|
|
128
|
+
state.effects.push({ kind: 'learn_spell', spellId: spellToLearn });
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// SPELL_EFFECT_LEARN_PET_SPELL (57). Player caster; EffectTriggerSpell is the taught id. Cite:
|
|
132
|
+
// SpellEffects.cpp:3386–3404 EffectLearnPetSpell.
|
|
133
|
+
export function handleLearnPetSpell(state: HandlerState, raw: RawSpellEffect, _slot: number): void {
|
|
134
|
+
if (state.ctx.casterIsPlayer === false) {
|
|
135
|
+
state.intents.push({ kind: 'skip_effect', reason: 'LEARN_PET_SPELL requires player caster' });
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
const spellToLearn = raw.triggerSpell | 0;
|
|
139
|
+
if (!spellToLearn) {
|
|
140
|
+
state.intents.push({ kind: 'skip_effect', reason: 'LEARN_PET_SPELL empty EffectTriggerSpell' });
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
state.effects.push({ kind: 'learn_pet_spell', spellId: spellToLearn });
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// SPELL_EFFECT_KNOCK_BACK: the speeds are the misc value and damage over ten, skipped on a rooted
|
|
147
|
+
// target. A real effect, since the resolver drops intents and multi-slot rows lost the knock.
|
|
148
|
+
export function handleKnockBack(state: HandlerState, raw: RawSpellEffect, _slot: number): void {
|
|
149
|
+
if (state.ctx.targetRooted === true) {
|
|
150
|
+
state.intents.push({ kind: 'skip_effect', reason: 'KNOCK_BACK target rooted (UNIT_STAT_ROOT)' });
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
const horizontal = (raw.miscValue | 0) / 10;
|
|
154
|
+
// damage = calculated effect value; emit min of range as vertical source (no roll).
|
|
155
|
+
const { min } = effectAmountRange(raw, state.ctx);
|
|
156
|
+
const vertical = min / 10;
|
|
157
|
+
// Combat / resolveCombatSpell surface — speeds for SMSG_MOVE_KNOCK_BACK trajectory.
|
|
158
|
+
state.effects.push({ kind: 'knock_back', horizontal, vertical });
|
|
159
|
+
state.intents.push({ kind: 'knock_back', horizontal, vertical });
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// ── SI3 batch 2 ─────────────────────────────────────────────────────────────
|
|
163
|
+
|
|
164
|
+
// SPELL_EFFECT_HEALTH_LEECH: a damage range plus the heal multiplier, where 0 reads as 1 for bake
|
|
165
|
+
// parity. A supplied target health caps the damage. Cite: SpellEffects.cpp:1905-1934.
|
|
166
|
+
export function handleHealthLeech(state: HandlerState, raw: RawSpellEffect, _slot: number): void {
|
|
167
|
+
const { min: baseMin, max: baseMax } = effectAmountRange(raw, state.ctx);
|
|
168
|
+
// damage < 0 → return. Cite: :1910–1911.
|
|
169
|
+
if (baseMax < 0 && baseMin < 0) return;
|
|
170
|
+
|
|
171
|
+
let min = baseMin;
|
|
172
|
+
let max = baseMax;
|
|
173
|
+
// Cap to current health when supplied. Cite: :1915–1918 (after SpellNonMeleeDamageLog).
|
|
174
|
+
const curHp = state.ctx.targetHealth;
|
|
175
|
+
if (curHp != null) {
|
|
176
|
+
const cap = Math.trunc(curHp);
|
|
177
|
+
if (min > cap) min = cap;
|
|
178
|
+
if (max > cap) max = cap;
|
|
179
|
+
}
|
|
180
|
+
const clamped = clampPositiveRange(min, max);
|
|
181
|
+
const multRaw = Number(raw.multipleValue);
|
|
182
|
+
// C++ uses float multiplier as-is; bake uses mult || 1 when 0/absent.
|
|
183
|
+
// Cite: :1920; map-spell-effects :452.
|
|
184
|
+
const multiplier = multRaw && multRaw !== 0 ? multRaw : 1;
|
|
185
|
+
state.effects.push({
|
|
186
|
+
kind: 'health_leech',
|
|
187
|
+
school: schoolOf(state.spell),
|
|
188
|
+
min: clamped.min,
|
|
189
|
+
max: clamped.max,
|
|
190
|
+
multiplier,
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// SPELL_EFFECT_POWER_BURN (62). Burn power (misc = Powers), deal burned × EffectMultipleValue as school
|
|
195
|
+
// damage. Cite: SpellEffects.cpp:1801–1829; map-spell-effects.mjs :445–448.
|
|
196
|
+
export function handlePowerBurn(state: HandlerState, raw: RawSpellEffect, _slot: number): void {
|
|
197
|
+
const powerType = raw.miscValue | 0;
|
|
198
|
+
// Cite: :1803–1804 invalid power.
|
|
199
|
+
if (powerType < 0 || powerType >= MAX_POWERS) return;
|
|
200
|
+
|
|
201
|
+
// Target power type must match when provided. Cite: :1808.
|
|
202
|
+
if (state.ctx.targetPowerType != null && (state.ctx.targetPowerType | 0) !== powerType) {
|
|
203
|
+
state.intents.push({
|
|
204
|
+
kind: 'skip_effect',
|
|
205
|
+
reason: 'POWER_BURN target power type mismatch',
|
|
206
|
+
});
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const { min: baseMin, max: baseMax } = effectAmountRange(raw, state.ctx);
|
|
211
|
+
// damage < 0 → return. Cite: :1811–1812.
|
|
212
|
+
if (baseMax < 0 && baseMin < 0) return;
|
|
213
|
+
|
|
214
|
+
let min = baseMin;
|
|
215
|
+
let max = baseMax;
|
|
216
|
+
// Clamp to current power when supplied. Cite: :1814–1816.
|
|
217
|
+
if (state.ctx.targetPower != null) {
|
|
218
|
+
const cur = Math.trunc(state.ctx.targetPower);
|
|
219
|
+
if (min > cur) min = cur;
|
|
220
|
+
if (max > cur) max = cur;
|
|
221
|
+
}
|
|
222
|
+
const clamped = clampPositiveRange(min, max);
|
|
223
|
+
const multRaw = Number(raw.multipleValue);
|
|
224
|
+
const multiplier = multRaw && multRaw !== 0 ? multRaw : 1;
|
|
225
|
+
state.effects.push({
|
|
226
|
+
kind: 'power_burn',
|
|
227
|
+
powerType,
|
|
228
|
+
min: clamped.min,
|
|
229
|
+
max: clamped.max,
|
|
230
|
+
multiplier,
|
|
231
|
+
school: schoolOf(state.spell),
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// SPELL_EFFECT_THREAT (63). AddThreat(caster, damage, false, school, spell). Bake amount = effect min
|
|
236
|
+
// (signed OK). Cite: SpellEffects.cpp:3567–3577; map-spell-effects.mjs :357–358.
|
|
237
|
+
export function handleThreat(state: HandlerState, raw: RawSpellEffect, _slot: number): void {
|
|
238
|
+
// Alive gates are runtime. Emit amount from calculated damage (range min for bake).
|
|
239
|
+
const { min } = effectAmountRange(raw, state.ctx);
|
|
240
|
+
state.effects.push({ kind: 'threat', amount: min });
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// SPELL_EFFECT_DISPEL: the count is the damage, with 0 reading as 1. Cite: SpellEffects.cpp:2467-2497
|
|
244
|
+
// EffectDispel; ProcessDispelList :2521-2575; EvaluateResultLists :2577.
|
|
245
|
+
export function handleDispel(state: HandlerState, raw: RawSpellEffect, _slot: number): void {
|
|
246
|
+
const fam = familyOf(state.spell);
|
|
247
|
+
const flags = BigInt(state.spell.spellFamilyFlags ?? 0);
|
|
248
|
+
// Shield Slam 50% chance dispel. Cite: :2472–2475.
|
|
249
|
+
const isShieldSlamDispel =
|
|
250
|
+
fam === SPELLFAMILY_WARRIOR
|
|
251
|
+
&& ((flags & SHIELD_SLAM_DISPEL_FLAG) !== 0n
|
|
252
|
+
|| SHIELD_SLAM_IDS.has(state.spell.id | 0));
|
|
253
|
+
if (isShieldSlamDispel) {
|
|
254
|
+
// Bake-thin (no rng): always emit (oracle). With rng: roll_chance_i(50) → 50% skip.
|
|
255
|
+
// Cite: :2474 !roll_chance_i(50) return. roll_chance_i(50) true when roll < 50.
|
|
256
|
+
if (state.ctx.rng) {
|
|
257
|
+
// Draw: [0,1) → *100 as int percent; fail path when roll >= 50.
|
|
258
|
+
const roll = Math.floor(state.ctx.rng() * 100);
|
|
259
|
+
if (roll >= 50) {
|
|
260
|
+
state.intents.push({
|
|
261
|
+
kind: 'skip_effect',
|
|
262
|
+
reason: 'DISPEL Shield Slam 50% chance failed',
|
|
263
|
+
});
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const { min } = effectAmountRange(raw, state.ctx);
|
|
270
|
+
// ProcessDispelList: if (!damage) damage = 1. Cite: :2527–2528; bake Math.max(1, min).
|
|
271
|
+
const count = Math.max(1, min);
|
|
272
|
+
const dispelType = raw.miscValue | 0;
|
|
273
|
+
state.effects.push({ kind: 'dispel', dispelType, count });
|
|
274
|
+
state.role = state.role || 'buff';
|
|
275
|
+
|
|
276
|
+
// Devour Magic heal table — only after successful dispel at runtime.
|
|
277
|
+
// Cite: :2596–2612; SpellMgr.h SPELLCATEGORY_DEVOUR_MAGIC = 12.
|
|
278
|
+
if (fam === SPELLFAMILY_WARLOCK) {
|
|
279
|
+
const cat = state.ctx.spellCategory ?? state.spell.category ?? 0;
|
|
280
|
+
if (cat === SPELLCATEGORY_DEVOUR_MAGIC) {
|
|
281
|
+
const healSpell = DEVOUR_MAGIC_HEAL[state.spell.id | 0];
|
|
282
|
+
if (healSpell) {
|
|
283
|
+
state.intents.push({ kind: 'devour_magic_heal', healSpellId: healSpell });
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// SPELL_EFFECT_ADD_COMBO_POINTS: a player caster with a positive amount adds that many points. The
|
|
290
|
+
// bake also stamps the generated count; the kind covers a standalone grant.
|
|
291
|
+
export function handleAddComboPoints(state: HandlerState, raw: RawSpellEffect, _slot: number): void {
|
|
292
|
+
// Non-player caster → return. Cite: :4217–4218.
|
|
293
|
+
if (state.ctx.casterIsPlayer === false) {
|
|
294
|
+
state.intents.push({ kind: 'skip_effect', reason: 'ADD_COMBO_POINTS non-player caster' });
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
const { min } = effectAmountRange(raw, state.ctx);
|
|
298
|
+
// damage <= 0 → return. Cite: :4221–4222.
|
|
299
|
+
if (min <= 0) return;
|
|
300
|
+
// Bake uses Math.max(1, min) for comboPointsGenerated; C++ uses damage when > 0.
|
|
301
|
+
// Emit schema kind for Premeditation / openers. Cite: schema SpellEffect combo_points.
|
|
302
|
+
state.effects.push({ kind: 'combo_points', amount: min });
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// SPELL_EFFECT_ACTIVATE_OBJECT: the misc value is the gameobject action. A real effect, since
|
|
306
|
+
// intent-only left pure and multi-slot rows without the GO action at the combat surface.
|
|
307
|
+
export function handleActivateObject(state: HandlerState, raw: RawSpellEffect, _slot: number): void {
|
|
308
|
+
// gameObjTarget null → return is runtime; emit action always so resolveCombatSpell sees the slot.
|
|
309
|
+
const action = raw.miscValue | 0;
|
|
310
|
+
const intent: InterpretIntent = { kind: 'activate_object', action };
|
|
311
|
+
const id = state.spell.id | 0;
|
|
312
|
+
|
|
313
|
+
// OPEN / DISTURB specials. Cite: :4394–4405.
|
|
314
|
+
if (action === GO_ACTION.DISTURB || action === GO_ACTION.OPEN) {
|
|
315
|
+
if (id === 17731) {
|
|
316
|
+
// Onyxia Eruption — custom anim, not Use.
|
|
317
|
+
intent.customAnim = true;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// MAKE_INERT / MAKE_ACTIVE Twilight Council summons. Cite: :4421–4470.
|
|
322
|
+
if (action === GO_ACTION.MAKE_INERT || action === GO_ACTION.MAKE_ACTIVE) {
|
|
323
|
+
let npcEntry = TWILIGHT_FIXED_NPC[id];
|
|
324
|
+
if (npcEntry == null) {
|
|
325
|
+
// Random tables: 24734 templars, 24763 dukes, 24784 royals. Cite: :4448–4450.
|
|
326
|
+
const table =
|
|
327
|
+
id === 24734 ? TWILIGHT_TEMPLARS
|
|
328
|
+
: id === 24763 ? TWILIGHT_DUKES
|
|
329
|
+
: id === 24784 ? TWILIGHT_ROYALS
|
|
330
|
+
: null;
|
|
331
|
+
if (table) {
|
|
332
|
+
if (state.ctx.rng) {
|
|
333
|
+
// urand(0, 3) — four entries. Cite: :4448.
|
|
334
|
+
const idx = Math.floor(state.ctx.rng() * table.length) % table.length;
|
|
335
|
+
npcEntry = table[idx];
|
|
336
|
+
} else {
|
|
337
|
+
// Bake-thin: first entry (deterministic, no Math.random).
|
|
338
|
+
npcEntry = table[0];
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
if (npcEntry != null) intent.summonNpcEntry = npcEntry;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// Combat / resolveCombatSpell / OOC surface — GO action payload for activateObjectFromSpell.
|
|
346
|
+
state.effects.push({
|
|
347
|
+
kind: 'activate_object',
|
|
348
|
+
action,
|
|
349
|
+
...(intent.customAnim ? { customAnim: true as const } : {}),
|
|
350
|
+
...(intent.summonNpcEntry != null ? { summonNpcEntry: intent.summonNpcEntry } : {}),
|
|
351
|
+
});
|
|
352
|
+
state.intents.push(intent);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// SPELL_EFFECT_TRANS_DOOR summons a gameobject by misc value. A mage capital portal has no
|
|
356
|
+
// multiplayer pad here, so it pushes a teleport and routes through the city travel path.
|
|
357
|
+
export function handleTransDoor(state: HandlerState, raw: RawSpellEffect, _slot: number): void {
|
|
358
|
+
const goEntry = raw.miscValue | 0;
|
|
359
|
+
// !goinfo → return. Cite: :5275–5280.
|
|
360
|
+
if (!goEntry) return;
|
|
361
|
+
|
|
362
|
+
let durationMs = durationMsResolved(state);
|
|
363
|
+
const goType = state.ctx.gameObjectType;
|
|
364
|
+
// Materialize effects[] for fishing node / door GO place (census + combat place-GO).
|
|
365
|
+
// Cite: SpellEffects.cpp EffectTransmitted.
|
|
366
|
+
const effect: Record<string, unknown> = {
|
|
367
|
+
kind: 'transmitted_object',
|
|
368
|
+
goEntry,
|
|
369
|
+
durationMs,
|
|
370
|
+
};
|
|
371
|
+
if (goType != null) {
|
|
372
|
+
effect.goType = goType | 0;
|
|
373
|
+
// Fishing bobber lastSec urand(0,3) → 3/7/13/17; duration adjust. Cite: :5351–5368.
|
|
374
|
+
if ((goType | 0) === GO_TYPE_FISHINGNODE) {
|
|
375
|
+
let lastSec = 3;
|
|
376
|
+
if (state.ctx.rng) {
|
|
377
|
+
const pick = Math.floor(state.ctx.rng() * 4) % 4;
|
|
378
|
+
lastSec = pick === 0 ? 3 : pick === 1 ? 7 : pick === 2 ? 13 : 17;
|
|
379
|
+
}
|
|
380
|
+
effect.fishingBobberLastSec = lastSec;
|
|
381
|
+
// duration = duration - lastSec*IN_MS + FISHING_BOBBER_READY_TIME*IN_MS
|
|
382
|
+
durationMs = durationMs - lastSec * 1000 + FISHING_BOBBER_READY_TIME * 1000;
|
|
383
|
+
effect.durationMs = durationMs;
|
|
384
|
+
}
|
|
385
|
+
// SUMMONING_RITUAL (18) / SPELLCASTER (22) / BUTTON (1): position + flag specials
|
|
386
|
+
// are world/runtime (caster pos, AddGameObject, startOpen). Cite: :5370–5392.
|
|
387
|
+
}
|
|
388
|
+
// Always materialize so non-portal fishing/door slots are readable (B/D).
|
|
389
|
+
state.effects.push(effect as never);
|
|
390
|
+
|
|
391
|
+
// A capital portal travels like a city teleport. Ritual of Summoning and Lightwell stay intent-only
|
|
392
|
+
// or kit-materialized. Cite: mage-portals.ts.
|
|
393
|
+
if (
|
|
394
|
+
isMagePortalGo(goEntry)
|
|
395
|
+
|| isMagePortalSpell(state.spell.id | 0)
|
|
396
|
+
) {
|
|
397
|
+
if (!state.effects.some((e) => e.kind === 'teleport')) {
|
|
398
|
+
state.effects.push({ kind: 'teleport' });
|
|
399
|
+
}
|
|
400
|
+
state.role = state.role || 'utility';
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// SPELL_EFFECT_INTERRUPT_CAST (68). Lockout duration = GetSpellDuration(interrupt spell); school from
|
|
405
|
+
// interrupted cast (runtime). Cite: SpellEffects.cpp:3591–3618; map-spell-effects.mjs :343–348.
|
|
406
|
+
export function handleInterruptCast(state: HandlerState, raw: RawSpellEffect, _slot: number): void {
|
|
407
|
+
// Alive / interruptible current-spell scan is runtime. Bake emits lockoutMs from DurationIndex.
|
|
408
|
+
const lockoutMs = durationMsResolved(state);
|
|
409
|
+
state.effects.push({ kind: 'interrupt_cast', lockoutMs });
|
|
410
|
+
if (!state.role) state.role = 'control';
|
|
411
|
+
// mechanic on raw (26 = interrupt) is payload for engine; no amount use here.
|
|
412
|
+
void raw;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// ── SI3 batch 3 ─────────────────────────────────────────────────────────────
|
|
416
|
+
|
|
417
|
+
// SPELL_EFFECT_INSTAKILL deals the target's whole health. It emits a combat effect as well as the
|
|
418
|
+
// intent, and is self-cast when the target is the caster or targetA names the caster.
|
|
419
|
+
export function handleInstaKill(state: HandlerState, raw: RawSpellEffect, _slot: number): void {
|
|
420
|
+
// Alive gate when supplied. Cite: :207.
|
|
421
|
+
if (state.ctx.unitTargetIsAlive === false) {
|
|
422
|
+
state.intents.push({ kind: 'skip_effect', reason: 'INSTAKILL target not alive' });
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
// A raw targetA of caster is the common self path when the context is not filled in. Effects are
|
|
426
|
+
// materialized for combat and intents for the census.
|
|
427
|
+
const selfCast =
|
|
428
|
+
state.ctx.unitTargetIsSelf === true
|
|
429
|
+
|| (raw.targetA | 0) === 1;
|
|
430
|
+
const intent: InterpretIntent = { kind: 'instakill' };
|
|
431
|
+
if (selfCast) intent.selfCast = true;
|
|
432
|
+
state.intents.push(intent);
|
|
433
|
+
state.effects.push({ kind: 'instakill', self: selfCast });
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// SPELL_EFFECT_PROFICIENCY (60). Player-only; add weapon or armor proficiency from
|
|
437
|
+
// EquippedItemClass/SubClassMask. Cite: SpellEffects.cpp:2314–2331.
|
|
438
|
+
export function handleProficiency(state: HandlerState, _raw: RawSpellEffect, _slot: number): void {
|
|
439
|
+
// Cite: :2316–2317 non-player return.
|
|
440
|
+
if (state.ctx.casterIsPlayer === false) {
|
|
441
|
+
state.intents.push({ kind: 'skip_effect', reason: 'PROFICIENCY requires player caster' });
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
const itemClass =
|
|
445
|
+
state.ctx.equippedItemClass
|
|
446
|
+
?? state.spell.equippedItemClass
|
|
447
|
+
?? 0;
|
|
448
|
+
const subClassMask =
|
|
449
|
+
state.ctx.equippedItemSubClassMask
|
|
450
|
+
?? state.spell.equippedItemSubClassMask
|
|
451
|
+
?? 0;
|
|
452
|
+
// Only weapon / armor classes apply. Cite: :2322–2330.
|
|
453
|
+
if (itemClass !== ITEM_CLASS_WEAPON && itemClass !== ITEM_CLASS_ARMOR) {
|
|
454
|
+
state.intents.push({
|
|
455
|
+
kind: 'skip_effect',
|
|
456
|
+
reason: 'PROFICIENCY EquippedItemClass is not weapon/armor',
|
|
457
|
+
});
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
state.intents.push({
|
|
461
|
+
kind: 'proficiency',
|
|
462
|
+
itemClass,
|
|
463
|
+
subClassMask,
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// SPELL_EFFECT_DISPEL_MECHANIC (108). mechanic = EffectMiscValue; count = damage (0 → 1). ProcessDispelList
|
|
468
|
+
// shared with DISPEL. Cite: SpellEffects.cpp:2499–2519; ProcessDispelList :2521–2575.
|
|
469
|
+
export function handleDispelMechanic(state: HandlerState, raw: RawSpellEffect, _slot: number): void {
|
|
470
|
+
const mechanic = raw.miscValue | 0;
|
|
471
|
+
const { min } = effectAmountRange(raw, state.ctx);
|
|
472
|
+
// ProcessDispelList: if (!damage) damage = 1. Cite: :2527–2528.
|
|
473
|
+
const count = Math.max(1, min);
|
|
474
|
+
state.intents.push({ kind: 'dispel_mechanic', mechanic, count });
|
|
475
|
+
state.effects.push({ kind: 'dispel_mechanic', mechanic, count });
|
|
476
|
+
state.role = state.role || 'buff';
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// SPELL_EFFECT_QUEST_COMPLETE (16). AreaExploredOrEventHappens(EffectMiscValue) on player unitTarget. Cite:
|
|
480
|
+
// SpellEffects.cpp:4928–4935.
|
|
481
|
+
export function handleQuestComplete(state: HandlerState, raw: RawSpellEffect, _slot: number): void {
|
|
482
|
+
// Cite: :4930 non-player / missing target return.
|
|
483
|
+
if (state.ctx.unitTargetIsPlayer === false) {
|
|
484
|
+
state.intents.push({ kind: 'skip_effect', reason: 'QUEST_COMPLETE requires player unitTarget' });
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
const questId = raw.miscValue | 0;
|
|
488
|
+
// C++ still calls with quest_id 0; skip empty for honesty.
|
|
489
|
+
if (!questId) {
|
|
490
|
+
state.intents.push({ kind: 'skip_effect', reason: 'QUEST_COMPLETE empty EffectMiscValue' });
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
// Emitted as an EFFECT as well as an intent — resolveCombatSpell drops intents, so intent-only
|
|
494
|
+
// left the BG objective-credit spells (23658 / 24061 / 24064) as inert stubs. W2b.
|
|
495
|
+
state.effects.push({ kind: 'quest_complete', questId });
|
|
496
|
+
state.intents.push({ kind: 'quest_complete', questId });
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// SPELL_EFFECT_TELEPORT_GRAVEYARD sends the target to the nearest graveyard its team owns — in a BG,
|
|
500
|
+
// the resurrection cycle a spirit guide pulses every 30 s over the dead in range.
|
|
501
|
+
export function handleTeleportGraveyard(state: HandlerState, _raw: RawSpellEffect, _slot: number): void {
|
|
502
|
+
state.effects.push({ kind: 'teleport_graveyard' });
|
|
503
|
+
state.intents.push({ kind: 'teleport_graveyard' });
|
|
504
|
+
state.role = state.role || 'utility';
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// SPELL_EFFECT_CHARGE (96). MoveCharge(unitTarget, speed); player fall reset; optional auto-attack. Cite:
|
|
508
|
+
// SpellEffects.cpp:5009–5028; Unit.h BASE_CHARGE_SPEED 27.0f.
|
|
509
|
+
export function handleCharge(state: HandlerState, _raw: RawSpellEffect, _slot: number): void {
|
|
510
|
+
// Cite: :5011 missing unit/caster return — interpret always has spell caster.
|
|
511
|
+
const speedRaw = state.ctx.spellSpeed ?? state.spell.speed ?? 0;
|
|
512
|
+
// Cite: :5021 speed != 0 ? speed : BASE_CHARGE_SPEED.
|
|
513
|
+
const speed = speedRaw !== 0 ? speedRaw : BASE_CHARGE_SPEED;
|
|
514
|
+
// Cite: :5027–5028 client-controlled + CanAttack → Attack. Default true for player caster.
|
|
515
|
+
const initiateAutoAttack = state.ctx.casterIsPlayer !== false;
|
|
516
|
+
state.intents.push({ kind: 'charge', speed, initiateAutoAttack });
|
|
517
|
+
if (!state.role) state.role = 'charge';
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// SPELL_EFFECT_RESURRECT_NEW, sharing EffectResurrect with 18: a FLAT health from the amount and mana
|
|
521
|
+
// from the misc value. It emits a resurrect effect so the OOC cast is not a no-op.
|
|
522
|
+
export function handleResurrectNew(state: HandlerState, raw: RawSpellEffect, _slot: number): void {
|
|
523
|
+
// Alive player / non-player unitTarget returns. Cite: :4807–4808.
|
|
524
|
+
if (state.ctx.unitTargetIsAlive === true) {
|
|
525
|
+
state.intents.push({ kind: 'skip_effect', reason: 'RESURRECT_NEW target is alive' });
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
528
|
+
// Already has resurrect request. Cite: :4857–4858.
|
|
529
|
+
if (state.ctx.hasResurrectRequest === true) {
|
|
530
|
+
state.intents.push({ kind: 'skip_effect', reason: 'RESURRECT_NEW already has resurrect request' });
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
// Flat health = calculated damage; mana = misc. Cite: :4815–4818.
|
|
534
|
+
const { min } = effectAmountRange(raw, state.ctx);
|
|
535
|
+
const health = Math.max(0, min);
|
|
536
|
+
const mana = raw.miscValue | 0;
|
|
537
|
+
state.intents.push({ kind: 'resurrect_new', health, mana });
|
|
538
|
+
// Combat/OOC surface — same numbers as the intent (engine applies when a corpse exists).
|
|
539
|
+
state.effects.push({ kind: 'resurrect', health, mana });
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
// SPELL_EFFECT_TELEPORT_UNITS (5). NearTeleport same map; player TeleportTo cross-map. Needs dest location
|
|
543
|
+
// at cast. Cite: SpellEffects.cpp:1683–1728.
|
|
544
|
+
export function handleTeleportUnits(state: HandlerState, raw: RawSpellEffect, _slot: number): void {
|
|
545
|
+
if (state.spell.id === EVERLOOK_TRANSPORTER_SPELL_ID) {
|
|
546
|
+
const roll = state.ctx.rng ? Math.floor(state.ctx.rng() * 6) : 1;
|
|
547
|
+
const triggerId = everlookTransporterOutcomeSpellId(roll);
|
|
548
|
+
state.intents.push({ kind: 'cast_triggered_spell', spellId: triggerId });
|
|
549
|
+
state.effects.push({ kind: 'trigger_spell', spellId: triggerId });
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// Cite: :1685 taxi flying return.
|
|
553
|
+
if (state.ctx.unitTargetTaxiFlying === true) {
|
|
554
|
+
state.intents.push({
|
|
555
|
+
kind: 'teleport_units',
|
|
556
|
+
targetA: raw.targetA | 0,
|
|
557
|
+
targetB: raw.targetB | 0,
|
|
558
|
+
skippedTaxiFlying: true,
|
|
559
|
+
});
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
const targetA = raw.targetA | 0;
|
|
563
|
+
const targetB = raw.targetB | 0;
|
|
564
|
+
// TARGET_LOCATION_CASTER_HOME_BIND = 9 — Astral Recall / homebind teleports.
|
|
565
|
+
// Cite: SpellTargetDefines.h:33; KI-532.
|
|
566
|
+
const TARGET_LOCATION_CASTER_HOME_BIND = 9;
|
|
567
|
+
if (targetA === TARGET_LOCATION_CASTER_HOME_BIND || targetB === TARGET_LOCATION_CASTER_HOME_BIND) {
|
|
568
|
+
state.effects.push({ kind: 'teleport_hearth' });
|
|
569
|
+
state.role = state.role || 'utility';
|
|
570
|
+
} else {
|
|
571
|
+
// A city or database-destination teleport surfaces as a teleport effect so the OOC path routes
|
|
572
|
+
// through castTeleport with its reagents instead of sinking mana.
|
|
573
|
+
state.effects.push({ kind: 'teleport' });
|
|
574
|
+
state.role = state.role || 'utility';
|
|
575
|
+
}
|
|
576
|
+
// Database dest targets use NearTeleport with useFarTeleport semantics. Cite: :1712–1713.
|
|
577
|
+
void TARGET_LOCATION_DATABASE;
|
|
578
|
+
const intent: InterpretIntent = {
|
|
579
|
+
kind: 'teleport_units',
|
|
580
|
+
targetA,
|
|
581
|
+
targetB,
|
|
582
|
+
};
|
|
583
|
+
if (state.ctx.teleportMapId != null) {
|
|
584
|
+
intent.mapId = state.ctx.teleportMapId | 0;
|
|
585
|
+
}
|
|
586
|
+
state.intents.push(intent);
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
// ── SI3 batch 4 ─────────────────────────────────────────────────────────────
|
|
590
|
+
|
|
591
|
+
// SPELL_EFFECT_POWER_DRAIN drains up to the amount and energizes the caster by that times the
|
|
592
|
+
// multiple. Happiness is a pet resource, emitted on the display scale so Dismiss Pet drains 50.
|
|
593
|
+
export function handlePowerDrain(state: HandlerState, raw: RawSpellEffect, _slot: number): void {
|
|
594
|
+
const powerType = raw.miscValue | 0;
|
|
595
|
+
// Cite: :1752–1753 invalid power.
|
|
596
|
+
if (powerType < 0 || powerType >= MAX_POWERS) return;
|
|
597
|
+
|
|
598
|
+
// Alive gate when supplied. Cite: :1758.
|
|
599
|
+
if (state.ctx.unitTargetIsAlive === false) {
|
|
600
|
+
state.intents.push({ kind: 'skip_effect', reason: 'POWER_DRAIN target not alive' });
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
// Power type must match unless POWER_HAPPINESS. Cite: :1758.
|
|
605
|
+
if (
|
|
606
|
+
state.ctx.targetPowerType != null
|
|
607
|
+
&& (state.ctx.targetPowerType | 0) !== powerType
|
|
608
|
+
&& powerType !== POWER_HAPPINESS
|
|
609
|
+
) {
|
|
610
|
+
state.intents.push({
|
|
611
|
+
kind: 'skip_effect',
|
|
612
|
+
reason: 'POWER_DRAIN target power type mismatch',
|
|
613
|
+
});
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
const { min: baseMin, max: baseMax } = effectAmountRange(raw, state.ctx);
|
|
618
|
+
// damage < 0 → return. Cite: :1760–1761.
|
|
619
|
+
if (baseMax < 0 && baseMin < 0) return;
|
|
620
|
+
|
|
621
|
+
// Pet happiness: convert raw UNIT_FIELD_POWER5 → Phonecraft display (÷1000). No caster
|
|
622
|
+
// energize path (hunter power type is never HAPPINESS). Cite: domain/pet/pet.ts scale comments.
|
|
623
|
+
if (powerType === POWER_HAPPINESS) {
|
|
624
|
+
// Import-free constant: matches HAPPINESS_RAW_PER_DISPLAY in domain/pet/pet.ts.
|
|
625
|
+
const RAW_PER_DISPLAY = 1000;
|
|
626
|
+
let min = Math.max(0, Math.trunc(baseMin / RAW_PER_DISPLAY));
|
|
627
|
+
let max = Math.max(0, Math.trunc(baseMax / RAW_PER_DISPLAY));
|
|
628
|
+
if (state.ctx.targetPower != null) {
|
|
629
|
+
const cur = Math.trunc(state.ctx.targetPower);
|
|
630
|
+
if (min > cur) min = cur;
|
|
631
|
+
if (max > cur) max = cur;
|
|
632
|
+
}
|
|
633
|
+
state.effects.push({
|
|
634
|
+
kind: 'power_drain',
|
|
635
|
+
powerType,
|
|
636
|
+
min,
|
|
637
|
+
max,
|
|
638
|
+
// Caster never gains happiness (power-type mismatch → gainMultiplier stays 1 in C++,
|
|
639
|
+
// but EnergizeBySpell of happiness on a player is a no-op here). Bake mult || 1 unused.
|
|
640
|
+
multiplier: 0,
|
|
641
|
+
});
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
let min = baseMin;
|
|
646
|
+
let max = baseMax;
|
|
647
|
+
// Clamp to current power when supplied. Cite: :1769–1773.
|
|
648
|
+
if (state.ctx.targetPower != null) {
|
|
649
|
+
const cur = Math.trunc(state.ctx.targetPower);
|
|
650
|
+
if (min > cur) min = cur;
|
|
651
|
+
if (max > cur) max = cur;
|
|
652
|
+
}
|
|
653
|
+
const clamped = clampPositiveRange(min, max);
|
|
654
|
+
|
|
655
|
+
// Bake-thin multiplier: mult || 1. Cite: map-spell-effects :443.
|
|
656
|
+
const multRaw = Number(raw.multipleValue);
|
|
657
|
+
let multiplier = multRaw && multRaw !== 0 ? multRaw : 1;
|
|
658
|
+
|
|
659
|
+
// Full C++ gain path when caster power type known. Cite: :1777–1786. gainMultiplier starts 1.0; only when
|
|
660
|
+
// caster power matches AND not self-drain is EffectMultipleValue applied (may be 0).
|
|
661
|
+
if (state.ctx.casterPowerType != null) {
|
|
662
|
+
const selfDrain = state.ctx.unitTargetIsSelf === true;
|
|
663
|
+
if ((state.ctx.casterPowerType | 0) === powerType && !selfDrain) {
|
|
664
|
+
multiplier = Number.isFinite(multRaw) ? multRaw : 0;
|
|
665
|
+
} else {
|
|
666
|
+
multiplier = 1;
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
state.effects.push({
|
|
671
|
+
kind: 'power_drain',
|
|
672
|
+
powerType,
|
|
673
|
+
min: clamped.min,
|
|
674
|
+
max: clamped.max,
|
|
675
|
+
multiplier,
|
|
676
|
+
});
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
// SPELL_EFFECT_ADD_EXTRA_ATTACKS (19). m_extraAttacks += damage; cap at 5. Bake: count = max(1, min). Cite:
|
|
680
|
+
// SpellEffects.cpp:4873–4882; map-spell-effects.mjs :362–366.
|
|
681
|
+
export function handleAddExtraAttacks(state: HandlerState, raw: RawSpellEffect, _slot: number): void {
|
|
682
|
+
// Alive gate when supplied. Cite: :4875–4876.
|
|
683
|
+
if (state.ctx.unitTargetIsAlive === false) {
|
|
684
|
+
state.intents.push({ kind: 'skip_effect', reason: 'ADD_EXTRA_ATTACKS target not alive' });
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
687
|
+
const { min } = effectAmountRange(raw, state.ctx);
|
|
688
|
+
// Bake path: Math.max(1, min). Cite: map-spell-effects :366.
|
|
689
|
+
let count = Math.max(1, min);
|
|
690
|
+
|
|
691
|
+
// Full C++ cumulative cap when existing stack supplied. Cite: :4878–4880.
|
|
692
|
+
if (state.ctx.existingExtraAttacks != null) {
|
|
693
|
+
const existing = Math.max(0, Math.trunc(state.ctx.existingExtraAttacks));
|
|
694
|
+
// damage as C++ uses calculated amount (can be 0); bake used max(1,min) above for grant.
|
|
695
|
+
// When existing set, apply += min then cap total at 5; emit final total as count.
|
|
696
|
+
const total = existing + Math.max(0, min);
|
|
697
|
+
count = total > MAX_EXTRA_ATTACKS ? MAX_EXTRA_ATTACKS : total;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
state.effects.push({ kind: 'extra_attacks', count });
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
// SPELL_EFFECT_WEAPON (25) — EffectEmpty. Marker for weapon-type proficiency / equip usability
|
|
704
|
+
// (ItemSubclass mask). No server implementation body. Cite: SpellEffects.cpp:190–193; dispatch :83.
|
|
705
|
+
export function handleWeaponEmpty(state: HandlerState, _raw: RawSpellEffect, _slot: number): void {
|
|
706
|
+
state.intents.push({ kind: 'weapon_marker' });
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
// SPELL_EFFECT_INEBRIATE (100). drunkMod = damage * 256; clamp current + mod to 0xFFFF. Cite:
|
|
710
|
+
// SpellEffects.cpp:4666–4678.
|
|
711
|
+
export function handleInebriate(state: HandlerState, raw: RawSpellEffect, _slot: number): void {
|
|
712
|
+
// Cite: :4668 non-player return.
|
|
713
|
+
if (state.ctx.unitTargetIsPlayer === false) {
|
|
714
|
+
state.intents.push({ kind: 'skip_effect', reason: 'INEBRIATE requires player unitTarget' });
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
717
|
+
const { min } = effectAmountRange(raw, state.ctx);
|
|
718
|
+
// damage * 256. Cite: :4673.
|
|
719
|
+
const drunkMod = Math.trunc(min) * 256;
|
|
720
|
+
// Alcohol drunkness — effects[] for combat flavor consumer.
|
|
721
|
+
state.effects.push({
|
|
722
|
+
kind: 'inebriate',
|
|
723
|
+
drunkMod,
|
|
724
|
+
...(state.ctx.castItemEntry != null ? { castItemEntry: state.ctx.castItemEntry | 0 } : {}),
|
|
725
|
+
} as never);
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
// SPELL_EFFECT_LANGUAGE (39) — EffectEmpty. Misc stores language id; no server body (client/chat marker).
|
|
729
|
+
// Cite: SpellEffects.cpp:97 dispatch → EffectEmpty :190–193.
|
|
730
|
+
export function handleLanguage(state: HandlerState, raw: RawSpellEffect, _slot: number): void {
|
|
731
|
+
state.intents.push({
|
|
732
|
+
kind: 'language_marker',
|
|
733
|
+
languageId: raw.miscValue | 0,
|
|
734
|
+
});
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
// SPELL_EFFECT_TELEPORT_UNITS_FACE_CASTER (43). NearTeleportTo(dest, -caster orientation); requires dest
|
|
738
|
+
// location. Cite: SpellEffects.cpp:2994–3006.
|
|
739
|
+
export function handleTeleUnitsFaceCaster(state: HandlerState, raw: RawSpellEffect, _slot: number): void {
|
|
740
|
+
// Cite: :2996 taxi flying return.
|
|
741
|
+
if (state.ctx.unitTargetTaxiFlying === true) {
|
|
742
|
+
state.intents.push({
|
|
743
|
+
kind: 'teleport_face_caster',
|
|
744
|
+
targetA: raw.targetA | 0,
|
|
745
|
+
targetB: raw.targetB | 0,
|
|
746
|
+
skippedTaxiFlying: true,
|
|
747
|
+
requiresDestLocation: true,
|
|
748
|
+
});
|
|
749
|
+
return;
|
|
750
|
+
}
|
|
751
|
+
// Dest location required at cast time. Cite: :3000–3003 — without dest, C++ returns.
|
|
752
|
+
state.effects.push({
|
|
753
|
+
kind: 'teleport_face_caster',
|
|
754
|
+
targetA: raw.targetA | 0,
|
|
755
|
+
targetB: raw.targetB | 0,
|
|
756
|
+
requiresDestLocation: true,
|
|
757
|
+
} as never);
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
// SPELL_EFFECT_SKILL_STEP (44) EffectLearnSkill. SetSkillStep(skillid=misc, step=damage) for player
|
|
761
|
+
// unitTarget. Cite: SpellEffects.cpp:3008–3024.
|
|
762
|
+
export function handleSkillStep(state: HandlerState, raw: RawSpellEffect, _slot: number): void {
|
|
763
|
+
// Cite: :3010 non-player return.
|
|
764
|
+
if (state.ctx.unitTargetIsPlayer === false) {
|
|
765
|
+
state.intents.push({ kind: 'skip_effect', reason: 'SKILL_STEP requires player unitTarget' });
|
|
766
|
+
return;
|
|
767
|
+
}
|
|
768
|
+
const { min } = effectAmountRange(raw, state.ctx);
|
|
769
|
+
// damage < 0 → return. Cite: :3013–3014.
|
|
770
|
+
if (min < 0) {
|
|
771
|
+
state.intents.push({ kind: 'skip_effect', reason: 'SKILL_STEP damage < 0' });
|
|
772
|
+
return;
|
|
773
|
+
}
|
|
774
|
+
// uint16 skillid = misc; uint16 step = damage. Cite: :3018–3020.
|
|
775
|
+
const skillId = raw.miscValue | 0;
|
|
776
|
+
const step = Math.trunc(min) & 0xffff;
|
|
777
|
+
state.effects.push({ kind: 'skill_step', skillId, step } as never);
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
// SPELL_EFFECT_HEAL_MAX_HEALTH (67). m_healing += m_caster->GetMaxHealth(). Bake: heal_max_health (no
|
|
781
|
+
// amount). Cite: SpellEffects.cpp:3579–3589; map-spell-effects.mjs :453–456.
|
|
782
|
+
export function handleHealMaxHealth(state: HandlerState, _raw: RawSpellEffect, _slot: number): void {
|
|
783
|
+
// Cite: :3581–3584 missing / dead unitTarget.
|
|
784
|
+
if (state.ctx.unitTargetIsAlive === false) {
|
|
785
|
+
state.intents.push({ kind: 'skip_effect', reason: 'HEAL_MAX_HEALTH target not alive' });
|
|
786
|
+
return;
|
|
787
|
+
}
|
|
788
|
+
// Bake surface — amount is caster max health at cast time (SI5 / m_healing +=).
|
|
789
|
+
// Cite: :3586–3588; map-spell-effects.mjs :453–456. ctx.casterMaxHealth reserved for SI5.
|
|
790
|
+
state.effects.push({ kind: 'heal_max_health' });
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
// SPELL_EFFECT_SKILL (118). DEBUG_LOG only — no skill mutation in Classic CMaNGOS body. Cite:
|
|
794
|
+
// SpellEffects.cpp:5425–5428.
|
|
795
|
+
export function handleSkill(state: HandlerState, _raw: RawSpellEffect, _slot: number): void {
|
|
796
|
+
state.effects.push({ kind: 'skill_marker' } as never);
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
// SPELL_EFFECT_SELF_RESURRECT, the Reincarnation payload: a negative amount is flat health with mana
|
|
800
|
+
// from the misc value, otherwise both are percentages of maximum. Cite: SpellEffects.cpp:4937-4968.
|
|
801
|
+
export function handleSelfResurrect(state: HandlerState, raw: RawSpellEffect, _slot: number): void {
|
|
802
|
+
// Cite: :4939–4940 !IsPlayer return.
|
|
803
|
+
if (state.ctx.casterIsPlayer === false) {
|
|
804
|
+
state.intents.push({ kind: 'skip_effect', reason: 'SELF_RESURRECT non-player caster' });
|
|
805
|
+
return;
|
|
806
|
+
}
|
|
807
|
+
const { min } = effectAmountRange(raw, state.ctx);
|
|
808
|
+
// damage < 0 → flat. Cite: :4946–4951.
|
|
809
|
+
if (min < 0) {
|
|
810
|
+
const health = Math.max(0, Math.trunc(-min));
|
|
811
|
+
const mana = raw.miscValue | 0;
|
|
812
|
+
state.effects.push({ kind: 'self_resurrect', health, mana });
|
|
813
|
+
state.intents.push({ kind: 'self_resurrect', health, mana });
|
|
814
|
+
return;
|
|
815
|
+
}
|
|
816
|
+
// Percent path. Cite: :4952–4958.
|
|
817
|
+
const percent = Math.max(0, Math.trunc(min));
|
|
818
|
+
state.effects.push({ kind: 'self_resurrect', percent });
|
|
819
|
+
state.intents.push({ kind: 'self_resurrect', percent });
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
// SPELL_EFFECT_PORTAL (12) — EffectUnused. Unused from pre-1.2.1; two spells remain with no real usage
|
|
823
|
+
// data. Cite: SpellEffects.cpp:70 dispatch → EffectUnused :200–203.
|
|
824
|
+
export function handlePortal(state: HandlerState, _raw: RawSpellEffect, _slot: number): void {
|
|
825
|
+
state.intents.push({ kind: 'portal_unused' });
|
|
826
|
+
state.effects.push({ kind: 'client_inform' });
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
// SPELL_EFFECT_RESURRECT, sharing EffectResurrect with 113: health and mana are percentages of the
|
|
830
|
+
// maximum. Emitted as a percent resurrect for the engine to apply.
|
|
831
|
+
export function handleResurrect(state: HandlerState, raw: RawSpellEffect, _slot: number): void {
|
|
832
|
+
// Alive player unitTarget returns. Cite: :4807–4808.
|
|
833
|
+
if (state.ctx.unitTargetIsAlive === true) {
|
|
834
|
+
state.intents.push({ kind: 'skip_effect', reason: 'RESURRECT target is alive' });
|
|
835
|
+
return;
|
|
836
|
+
}
|
|
837
|
+
// Already has resurrect request. Cite: :4857–4858.
|
|
838
|
+
if (state.ctx.hasResurrectRequest === true) {
|
|
839
|
+
state.intents.push({ kind: 'skip_effect', reason: 'RESURRECT already has resurrect request' });
|
|
840
|
+
return;
|
|
841
|
+
}
|
|
842
|
+
// damage = effect amount (percent). Cite: :4822–4823.
|
|
843
|
+
const { min } = effectAmountRange(raw, state.ctx);
|
|
844
|
+
const percent = Math.max(0, min);
|
|
845
|
+
const intent: InterpretIntent = { kind: 'resurrect', percent };
|
|
846
|
+
// Resolve flat when max stats supplied. Cite: :4822–4823 integer division.
|
|
847
|
+
if (state.ctx.targetMaxHealth != null) {
|
|
848
|
+
intent.health = Math.trunc((Math.trunc(state.ctx.targetMaxHealth) * percent) / 100);
|
|
849
|
+
}
|
|
850
|
+
if (state.ctx.targetMaxMana != null) {
|
|
851
|
+
intent.mana = Math.trunc((Math.trunc(state.ctx.targetMaxMana) * percent) / 100);
|
|
852
|
+
}
|
|
853
|
+
state.intents.push(intent);
|
|
854
|
+
state.effects.push({ kind: 'resurrect_pct', percent });
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
// SPELL_EFFECT_LEAP (29) EffectLeapForward. NearTeleportTo(dest x/y/z, unit orientation). Cite:
|
|
858
|
+
// SpellEffects.cpp:4895–4906.
|
|
859
|
+
export function handleLeap(state: HandlerState, raw: RawSpellEffect, _slot: number): void {
|
|
860
|
+
// Cite: :4897 missing unitTarget — interpret always has caster context.
|
|
861
|
+
// Blink / gap-close: effects[] so combat can apply motion (sessionMotion).
|
|
862
|
+
state.effects.push({
|
|
863
|
+
kind: 'leap',
|
|
864
|
+
targetA: raw.targetA | 0,
|
|
865
|
+
targetB: raw.targetB | 0,
|
|
866
|
+
} as never);
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
// SPELL_EFFECT_TRADE_SKILL (47). Player unitTarget only; skill raise body is commented out in Classic.
|
|
870
|
+
// Cite: SpellEffects.cpp:3048–3055.
|
|
871
|
+
export function handleTradeSkill(state: HandlerState, raw: RawSpellEffect, _slot: number): void {
|
|
872
|
+
// Cite: :3050 non-player return.
|
|
873
|
+
if (state.ctx.unitTargetIsPlayer === false) {
|
|
874
|
+
state.intents.push({ kind: 'skip_effect', reason: 'TRADE_SKILL requires player unitTarget' });
|
|
875
|
+
return;
|
|
876
|
+
}
|
|
877
|
+
const skillId = raw.miscValue | 0;
|
|
878
|
+
// Profession open — effects[] for OOC skill UI; no combat payload beyond the cast.
|
|
879
|
+
// Cite: SpellEffects.cpp:3048–3055.
|
|
880
|
+
state.effects.push({
|
|
881
|
+
kind: 'trade_skill',
|
|
882
|
+
...(skillId ? { skillId } : {}),
|
|
883
|
+
} as never);
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
// ── SI3 batch 6 ─────────────────────────────────────────────────────────────
|
|
887
|
+
|
|
888
|
+
// SPELL_EFFECT_ADD_FARSIGHT (72). Player-only DynamicObject at dest (radius 0, FARSIGHT_FOCUS) + camera
|
|
889
|
+
// SetView. Cite: SpellEffects.cpp:2708–2727.
|
|
890
|
+
export function handleAddFarsight(state: HandlerState, _raw: RawSpellEffect, _slot: number): void {
|
|
891
|
+
// Cite: :2710–2711 !IsPlayer return.
|
|
892
|
+
if (state.ctx.casterIsPlayer === false) {
|
|
893
|
+
state.intents.push({ kind: 'skip_effect', reason: 'ADD_FARSIGHT requires player caster' });
|
|
894
|
+
return;
|
|
895
|
+
}
|
|
896
|
+
state.effects.push({
|
|
897
|
+
kind: 'add_farsight',
|
|
898
|
+
durationMs: durationMsResolved(state),
|
|
899
|
+
radius: 0,
|
|
900
|
+
dynObjectType: DYNAMIC_OBJECT_FARSIGHT_FOCUS,
|
|
901
|
+
} as never);
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
// SPELL_EFFECT_SANCTUARY (79). Interrupt casts on target + CombatStop; Vanish family clears root + 1s
|
|
905
|
+
// undetect. Cite: SpellEffects.cpp:4194–4211.
|
|
906
|
+
export function handleSanctuary(state: HandlerState, _raw: RawSpellEffect, _slot: number): void {
|
|
907
|
+
// unitTarget required — interpret always has a logical target for unit effects.
|
|
908
|
+
const family = familyOf(state.spell);
|
|
909
|
+
const flags = flagsOf(state.spell);
|
|
910
|
+
// IsFitToFamily(SPELLFAMILY_ROGUE, 0x800). Cite: :4203.
|
|
911
|
+
const vanishRogueSpecial =
|
|
912
|
+
family === SPELLFAMILY_ROGUE && (flags & VANISH_SANCTUARY_FAMILY_FLAGS) !== 0;
|
|
913
|
+
// Vanish sanctuary — drop combat / brief untrackable. Cite: SpellEffects.cpp EffectSanctuary.
|
|
914
|
+
state.effects.push({
|
|
915
|
+
kind: 'sanctuary',
|
|
916
|
+
vanishRogueSpecial,
|
|
917
|
+
...(vanishRogueSpecial ? { cannotBeDetectedMs: VANISH_CANNOT_BE_DETECTED_MS } : {}),
|
|
918
|
+
} as never);
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
// SPELL_EFFECT_REPUTATION: the base points are the change and the misc value the faction, run through
|
|
922
|
+
// the spell-source reputation gain. Cite: SpellEffects.cpp:4908-4926; Player.cpp:6293-6369.
|
|
923
|
+
export function handleReputation(state: HandlerState, raw: RawSpellEffect, _slot: number): void {
|
|
924
|
+
// Cite: :4910 non-player unitTarget return.
|
|
925
|
+
if (state.ctx.unitTargetIsPlayer === false) {
|
|
926
|
+
state.intents.push({ kind: 'skip_effect', reason: 'REPUTATION requires player unitTarget' });
|
|
927
|
+
return;
|
|
928
|
+
}
|
|
929
|
+
const factionId = raw.miscValue | 0;
|
|
930
|
+
// Cite: :4918–4921 !factionEntry return — empty faction id has no FactionStore row.
|
|
931
|
+
if (!factionId) {
|
|
932
|
+
state.intents.push({ kind: 'skip_effect', reason: 'REPUTATION empty EffectMiscValue faction' });
|
|
933
|
+
return;
|
|
934
|
+
}
|
|
935
|
+
// m_currentBasePoints = CalculateSimpleValue. Cite: Spell.cpp:425; DBCStructure.h:817.
|
|
936
|
+
const repChange = calculateSimpleValue(raw);
|
|
937
|
+
const intent: InterpretIntent = {
|
|
938
|
+
kind: 'reputation',
|
|
939
|
+
factionId,
|
|
940
|
+
repChange,
|
|
941
|
+
};
|
|
942
|
+
// Optional full CalculateReputationGain when rate/aura ctx supplied. Cite: Player.cpp:6293–6369 — percent
|
|
943
|
+
// starts 100; spell source skips level trivial; return int32(rate * rep * percent / 100).
|
|
944
|
+
const hasRateCtx =
|
|
945
|
+
state.ctx.reputationAuraMod != null
|
|
946
|
+
|| state.ctx.reputationGainRate != null
|
|
947
|
+
|| state.ctx.reputationSpellRate != null;
|
|
948
|
+
if (hasRateCtx) {
|
|
949
|
+
const repMod = state.ctx.reputationAuraMod ?? 0;
|
|
950
|
+
let percent = 100 + (repChange > 0 ? repMod : -repMod);
|
|
951
|
+
const spellRate = state.ctx.reputationSpellRate;
|
|
952
|
+
// Cite: :6362–6364 repRate <= 0 → return 0.
|
|
953
|
+
if (spellRate != null && spellRate <= 0) {
|
|
954
|
+
intent.finalRepChange = 0;
|
|
955
|
+
} else {
|
|
956
|
+
if (spellRate != null) percent *= spellRate;
|
|
957
|
+
if (percent <= 0) {
|
|
958
|
+
intent.finalRepChange = 0;
|
|
959
|
+
} else {
|
|
960
|
+
const gainRate = state.ctx.reputationGainRate ?? 1;
|
|
961
|
+
intent.finalRepChange = trunc((gainRate * repChange * percent) / 100);
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
state.intents.push(intent);
|
|
966
|
+
// The intent alone had no reader, so every REPUTATION spell was a no-op. Only effects[]
|
|
967
|
+
// reaches the OOC cast applier. Rate/aura scaling stays there (it owns the player context).
|
|
968
|
+
state.effects.push({
|
|
969
|
+
kind: 'reputation',
|
|
970
|
+
factionId,
|
|
971
|
+
amount: repChange,
|
|
972
|
+
});
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
/** SPELL_EFFECT_BIND (11) — SetHomebindToLocation at current position. Cite: SpellEffects.cpp:5544–5578. */
|
|
976
|
+
export function handleBind(state: HandlerState, _raw: RawSpellEffect, _slot: number): void {
|
|
977
|
+
// Cite: :5546–5547 !unitTarget || !IsPlayer() return.
|
|
978
|
+
if (state.ctx.unitTargetIsPlayer === false) {
|
|
979
|
+
state.intents.push({ kind: 'skip_effect', reason: 'BIND requires player unitTarget' });
|
|
980
|
+
return;
|
|
981
|
+
}
|
|
982
|
+
// Combat/OOC surface — hearthBind consumer (travel.bindHearth / oocCastEffects).
|
|
983
|
+
state.effects.push({ kind: 'bind_hearth' });
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
// SPELL_EFFECT_BIND_SIGHT (82) — EffectNULL. No server implementation (DEBUG_LOG only). Cite:
|
|
987
|
+
// SpellEffects.cpp:140, :195–198.
|
|
988
|
+
export function handleBindSight(state: HandlerState, _raw: RawSpellEffect, _slot: number): void {
|
|
989
|
+
state.intents.push({ kind: 'bind_sight_null' });
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
// SPELL_EFFECT_PULL_TOWARDS (124). speedXY = max(1, misc)*0.1; KnockBackWithAngle toward caster (or dest
|
|
993
|
+
// for sibling opcode). Cite: SpellEffects.cpp:5132–5162.
|
|
994
|
+
export function handlePullTowards(state: HandlerState, raw: RawSpellEffect, _slot: number): void {
|
|
995
|
+
const dist = state.ctx.pullDistance;
|
|
996
|
+
// Cite: :5152–5153 dist < 0.1f return.
|
|
997
|
+
if (dist != null && dist < 0.1) {
|
|
998
|
+
state.intents.push({ kind: 'skip_effect', reason: 'PULL_TOWARDS distance < 0.1' });
|
|
999
|
+
return;
|
|
1000
|
+
}
|
|
1001
|
+
// Cite: :5156 max(1, EffectMiscValue) * 0.1f.
|
|
1002
|
+
const misc = raw.miscValue | 0;
|
|
1003
|
+
const speedXY = Math.max(1, misc) * 0.1;
|
|
1004
|
+
const intent: InterpretIntent = { kind: 'pull_towards', speedXY };
|
|
1005
|
+
if (dist != null && dist >= 0.1) {
|
|
1006
|
+
// Cite: :5157–5158 time = dist/speedXY; speedZ = (dz + 0.5*t*t*g)/t.
|
|
1007
|
+
const time = dist / speedXY;
|
|
1008
|
+
const dz = state.ctx.pullHeightDelta ?? 0;
|
|
1009
|
+
const speedZ = (dz + 0.5 * time * time * MOVEMENT_GRAVITY) / time;
|
|
1010
|
+
intent.speedZ = speedZ;
|
|
1011
|
+
intent.distance = dist;
|
|
1012
|
+
}
|
|
1013
|
+
state.intents.push(intent);
|
|
1014
|
+
state.effects.push({
|
|
1015
|
+
kind: 'pull_towards',
|
|
1016
|
+
speedXY,
|
|
1017
|
+
...(intent.speedZ != null ? { speedZ: intent.speedZ } : {}),
|
|
1018
|
+
...(intent.distance != null ? { distance: intent.distance } : {}),
|
|
1019
|
+
});
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
// SPELL_EFFECT_DODGE (20) — EffectEmpty marker. Cite: SpellEffects.cpp:78 dispatch → EffectEmpty :190–193.
|
|
1023
|
+
export function handleDodge(state: HandlerState, _raw: RawSpellEffect, _slot: number): void {
|
|
1024
|
+
state.intents.push({ kind: 'dodge_marker' });
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
// SPELL_EFFECT_PARRY grants the ability to parry. The effect is pushed as well as the intent, or the
|
|
1028
|
+
// catalog left Parry 3127 with an empty effect list.
|
|
1029
|
+
export function handleParry(state: HandlerState, _raw: RawSpellEffect, _slot: number): void {
|
|
1030
|
+
state.intents.push({ kind: 'can_parry' });
|
|
1031
|
+
state.effects.push({ kind: 'can_parry' } as never);
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
// SPELL_EFFECT_BLOCK (23). SetCanBlock(true). Dual-push effects[] for catalog surface (same hollow-bake
|
|
1035
|
+
// shape as Parry). Cite: SpellEffects.cpp:4890–4893.
|
|
1036
|
+
export function handleBlock(state: HandlerState, _raw: RawSpellEffect, _slot: number): void {
|
|
1037
|
+
state.intents.push({ kind: 'can_block' });
|
|
1038
|
+
state.effects.push({ kind: 'can_block' } as never);
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
// SPELL_EFFECT_DEFENSE (26) — EffectEmpty marker. Cite: SpellEffects.cpp:84 dispatch → EffectEmpty
|
|
1042
|
+
// :190–193.
|
|
1043
|
+
export function handleDefense(state: HandlerState, _raw: RawSpellEffect, _slot: number): void {
|
|
1044
|
+
state.intents.push({ kind: 'defense_marker' });
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
// SPELL_EFFECT_SPELL_DEFENSE (37) — EffectEmpty marker. Cite: SpellEffects.cpp:95 dispatch → EffectEmpty
|
|
1048
|
+
// :190–193.
|
|
1049
|
+
export function handleSpellDefense(state: HandlerState, _raw: RawSpellEffect, _slot: number): void {
|
|
1050
|
+
state.intents.push({ kind: 'spell_defense_marker' });
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
// SPELL_EFFECT_DUAL_WIELD (40) no-op. C++ SetCanDualWield(true) (SpellEffects.cpp:2628–2632); live off-hand
|
|
1054
|
+
// gate is spellbook 674 (equipment.ts DUAL_WIELD_SPELL_ID).
|
|
1055
|
+
export function handleDualWield(state: HandlerState, _raw: RawSpellEffect, _slot: number): void {
|
|
1056
|
+
state.effects.push({ kind: 'can_dual_wield' });
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
// SPELL_EFFECT_DETECT (49) — EffectUnused (no body). Cite: SpellEffects.cpp:107 dispatch → EffectUnused
|
|
1060
|
+
// :200–203.
|
|
1061
|
+
export function handleDetect(state: HandlerState, _raw: RawSpellEffect, _slot: number): void {
|
|
1062
|
+
state.intents.push({ kind: 'detect_unused' });
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
// SPELL_EFFECT_DISTRACT (69). Skip if in combat / CAN_NOT_REACT; MoveDistract(damage*1000) for creatures;
|
|
1066
|
+
// face dest. Cite: SpellEffects.cpp:2640–2660; Common.h IN_MILLISECONDS=1000.
|
|
1067
|
+
export function handleDistract(state: HandlerState, raw: RawSpellEffect, _slot: number): void {
|
|
1068
|
+
// Cite: :2643 in combat return.
|
|
1069
|
+
if (state.ctx.unitTargetInCombat === true) {
|
|
1070
|
+
state.intents.push({ kind: 'skip_effect', reason: 'DISTRACT target in combat' });
|
|
1071
|
+
return;
|
|
1072
|
+
}
|
|
1073
|
+
// Cite: :2647 UNIT_STAT_CAN_NOT_REACT return.
|
|
1074
|
+
if (state.ctx.unitTargetCanNotReact === true) {
|
|
1075
|
+
state.intents.push({ kind: 'skip_effect', reason: 'DISTRACT target cannot react' });
|
|
1076
|
+
return;
|
|
1077
|
+
}
|
|
1078
|
+
const { min } = effectAmountRange(raw, state.ctx);
|
|
1079
|
+
// damage * IN_MILLISECONDS. Cite: :2653; Common.h:119.
|
|
1080
|
+
const durationMs = Math.trunc(min) * IN_MILLISECONDS;
|
|
1081
|
+
// MoveDistract only for TYPEID_UNIT; players still get SetFacingTo. Cite: :2652–2658.
|
|
1082
|
+
// Omit unitTargetIsCreature → assume creature (Distract AoE hits NPCs).
|
|
1083
|
+
const moveDistract = state.ctx.unitTargetIsCreature !== false;
|
|
1084
|
+
state.intents.push({ kind: 'distract', durationMs, moveDistract });
|
|
1085
|
+
// Catalog / OOC surface — not a pure energy sink. World attention is intent-driven.
|
|
1086
|
+
// Cite: schema distract; KI-159.
|
|
1087
|
+
state.effects.push({ kind: 'distract', durationMs });
|
|
1088
|
+
state.role = state.role || 'utility';
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
// SPELL_EFFECT_PICKPOCKET (71). Player caster + creature attackable target → LOOT_PICKPOCKETING
|
|
1092
|
+
// ShowContentTo. Cite: SpellEffects.cpp:2662–2706.
|
|
1093
|
+
export function handlePickPocket(state: HandlerState, _raw: RawSpellEffect, _slot: number): void {
|
|
1094
|
+
// Cite: :2664–2665 !IsPlayer return.
|
|
1095
|
+
if (state.ctx.casterIsPlayer === false) {
|
|
1096
|
+
state.intents.push({ kind: 'skip_effect', reason: 'PICKPOCKET requires player caster' });
|
|
1097
|
+
return;
|
|
1098
|
+
}
|
|
1099
|
+
// Cite: :2668 creature + CanAttackNow.
|
|
1100
|
+
if (state.ctx.unitTargetIsCreature === false) {
|
|
1101
|
+
state.intents.push({ kind: 'skip_effect', reason: 'PICKPOCKET requires creature unitTarget' });
|
|
1102
|
+
return;
|
|
1103
|
+
}
|
|
1104
|
+
if (state.ctx.canAttackTarget === false) {
|
|
1105
|
+
state.intents.push({ kind: 'skip_effect', reason: 'PICKPOCKET cannot attack target' });
|
|
1106
|
+
return;
|
|
1107
|
+
}
|
|
1108
|
+
// Already pickpocketed + no restock → LOOT_ERROR_ALREADY_PICKPOCKETED. Cite: :2683–2696.
|
|
1109
|
+
if (
|
|
1110
|
+
state.ctx.pickpocketAlreadyLooted === true
|
|
1111
|
+
&& state.ctx.pickpocketCanRestock !== true
|
|
1112
|
+
) {
|
|
1113
|
+
state.intents.push({
|
|
1114
|
+
kind: 'pickpocket',
|
|
1115
|
+
alreadyPickpocketed: true,
|
|
1116
|
+
});
|
|
1117
|
+
// Still surface the effect kind so the ability is not empty-effects inert.
|
|
1118
|
+
state.effects.push({ kind: 'pickpocket' });
|
|
1119
|
+
state.role = state.role || 'utility';
|
|
1120
|
+
return;
|
|
1121
|
+
}
|
|
1122
|
+
const intent: InterpretIntent = { kind: 'pickpocket' };
|
|
1123
|
+
if (state.ctx.pickpocketAlreadyLooted === true && state.ctx.pickpocketCanRestock === true) {
|
|
1124
|
+
intent.restock = true;
|
|
1125
|
+
}
|
|
1126
|
+
// pickpocketLootExists informs runtime refill vs create — both ShowContentTo. Cite: :2676–2705.
|
|
1127
|
+
void state.ctx.pickpocketLootExists;
|
|
1128
|
+
state.intents.push(intent);
|
|
1129
|
+
// Catalog / OOC / kit surface. Runtime loot is domain/pickpocket + game.pickPocket.
|
|
1130
|
+
// Cite: schema pickpocket; SpellEffects.cpp:2662; KI-112.
|
|
1131
|
+
state.effects.push({ kind: 'pickpocket' });
|
|
1132
|
+
state.role = state.role || 'utility';
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
// SPELL_EFFECT_HEAL_MECHANICAL (75). Bonus-adjusted heal; mechanical creature type is TargetCreatureType
|
|
1136
|
+
// cast filter. Cite: SpellEffects.cpp:1888–1903; map-spell-effects heal path.
|
|
1137
|
+
export function handleHealMechanical(state: HandlerState, raw: RawSpellEffect, _slot: number): void {
|
|
1138
|
+
// Cite: :1891 alive + damage >= 0.
|
|
1139
|
+
if (state.ctx.unitTargetIsAlive === false) {
|
|
1140
|
+
state.intents.push({ kind: 'skip_effect', reason: 'HEAL_MECHANICAL target not alive' });
|
|
1141
|
+
return;
|
|
1142
|
+
}
|
|
1143
|
+
const { min, max } = effectAmountRange(raw, state.ctx);
|
|
1144
|
+
if (max < 0 && min < 0) {
|
|
1145
|
+
state.intents.push({ kind: 'skip_effect', reason: 'HEAL_MECHANICAL damage < 0' });
|
|
1146
|
+
return;
|
|
1147
|
+
}
|
|
1148
|
+
// Bake-thin: emit heal range like EffectHeal (no SpellHealingBonusDone/Taken at interpret time).
|
|
1149
|
+
// Cite: :1899–1901 m_healingPerEffect = addhealth after bonuses (runtime SI5).
|
|
1150
|
+
const clamped = clampPositiveRange(min, max);
|
|
1151
|
+
state.effects.push({ kind: 'heal', min: clamped.min, max: clamped.max });
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
// SPELL_EFFECT_ATTACK (78) — EffectUnused (no body). Cite: SpellEffects.cpp:136 dispatch → EffectUnused
|
|
1155
|
+
// :200–203.
|
|
1156
|
+
export function handleAttackUnused(state: HandlerState, _raw: RawSpellEffect, _slot: number): void {
|
|
1157
|
+
state.intents.push({ kind: 'attack_unused' });
|
|
1158
|
+
state.effects.push({ kind: 'client_inform' });
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
// SPELL_EFFECT_DUEL (83). Player–player only; area duel flag; create flag GO + DuelInfo + request packets.
|
|
1162
|
+
// Cite: SpellEffects.cpp:4227–4308.
|
|
1163
|
+
export function handleDuel(state: HandlerState, raw: RawSpellEffect, _slot: number): void {
|
|
1164
|
+
const flagGoEntry = raw.miscValue | 0;
|
|
1165
|
+
// Cite: :4274 SetRespawnTime(m_duration > 0 ? m_duration / IN_MILLISECONDS : 0).
|
|
1166
|
+
const durationMs = durationMsResolved(state);
|
|
1167
|
+
|
|
1168
|
+
// Cite: :4229 both players.
|
|
1169
|
+
if (state.ctx.casterIsPlayer === false || state.ctx.unitTargetIsPlayer === false) {
|
|
1170
|
+
state.intents.push({
|
|
1171
|
+
kind: 'duel',
|
|
1172
|
+
flagGoEntry,
|
|
1173
|
+
durationMs,
|
|
1174
|
+
skipped: 'not_players',
|
|
1175
|
+
});
|
|
1176
|
+
return;
|
|
1177
|
+
}
|
|
1178
|
+
// Cite: :4236 already dueling.
|
|
1179
|
+
if (state.ctx.casterOrTargetHasDuel === true) {
|
|
1180
|
+
state.intents.push({
|
|
1181
|
+
kind: 'duel',
|
|
1182
|
+
flagGoEntry,
|
|
1183
|
+
durationMs,
|
|
1184
|
+
skipped: 'already_dueling',
|
|
1185
|
+
});
|
|
1186
|
+
return;
|
|
1187
|
+
}
|
|
1188
|
+
// Cite: :4236 HasIgnore.
|
|
1189
|
+
if (state.ctx.targetIgnoresCaster === true) {
|
|
1190
|
+
state.intents.push({
|
|
1191
|
+
kind: 'duel',
|
|
1192
|
+
flagGoEntry,
|
|
1193
|
+
durationMs,
|
|
1194
|
+
skipped: 'ignored',
|
|
1195
|
+
});
|
|
1196
|
+
return;
|
|
1197
|
+
}
|
|
1198
|
+
// Cite: :4240–4252 AREA_FLAG_DUEL (flags & AREA_FLAG_DUEL required; fail when absent).
|
|
1199
|
+
if (state.ctx.areaAllowsDuel === false) {
|
|
1200
|
+
state.intents.push({
|
|
1201
|
+
kind: 'duel',
|
|
1202
|
+
flagGoEntry,
|
|
1203
|
+
durationMs,
|
|
1204
|
+
skipped: 'area_no_duel',
|
|
1205
|
+
});
|
|
1206
|
+
return;
|
|
1207
|
+
}
|
|
1208
|
+
// Full path: create GO at midpoint, faction/level, DuelInfo, SMSG_DUEL_REQUESTED.
|
|
1209
|
+
// Cite: :4254–4308. Coordinates are runtime-only.
|
|
1210
|
+
state.intents.push({ kind: 'duel', flagGoEntry, durationMs });
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
// SPELL_EFFECT_STUCK (84). Ghost → graveyard; hearth if ready+item; else dungeon entrance or 10y nudge.
|
|
1214
|
+
// Cite: SpellEffects.cpp:4310–4353.
|
|
1215
|
+
export function handleStuck(state: HandlerState, _raw: RawSpellEffect, _slot: number): void {
|
|
1216
|
+
// Cite: :4312–4313 player only.
|
|
1217
|
+
if (state.ctx.casterIsPlayer === false) {
|
|
1218
|
+
state.intents.push({ kind: 'skip_effect', reason: 'STUCK requires player caster' });
|
|
1219
|
+
return;
|
|
1220
|
+
}
|
|
1221
|
+
// Cite: :4315–4316 CONFIG_BOOL_CAST_UNSTUCK.
|
|
1222
|
+
if (state.ctx.castUnstuckEnabled === false) {
|
|
1223
|
+
state.intents.push({ kind: 'skip_effect', reason: 'STUCK cast unstuck disabled' });
|
|
1224
|
+
return;
|
|
1225
|
+
}
|
|
1226
|
+
// Cite: :4323–4324 taxi flying.
|
|
1227
|
+
if (state.ctx.casterTaxiFlying === true) {
|
|
1228
|
+
state.intents.push({ kind: 'skip_effect', reason: 'STUCK caster taxi flying' });
|
|
1229
|
+
return;
|
|
1230
|
+
}
|
|
1231
|
+
// Cite: :4327–4330 ghost → RepopAtGraveyard.
|
|
1232
|
+
if (state.ctx.casterIsGhost === true) {
|
|
1233
|
+
state.intents.push({ kind: 'stuck', path: 'graveyard' });
|
|
1234
|
+
return;
|
|
1235
|
+
}
|
|
1236
|
+
// With either gate explicitly false this is a dungeon entrance or a short nudge; with both true, or
|
|
1237
|
+
// both omitted on a thin bake, it takes the hearth path. Cite: SpellEffects.cpp:4335-4336.
|
|
1238
|
+
const hearthBlocked =
|
|
1239
|
+
state.ctx.hearthstoneReady === false || state.ctx.hasHearthstoneItem === false;
|
|
1240
|
+
if (!hearthBlocked) {
|
|
1241
|
+
state.intents.push({
|
|
1242
|
+
kind: 'stuck',
|
|
1243
|
+
path: 'hearthstone',
|
|
1244
|
+
hearthSpellId: STUCK_HEARTHSTONE_SPELL,
|
|
1245
|
+
});
|
|
1246
|
+
return;
|
|
1247
|
+
}
|
|
1248
|
+
// Cite: :4339–4351 dungeon entrance vs nudge.
|
|
1249
|
+
if (state.ctx.casterInDungeon === true) {
|
|
1250
|
+
state.intents.push({ kind: 'stuck', path: 'dungeon_entrance' });
|
|
1251
|
+
return;
|
|
1252
|
+
}
|
|
1253
|
+
state.intents.push({
|
|
1254
|
+
kind: 'stuck',
|
|
1255
|
+
path: 'nudge',
|
|
1256
|
+
nudgeDistance: STUCK_NUDGE_DISTANCE,
|
|
1257
|
+
});
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
// Player.h InventorySlots — first invalid equipment/bag slot for GetItemByPos bag0. Cite: Player.h:583–586
|
|
1261
|
+
// INVENTORY_SLOT_BAG_END = 23.
|
|
1262
|
+
export const INVENTORY_SLOT_BAG_END = 23;
|
|
1263
|
+
|
|
1264
|
+
// SPELL_EFFECT_DURABILITY_DAMAGE, player targets only: the misc value is the slot and the amount the
|
|
1265
|
+
// points. A negative slot hits everything, and a slot past the bag end returns.
|
|
1266
|
+
export function handleDurabilityDamage(state: HandlerState, raw: RawSpellEffect, _slot: number): void {
|
|
1267
|
+
// Cite: :5211–5212 !unitTarget || !IsPlayer.
|
|
1268
|
+
if (state.ctx.unitTargetIsPlayer === false) {
|
|
1269
|
+
state.intents.push({
|
|
1270
|
+
kind: 'skip_effect',
|
|
1271
|
+
reason: 'DURABILITY_DAMAGE requires player unitTarget',
|
|
1272
|
+
});
|
|
1273
|
+
return;
|
|
1274
|
+
}
|
|
1275
|
+
// EffectMiscValue as signed slot. Cite: :5214.
|
|
1276
|
+
const invSlot = raw.miscValue | 0;
|
|
1277
|
+
// C++ `damage` = calculated effect value; bake-thin uses range min (no die roll).
|
|
1278
|
+
// Cite: amount.ts; Object.cpp CalculateSpellEffectValue.
|
|
1279
|
+
const { min } = effectAmountRange(raw, state.ctx);
|
|
1280
|
+
const points = Math.trunc(min);
|
|
1281
|
+
|
|
1282
|
+
// slot < 0: all equipped (−1) or equipped + inventory (−2). Cite: :5218–5222.
|
|
1283
|
+
if (invSlot < 0) {
|
|
1284
|
+
state.intents.push({
|
|
1285
|
+
kind: 'durability_damage',
|
|
1286
|
+
slot: invSlot,
|
|
1287
|
+
points,
|
|
1288
|
+
allItems: true,
|
|
1289
|
+
includeInventory: invSlot < -1,
|
|
1290
|
+
});
|
|
1291
|
+
return;
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
// invalid slot value. Cite: :5226–5227.
|
|
1295
|
+
if (invSlot >= INVENTORY_SLOT_BAG_END) {
|
|
1296
|
+
state.intents.push({
|
|
1297
|
+
kind: 'skip_effect',
|
|
1298
|
+
reason: 'DURABILITY_DAMAGE invalid slot >= INVENTORY_SLOT_BAG_END',
|
|
1299
|
+
});
|
|
1300
|
+
return;
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
// if (Item* item = GetItemByPos(BAG_0, slot)) — silent when empty. Cite: :5229–5233.
|
|
1304
|
+
if (state.ctx.durabilitySlotHasItem === false) {
|
|
1305
|
+
return;
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
state.intents.push({
|
|
1309
|
+
kind: 'durability_damage',
|
|
1310
|
+
slot: invSlot,
|
|
1311
|
+
points,
|
|
1312
|
+
allItems: false,
|
|
1313
|
+
includeInventory: false,
|
|
1314
|
+
});
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
// SPELL_EFFECT_DURABILITY_DAMAGE_PCT, player targets only: the misc value is the slot and the amount a
|
|
1318
|
+
// percent. A negative slot hits everything, and a slot past the bag end returns.
|
|
1319
|
+
export function handleDurabilityDamagePCT(state: HandlerState, raw: RawSpellEffect, _slot: number): void {
|
|
1320
|
+
// Cite: :5238–5239 !unitTarget || !IsPlayer.
|
|
1321
|
+
if (state.ctx.unitTargetIsPlayer === false) {
|
|
1322
|
+
state.intents.push({
|
|
1323
|
+
kind: 'skip_effect',
|
|
1324
|
+
reason: 'DURABILITY_DAMAGE_PCT requires player unitTarget',
|
|
1325
|
+
});
|
|
1326
|
+
return;
|
|
1327
|
+
}
|
|
1328
|
+
// EffectMiscValue as signed slot. Cite: :5241.
|
|
1329
|
+
const invSlot = raw.miscValue | 0;
|
|
1330
|
+
// C++ `damage` = calculated effect value; bake-thin uses range min (no die roll).
|
|
1331
|
+
const { min } = effectAmountRange(raw, state.ctx);
|
|
1332
|
+
const damage = Math.trunc(min);
|
|
1333
|
+
const percent = damage / 100;
|
|
1334
|
+
|
|
1335
|
+
// slot < 0: all equipped (−1) or equipped + inventory (−2). Cite: :5245–5248.
|
|
1336
|
+
if (invSlot < 0) {
|
|
1337
|
+
const payload = {
|
|
1338
|
+
kind: 'durability_damage' as const,
|
|
1339
|
+
slot: invSlot,
|
|
1340
|
+
percent,
|
|
1341
|
+
allItems: true,
|
|
1342
|
+
includeInventory: invSlot < -1,
|
|
1343
|
+
};
|
|
1344
|
+
state.effects.push(payload);
|
|
1345
|
+
state.intents.push(payload);
|
|
1346
|
+
return;
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1349
|
+
// invalid slot value. Cite: :5252–5253.
|
|
1350
|
+
if (invSlot >= INVENTORY_SLOT_BAG_END) {
|
|
1351
|
+
state.intents.push({
|
|
1352
|
+
kind: 'skip_effect',
|
|
1353
|
+
reason: 'DURABILITY_DAMAGE_PCT invalid slot >= INVENTORY_SLOT_BAG_END',
|
|
1354
|
+
});
|
|
1355
|
+
return;
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
// if (damage <= 0) return. Cite: :5255–5256.
|
|
1359
|
+
if (damage <= 0) return;
|
|
1360
|
+
|
|
1361
|
+
// if (Item* item = GetItemByPos(BAG_0, slot)) — silent when empty. Cite: :5258–5259.
|
|
1362
|
+
if (state.ctx.durabilitySlotHasItem === false) {
|
|
1363
|
+
return;
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
const payload = {
|
|
1367
|
+
kind: 'durability_damage' as const,
|
|
1368
|
+
slot: invSlot,
|
|
1369
|
+
percent,
|
|
1370
|
+
allItems: false,
|
|
1371
|
+
includeInventory: false,
|
|
1372
|
+
};
|
|
1373
|
+
state.effects.push(payload);
|
|
1374
|
+
state.intents.push(payload);
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
// SPELL_EFFECT_ATTACK_ME, the taunt effect: a threat list equalizes to the highest and sets the
|
|
1378
|
+
// current victim, while anything else starts an attack unless it cannot react.
|
|
1379
|
+
export function handleAttackMe(state: HandlerState, _raw: RawSpellEffect, _slot: number): void {
|
|
1380
|
+
// unitTarget null is runtime; bake-thin always proceeds.
|
|
1381
|
+
|
|
1382
|
+
// Non-player + GetVictim() == caster → fail. Cite: :3420–3426.
|
|
1383
|
+
// Players may re-taunt (no early out). Omit unitTargetIsPlayer → not this gate.
|
|
1384
|
+
if (
|
|
1385
|
+
state.ctx.unitTargetIsPlayer === false
|
|
1386
|
+
&& state.ctx.unitTargetVictimIsCaster === true
|
|
1387
|
+
) {
|
|
1388
|
+
state.intents.push({
|
|
1389
|
+
kind: 'skip_effect',
|
|
1390
|
+
reason: 'ATTACK_ME target already attacking caster',
|
|
1391
|
+
});
|
|
1392
|
+
return;
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
// Threat-list path (default / omit canHaveThreatList). Cite: :3430–3435.
|
|
1396
|
+
if (state.ctx.canHaveThreatList !== false) {
|
|
1397
|
+
// equalize + force victim — engine applies via taunt effect.
|
|
1398
|
+
// Cite: map-spell-effects.mjs :359–361 → { kind: 'taunt' }.
|
|
1399
|
+
state.effects.push({ kind: 'taunt' });
|
|
1400
|
+
return;
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
// No threat list + not CAN_NOT_REACT_OR_LOST_CONTROL → AI AttackStart. Cite: :3437–3440.
|
|
1404
|
+
if (state.ctx.unitTargetCanNotReactOrLostControl === true) {
|
|
1405
|
+
// else-if fails: log only, no AttackStart. Cite: :3437.
|
|
1406
|
+
return;
|
|
1407
|
+
}
|
|
1408
|
+
state.intents.push({ kind: 'attack_me_ai' });
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
// SPELL_EFFECT_SKIN_PLAYER_CORPSE, the battleground insignia: a live owner is stripped of their
|
|
1412
|
+
// insignia, and a corpse alone is converted and its insignia loot shown.
|
|
1413
|
+
export function handleSkinPlayerCorpse(state: HandlerState, _raw: RawSpellEffect, _slot: number): void {
|
|
1414
|
+
// Cite: :5502–5504 player caster only.
|
|
1415
|
+
if (state.ctx.casterIsPlayer === false) {
|
|
1416
|
+
state.intents.push({
|
|
1417
|
+
kind: 'skip_effect',
|
|
1418
|
+
reason: 'SKIN_PLAYER_CORPSE requires player caster',
|
|
1419
|
+
});
|
|
1420
|
+
return;
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1423
|
+
// Corpse-only (owner not found): bones + LOOT_INSIGNIA. Cite: :5510–5538.
|
|
1424
|
+
if (state.ctx.skinCorpseOnly === true) {
|
|
1425
|
+
state.intents.push({ kind: 'skin_player_corpse', path: 'corpse_bones_loot' });
|
|
1426
|
+
return;
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1429
|
+
// Primary: RemovedInsignia(caster) on player target. Cite: :5541.
|
|
1430
|
+
// unitTargetIsPlayer false → not a player corpse owner; silent skip.
|
|
1431
|
+
if (state.ctx.unitTargetIsPlayer === false) {
|
|
1432
|
+
state.intents.push({
|
|
1433
|
+
kind: 'skip_effect',
|
|
1434
|
+
reason: 'SKIN_PLAYER_CORPSE requires player unitTarget',
|
|
1435
|
+
});
|
|
1436
|
+
return;
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
state.intents.push({ kind: 'skin_player_corpse', path: 'removed_insignia' });
|
|
1440
|
+
state.effects.push({ kind: 'client_inform' });
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
/** effectType → handler (SI2 + SI3 batch 1–8). */
|
|
1444
|
+
|
|
1445
|
+
// SPELL_EFFECT_DUMMY (3) — SI4 registry dispatch. Unknown spell ids: handled no-op (empty secondary
|
|
1446
|
+
// finishers, etc.). Cite: SpellEffects.cpp EffectDummy ~346; registry/index.ts dispatchDummy.
|
|
1447
|
+
export function handleEffectDummy(state: HandlerState, raw: RawSpellEffect, slot: number): void {
|
|
1448
|
+
dispatchDummy(state, raw, slot);
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1451
|
+
// SPELL_EFFECT_SCRIPT_EFFECT (77) — SI4 registry dispatch. Cite: SpellEffects.cpp EffectScriptEffect ~3664;
|
|
1452
|
+
// registry/index.ts dispatchScript.
|
|
1453
|
+
export function handleEffectScriptEffect(state: HandlerState, raw: RawSpellEffect, slot: number): void {
|
|
1454
|
+
dispatchScript(state, raw, slot);
|
|
1455
|
+
}
|