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.
Files changed (1163) hide show
  1. package/README.md +138 -2
  2. package/bin/azsh.mjs +19 -0
  3. package/docs/aifid/eventai-texts.json +4414 -0
  4. package/docs/dungeons/atlas/atlas-rows-dungeons.json +8989 -0
  5. package/docs/dungeons/survey/pack-id-migration.json +2418 -0
  6. package/package.json +35 -4
  7. package/src/autopilot/bossSkip.ts +26 -0
  8. package/src/autopilot/combatRotation.ts +824 -0
  9. package/src/autopilot/config.ts +533 -0
  10. package/src/autopilot/consumables.ts +58 -0
  11. package/src/autopilot/encounterStand.ts +121 -0
  12. package/src/autopilot/objectGatePlan.ts +53 -0
  13. package/src/autopilot/pathEngage.ts +229 -0
  14. package/src/autopilot/plan.ts +1643 -0
  15. package/src/autopilot/questPolicy.ts +274 -0
  16. package/src/autopilot/routeStep.ts +69 -0
  17. package/src/autopilot/sessionReport.ts +88 -0
  18. package/src/autopilot/topOffPower.ts +26 -0
  19. package/src/autopilot/view.ts +476 -0
  20. package/src/cli/bootCheck.mjs +34 -0
  21. package/src/cli/bootData.mjs +57 -0
  22. package/src/cli/combatLogFilter.ts +153 -0
  23. package/src/cli/combatLogFormat.ts +613 -0
  24. package/src/cli/fileRepository.mjs +105 -0
  25. package/src/cli/gzLoader.mjs +31 -0
  26. package/src/cli/liveGauntletEscort.ts +978 -0
  27. package/src/cli/liveStratUndead.ts +305 -0
  28. package/src/cli/macroHostCombat.ts +545 -0
  29. package/src/cli/macroHostWorld.ts +330 -0
  30. package/src/cli/prefs.mjs +31 -0
  31. package/src/cli/register.mjs +4 -0
  32. package/src/cli/repl.mjs +693 -0
  33. package/src/cli/rotationText.mjs +226 -0
  34. package/src/cli/squad.mjs +105 -0
  35. package/src/cli/term.mjs +144 -0
  36. package/src/cli/text.mjs +122 -0
  37. package/src/cli/tui.mjs +626 -0
  38. package/src/cli/tuiArena.mjs +109 -0
  39. package/src/cli/tuiAutopilotSettings.mjs +93 -0
  40. package/src/cli/tuiBags.mjs +180 -0
  41. package/src/cli/tuiBars.mjs +133 -0
  42. package/src/cli/tuiBg.mjs +243 -0
  43. package/src/cli/tuiBulk.mjs +162 -0
  44. package/src/cli/tuiCheck.mjs +1882 -0
  45. package/src/cli/tuiCity.mjs +396 -0
  46. package/src/cli/tuiCompanionKit.mjs +300 -0
  47. package/src/cli/tuiCompanions.mjs +471 -0
  48. package/src/cli/tuiDev.mjs +62 -0
  49. package/src/cli/tuiDungeon.mjs +606 -0
  50. package/src/cli/tuiEnchantTarget.mjs +120 -0
  51. package/src/cli/tuiFight.mjs +577 -0
  52. package/src/cli/tuiFightPanes.mjs +228 -0
  53. package/src/cli/tuiFloor.mjs +506 -0
  54. package/src/cli/tuiGear.mjs +158 -0
  55. package/src/cli/tuiHonor.mjs +74 -0
  56. package/src/cli/tuiLoot.mjs +97 -0
  57. package/src/cli/tuiMacros.mjs +144 -0
  58. package/src/cli/tuiOutfitter.mjs +115 -0
  59. package/src/cli/tuiPet.mjs +116 -0
  60. package/src/cli/tuiProfessions.mjs +800 -0
  61. package/src/cli/tuiRaid.mjs +127 -0
  62. package/src/cli/tuiRep.mjs +65 -0
  63. package/src/cli/tuiSaves.mjs +69 -0
  64. package/src/cli/tuiSearch.mjs +363 -0
  65. package/src/cli/tuiSettings.mjs +151 -0
  66. package/src/cli/tuiSimLab.mjs +80 -0
  67. package/src/cli/tuiTalents.mjs +60 -0
  68. package/src/cli/tuiViews.mjs +1178 -0
  69. package/src/cli/tuiWiki.mjs +181 -0
  70. package/src/cli/tuiWorld.mjs +361 -0
  71. package/src/cli/wikiIndex.ts +259 -0
  72. package/src/cli/wikiSources.ts +355 -0
  73. package/src/data/alteracValley.ts +176 -0
  74. package/src/data/appearanceOptions.ts +534 -0
  75. package/src/data/applyLootChanceSanity.ts +21 -0
  76. package/src/data/createFlavor.ts +96 -0
  77. package/src/data/customItems.ts +33 -0
  78. package/src/data/dataStore.ts +1904 -0
  79. package/src/data/dungeonGameObjects.ts +84 -0
  80. package/src/data/dungeonPlateCover.ts +66 -0
  81. package/src/data/dungeonSpawns.ts +294 -0
  82. package/src/data/dungeons/alterac-valley.ts +135 -0
  83. package/src/data/dungeons/apply-dungeon-rooms.ts +29 -0
  84. package/src/data/dungeons/apply-encounter-outdoors.ts +72 -0
  85. package/src/data/dungeons/apply-instance-geometry.ts +65 -0
  86. package/src/data/dungeons/apply-spawn-placement.ts +171 -0
  87. package/src/data/dungeons/blackfathom-deeps.ts +539 -0
  88. package/src/data/dungeons/blackrock-depths.ts +1410 -0
  89. package/src/data/dungeons/blackwing-lair.ts +129 -0
  90. package/src/data/dungeons/boss-eventai.ts +8 -0
  91. package/src/data/dungeons/deadmines.ts +410 -0
  92. package/src/data/dungeons/dire-maul-east.ts +980 -0
  93. package/src/data/dungeons/dire-maul-north.ts +661 -0
  94. package/src/data/dungeons/dire-maul-west.ts +785 -0
  95. package/src/data/dungeons/dungeon-rooms.json +5479 -0
  96. package/src/data/dungeons/encounter-outdoors.json +359 -0
  97. package/src/data/dungeons/eventai/blackfathom-deeps.ts +308 -0
  98. package/src/data/dungeons/eventai/blackrock-depths.ts +1257 -0
  99. package/src/data/dungeons/eventai/deadmines.ts +264 -0
  100. package/src/data/dungeons/eventai/dire-maul-east.ts +320 -0
  101. package/src/data/dungeons/eventai/gnomeregan.ts +213 -0
  102. package/src/data/dungeons/eventai/helpers.ts +79 -0
  103. package/src/data/dungeons/eventai/index.ts +133 -0
  104. package/src/data/dungeons/eventai/lbrs.ts +372 -0
  105. package/src/data/dungeons/eventai/maraudon.ts +323 -0
  106. package/src/data/dungeons/eventai/ragefire-chasm.ts +108 -0
  107. package/src/data/dungeons/eventai/razorfen-downs.ts +207 -0
  108. package/src/data/dungeons/eventai/razorfen-kraul.ts +326 -0
  109. package/src/data/dungeons/eventai/scholomance.ts +680 -0
  110. package/src/data/dungeons/eventai/shadowfang-keep.ts +230 -0
  111. package/src/data/dungeons/eventai/sm-cathedral.ts +75 -0
  112. package/src/data/dungeons/eventai/sm-graveyard.ts +150 -0
  113. package/src/data/dungeons/eventai/sm-library.ts +55 -0
  114. package/src/data/dungeons/eventai/stratholme-undead.ts +127 -0
  115. package/src/data/dungeons/eventai/sunken-temple.ts +582 -0
  116. package/src/data/dungeons/eventai/the-stockade.ts +228 -0
  117. package/src/data/dungeons/eventai/ubrs.ts +225 -0
  118. package/src/data/dungeons/eventai/uldaman.ts +355 -0
  119. package/src/data/dungeons/eventai/wailing-caverns.ts +396 -0
  120. package/src/data/dungeons/eventai/zul-farrak.ts +591 -0
  121. package/src/data/dungeons/gnomeregan.ts +909 -0
  122. package/src/data/dungeons/index.ts +126 -0
  123. package/src/data/dungeons/instance-geometry.json +5233 -0
  124. package/src/data/dungeons/key-sources.json +169 -0
  125. package/src/data/dungeons/lbrs.ts +996 -0
  126. package/src/data/dungeons/maraudon.ts +786 -0
  127. package/src/data/dungeons/molten-core.ts +194 -0
  128. package/src/data/dungeons/naxxramas.ts +173 -0
  129. package/src/data/dungeons/onyxias-lair.ts +71 -0
  130. package/src/data/dungeons/pack-from-clusters.ts +1256 -0
  131. package/src/data/dungeons/ragefire-chasm.ts +238 -0
  132. package/src/data/dungeons/razorfen-downs.ts +698 -0
  133. package/src/data/dungeons/razorfen-kraul.ts +766 -0
  134. package/src/data/dungeons/ruins-of-ahnqiraj.ts +121 -0
  135. package/src/data/dungeons/scholomance.ts +1076 -0
  136. package/src/data/dungeons/shadowfang-keep.ts +740 -0
  137. package/src/data/dungeons/sm-armory.ts +266 -0
  138. package/src/data/dungeons/sm-cathedral.ts +430 -0
  139. package/src/data/dungeons/sm-graveyard.ts +482 -0
  140. package/src/data/dungeons/sm-library.ts +337 -0
  141. package/src/data/dungeons/stratholme-live.ts +808 -0
  142. package/src/data/dungeons/stratholme-undead.ts +1164 -0
  143. package/src/data/dungeons/sunken-temple.ts +931 -0
  144. package/src/data/dungeons/temple-of-ahnqiraj.ts +119 -0
  145. package/src/data/dungeons/the-stockade.ts +282 -0
  146. package/src/data/dungeons/ubrs.ts +1286 -0
  147. package/src/data/dungeons/uldaman.ts +685 -0
  148. package/src/data/dungeons/wailing-caverns.ts +356 -0
  149. package/src/data/dungeons/zul-farrak.ts +686 -0
  150. package/src/data/dungeons/zul-gurub.ts +142 -0
  151. package/src/data/dungeons.ts +42 -0
  152. package/src/data/fixtures.ts +446 -0
  153. package/src/data/gameobject-data.json.gz +0 -0
  154. package/src/data/item-data.json.gz +0 -0
  155. package/src/data/loot-data.json.gz +0 -0
  156. package/src/data/maps/atlas-approach-maps.json +1425 -0
  157. package/src/data/maps/atlas-landmarks.json +1711 -0
  158. package/src/data/maps/dungeon-viewports.json +392 -0
  159. package/src/data/materializeModDamagePercentTaken.ts +70 -0
  160. package/src/data/materializeModHealthRegen.ts +89 -0
  161. package/src/data/materializeSpellReputation.ts +26 -0
  162. package/src/data/pageTexts.ts +19 -0
  163. package/src/data/profession-data.json.gz +0 -0
  164. package/src/data/raid-plans/blackfathom-deeps.json +86 -0
  165. package/src/data/raid-plans/blackrock-depths.json +419 -0
  166. package/src/data/raid-plans/blackwing-lair.json +328 -0
  167. package/src/data/raid-plans/deadmines.json +55 -0
  168. package/src/data/raid-plans/dire-maul-east.json +94 -0
  169. package/src/data/raid-plans/dire-maul-north.json +70 -0
  170. package/src/data/raid-plans/dire-maul-west.json +75 -0
  171. package/src/data/raid-plans/gnomeregan.json +131 -0
  172. package/src/data/raid-plans/lbrs.json +176 -0
  173. package/src/data/raid-plans/maraudon.json +102 -0
  174. package/src/data/raid-plans/molten-core.json +169 -0
  175. package/src/data/raid-plans/naxxramas.json +354 -0
  176. package/src/data/raid-plans/onyxias-lair.json +18 -0
  177. package/src/data/raid-plans/ragefire-chasm.json +30 -0
  178. package/src/data/raid-plans/razorfen-downs.json +75 -0
  179. package/src/data/raid-plans/razorfen-kraul.json +53 -0
  180. package/src/data/raid-plans/ruins-of-ahnqiraj.json +110 -0
  181. package/src/data/raid-plans/scholomance.json +170 -0
  182. package/src/data/raid-plans/shadowfang-keep.json +87 -0
  183. package/src/data/raid-plans/sm-armory.json +53 -0
  184. package/src/data/raid-plans/sm-cathedral.json +72 -0
  185. package/src/data/raid-plans/sm-graveyard.json +63 -0
  186. package/src/data/raid-plans/sm-library.json +23 -0
  187. package/src/data/raid-plans/stratholme-live.json +129 -0
  188. package/src/data/raid-plans/stratholme-undead.json +73 -0
  189. package/src/data/raid-plans/sunken-temple.json +159 -0
  190. package/src/data/raid-plans/temple-of-ahnqiraj.json +163 -0
  191. package/src/data/raid-plans/the-stockade.json +42 -0
  192. package/src/data/raid-plans/ubrs.json +53 -0
  193. package/src/data/raid-plans/uldaman.json +131 -0
  194. package/src/data/raid-plans/wailing-caverns.json +90 -0
  195. package/src/data/raid-plans/zul-farrak.json +156 -0
  196. package/src/data/raid-plans/zul-gurub.json +307 -0
  197. package/src/data/raidInstanceCreatures.mjs +473 -0
  198. package/src/data/rotations/index.ts +152 -0
  199. package/src/data/rotations/layout.mjs +72 -0
  200. package/src/data/rotations/pve/druid/balance.json +754 -0
  201. package/src/data/rotations/pve/druid/feral-tank.json +2448 -0
  202. package/src/data/rotations/pve/druid/feral.json +2660 -0
  203. package/src/data/rotations/pve/druid/restoration.json +424 -0
  204. package/src/data/rotations/pve/hunter/beast-mastery.json +1288 -0
  205. package/src/data/rotations/pve/hunter/marksmanship.json +1260 -0
  206. package/src/data/rotations/pve/hunter/survival.json +1373 -0
  207. package/src/data/rotations/pve/mage/arcane-leveling.json +1798 -0
  208. package/src/data/rotations/pve/mage/fire-leveling.json +2003 -0
  209. package/src/data/rotations/pve/mage/frost-leveling.json +1912 -0
  210. package/src/data/rotations/pve/paladin/holy.json +755 -0
  211. package/src/data/rotations/pve/paladin/protection.json +962 -0
  212. package/src/data/rotations/pve/paladin/retribution.json +924 -0
  213. package/src/data/rotations/pve/priest/discipline.json +772 -0
  214. package/src/data/rotations/pve/priest/holy.json +707 -0
  215. package/src/data/rotations/pve/priest/shadow.json +787 -0
  216. package/src/data/rotations/pve/rogue/assassination.json +2477 -0
  217. package/src/data/rotations/pve/rogue/combat.json +2483 -0
  218. package/src/data/rotations/pve/rogue/subtlety.json +2523 -0
  219. package/src/data/rotations/pve/shaman/elemental.json +1157 -0
  220. package/src/data/rotations/pve/shaman/enhancement.json +1085 -0
  221. package/src/data/rotations/pve/shaman/restoration.json +592 -0
  222. package/src/data/rotations/pve/warlock/affliction.json +1185 -0
  223. package/src/data/rotations/pve/warlock/demonology.json +1165 -0
  224. package/src/data/rotations/pve/warlock/destruction.json +1177 -0
  225. package/src/data/rotations/pve/warrior/arms.json +3020 -0
  226. package/src/data/rotations/pve/warrior/fury.json +2996 -0
  227. package/src/data/rotations/pve/warrior/protection.json +2509 -0
  228. package/src/data/rotations/pvp/druid/balance.json +593 -0
  229. package/src/data/rotations/pvp/druid/feral.json +3276 -0
  230. package/src/data/rotations/pvp/druid/restoration.json +662 -0
  231. package/src/data/rotations/pvp/hunter/beast-mastery.json +665 -0
  232. package/src/data/rotations/pvp/hunter/marksmanship.json +2406 -0
  233. package/src/data/rotations/pvp/hunter/survival.json +635 -0
  234. package/src/data/rotations/pvp/mage/arcane.json +574 -0
  235. package/src/data/rotations/pvp/mage/fire.json +570 -0
  236. package/src/data/rotations/pvp/mage/frost.json +4048 -0
  237. package/src/data/rotations/pvp/paladin/holy.json +1498 -0
  238. package/src/data/rotations/pvp/paladin/protection.json +567 -0
  239. package/src/data/rotations/pvp/paladin/retribution.json +1110 -0
  240. package/src/data/rotations/pvp/priest/discipline.json +1848 -0
  241. package/src/data/rotations/pvp/priest/holy.json +6340 -0
  242. package/src/data/rotations/pvp/priest/shadow.json +2329 -0
  243. package/src/data/rotations/pvp/rogue/assassination.json +1322 -0
  244. package/src/data/rotations/pvp/rogue/combat.json +5258 -0
  245. package/src/data/rotations/pvp/rogue/subtlety.json +586 -0
  246. package/src/data/rotations/pvp/shaman/elemental.json +1404 -0
  247. package/src/data/rotations/pvp/shaman/enhancement.json +1277 -0
  248. package/src/data/rotations/pvp/shaman/restoration.json +2490 -0
  249. package/src/data/rotations/pvp/warlock/affliction.json +1017 -0
  250. package/src/data/rotations/pvp/warlock/demonology.json +1503 -0
  251. package/src/data/rotations/pvp/warlock/destruction.json +638 -0
  252. package/src/data/rotations/pvp/warrior/arms.json +5665 -0
  253. package/src/data/rotations/pvp/warrior/fury.json +850 -0
  254. package/src/data/rotations/pvp/warrior/protection.json +777 -0
  255. package/src/data/schema/bundle.ts +511 -0
  256. package/src/data/schema/constants.ts +49 -0
  257. package/src/data/schema/creature.ts +454 -0
  258. package/src/data/schema/encounter.ts +155 -0
  259. package/src/data/schema/faction.ts +48 -0
  260. package/src/data/schema/item.ts +297 -0
  261. package/src/data/schema/itemEnchant.ts +105 -0
  262. package/src/data/schema/npc.ts +44 -0
  263. package/src/data/schema/primitives.ts +295 -0
  264. package/src/data/schema/profession.ts +226 -0
  265. package/src/data/schema/quest.ts +154 -0
  266. package/src/data/schema/spell.ts +1149 -0
  267. package/src/data/schema/talent.ts +74 -0
  268. package/src/data/schema/world.ts +124 -0
  269. package/src/data/schema.ts +19 -0
  270. package/src/data/skinNodeGapFill.ts +68 -0
  271. package/src/data/spell-data.json.gz +0 -0
  272. package/src/data/spell-script-targets.json +1 -0
  273. package/src/data/tables/ahItemValues.json.gz +0 -0
  274. package/src/data/tables/alteracValley.json.gz +0 -0
  275. package/src/data/tables/areatriggerTeleports.json.gz +0 -0
  276. package/src/data/tables/botNames.json.gz +0 -0
  277. package/src/data/tables/botPopulation.json.gz +0 -0
  278. package/src/data/tables/botSpecs.json.gz +0 -0
  279. package/src/data/tables/classLevelStats.json.gz +0 -0
  280. package/src/data/tables/combatConditions.json.gz +0 -0
  281. package/src/data/tables/combatSkillMeta.json.gz +0 -0
  282. package/src/data/tables/conditions.json.gz +0 -0
  283. package/src/data/tables/createInfo.json.gz +0 -0
  284. package/src/data/tables/creatureAggroGroups.json.gz +0 -0
  285. package/src/data/tables/creatureClassLevelStats.json.gz +0 -0
  286. package/src/data/tables/creaturePatrolPacks.json.gz +0 -0
  287. package/src/data/tables/creatureWeapons.json.gz +0 -0
  288. package/src/data/tables/creatures.json.gz +0 -0
  289. package/src/data/tables/dbScripts.json.gz +0 -0
  290. package/src/data/tables/dungeonAuthoredRoutes.json.gz +0 -0
  291. package/src/data/tables/dungeonCensus.json.gz +0 -0
  292. package/src/data/tables/dungeonGameObjects.json.gz +0 -0
  293. package/src/data/tables/dungeonPacks.json.gz +0 -0
  294. package/src/data/tables/dungeonPlateCover.json.gz +0 -0
  295. package/src/data/tables/dungeonSpawns.json.gz +0 -0
  296. package/src/data/tables/dungeonSurvey.json.gz +0 -0
  297. package/src/data/tables/durabilityCosts.json.gz +0 -0
  298. package/src/data/tables/durabilityQuality.json.gz +0 -0
  299. package/src/data/tables/enchantApplicators.json.gz +0 -0
  300. package/src/data/tables/entranceYards.json.gz +0 -0
  301. package/src/data/tables/explorationBasexp.json.gz +0 -0
  302. package/src/data/tables/factionTemplates.json.gz +0 -0
  303. package/src/data/tables/factions.json.gz +0 -0
  304. package/src/data/tables/gameEvents.json.gz +0 -0
  305. package/src/data/tables/gameObjects.json.gz +0 -0
  306. package/src/data/tables/gossipMenus.json.gz +0 -0
  307. package/src/data/tables/graveyards.json.gz +0 -0
  308. package/src/data/tables/instanceMaps.json.gz +0 -0
  309. package/src/data/tables/itemEnchantmentTemplates.json.gz +0 -0
  310. package/src/data/tables/itemEnchants.json.gz +0 -0
  311. package/src/data/tables/itemRandomProperties.json.gz +0 -0
  312. package/src/data/tables/itemSets.json.gz +0 -0
  313. package/src/data/tables/items.json.gz +0 -0
  314. package/src/data/tables/levelStats.json.gz +0 -0
  315. package/src/data/tables/locks.json.gz +0 -0
  316. package/src/data/tables/map-pin-overrides.json.gz +0 -0
  317. package/src/data/tables/nonCombatTrash.json.gz +0 -0
  318. package/src/data/tables/npcTexts.json.gz +0 -0
  319. package/src/data/tables/pageTexts.json.gz +0 -0
  320. package/src/data/tables/petAbilityTP.json.gz +0 -0
  321. package/src/data/tables/petGenericSpells.json.gz +0 -0
  322. package/src/data/tables/petLevelStats.json.gz +0 -0
  323. package/src/data/tables/playerbot-weightscales.json.gz +0 -0
  324. package/src/data/tables/pools.json.gz +0 -0
  325. package/src/data/tables/quests.json.gz +0 -0
  326. package/src/data/tables/races.json.gz +0 -0
  327. package/src/data/tables/reputationRanks.json.gz +0 -0
  328. package/src/data/tables/reputationRates.json.gz +0 -0
  329. package/src/data/tables/reputationSpillover.json.gz +0 -0
  330. package/src/data/tables/ridingSpells.json.gz +0 -0
  331. package/src/data/tables/scriptTexts.json.gz +0 -0
  332. package/src/data/tables/soundClipVolume.json.gz +0 -0
  333. package/src/data/tables/spellChain.json.gz +0 -0
  334. package/src/data/tables/spellTeleports.json.gz +0 -0
  335. package/src/data/tables/spells.json.gz +0 -0
  336. package/src/data/tables/talents.json.gz +0 -0
  337. package/src/data/tables/tavernTriggers.json.gz +0 -0
  338. package/src/data/tables/taxiNodes.json.gz +0 -0
  339. package/src/data/tables/taxiRoutes.json.gz +0 -0
  340. package/src/data/tables/trainers.json.gz +0 -0
  341. package/src/data/tables/transports.json.gz +0 -0
  342. package/src/data/tables/travelAnchors.json.gz +0 -0
  343. package/src/data/tables/travelEdges.json.gz +0 -0
  344. package/src/data/tables/unitConditions.json.gz +0 -0
  345. package/src/data/tables/vendors.json.gz +0 -0
  346. package/src/data/tables/weaponEnchantProcs.json.gz +0 -0
  347. package/src/data/tables/xp.json.gz +0 -0
  348. package/src/data/tables/zoneWeather.json.gz +0 -0
  349. package/src/data/tables/zones.json.gz +0 -0
  350. package/src/data/world-data.ts +56 -0
  351. package/src/data/zone-music-sets.json +2751 -0
  352. package/src/dev/etl/lib/loot-chance-sanity.mjs +180 -0
  353. package/src/domain/audioQuality.mjs +145 -0
  354. package/src/domain/bg/ab.ts +433 -0
  355. package/src/domain/bg/arenaComps.ts +644 -0
  356. package/src/domain/bg/arenaLab.ts +695 -0
  357. package/src/domain/bg/arenaPace.ts +31 -0
  358. package/src/domain/bg/arenaRating.ts +110 -0
  359. package/src/domain/bg/av.ts +758 -0
  360. package/src/domain/bg/avEnemyPush.ts +219 -0
  361. package/src/domain/bg/avField.ts +283 -0
  362. package/src/domain/bg/avInstanceNodes.ts +197 -0
  363. package/src/domain/bg/avInstanceScore.ts +123 -0
  364. package/src/domain/bg/avQuestItems.ts +55 -0
  365. package/src/domain/bg/avQuests.ts +210 -0
  366. package/src/domain/bg/avSession.ts +196 -0
  367. package/src/domain/bg/bgConsumes.ts +269 -0
  368. package/src/domain/bg/bgPrebuff.ts +153 -0
  369. package/src/domain/bg/bgRest.ts +91 -0
  370. package/src/domain/bg/bgRoster.ts +305 -0
  371. package/src/domain/bg/bgTier.ts +61 -0
  372. package/src/domain/bg/bgWorld.ts +862 -0
  373. package/src/domain/bg/match.ts +215 -0
  374. package/src/domain/bg/strategies.ts +552 -0
  375. package/src/domain/bg/types.ts +291 -0
  376. package/src/domain/bg/wsg.ts +264 -0
  377. package/src/domain/bots/actionProviderSpec.ts +38 -0
  378. package/src/domain/bots/autoConsumables.ts +285 -0
  379. package/src/domain/bots/betweenPullRest.ts +239 -0
  380. package/src/domain/bots/botGear.ts +248 -0
  381. package/src/domain/bots/botInventory.ts +486 -0
  382. package/src/domain/bots/botPet.ts +535 -0
  383. package/src/domain/bots/botPool.ts +291 -0
  384. package/src/domain/bots/botProfessions.ts +334 -0
  385. package/src/domain/bots/companionActionBar.ts +209 -0
  386. package/src/domain/bots/companionHireCost.ts +236 -0
  387. package/src/domain/bots/companionMissions.ts +582 -0
  388. package/src/domain/bots/companionPrep.ts +254 -0
  389. package/src/domain/bots/companionQuests.ts +270 -0
  390. package/src/domain/bots/companionRep.ts +75 -0
  391. package/src/domain/bots/companionRotation.ts +708 -0
  392. package/src/domain/bots/companionXp.ts +66 -0
  393. package/src/domain/bots/consumableShare.ts +811 -0
  394. package/src/domain/bots/dpsMate.ts +1072 -0
  395. package/src/domain/bots/groupCompReport.ts +132 -0
  396. package/src/domain/bots/healthstoneSupply.ts +198 -0
  397. package/src/domain/bots/outfitter.ts +1056 -0
  398. package/src/domain/bots/paladinPrePull.ts +231 -0
  399. package/src/domain/bots/partyRole.ts +40 -0
  400. package/src/domain/bots/playerbotEquipCache.ts +412 -0
  401. package/src/domain/bots/playerbotItemEligibility.ts +60 -0
  402. package/src/domain/bots/playerbotStatWeight.ts +853 -0
  403. package/src/domain/bots/playerbotUnavailableItems.ts +147 -0
  404. package/src/domain/bots/prePullBuffs.ts +219 -0
  405. package/src/domain/bots/prePullStealth.ts +82 -0
  406. package/src/domain/bots/runSupply.ts +251 -0
  407. package/src/domain/bots/runSupplyPrefs.ts +28 -0
  408. package/src/domain/bots/sendTradeGoods.ts +394 -0
  409. package/src/domain/bots/soulShardCap.ts +35 -0
  410. package/src/domain/bots/squad.ts +842 -0
  411. package/src/domain/bots/squadRun.ts +81 -0
  412. package/src/domain/character/actionbar.ts +346 -0
  413. package/src/domain/character/appearance.ts +165 -0
  414. package/src/domain/character/buildCombatant.ts +1726 -0
  415. package/src/domain/character/character.ts +356 -0
  416. package/src/domain/character/characterName.ts +43 -0
  417. package/src/domain/character/characterRatings.ts +424 -0
  418. package/src/domain/character/characterSpells.ts +282 -0
  419. package/src/domain/character/characterStats.ts +1559 -0
  420. package/src/domain/character/characterTalents.ts +398 -0
  421. package/src/domain/character/combatSkills.ts +994 -0
  422. package/src/domain/character/displayForm.ts +72 -0
  423. package/src/domain/character/formTalentAuras.ts +171 -0
  424. package/src/domain/character/forms.ts +499 -0
  425. package/src/domain/character/races.ts +40 -0
  426. package/src/domain/character/resurrectionSickness.ts +49 -0
  427. package/src/domain/character/spellbook.ts +169 -0
  428. package/src/domain/character/stanceBar.ts +354 -0
  429. package/src/domain/character/stancePrereq.ts +70 -0
  430. package/src/domain/character/talentBuilds.ts +63 -0
  431. package/src/domain/character/talentImport.ts +130 -0
  432. package/src/domain/character/talentLink.ts +123 -0
  433. package/src/domain/character/talents.ts +201 -0
  434. package/src/domain/core/conditionSnapshot.ts +92 -0
  435. package/src/domain/core/hover.ts +14 -0
  436. package/src/domain/core/ids.ts +58 -0
  437. package/src/domain/core/internalNameTags.ts +5 -0
  438. package/src/domain/core/runtimeDataPatches.ts +357 -0
  439. package/src/domain/creature/aiTargeting.ts +588 -0
  440. package/src/domain/creature/combatCondition.ts +157 -0
  441. package/src/domain/creature/creatureAggro.ts +804 -0
  442. package/src/domain/creature/creatureAi.ts +206 -0
  443. package/src/domain/creature/creatureAiConstants.ts +661 -0
  444. package/src/domain/creature/creatureCastList.ts +1007 -0
  445. package/src/domain/creature/creatureEventAi.ts +1409 -0
  446. package/src/domain/creature/creatureGroups.ts +144 -0
  447. package/src/domain/creature/creatureLinking.ts +333 -0
  448. package/src/domain/creature/creatureScaling.ts +320 -0
  449. package/src/domain/creature/creatureTrigger.ts +53 -0
  450. package/src/domain/creature/creatureWander.ts +74 -0
  451. package/src/domain/creature/eventAiTexts.ts +145 -0
  452. package/src/domain/creature/faction.ts +287 -0
  453. package/src/domain/creature/forcedReaction.ts +48 -0
  454. package/src/domain/creature/outdoorHuntable.ts +74 -0
  455. package/src/domain/creature/patrolPacks.ts +210 -0
  456. package/src/domain/creature/reputation.ts +967 -0
  457. package/src/domain/creature/unitFlags.ts +74 -0
  458. package/src/domain/creature/worldBosses.ts +393 -0
  459. package/src/domain/dungeonMusic.mjs +48 -0
  460. package/src/domain/economy/auction.ts +282 -0
  461. package/src/domain/economy/auctionBot.ts +1060 -0
  462. package/src/domain/economy/honorPrice.ts +100 -0
  463. package/src/domain/economy/mail.ts +958 -0
  464. package/src/domain/economy/money.ts +42 -0
  465. package/src/domain/economy/syntheticClassTrainers.ts +62 -0
  466. package/src/domain/economy/trading.ts +296 -0
  467. package/src/domain/economy/vendorSellAll.ts +252 -0
  468. package/src/domain/economy/vendorStock.ts +146 -0
  469. package/src/domain/items/ammo.ts +817 -0
  470. package/src/domain/items/bank.ts +162 -0
  471. package/src/domain/items/consumableFamily.ts +205 -0
  472. package/src/domain/items/craftUpgrades.ts +83 -0
  473. package/src/domain/items/createItem.ts +93 -0
  474. package/src/domain/items/durability.ts +581 -0
  475. package/src/domain/items/equipment.ts +441 -0
  476. package/src/domain/items/equipmentHelper.ts +767 -0
  477. package/src/domain/items/inventory.ts +561 -0
  478. package/src/domain/items/itemBonding.ts +93 -0
  479. package/src/domain/items/itemContainer.ts +245 -0
  480. package/src/domain/items/itemDisplayName.ts +33 -0
  481. package/src/domain/items/itemInstance.ts +181 -0
  482. package/src/domain/items/itemRandomEnchantOutcomes.ts +315 -0
  483. package/src/domain/items/itemRandomProps.ts +77 -0
  484. package/src/domain/items/itemRestrictions.ts +329 -0
  485. package/src/domain/items/itemTeaches.ts +82 -0
  486. package/src/domain/items/itemUseCooldown.ts +52 -0
  487. package/src/domain/items/itemUseTarget.ts +90 -0
  488. package/src/domain/items/junkCategories.ts +174 -0
  489. package/src/domain/items/junkFilter.ts +368 -0
  490. package/src/domain/items/keyring.ts +131 -0
  491. package/src/domain/items/loot.ts +221 -0
  492. package/src/domain/items/mounts.ts +190 -0
  493. package/src/domain/items/onUsePayload.ts +102 -0
  494. package/src/domain/items/pendingLoot.ts +107 -0
  495. package/src/domain/items/relic.ts +105 -0
  496. package/src/domain/items/rocketBoots.ts +36 -0
  497. package/src/domain/items/sortContainer.ts +139 -0
  498. package/src/domain/items/spareContainers.ts +71 -0
  499. package/src/domain/items/spellReagentIndex.ts +131 -0
  500. package/src/domain/items/staleConsumables.ts +66 -0
  501. package/src/domain/items/weaponSpender.ts +110 -0
  502. package/src/domain/listDungeonIds.mjs +28 -0
  503. package/src/domain/macro/compose.ts +239 -0
  504. package/src/domain/macro/conditions.ts +260 -0
  505. package/src/domain/macro/creatureTypes.ts +40 -0
  506. package/src/domain/macro/describe.ts +169 -0
  507. package/src/domain/macro/icon.ts +60 -0
  508. package/src/domain/macro/index.ts +20 -0
  509. package/src/domain/macro/normalize.ts +112 -0
  510. package/src/domain/macro/parse.ts +316 -0
  511. package/src/domain/macro/plan.ts +288 -0
  512. package/src/domain/macro/rotation.ts +590 -0
  513. package/src/domain/macro/rotationEnv.ts +173 -0
  514. package/src/domain/macro/slots.ts +101 -0
  515. package/src/domain/macro/snapshot.ts +114 -0
  516. package/src/domain/macro/templates.ts +446 -0
  517. package/src/domain/macro/types.ts +70 -0
  518. package/src/domain/macro/units.ts +93 -0
  519. package/src/domain/macro/weaponTypes.ts +83 -0
  520. package/src/domain/nav/cityTeleports.ts +10 -0
  521. package/src/domain/nav/mounting.ts +113 -0
  522. package/src/domain/nav/nav.ts +43 -0
  523. package/src/domain/nav/navCollision.ts +606 -0
  524. package/src/domain/nav/navOutdoors.ts +91 -0
  525. package/src/domain/nav/navRegistry.ts +179 -0
  526. package/src/domain/nav/navRoomGraph.ts +302 -0
  527. package/src/domain/nav/navTypes.ts +43 -0
  528. package/src/domain/nav/navmesh.ts +1715 -0
  529. package/src/domain/nav/speed.ts +200 -0
  530. package/src/domain/nav/storey.ts +10 -0
  531. package/src/domain/nav/travel.ts +725 -0
  532. package/src/domain/nav/travelTypes.ts +75 -0
  533. package/src/domain/pet/pet.ts +1150 -0
  534. package/src/domain/pet/petActionBar.ts +180 -0
  535. package/src/domain/pet/petSpellbook.ts +39 -0
  536. package/src/domain/pet/petStable.ts +81 -0
  537. package/src/domain/professions/autoDisenchant.ts +236 -0
  538. package/src/domain/professions/characterEnchants.ts +176 -0
  539. package/src/domain/professions/craftCooldown.ts +52 -0
  540. package/src/domain/professions/enchantApply.ts +62 -0
  541. package/src/domain/professions/enchantTargets.ts +114 -0
  542. package/src/domain/professions/enchanting.ts +236 -0
  543. package/src/domain/professions/gatherFilter.ts +149 -0
  544. package/src/domain/professions/gearEnchant.ts +137 -0
  545. package/src/domain/professions/heldTools.ts +29 -0
  546. package/src/domain/professions/learnAllRecipes.ts +146 -0
  547. package/src/domain/professions/miningHarvest.ts +69 -0
  548. package/src/domain/professions/partyDisenchant.ts +294 -0
  549. package/src/domain/professions/partyWork.ts +83 -0
  550. package/src/domain/professions/pickpocket.ts +172 -0
  551. package/src/domain/professions/poisons.ts +240 -0
  552. package/src/domain/professions/professions.ts +677 -0
  553. package/src/domain/professions/rareNodes.ts +45 -0
  554. package/src/domain/professions/skinLoot.ts +27 -0
  555. package/src/domain/professions/vendorReagents.ts +152 -0
  556. package/src/domain/professions/weaponImbueHand.ts +171 -0
  557. package/src/domain/questFarmFilter.mjs +42 -0
  558. package/src/domain/quests/doQuest.ts +1204 -0
  559. package/src/domain/quests/gossip.ts +308 -0
  560. package/src/domain/quests/gossipTalk.ts +298 -0
  561. package/src/domain/quests/interpreter/canComplete.ts +150 -0
  562. package/src/domain/quests/interpreter/claim.ts +137 -0
  563. package/src/domain/quests/interpreter/dbscript/handlers.ts +1371 -0
  564. package/src/domain/quests/interpreter/dbscript/handlersBatch.ts +1251 -0
  565. package/src/domain/quests/interpreter/dbscript/hooks.ts +331 -0
  566. package/src/domain/quests/interpreter/dbscript/index.ts +212 -0
  567. package/src/domain/quests/interpreter/dbscript/opcodes.ts +257 -0
  568. package/src/domain/quests/interpreter/dbscript/registry.ts +1194 -0
  569. package/src/domain/quests/interpreter/dbscript/registryAreatriggerScripts.ts +539 -0
  570. package/src/domain/quests/interpreter/dbscript/registryAshenvale.ts +553 -0
  571. package/src/domain/quests/interpreter/dbscript/registryAzshara.ts +272 -0
  572. package/src/domain/quests/interpreter/dbscript/registryDarkshore.ts +637 -0
  573. package/src/domain/quests/interpreter/dbscript/registryDesolace.ts +501 -0
  574. package/src/domain/quests/interpreter/dbscript/registryDustwallowMarsh.ts +735 -0
  575. package/src/domain/quests/interpreter/dbscript/registryFelwood.ts +493 -0
  576. package/src/domain/quests/interpreter/dbscript/registryFeralas.ts +485 -0
  577. package/src/domain/quests/interpreter/dbscript/registryGoScripts.ts +203 -0
  578. package/src/domain/quests/interpreter/dbscript/registryMoonglade.ts +288 -0
  579. package/src/domain/quests/interpreter/dbscript/registryNpcProfessions.ts +230 -0
  580. package/src/domain/quests/interpreter/dbscript/registryNpcsSpecial.ts +545 -0
  581. package/src/domain/quests/interpreter/dbscript/registryRazorfenDowns.ts +293 -0
  582. package/src/domain/quests/interpreter/dbscript/registryRazorfenKraul.ts +221 -0
  583. package/src/domain/quests/interpreter/dbscript/registryRedridge.ts +112 -0
  584. package/src/domain/quests/interpreter/dbscript/registrySearingGorge.ts +125 -0
  585. package/src/domain/quests/interpreter/dbscript/registrySiblings.ts +51 -0
  586. package/src/domain/quests/interpreter/dbscript/registrySilverpineForest.ts +172 -0
  587. package/src/domain/quests/interpreter/dbscript/registryStonetalonMountains.ts +180 -0
  588. package/src/domain/quests/interpreter/dbscript/registryStormwindCity.ts +369 -0
  589. package/src/domain/quests/interpreter/dbscript/registryStranglethornVale.ts +115 -0
  590. package/src/domain/quests/interpreter/dbscript/registryStratholme.ts +146 -0
  591. package/src/domain/quests/interpreter/dbscript/registrySunkenTemple.ts +149 -0
  592. package/src/domain/quests/interpreter/dbscript/registrySwampOfSorrows.ts +163 -0
  593. package/src/domain/quests/interpreter/dbscript/registryTanaris.ts +318 -0
  594. package/src/domain/quests/interpreter/dbscript/registryTeldrassil.ts +120 -0
  595. package/src/domain/quests/interpreter/dbscript/registryTheBarrens.ts +603 -0
  596. package/src/domain/quests/interpreter/dbscript/registryThousandNeedles.ts +499 -0
  597. package/src/domain/quests/interpreter/dbscript/registryTirisfalGlades.ts +222 -0
  598. package/src/domain/quests/interpreter/dbscript/registryUngoroCrater.ts +291 -0
  599. package/src/domain/quests/interpreter/dbscript/registryWarEffort.ts +773 -0
  600. package/src/domain/quests/interpreter/dbscript/registryWesternPlaguelands.ts +206 -0
  601. package/src/domain/quests/interpreter/dbscript/registryWestfall.ts +190 -0
  602. package/src/domain/quests/interpreter/dbscript/registryWetlands.ts +186 -0
  603. package/src/domain/quests/interpreter/dbscript/registryWinterspring.ts +235 -0
  604. package/src/domain/quests/interpreter/dbscript/types.ts +817 -0
  605. package/src/domain/quests/interpreter/dbscript/vm.ts +171 -0
  606. package/src/domain/quests/interpreter/diff.ts +349 -0
  607. package/src/domain/quests/interpreter/fixtures/oracle-questlog-qi2.json +2417 -0
  608. package/src/domain/quests/interpreter/flags.ts +101 -0
  609. package/src/domain/quests/interpreter/index.ts +113 -0
  610. package/src/domain/quests/interpreter/live.ts +178 -0
  611. package/src/domain/quests/interpreter/types.ts +81 -0
  612. package/src/domain/quests/interpreter/updaters.ts +412 -0
  613. package/src/domain/quests/questCorpseUse.ts +155 -0
  614. package/src/domain/quests/questDialog.ts +43 -0
  615. package/src/domain/quests/questFight.ts +34 -0
  616. package/src/domain/quests/questObjectiveLabel.ts +39 -0
  617. package/src/domain/quests/questRewardChoice.ts +85 -0
  618. package/src/domain/quests/questScriptCredit.ts +26 -0
  619. package/src/domain/quests/questScriptItemUse.ts +74 -0
  620. package/src/domain/quests/questTameRods.ts +79 -0
  621. package/src/domain/quests/questlog.ts +1545 -0
  622. package/src/domain/quests/talkDeliver.ts +170 -0
  623. package/src/domain/raid/globalActionBar.ts +195 -0
  624. package/src/domain/raid/instanceRoster.ts +194 -0
  625. package/src/domain/raid/partySummon.ts +198 -0
  626. package/src/domain/raid/partyVault.ts +320 -0
  627. package/src/domain/raid/raidBossAbilities.ts +268 -0
  628. package/src/domain/raid/raidComp.ts +335 -0
  629. package/src/domain/raid/raidCustomRows.ts +453 -0
  630. package/src/domain/raid/raidEncounterGear.ts +298 -0
  631. package/src/domain/raid/raidEncounterTactics.ts +412 -0
  632. package/src/domain/raid/raidPlan.ts +509 -0
  633. package/src/domain/raid/raidPlanEdits.ts +98 -0
  634. package/src/domain/raid/raidPlanPacks.ts +480 -0
  635. package/src/domain/raid/raidRecovery.ts +183 -0
  636. package/src/domain/raid/raidRosterTemplate.ts +138 -0
  637. package/src/domain/rotation/auraSide.ts +68 -0
  638. package/src/domain/rotation/build.ts +3031 -0
  639. package/src/domain/rotation/classes/druid.ts +58 -0
  640. package/src/domain/rotation/classes/hooks.ts +59 -0
  641. package/src/domain/rotation/classes/hunter.ts +156 -0
  642. package/src/domain/rotation/classes/index.ts +104 -0
  643. package/src/domain/rotation/classes/mage.ts +99 -0
  644. package/src/domain/rotation/classes/paladin.ts +296 -0
  645. package/src/domain/rotation/classes/priest.ts +94 -0
  646. package/src/domain/rotation/classes/pullOpener.ts +92 -0
  647. package/src/domain/rotation/classes/rogue.ts +69 -0
  648. package/src/domain/rotation/classes/shaman.ts +29 -0
  649. package/src/domain/rotation/classes/supportSpec.ts +94 -0
  650. package/src/domain/rotation/classes/warlock.ts +81 -0
  651. package/src/domain/rotation/classes/warrior.ts +402 -0
  652. package/src/domain/rotation/classify.ts +1381 -0
  653. package/src/domain/rotation/condMetricPicker.ts +379 -0
  654. package/src/domain/rotation/consumables.ts +242 -0
  655. package/src/domain/rotation/evaluate.ts +1115 -0
  656. package/src/domain/rotation/finisherLadder.ts +145 -0
  657. package/src/domain/rotation/invent.ts +938 -0
  658. package/src/domain/rotation/killOrder.ts +30 -0
  659. package/src/domain/rotation/openerForm.ts +22 -0
  660. package/src/domain/rotation/paladinBuffs.ts +568 -0
  661. package/src/domain/rotation/prebuff.ts +283 -0
  662. package/src/domain/rotation/raidRowTuning.ts +34 -0
  663. package/src/domain/rotation/rotation.ts +11 -0
  664. package/src/domain/rotation/rotationProfiles.ts +180 -0
  665. package/src/domain/rotation/rotationSuggestions.ts +851 -0
  666. package/src/domain/rotation/ruleAfterChain.ts +49 -0
  667. package/src/domain/rotation/spec.ts +70 -0
  668. package/src/domain/rotation/spellPicker.ts +140 -0
  669. package/src/domain/rotation/stand.ts +143 -0
  670. package/src/domain/rotation/types.ts +718 -0
  671. package/src/domain/run/battlegroundRun.ts +111 -0
  672. package/src/domain/run/canEnterContext.ts +32 -0
  673. package/src/domain/run/death.ts +561 -0
  674. package/src/domain/run/deathRecovery.ts +289 -0
  675. package/src/domain/run/duel.ts +141 -0
  676. package/src/domain/run/dungeonRun.ts +344 -0
  677. package/src/domain/run/floorRest.ts +159 -0
  678. package/src/domain/run/honor.ts +557 -0
  679. package/src/domain/run/honorWeek.ts +180 -0
  680. package/src/domain/run/whenMap.ts +977 -0
  681. package/src/domain/scriptdev/arathiHighlands.ts +445 -0
  682. package/src/domain/scriptdev/ashenvale.ts +635 -0
  683. package/src/domain/scriptdev/ashenvaleHelpers.ts +1215 -0
  684. package/src/domain/scriptdev/azshara.ts +892 -0
  685. package/src/domain/scriptdev/blackfathomDeeps.ts +212 -0
  686. package/src/domain/scriptdev/blackrockDepths.ts +1434 -0
  687. package/src/domain/scriptdev/blackrockSpire.ts +730 -0
  688. package/src/domain/scriptdev/blackwingLair.ts +521 -0
  689. package/src/domain/scriptdev/burningSteppes.ts +62 -0
  690. package/src/domain/scriptdev/classFilter.ts +43 -0
  691. package/src/domain/scriptdev/darkshore.ts +817 -0
  692. package/src/domain/scriptdev/darkshoreHelpers.ts +853 -0
  693. package/src/domain/scriptdev/deadmines.ts +331 -0
  694. package/src/domain/scriptdev/desolace.ts +1239 -0
  695. package/src/domain/scriptdev/direMaul.ts +859 -0
  696. package/src/domain/scriptdev/durotar.ts +532 -0
  697. package/src/domain/scriptdev/dustwallowMarsh.ts +758 -0
  698. package/src/domain/scriptdev/dustwallowMarshHelpers.ts +994 -0
  699. package/src/domain/scriptdev/easternPlaguelands.ts +21 -0
  700. package/src/domain/scriptdev/felwood.ts +1055 -0
  701. package/src/domain/scriptdev/feralas.ts +571 -0
  702. package/src/domain/scriptdev/feralasHelpers.ts +838 -0
  703. package/src/domain/scriptdev/gnomeregan.ts +466 -0
  704. package/src/domain/scriptdev/guards.ts +616 -0
  705. package/src/domain/scriptdev/hinterlands.ts +693 -0
  706. package/src/domain/scriptdev/host.ts +1134 -0
  707. package/src/domain/scriptdev/index.ts +123 -0
  708. package/src/domain/scriptdev/instanceRaids.ts +136 -0
  709. package/src/domain/scriptdev/itemScripts.ts +37 -0
  710. package/src/domain/scriptdev/lochModan.ts +283 -0
  711. package/src/domain/scriptdev/maraudon.ts +232 -0
  712. package/src/domain/scriptdev/moltenCore.ts +831 -0
  713. package/src/domain/scriptdev/moonglade.ts +659 -0
  714. package/src/domain/scriptdev/moongladeHelpers.ts +933 -0
  715. package/src/domain/scriptdev/naxxramas.ts +577 -0
  716. package/src/domain/scriptdev/naxxramasConstruct.ts +364 -0
  717. package/src/domain/scriptdev/naxxramasFrostwyrm.ts +378 -0
  718. package/src/domain/scriptdev/naxxramasMilitary.ts +276 -0
  719. package/src/domain/scriptdev/npcProfessions.ts +1025 -0
  720. package/src/domain/scriptdev/npcsSpecial.ts +1379 -0
  721. package/src/domain/scriptdev/onyxiasLair.ts +313 -0
  722. package/src/domain/scriptdev/orgrimmar.ts +147 -0
  723. package/src/domain/scriptdev/professionSpecConsumer.ts +481 -0
  724. package/src/domain/scriptdev/razorfenDowns.ts +468 -0
  725. package/src/domain/scriptdev/razorfenKraul.ts +410 -0
  726. package/src/domain/scriptdev/redridgeMountains.ts +163 -0
  727. package/src/domain/scriptdev/registry.ts +945 -0
  728. package/src/domain/scriptdev/ruinsOfAhnqiraj.ts +356 -0
  729. package/src/domain/scriptdev/scarletMonastery.ts +713 -0
  730. package/src/domain/scriptdev/scholomance.ts +682 -0
  731. package/src/domain/scriptdev/searingGorge.ts +295 -0
  732. package/src/domain/scriptdev/shadowfangKeep.ts +181 -0
  733. package/src/domain/scriptdev/silithus.ts +71 -0
  734. package/src/domain/scriptdev/silverpineForest.ts +662 -0
  735. package/src/domain/scriptdev/stonetalonMountains.ts +493 -0
  736. package/src/domain/scriptdev/stormwindCity.ts +1192 -0
  737. package/src/domain/scriptdev/stranglethornVale.ts +402 -0
  738. package/src/domain/scriptdev/stratholme.ts +650 -0
  739. package/src/domain/scriptdev/stratholmeScripts.ts +505 -0
  740. package/src/domain/scriptdev/sunkenTemple.ts +736 -0
  741. package/src/domain/scriptdev/swampOfSorrows.ts +22 -0
  742. package/src/domain/scriptdev/tanaris.ts +391 -0
  743. package/src/domain/scriptdev/tanarisHelpers.ts +661 -0
  744. package/src/domain/scriptdev/teldrassil.ts +143 -0
  745. package/src/domain/scriptdev/templeOfAhnqiraj.ts +1013 -0
  746. package/src/domain/scriptdev/theBarrens.ts +709 -0
  747. package/src/domain/scriptdev/theBarrensHelpers.ts +1026 -0
  748. package/src/domain/scriptdev/thousandNeedles.ts +426 -0
  749. package/src/domain/scriptdev/thousandNeedlesHelpers.ts +566 -0
  750. package/src/domain/scriptdev/tirisfalGlades.ts +533 -0
  751. package/src/domain/scriptdev/types.ts +1026 -0
  752. package/src/domain/scriptdev/uldaman.ts +437 -0
  753. package/src/domain/scriptdev/ungoroCrater.ts +549 -0
  754. package/src/domain/scriptdev/ungoroCraterHelpers.ts +1006 -0
  755. package/src/domain/scriptdev/wailingCaverns.ts +700 -0
  756. package/src/domain/scriptdev/warEffort.ts +371 -0
  757. package/src/domain/scriptdev/westernPlaguelands.ts +952 -0
  758. package/src/domain/scriptdev/westfall.ts +767 -0
  759. package/src/domain/scriptdev/wetlands.ts +660 -0
  760. package/src/domain/scriptdev/winterspring.ts +482 -0
  761. package/src/domain/scriptdev/winterspringHelpers.ts +801 -0
  762. package/src/domain/scriptdev/worldBosses.ts +605 -0
  763. package/src/domain/scriptdev/zulfarrak.ts +478 -0
  764. package/src/domain/scriptdev/zulgurub.ts +1152 -0
  765. package/src/domain/sim/dummySim.ts +408 -0
  766. package/src/domain/sim/targetDummy.ts +29 -0
  767. package/src/domain/social/guild.ts +148 -0
  768. package/src/domain/spells/areaAuraMembership.ts +93 -0
  769. package/src/domain/spells/breakableStunCc.ts +166 -0
  770. package/src/domain/spells/cancelAura.ts +80 -0
  771. package/src/domain/spells/diminishing.ts +167 -0
  772. package/src/domain/spells/dispel.ts +149 -0
  773. package/src/domain/spells/dispelMechanic.ts +105 -0
  774. package/src/domain/spells/equipSpells.ts +247 -0
  775. package/src/domain/spells/learnOnTargets.ts +260 -0
  776. package/src/domain/spells/mergeSchoolImmunityFromRaw.ts +66 -0
  777. package/src/domain/spells/resurrectSpell.ts +124 -0
  778. package/src/domain/spells/spellPushback.ts +39 -0
  779. package/src/domain/spells/spellReflect.ts +43 -0
  780. package/src/domain/spells/stealth.ts +183 -0
  781. package/src/domain/spells/totem.ts +394 -0
  782. package/src/domain/spells/worldCast.ts +187 -0
  783. package/src/domain/world/areaTriggerLocks.ts +177 -0
  784. package/src/domain/world/dungeonMusic.ts +108 -0
  785. package/src/domain/world/gameEvents.ts +119 -0
  786. package/src/domain/world/gameObjects.ts +1030 -0
  787. package/src/domain/world/goEventFire.ts +234 -0
  788. package/src/domain/world/goMoTransport.ts +74 -0
  789. package/src/domain/world/goTransport.ts +81 -0
  790. package/src/domain/world/hazards.ts +459 -0
  791. package/src/domain/world/openLock.ts +398 -0
  792. package/src/domain/world/pools.ts +470 -0
  793. package/src/domain/world/restArea.ts +37 -0
  794. package/src/domain/world/weather.ts +201 -0
  795. package/src/domain/zoneAmbience.mjs +87 -0
  796. package/src/domain/zoneMusic.mjs +62 -0
  797. package/src/domain/zoneMusicQueue.mjs +7 -0
  798. package/src/engine/combat/activeSeal.ts +39 -0
  799. package/src/engine/combat/aiTargetResolve.ts +64 -0
  800. package/src/engine/combat/aoeCast.ts +289 -0
  801. package/src/engine/combat/applyConsumeAura.ts +74 -0
  802. package/src/engine/combat/applyDemonicSacrifice.ts +70 -0
  803. package/src/engine/combat/applyDispelMechanic.ts +30 -0
  804. package/src/engine/combat/applyHealEffect.ts +301 -0
  805. package/src/engine/combat/applySchoolAbsorb.ts +114 -0
  806. package/src/engine/combat/applySpell.ts +3169 -0
  807. package/src/engine/combat/applySpellLate.ts +2026 -0
  808. package/src/engine/combat/applySpellLateBag.ts +144 -0
  809. package/src/engine/combat/areaAura.ts +283 -0
  810. package/src/engine/combat/attackTable.ts +508 -0
  811. package/src/engine/combat/battleRez.ts +87 -0
  812. package/src/engine/combat/castAction.ts +36 -0
  813. package/src/engine/combat/castTargetGates.ts +75 -0
  814. package/src/engine/combat/castableKit.ts +68 -0
  815. package/src/engine/combat/ccMirrorUpgrade.ts +39 -0
  816. package/src/engine/combat/chainFan.ts +210 -0
  817. package/src/engine/combat/channel.ts +423 -0
  818. package/src/engine/combat/chaseCloseness.ts +95 -0
  819. package/src/engine/combat/cmangosCastGates.ts +129 -0
  820. package/src/engine/combat/combatFlag.ts +77 -0
  821. package/src/engine/combat/combatGeometry.ts +1329 -0
  822. package/src/engine/combat/combustion.ts +68 -0
  823. package/src/engine/combat/context.ts +730 -0
  824. package/src/engine/combat/createSession.ts +1165 -0
  825. package/src/engine/combat/creatureLeash.ts +38 -0
  826. package/src/engine/combat/crowdControl.ts +1259 -0
  827. package/src/engine/combat/dispelDuty.ts +54 -0
  828. package/src/engine/combat/dispelReflect.ts +609 -0
  829. package/src/engine/combat/displaceFloor.ts +113 -0
  830. package/src/engine/combat/duelBounds.ts +47 -0
  831. package/src/engine/combat/equippedItemGate.ts +297 -0
  832. package/src/engine/combat/evadeLifecycle.ts +63 -0
  833. package/src/engine/combat/eventAi.ts +2220 -0
  834. package/src/engine/combat/facing.ts +74 -0
  835. package/src/engine/combat/fearFlee.ts +137 -0
  836. package/src/engine/combat/fighterFactory.ts +1144 -0
  837. package/src/engine/combat/forceDespawnAction.ts +51 -0
  838. package/src/engine/combat/formulas.ts +807 -0
  839. package/src/engine/combat/friendlyFan.ts +55 -0
  840. package/src/engine/combat/groundPoolTick.ts +210 -0
  841. package/src/engine/combat/hazards.ts +114 -0
  842. package/src/engine/combat/healTargets.ts +226 -0
  843. package/src/engine/combat/heartbeatResist.ts +49 -0
  844. package/src/engine/combat/hostileItemUse.ts +75 -0
  845. package/src/engine/combat/interruptAuras.ts +66 -0
  846. package/src/engine/combat/itemRescue.ts +49 -0
  847. package/src/engine/combat/itemUse.ts +260 -0
  848. package/src/engine/combat/kitById.ts +23 -0
  849. package/src/engine/combat/knockBack.ts +201 -0
  850. package/src/engine/combat/leap.ts +89 -0
  851. package/src/engine/combat/mitigation.ts +587 -0
  852. package/src/engine/combat/modTaunt.ts +43 -0
  853. package/src/engine/combat/motionClaim.ts +268 -0
  854. package/src/engine/combat/motionGate.ts +150 -0
  855. package/src/engine/combat/motionGenerator.ts +27 -0
  856. package/src/engine/combat/navSight.ts +138 -0
  857. package/src/engine/combat/perfSink.ts +28 -0
  858. package/src/engine/combat/periodicDue.ts +55 -0
  859. package/src/engine/combat/petAuras.ts +86 -0
  860. package/src/engine/combat/pickAction.ts +3449 -0
  861. package/src/engine/combat/powerForm.ts +735 -0
  862. package/src/engine/combat/powerPools.ts +49 -0
  863. package/src/engine/combat/predicates.ts +538 -0
  864. package/src/engine/combat/procsDamage.ts +1823 -0
  865. package/src/engine/combat/reactiveGates.ts +135 -0
  866. package/src/engine/combat/roles.ts +389 -0
  867. package/src/engine/combat/rotationSubject.ts +78 -0
  868. package/src/engine/combat/ruleAfter.ts +106 -0
  869. package/src/engine/combat/seedDisplayAuras.ts +203 -0
  870. package/src/engine/combat/seedOocEffects.ts +317 -0
  871. package/src/engine/combat/seedSchoolImmunities.ts +45 -0
  872. package/src/engine/combat/session-types.ts +458 -0
  873. package/src/engine/combat/sessionApi.ts +1785 -0
  874. package/src/engine/combat/sessionCharge.ts +124 -0
  875. package/src/engine/combat/sessionChase.ts +869 -0
  876. package/src/engine/combat/sessionDeaths.ts +469 -0
  877. package/src/engine/combat/sessionFocus.ts +506 -0
  878. package/src/engine/combat/sessionFollow.ts +167 -0
  879. package/src/engine/combat/sessionGeometry.ts +188 -0
  880. package/src/engine/combat/sessionHome.ts +75 -0
  881. package/src/engine/combat/sessionInject.ts +719 -0
  882. package/src/engine/combat/sessionMechanicMove.ts +555 -0
  883. package/src/engine/combat/sessionMotion.ts +1553 -0
  884. package/src/engine/combat/sessionMotionKit.ts +247 -0
  885. package/src/engine/combat/sessionMoveIntent.ts +49 -0
  886. package/src/engine/combat/sessionMoveOrder.ts +200 -0
  887. package/src/engine/combat/sessionPathTo.ts +657 -0
  888. package/src/engine/combat/sessionPools.ts +688 -0
  889. package/src/engine/combat/sessionSteer.ts +144 -0
  890. package/src/engine/combat/sessionStepBack.ts +151 -0
  891. package/src/engine/combat/sessionStepWrap.ts +116 -0
  892. package/src/engine/combat/sessionView.ts +442 -0
  893. package/src/engine/combat/simEffects.ts +32 -0
  894. package/src/engine/combat/simFacade.ts +620 -0
  895. package/src/engine/combat/simPump.ts +264 -0
  896. package/src/engine/combat/simSnapshot.ts +165 -0
  897. package/src/engine/combat/simThreadPool.ts +35 -0
  898. package/src/engine/combat/skeramImages.ts +57 -0
  899. package/src/engine/combat/slowUpdate.ts +178 -0
  900. package/src/engine/combat/snapToFloor.ts +42 -0
  901. package/src/engine/combat/speedAura.ts +29 -0
  902. package/src/engine/combat/spellReagents.ts +71 -0
  903. package/src/engine/combat/stealthDetect.ts +93 -0
  904. package/src/engine/combat/stepBackMotion.ts +171 -0
  905. package/src/engine/combat/stepSession.ts +3102 -0
  906. package/src/engine/combat/summonCast.ts +243 -0
  907. package/src/engine/combat/thaddiusPolarity.ts +97 -0
  908. package/src/engine/combat/threat.ts +388 -0
  909. package/src/engine/combat/threatOps.ts +272 -0
  910. package/src/engine/combat/tickWorkerBody.ts +211 -0
  911. package/src/engine/combat/totemAuras.ts +122 -0
  912. package/src/engine/combat/triggerPayload.ts +42 -0
  913. package/src/engine/combat/triggeredCompanion.ts +63 -0
  914. package/src/engine/combat/types.ts +1897 -0
  915. package/src/engine/combat/useObjectTrap.ts +140 -0
  916. package/src/engine/combat/walkSurface.ts +54 -0
  917. package/src/engine/combat/weapon.ts +1248 -0
  918. package/src/engine/combat/worldFrame.ts +43 -0
  919. package/src/engine/combat.solo-golden.json +2242 -0
  920. package/src/engine/combat.ts +84 -0
  921. package/src/engine/core/detMath.ts +289 -0
  922. package/src/engine/core/events.ts +357 -0
  923. package/src/engine/core/rng.ts +36 -0
  924. package/src/engine/game/actionBars.ts +124 -0
  925. package/src/engine/game/activateObjectFromSpell.ts +320 -0
  926. package/src/engine/game/applyActiveObjectFromDbScript.ts +98 -0
  927. package/src/engine/game/applyDeathAndSpellScripts.ts +268 -0
  928. package/src/engine/game/applyDoorFromDbScript.ts +388 -0
  929. package/src/engine/game/applyEscortFromDbScript.ts +290 -0
  930. package/src/engine/game/applyGoSpellcasterCast.ts +40 -0
  931. package/src/engine/game/applyHoverFromDbScript.ts +110 -0
  932. package/src/engine/game/applyOocResurrect.ts +71 -0
  933. package/src/engine/game/applySpellDurability.ts +115 -0
  934. package/src/engine/game/applyTailFromDbScript.ts +478 -0
  935. package/src/engine/game/applyTerminateFromDbScript.ts +95 -0
  936. package/src/engine/game/auctionMail.ts +269 -0
  937. package/src/engine/game/bagOverflow.ts +130 -0
  938. package/src/engine/game/bank.ts +110 -0
  939. package/src/engine/game/barAutofill.ts +26 -0
  940. package/src/engine/game/battleground.ts +369 -0
  941. package/src/engine/game/combatEnemySpec.ts +216 -0
  942. package/src/engine/game/combatObjectTraps.ts +167 -0
  943. package/src/engine/game/combatSeed.ts +320 -0
  944. package/src/engine/game/combatSession.ts +3241 -0
  945. package/src/engine/game/combatSessionClock.ts +62 -0
  946. package/src/engine/game/companionQuestActions.ts +242 -0
  947. package/src/engine/game/companionTraining.ts +40 -0
  948. package/src/engine/game/consumables.ts +624 -0
  949. package/src/engine/game/context.ts +53 -0
  950. package/src/engine/game/createHealthstone.ts +45 -0
  951. package/src/engine/game/deathFlow.ts +127 -0
  952. package/src/engine/game/devGrant.ts +117 -0
  953. package/src/engine/game/dynamicObject.ts +365 -0
  954. package/src/engine/game/enchantItem.ts +177 -0
  955. package/src/engine/game/equipmentHelper.ts +806 -0
  956. package/src/engine/game/factionState.ts +108 -0
  957. package/src/engine/game/garmentsOoc.ts +69 -0
  958. package/src/engine/game/globalActionBar.ts +179 -0
  959. package/src/engine/game/goInteractQuestCredit.ts +51 -0
  960. package/src/engine/game/goMoTransportUse.ts +61 -0
  961. package/src/engine/game/goTransportUse.ts +60 -0
  962. package/src/engine/game/healthstoneUpkeep.ts +59 -0
  963. package/src/engine/game/killSkin.ts +124 -0
  964. package/src/engine/game/learnFromItem.ts +118 -0
  965. package/src/engine/game/loadReconcile.ts +551 -0
  966. package/src/engine/game/loot.ts +294 -0
  967. package/src/engine/game/macros.ts +125 -0
  968. package/src/engine/game/mail.ts +293 -0
  969. package/src/engine/game/meetingStoneQueue.ts +75 -0
  970. package/src/engine/game/meetingStoneUse.ts +54 -0
  971. package/src/engine/game/newGameState.ts +363 -0
  972. package/src/engine/game/oocAuraInterrupt.ts +92 -0
  973. package/src/engine/game/oocCast.ts +241 -0
  974. package/src/engine/game/oocCastEffects.ts +803 -0
  975. package/src/engine/game/oocCastMethods.ts +1077 -0
  976. package/src/engine/game/openLockFromSpell.ts +174 -0
  977. package/src/engine/game/outdoorPvp.ts +187 -0
  978. package/src/engine/game/outfitter.ts +644 -0
  979. package/src/engine/game/paladinBoard.ts +393 -0
  980. package/src/engine/game/partyAuras.ts +121 -0
  981. package/src/engine/game/persistCombatAuras.ts +91 -0
  982. package/src/engine/game/pets.ts +459 -0
  983. package/src/engine/game/prePullHealthstones.ts +154 -0
  984. package/src/engine/game/professions.ts +2227 -0
  985. package/src/engine/game/pruneStaleRuntime.ts +39 -0
  986. package/src/engine/game/questFightCredit.ts +36 -0
  987. package/src/engine/game/questItemOnCorpse.ts +166 -0
  988. package/src/engine/game/questItemOnCreature.ts +103 -0
  989. package/src/engine/game/questLog.ts +879 -0
  990. package/src/engine/game/questSpellComplete.ts +21 -0
  991. package/src/engine/game/questTurnIn.ts +598 -0
  992. package/src/engine/game/raidParty.ts +32 -0
  993. package/src/engine/game/reagentBuyIn.ts +39 -0
  994. package/src/engine/game/regenRest.ts +186 -0
  995. package/src/engine/game/rotation.ts +425 -0
  996. package/src/engine/game/runFullDungeon.ts +611 -0
  997. package/src/engine/game/runSupply.ts +80 -0
  998. package/src/engine/game/sanitizeDriftedItems.ts +135 -0
  999. package/src/engine/game/scriptDevGoUse.ts +397 -0
  1000. package/src/engine/game/scriptDevInnkeeper.ts +150 -0
  1001. package/src/engine/game/scriptDevProfessionSpec.ts +99 -0
  1002. package/src/engine/game/sendEventFromSpell.ts +87 -0
  1003. package/src/engine/game/sortContainers.ts +85 -0
  1004. package/src/engine/game/spellReputation.ts +69 -0
  1005. package/src/engine/game/squad/squadDungeonSeating.ts +197 -0
  1006. package/src/engine/game/squadDungeon.ts +4222 -0
  1007. package/src/engine/game/squadQuestShare.ts +260 -0
  1008. package/src/engine/game/summonObjectSlot.ts +69 -0
  1009. package/src/engine/game/summonObjectWild.ts +73 -0
  1010. package/src/engine/game/summoningRitualUse.ts +141 -0
  1011. package/src/engine/game/talents.ts +356 -0
  1012. package/src/engine/game/tamingRodUse.ts +66 -0
  1013. package/src/engine/game/tempSpawnCreature.ts +136 -0
  1014. package/src/engine/game/tickDungeonPatrolScripts.ts +205 -0
  1015. package/src/engine/game/tickTimedQuests.ts +28 -0
  1016. package/src/engine/game/trade.ts +586 -0
  1017. package/src/engine/game/travel.ts +1155 -0
  1018. package/src/engine/game/types.ts +1855 -0
  1019. package/src/engine/game/vendorOffers.ts +244 -0
  1020. package/src/engine/game/weaponImbueItem.ts +281 -0
  1021. package/src/engine/game/worldSession.ts +183 -0
  1022. package/src/engine/game/worldState.ts +166 -0
  1023. package/src/engine/game.ts +968 -0
  1024. package/src/engine/spells/GAP.md +67 -0
  1025. package/src/engine/spells/absorbs.ts +376 -0
  1026. package/src/engine/spells/addTargetTrigger.ts +53 -0
  1027. package/src/engine/spells/auraPerSecondCost.ts +50 -0
  1028. package/src/engine/spells/auraStacking.ts +497 -0
  1029. package/src/engine/spells/cmangos-special-cases-combustion.ts +13 -0
  1030. package/src/engine/spells/cmangos-special-cases-s18.ts +445 -0
  1031. package/src/engine/spells/cmangos-special-cases-s19.ts +324 -0
  1032. package/src/engine/spells/cmangos-special-cases-s20.ts +385 -0
  1033. package/src/engine/spells/cmangos-special-cases-s21.ts +116 -0
  1034. package/src/engine/spells/cmangos-special-cases-s22.ts +24 -0
  1035. package/src/engine/spells/cmangos-special-cases-s23.ts +52 -0
  1036. package/src/engine/spells/cmangos-special-cases.ts +1570 -0
  1037. package/src/engine/spells/conditions.ts +718 -0
  1038. package/src/engine/spells/effects.ts +203 -0
  1039. package/src/engine/spells/fixtures/oracle-effects-lock9a.json +1 -0
  1040. package/src/engine/spells/immunity.ts +942 -0
  1041. package/src/engine/spells/intentEffects.ts +351 -0
  1042. package/src/engine/spells/interpreter/GAPS.md +21 -0
  1043. package/src/engine/spells/interpreter/amount.ts +109 -0
  1044. package/src/engine/spells/interpreter/class-scripts-kit.ts +478 -0
  1045. package/src/engine/spells/interpreter/diff.ts +246 -0
  1046. package/src/engine/spells/interpreter/duration.ts +90 -0
  1047. package/src/engine/spells/interpreter/form-attrs.json +1 -0
  1048. package/src/engine/spells/interpreter/form-attrs.ts +95 -0
  1049. package/src/engine/spells/interpreter/handlers/aura-remove.ts +113 -0
  1050. package/src/engine/spells/interpreter/handlers/auras-control.ts +1547 -0
  1051. package/src/engine/spells/interpreter/handlers/auras-core.ts +1583 -0
  1052. package/src/engine/spells/interpreter/handlers/auras-misc.ts +1774 -0
  1053. package/src/engine/spells/interpreter/handlers/effect-disenchant.ts +17 -0
  1054. package/src/engine/spells/interpreter/handlers/effect-skinning.ts +20 -0
  1055. package/src/engine/spells/interpreter/handlers/effects-action-utility.ts +1455 -0
  1056. package/src/engine/spells/interpreter/handlers/effects-core.ts +432 -0
  1057. package/src/engine/spells/interpreter/handlers/effects-remainder.ts +105 -0
  1058. package/src/engine/spells/interpreter/handlers/effects-summon-item.ts +687 -0
  1059. package/src/engine/spells/interpreter/handlers/mod-increase-energy-percent.ts +36 -0
  1060. package/src/engine/spells/interpreter/handlers/mod-increase-energy.ts +36 -0
  1061. package/src/engine/spells/interpreter/handlers/periodic-trigger-zero.ts +327 -0
  1062. package/src/engine/spells/interpreter/handlers/procs.ts +131 -0
  1063. package/src/engine/spells/interpreter/handlers/registry.ts +975 -0
  1064. package/src/engine/spells/interpreter/handlers/shared.ts +667 -0
  1065. package/src/engine/spells/interpreter/handlers.ts +9 -0
  1066. package/src/engine/spells/interpreter/hunter-kit.ts +202 -0
  1067. package/src/engine/spells/interpreter/index.ts +525 -0
  1068. package/src/engine/spells/interpreter/item-triggers.ts +1121 -0
  1069. package/src/engine/spells/interpreter/lift-consume-energy.ts +37 -0
  1070. package/src/engine/spells/interpreter/mage-portals.ts +37 -0
  1071. package/src/engine/spells/interpreter/materialize-charm.ts +88 -0
  1072. package/src/engine/spells/interpreter/materialize-dispel-mechanic.ts +25 -0
  1073. package/src/engine/spells/interpreter/materialize-rating-buffs.ts +81 -0
  1074. package/src/engine/spells/interpreter/materialize-speed.ts +109 -0
  1075. package/src/engine/spells/interpreter/materialize-spell-magnet.ts +22 -0
  1076. package/src/engine/spells/interpreter/opcodes.ts +789 -0
  1077. package/src/engine/spells/interpreter/paladin-kit.ts +95 -0
  1078. package/src/engine/spells/interpreter/paladin-seals.ts +499 -0
  1079. package/src/engine/spells/interpreter/priest-warlock-kit.ts +135 -0
  1080. package/src/engine/spells/interpreter/proc.ts +443 -0
  1081. package/src/engine/spells/interpreter/registry/helpers.ts +16 -0
  1082. package/src/engine/spells/interpreter/registry/index.ts +216 -0
  1083. package/src/engine/spells/interpreter/registry/packs/class-abilities.ts +304 -0
  1084. package/src/engine/spells/interpreter/registry/packs/dire-maul.ts +44 -0
  1085. package/src/engine/spells/interpreter/registry/packs/dummy-script-tail.ts +1065 -0
  1086. package/src/engine/spells/interpreter/registry/packs/ex-wave-m.ts +70 -0
  1087. package/src/engine/spells/interpreter/registry/packs/life-tap.ts +107 -0
  1088. package/src/engine/spells/interpreter/registry/packs/paladin.ts +246 -0
  1089. package/src/engine/spells/interpreter/registry/packs/raid-script-summons.ts +26 -0
  1090. package/src/engine/spells/interpreter/registry/packs/raid-threat-wipe.ts +22 -0
  1091. package/src/engine/spells/interpreter/registry/packs/shaman.ts +214 -0
  1092. package/src/engine/spells/interpreter/registry/packs/si4-dummy-generic-a.ts +269 -0
  1093. package/src/engine/spells/interpreter/registry/packs/si4-dummy-generic-b.ts +370 -0
  1094. package/src/engine/spells/interpreter/registry/packs/si4-dummy-generic-c.ts +172 -0
  1095. package/src/engine/spells/interpreter/registry/packs/si4-dummy-hunter.ts +60 -0
  1096. package/src/engine/spells/interpreter/registry/packs/si4-dummy-warrior.ts +31 -0
  1097. package/src/engine/spells/interpreter/registry/packs/si4-script-generic-a.ts +114 -0
  1098. package/src/engine/spells/interpreter/registry/packs/si4-script-generic-b.ts +249 -0
  1099. package/src/engine/spells/interpreter/registry/packs/si4-script-generic-c.ts +233 -0
  1100. package/src/engine/spells/interpreter/registry/types.ts +48 -0
  1101. package/src/engine/spells/interpreter/talent-scripted.ts +329 -0
  1102. package/src/engine/spells/interpreter/targeting.ts +90 -0
  1103. package/src/engine/spells/interpreter/types-intent.ts +1501 -0
  1104. package/src/engine/spells/interpreter/types.ts +419 -0
  1105. package/src/engine/spells/interpreter/warrior-druid-kit.ts +314 -0
  1106. package/src/engine/spells/interpreter/weapon-imbue-kit.ts +384 -0
  1107. package/src/engine/spells/interrupt.ts +217 -0
  1108. package/src/engine/spells/meleeAttackReset.ts +60 -0
  1109. package/src/engine/spells/oocTriggerSpell.ts +49 -0
  1110. package/src/engine/spells/overrideClassScripts.ts +319 -0
  1111. package/src/engine/spells/parryHaste.ts +20 -0
  1112. package/src/engine/spells/procs.ts +300 -0
  1113. package/src/engine/spells/recovery.ts +203 -0
  1114. package/src/engine/spells/scriptTargetDest.ts +76 -0
  1115. package/src/engine/spells/scriptTargets.ts +85 -0
  1116. package/src/engine/spells/singleTargetAura.ts +82 -0
  1117. package/src/engine/spells/specialCasePayloads.ts +57 -0
  1118. package/src/engine/spells/spellAttributes.ts +249 -0
  1119. package/src/engine/spells/spellBonus.ts +143 -0
  1120. package/src/engine/spells/spellModIntents.ts +419 -0
  1121. package/src/engine/spells/spellMods.ts +283 -0
  1122. package/src/engine/spells/spellRadius.ts +15 -0
  1123. package/src/engine/spells/trapObjects.ts +26 -0
  1124. package/src/json.d.ts +7 -0
  1125. package/src/persistence/memoryRepository.ts +23 -0
  1126. package/src/persistence/saveBackups.ts +162 -0
  1127. package/src/persistence/saveFile.ts +68 -0
  1128. package/src/persistence/saveRepository.ts +11 -0
  1129. package/src/persistence/saveShape.ts +668 -0
  1130. package/src/progression/leveling.ts +89 -0
  1131. package/src/progression/rest.ts +1434 -0
  1132. package/src/progression/training.ts +226 -0
  1133. package/src/progression/xpGain.ts +369 -0
  1134. package/src/quests/quests.ts +1580 -0
  1135. package/src/world/aoePull.ts +198 -0
  1136. package/src/world/areaTableDbc.ts +39 -0
  1137. package/src/world/areaTableMapIdPairs.ts +141 -0
  1138. package/src/world/bakeSlug.ts +43 -0
  1139. package/src/world/continuousDungeon.ts +1250 -0
  1140. package/src/world/dungeonBakeProjection.ts +190 -0
  1141. package/src/world/dungeonMapDensity.ts +342 -0
  1142. package/src/world/dungeonMapProjection.ts +135 -0
  1143. package/src/world/dungeonNav.ts +904 -0
  1144. package/src/world/dungeonNpcSim.ts +147 -0
  1145. package/src/world/dungeonObjectives.ts +234 -0
  1146. package/src/world/dungeonPreset.ts +35 -0
  1147. package/src/world/dungeonRoute.ts +311 -0
  1148. package/src/world/dungeonRouteGeometry.ts +732 -0
  1149. package/src/world/dungeonRouteOrder.ts +1037 -0
  1150. package/src/world/dungeonWingRooms.ts +426 -0
  1151. package/src/world/escort.ts +296 -0
  1152. package/src/world/goPoolRoll.ts +75 -0
  1153. package/src/world/instanceObjects.ts +785 -0
  1154. package/src/world/instances.ts +1572 -0
  1155. package/src/world/meetingStones.ts +56 -0
  1156. package/src/world/navmeshFloors.ts +145 -0
  1157. package/src/world/nearGrid.ts +167 -0
  1158. package/src/world/packBudget.ts +613 -0
  1159. package/src/world/rareRoll.ts +335 -0
  1160. package/src/world/routeAuthoring.ts +132 -0
  1161. package/src/world/routeEdit.ts +209 -0
  1162. package/src/world/world.ts +812 -0
  1163. package/src/world/zoneHazards.ts +47 -0
@@ -0,0 +1,1248 @@
1
+ // Peeled from createSession (Wave 4 hub: weapon). Pattern (D). Extracted modules must not import combat.ts.
2
+ // NOTE: imports may need expanding when first run against seam tests.
3
+
4
+ import type { CombatContext } from './context.ts';
5
+ import type { Fighter, ActiveEffect, WeaponProfile } from './types.ts';
6
+ import type { SpellDef, BuffMod } from '../../data/schema.ts';
7
+ import type { HitResult } from '../core/events.ts';
8
+ import { isEnemySide, isAllySide, isTotemSide } from '../core/events.ts';
9
+ import {
10
+ TUNING,
11
+ meleeBaseDamage,
12
+ offhandDamageTotalPct,
13
+ critMultiplierWithVersus,
14
+ disarmedWeaponProfile,
15
+ meleeAttackTable,
16
+ pickMeleeOutcome,
17
+ pickMeleeSpellLand,
18
+ remapRangedWhiteOutcome,
19
+ meleeOutcomeMultiplier,
20
+ spellCritChance,
21
+ calculateEffectiveCritChance,
22
+ calculateEffectiveBlockChance,
23
+ calculateEffectiveDazeChance,
24
+ SPELL_RANGE_IDX,
25
+ scaleTime,
26
+ applyShieldBlockAbsorb,
27
+ rewardRage,
28
+ BASE_ATTACK_TIME_MS,
29
+ } from './formulas.ts';
30
+ import { flametongueSwingDamage } from '../spells/interpreter/weapon-imbue-kit.ts';
31
+ import { fighterSpellCrit, applyBuffMod, reverseBuffMod } from './fighterFactory.ts';
32
+ import { applyOwnedSpellMod, SPELLMOD_CRITICAL_CHANCE } from '../spells/spellModIntents.ts';
33
+ import { roleOf } from './roles.ts';
34
+ import { sumVersusForCreatureType } from '../../domain/character/characterStats.ts';
35
+ import { markSpeedDirty } from './speedAura.ts';
36
+ import { canUseEquippedWeapon } from './equippedItemGate.ts';
37
+ import { creatureSkipsParryHaste, parryHasteRemaining } from '../spells/parryHaste.ts';
38
+ import { CREATURE_STATIC_FLAGS2 } from '../../domain/creature/creatureAiConstants.ts';
39
+ import { isNextMeleeSwingSpell } from '../spells/recovery.ts';
40
+
41
+ /** Versus AP for a creature type — thin alias for MeleeDamageBonusDone APbonus. */
42
+ function sumVersusAp(
43
+ rows: Array<{ creatureTypeMask: number; amount: number }> | undefined,
44
+ creatureType: number,
45
+ ): number {
46
+ return sumVersusForCreatureType(rows, creatureType);
47
+ }
48
+
49
+ // CanCrit for the ability crit roll: a creature can never crit with a spell, and the spell must carry
50
+ // a crittable effect. The white swing keeps its own crit. Cite: Unit.cpp:3434-3448.
51
+ function canCritWithAbility(src: Fighter, spell: SpellDef | null): boolean {
52
+ if (isEnemySide(src.side)) return false;
53
+ return !spell || spellCanCrit(spell);
54
+ }
55
+
56
+ // Instant-ability defense gates. Land-die block is not baked, so yellows partial-block after the crit
57
+ // roll. Cite: Unit.cpp:3129-3186 CanReactOnAbility / CanDodgeAbility / CanParryAbility.
58
+ function abilityDefense(
59
+ spell: SpellDef | null,
60
+ attackType: 'melee' | 'ranged',
61
+ ): { dodge: boolean; parry: boolean; deflect: boolean; partialBlock: boolean; completelyBlocked: boolean } {
62
+ if (!spell) return { dodge: true, parry: attackType !== 'ranged', deflect: false, partialBlock: false, completelyBlocked: false };
63
+ if (hasAttr(spell, SPELL_ATTR.NO_ACTIVE_DEFENSE)) {
64
+ return { dodge: false, parry: false, deflect: false, partialBlock: false, completelyBlocked: false };
65
+ }
66
+ const dc = spell.dmgClass;
67
+ const melee = dc == null ? attackType === 'melee' : dc === SPELL_DAMAGE_CLASS.MELEE;
68
+ const ranged = dc == null ? attackType === 'ranged' : dc === SPELL_DAMAGE_CLASS.RANGED;
69
+ const physical = spell.school == null || spell.school === 'physical';
70
+ // Parry versus deflect is one exclusive split: parry needs a MELEE class, a physical school and a
71
+ // close-combat range index. Everything else deflects at the same chance. Cite: Unit.cpp:3145-3212.
72
+ const ri = spell.rangeIndex ?? 0;
73
+ const closeRange = ri === 0 || ri === SPELL_RANGE_IDX.SELF_ONLY || ri === SPELL_RANGE_IDX.COMBAT;
74
+ const parry = melee && physical && closeRange;
75
+ const completelyBlocked = hasAttrEx3(spell, SPELL_ATTR_EX3.COMPLETELY_BLOCKED);
76
+ return {
77
+ dodge: melee,
78
+ parry,
79
+ deflect: ranged || (melee && !parry),
80
+ partialBlock: (melee || ranged) && !completelyBlocked,
81
+ completelyBlocked,
82
+ };
83
+ }
84
+ import { isTotemCreatureType } from '../spells/immunity.ts';
85
+ import {
86
+ SPELL_ATTR, SPELL_ATTR_EX3, hasAttr, hasAttrEx3, spellCanCrit,
87
+ } from '../spells/spellAttributes.ts';
88
+ import { effectiveMagicResistancePercent, SPELL_DAMAGE_CLASS } from './mitigation.ts';
89
+ import { resolveWeaponPercentDamage } from '../spells/effects.ts';
90
+ import { consumeProcCharge } from '../spells/auraStacking.ts';
91
+ import {
92
+ flurryMayConsumeCharge,
93
+ isFlurryHasteRank,
94
+ SWEEPING_STRIKES_PLAYER_SPELL_ID,
95
+ SWEEPING_STRIKES_NPC_SPELL_ID,
96
+ SWEEPING_STRIKES_COPY_DAMAGE_SPELL_ID,
97
+ SWEEPING_STRIKES_NORMALIZED_SPELL_ID,
98
+ isSweepingStrikesAuraSpell,
99
+ normalizedMeleeSpeedSec,
100
+ sweepingStrikesUsesNormalized,
101
+ sealOfRighteousnessProcDamageSpellId,
102
+ } from '../spells/cmangos-special-cases.ts';
103
+ import {
104
+ isAttackSpeedOverriddenShapeShift,
105
+ } from '../../domain/character/forms.ts';
106
+ import { COMBAT_SKILL, tryCombatSkillGain } from '../../domain/character/combatSkills.ts';
107
+ import { emitSimEffect } from './simEffects.ts';
108
+ import { CREATURE_TYPE } from '../../data/schema.ts';
109
+ import { MIN_AGGRO_DISTANCE } from '../../domain/creature/creatureAi.ts';
110
+ import { stampDefensiveAuraState } from './reactiveGates.ts';
111
+
112
+ export type installWeaponDeps = {
113
+ dead: (...args: any[]) => any;
114
+ sanitizeHp: (...args: any[]) => any;
115
+ spend: (...args: any[]) => any;
116
+ gainRage: (...args: any[]) => any;
117
+ addComboPoints: (...args: any[]) => any;
118
+ clearComboPoints: (...args: any[]) => any;
119
+ comboOn: (...args: any[]) => any;
120
+ onDamaged: (...args: any[]) => any;
121
+ dealDamage: (...args: any[]) => any;
122
+ applySpell: (...args: any[]) => any;
123
+ triggerProcs: (...args: any[]) => any;
124
+ triggerTakenProcs: (...args: any[]) => any;
125
+ rollGearProcs: (...args: any[]) => any;
126
+ fireDamageShields: (...args: any[]) => any;
127
+ fireDealMeleeAura42: (...args: any[]) => any;
128
+ breakStealth: (...args: any[]) => any;
129
+ maybeBreakStealthOn: (...args: any[]) => any;
130
+ sealRighteousnessSwing: (...args: any[]) => any;
131
+ };
132
+
133
+ export function installWeapon(ctx: CombatContext, deps: installWeaponDeps): void {
134
+ const push = (e: any) => ctx.push(e);
135
+ const player = ctx.player;
136
+ const pet = ctx.pet;
137
+ const effects = ctx.effects;
138
+ const touchEffect = (e: ActiveEffect): void => { ctx.touchEffect(e); };
139
+ const itemsSpent = ctx.itemsSpent;
140
+ const rng = ctx.rng;
141
+ const tickMs = ctx.tickMs;
142
+ const livingEnemies = ctx.livingEnemies;
143
+ const distanceBetween = ctx.distanceBetween;
144
+ // CanReactInCombat plus the in-flight-spell clause all three avoidance gates carry: a controlled or
145
+ // mid-cast unit sets no dodge, parry or block. Parry also needs a usable main hand, so disarm ends it.
146
+ const canReactNow = (tgt: Fighter): boolean => {
147
+ if ((ctx.isControlledHard as (u: Fighter) => boolean)(tgt)) return false;
148
+ if (tgt.castingSpell) return false;
149
+ return !(tgt.channeling && ctx.t < tgt.channeling.endsAt);
150
+ };
151
+ const canParryNow = (tgt: Fighter): boolean => {
152
+ if (!tgt.spec.canParry) return false;
153
+ if (tgt.disarmedUntil > ctx.t) return false;
154
+ return canReactNow(tgt);
155
+ };
156
+ // Attack profile for this hand, with DISARM folded into the MAIN hand only: a disarmed unit keeps
157
+ // swinging for 1-2 unarmed damage at the base time. Cite: Unit.h:1191-1204 CanUseEquippedWeapon.
158
+ const handProfile = (src: Fighter, hand: 'mainhand' | 'offhand'): WeaponProfile => {
159
+ if (hand === 'offhand' && src.spec.offhand) return src.spec.offhand;
160
+ if (src.disarmedUntil > ctx.t) return disarmedWeaponProfile(src.spec.weapon);
161
+ return src.spec.weapon;
162
+ };
163
+ const applyDamageThreat = (...args: any[]) => (ctx.applyDamageThreat as Function)(...args);
164
+ /** DISCOUNT_POWER_ON_MISS refund; a no-op when powerForm has not installed. */
165
+ const refundPowerOnMiss = (f: Fighter, spell: SpellDef | null, outcome: string): void => {
166
+ if (spell) ctx.refundPowerOnMiss?.(f, spell, outcome);
167
+ };
168
+ const applySpellThreatBonus = (...args: any[]) => (ctx.applySpellThreatBonus as Function)(...args);
169
+ const engageOnHostileSpell = (...args: any[]) => (ctx.engageOnHostileSpell as Function)(...args);
170
+ const dead = deps.dead;
171
+ const sanitizeHp = deps.sanitizeHp;
172
+ const spend = deps.spend;
173
+ const gainRage = deps.gainRage;
174
+ const addComboPoints = deps.addComboPoints;
175
+ const clearComboPoints = deps.clearComboPoints;
176
+ const comboOn = deps.comboOn;
177
+ const onDamaged = deps.onDamaged;
178
+ const dealDamage = deps.dealDamage;
179
+ const applySpell = deps.applySpell;
180
+ /** ctx.isInFrontArc, resolved lazily — sessionGeometry installs it after this module. */
181
+ const inFrontArc = (observer: Fighter, other: Fighter): boolean =>
182
+ ctx.isInFrontArc ? ctx.isInFrontArc(observer, other) : true;
183
+ const triggerProcs = deps.triggerProcs;
184
+ const triggerTakenProcs = deps.triggerTakenProcs;
185
+ const rollGearProcs = deps.rollGearProcs;
186
+ const fireDamageShields = deps.fireDamageShields;
187
+ const fireDealMeleeAura42 = deps.fireDealMeleeAura42;
188
+ const breakStealth = deps.breakStealth;
189
+ const maybeBreakStealthOn = deps.maybeBreakStealthOn;
190
+ const sealRighteousnessSwing = deps.sealRighteousnessSwing;
191
+
192
+ const applyWeaponImbueOnSwing = (
193
+ src: Fighter,
194
+ tgt: Fighter,
195
+ hand: 'mainhand' | 'offhand' = 'mainhand',
196
+ ): void => {
197
+ const t = ctx.t;
198
+ const imbue = src.weaponImbues[hand] ?? (hand === 'mainhand' ? src.weaponImbues.mainhand : undefined);
199
+ if (!imbue || imbue.expiresAt <= t) return;
200
+ // Cat and bear swings are paws, not the enchanted item, and a DISARMED main hand is not the item
201
+ // either — so no imbue procs at all. The off hand is exempt from the disarm arm.
202
+ if (!canUseEquippedWeapon(hand, src.form, src.disarmedUntil > t)) return;
203
+ // Flametongue, Frostbrand and Rockbiter ride the swing target only. On the endless floor a fan bug
204
+ // handed passives 40 yd out ~14 fire per white, so imbue damage is refused outside melee.
205
+ if (ctx.cfg.endless) {
206
+ const d = distanceBetween(src, tgt);
207
+ if (d == null || d > MIN_AGGRO_DISTANCE) return;
208
+ }
209
+ const wpn = handProfile(src, hand);
210
+ const speedSec = wpn.speedMs / 1000;
211
+
212
+ // TYPE_TOTEM Rockbiter is speed-scaled weapon damage; Classic DBC ranks use an attack-power mod
213
+ // instead, and never both — sheet AP plus a per-swing add double-counts.
214
+ if (imbue.rockbiterAmount && !imbue.attackPower) {
215
+ const add = imbue.rockbiterAmount * speedSec;
216
+ if (add > 0) {
217
+ const amount = dealDamage(tgt, add, 'physical', src.spec.level, { attacker: src });
218
+ push({
219
+ t, kind: 'spell_hit', actor: src.side, target: tgt.side, spellId: imbue.spellId, name: imbue.name,
220
+ result: 'hit', amount, school: 'physical',
221
+ });
222
+ }
223
+ }
224
+ // TYPE_DAMAGE flat
225
+ if (imbue.damageFlat) {
226
+ const amount = dealDamage(tgt, imbue.damageFlat, 'physical', src.spec.level, { attacker: src });
227
+ push({
228
+ t, kind: 'spell_hit', actor: src.side, target: tgt.side, spellId: imbue.spellId, name: imbue.name,
229
+ result: 'hit', amount, school: 'physical',
230
+ });
231
+ }
232
+
233
+ const combat = imbue.combat;
234
+ if (!combat) return;
235
+
236
+ // Chance: PPM first, else the flat percent amount, else the weapon-speed heuristic. Flametongue's
237
+ // DBC amount is 100, meaning 100% per landed swing. Cite: SpellItemEnchantment.
238
+ let chance01: number;
239
+ if (imbue.combatPpm && imbue.combatPpm > 0) {
240
+ chance01 = Math.min(1, (wpn.speedMs * imbue.combatPpm) / 60000);
241
+ } else if (imbue.combatChancePct != null && imbue.combatChancePct > 0) {
242
+ chance01 = Math.min(1, imbue.combatChancePct / 100);
243
+ } else {
244
+ // GetWeaponProcChance: speed * 1.8 / 1000 as percent → /100 for probability.
245
+ chance01 = Math.min(1, (speedSec * 1.8) / 100);
246
+ }
247
+ if (!rng.chance(chance01)) return;
248
+
249
+ if (combat.direct) {
250
+ // Flametongue's direct minimum is the DUMMY coefficient, not flat fire: the total is damage
251
+ // times 0.01 times weapon speed. Detect by family or by id, so a gap deals no raw coefficient.
252
+ const isFlametongue =
253
+ imbue.family === 'flametongue'
254
+ || combat.spellId === 10444
255
+ || (combat.name ?? '').toLowerCase() === 'flametongue attack';
256
+ let roll: number;
257
+ if (isFlametongue) {
258
+ roll = flametongueSwingDamage(combat.direct.min, speedSec);
259
+ } else {
260
+ roll = combat.direct.min + (combat.direct.max - combat.direct.min) * rng.next();
261
+ }
262
+ if (roll > 0) {
263
+ const amount = dealDamage(tgt, roll, combat.direct.school, src.spec.level, {
264
+ damageType: 'spell', attacker: src,
265
+ });
266
+ if (amount > 0) {
267
+ push({
268
+ t, kind: 'spell_hit', actor: src.side, target: tgt.side, spellId: combat.spellId, name: combat.name,
269
+ result: 'hit', amount, school: combat.direct.school,
270
+ });
271
+ }
272
+ }
273
+ }
274
+ if (combat.dot) {
275
+ // Deadly Poison DoT. stepSession ticks read e.min/e.max (applySpell path); amount-only
276
+ // left every tick at 0 (KI-716). Cite: applySpell.ts dot branch; stepSession rollBetween.
277
+ const d = combat.dot;
278
+ effects.push({
279
+ kind: 'dot',
280
+ source: src.side,
281
+ target: tgt.side,
282
+ spellId: combat.spellId,
283
+ name: combat.name,
284
+ school: d.school,
285
+ min: d.min,
286
+ max: d.max,
287
+ amount: d.min,
288
+ tickMs: d.tickMs,
289
+ nextTickAt: t + d.tickMs,
290
+ expiresAt: t + d.durationMs,
291
+ });
292
+ push({ t, kind: 'aura', actor: tgt.side, name: combat.name, applied: true });
293
+ }
294
+ // Frostbrand chill and Crippling Poison snare. The frostbrand family always snares even when the
295
+ // bake stamped only direct damage. Cite: spell 8034 effects.
296
+ const snare = combat.speed ?? (imbue.family === 'frostbrand'
297
+ ? { aura: 33, pct: -25, durationMs: 8000, hostile: true, mechanic: 'snare' }
298
+ : undefined);
299
+ if (snare) {
300
+ const exp = t + (snare.durationMs > 0 ? snare.durationMs : 8000);
301
+ const live = ctx.effectsOn(tgt.side).find(
302
+ (e) => e.kind === 'speed' && e.spellId === combat.spellId,
303
+ );
304
+ if (live) {
305
+ live.expiresAt = exp;
306
+ live.pct = snare.pct;
307
+ live.hostile = snare.hostile !== false;
308
+ markSpeedDirty(tgt);
309
+ touchEffect(live);
310
+ } else {
311
+ effects.push({
312
+ kind: 'speed',
313
+ source: src.side,
314
+ target: tgt.side,
315
+ spellId: combat.spellId,
316
+ name: combat.name,
317
+ aura: snare.aura,
318
+ pct: snare.pct,
319
+ hostile: snare.hostile !== false,
320
+ expiresAt: exp,
321
+ });
322
+ }
323
+ push({ t, kind: 'aura', actor: tgt.side, name: combat.name, applied: true });
324
+ }
325
+ if (combat.buffs?.length) {
326
+ for (const b of combat.buffs) {
327
+ const exp = t + (b.durationMs > 0 ? b.durationMs : 10_000);
328
+ const mod = b.mod as BuffMod;
329
+ const live = ctx.effectsOn(tgt.side).find(
330
+ (e) => e.kind === 'buff' && e.spellId === combat.spellId
331
+ && e.mod === mod,
332
+ );
333
+ if (live) {
334
+ reverseBuffMod(tgt, live.mod, live.amount ?? 0);
335
+ live.amount = b.amount;
336
+ live.expiresAt = exp;
337
+ applyBuffMod(tgt, mod, b.amount);
338
+ touchEffect(live);
339
+ } else {
340
+ applyBuffMod(tgt, mod, b.amount);
341
+ effects.push({
342
+ kind: 'buff',
343
+ source: src.side,
344
+ target: tgt.side,
345
+ spellId: combat.spellId,
346
+ name: combat.name,
347
+ mod,
348
+ amount: b.amount,
349
+ expiresAt: exp,
350
+ });
351
+ }
352
+ }
353
+ push({ t, kind: 'aura', actor: tgt.side, name: combat.name, applied: true });
354
+ }
355
+ // Non-damage poison procs: Crippling snare / Mind-numbing haste / Wound healing_taken.
356
+ // Cite: CastItemCombatSpell → combat spell; KI-717.
357
+ if (combat.speed) {
358
+ const s = combat.speed;
359
+ const exp = t + (s.durationMs > 0 ? s.durationMs : 12_000);
360
+ const live = ctx.effectsOn(tgt.side).find(
361
+ (e) => e.kind === 'speed' && e.spellId === combat.spellId,
362
+ );
363
+ if (live) {
364
+ live.expiresAt = exp;
365
+ live.pct = s.pct;
366
+ live.hostile = s.hostile !== false;
367
+ markSpeedDirty(tgt);
368
+ touchEffect(live);
369
+ } else {
370
+ effects.push({
371
+ kind: 'speed',
372
+ source: src.side,
373
+ target: tgt.side,
374
+ spellId: combat.spellId,
375
+ name: combat.name,
376
+ aura: s.aura,
377
+ pct: s.pct,
378
+ hostile: s.hostile !== false,
379
+ expiresAt: exp,
380
+ });
381
+ }
382
+ push({ t, kind: 'aura', actor: tgt.side, name: combat.name, applied: true });
383
+ push({
384
+ t, kind: 'spell_hit', actor: src.side, target: tgt.side,
385
+ spellId: combat.spellId, name: combat.name, result: 'hit', amount: 0,
386
+ });
387
+ }
388
+ if (combat.buffs?.length) {
389
+ for (const b of combat.buffs) {
390
+ const exp = t + (b.durationMs > 0 ? b.durationMs : 10_000);
391
+ const mod = b.mod as BuffMod;
392
+ const live = ctx.effectsOn(tgt.side).find(
393
+ (e) => e.kind === 'buff' && e.spellId === combat.spellId
394
+ && e.mod === mod,
395
+ );
396
+ if (live) {
397
+ // Refresh: reverse old amount, re-apply new (same stack).
398
+ reverseBuffMod(tgt, live.mod, live.amount ?? 0);
399
+ live.amount = b.amount;
400
+ live.expiresAt = exp;
401
+ applyBuffMod(tgt, mod, b.amount);
402
+ touchEffect(live);
403
+ } else {
404
+ applyBuffMod(tgt, mod, b.amount);
405
+ effects.push({
406
+ kind: 'buff',
407
+ source: src.side,
408
+ target: tgt.side,
409
+ spellId: combat.spellId,
410
+ name: combat.name,
411
+ mod,
412
+ amount: b.amount,
413
+ expiresAt: exp,
414
+ });
415
+ }
416
+ }
417
+ push({ t, kind: 'aura', actor: tgt.side, name: combat.name, applied: true });
418
+ // Do not emit amount:0 spell_hit for pure buff notifies — Windfury meters treat those
419
+ // as failed extra attacks (KI-580). Damage rows come from extraAttacks / weaponStrike.
420
+ }
421
+ if (combat.extraAttacks && combat.extraAttacks > 0) {
422
+ // not allow proc extra attack spell at extra attack
423
+ // Cite: Player.cpp:7383–7385 CastItemCombatSpell; Unit.cpp m_extraAttacksExecuting.
424
+ if (src.extraAttacksExecuting) return;
425
+ // Windfury: extra white hits with a temporary AP bonus on the extras only, never a standing
426
+ // aura. Through the full weaponStrike path so meters name the proc and mitigation is real.
427
+ const apBonus = combat.apBonus ?? 0;
428
+ const savedAp = src.attackPower;
429
+ src.attackPower = savedAp + apBonus;
430
+ src.extraAttacksExecuting = true;
431
+ src.extraAttackSource = { spellId: combat.spellId, name: combat.name };
432
+ for (let i = 0; i < combat.extraAttacks; i++) {
433
+ if (dead(tgt) || dead(src)) break;
434
+ weaponStrike(src, tgt, null, hand);
435
+ }
436
+ src.extraAttacksExecuting = false;
437
+ src.extraAttackSource = null;
438
+ src.attackPower = savedAp;
439
+ }
440
+ };
441
+
442
+ // A paladin seal fires on each landed melee swing while it is up: Righteousness is deterministic
443
+ // holy, Command a 7-PPM chance, Light a self-heal, Wisdom mana. Only Command draws rng.
444
+ // The dealer is passed because DealDamage needs one: the duel yield, threat and kill credit all
445
+ // hang off it (Unit.cpp:967 `if (dealer)`), and without it Seal of Command killed duelists.
446
+ const applySealOnSwing = (src: Fighter, tgt: Fighter, hand: 'mainhand' | 'offhand' = 'mainhand'): void => {
447
+ const t = ctx.t;
448
+ const seal = src.activeSeal;
449
+ if (!seal || seal.expiresAt <= t) return;
450
+ const wpn = handProfile(src, hand);
451
+ if (seal.seal === 'righteousness' && seal.coeff) {
452
+ const amount = dealDamage(tgt, sealRighteousnessSwing(seal.coeff, wpn), 'holy', src.spec.level, { attacker: src });
453
+ push({ t, kind: 'spell_hit', actor: src.side, target: tgt.side, spellId: sealOfRighteousnessProcDamageSpellId(seal.spellId) || seal.spellId, name: seal.name, result: 'hit', amount, school: 'holy' });
454
+ } else if (seal.seal === 'command' && seal.onSwingWeaponPct) {
455
+ // mangos PPM → per-swing chance: ppm * weaponSpeed / 60 (UnitAuraProcHandler proc gating).
456
+ const speed = wpn.speedMs / 1000;
457
+ const chance = Math.min(1, (seal.procPpm ?? 7) * speed / 60);
458
+ if (rng.chance(chance)) {
459
+ // Seal of Command uses weapon damage path; OH inherits the off-hand TOTAL_PCT.
460
+ const oh = hand === 'offhand';
461
+ const wd = meleeBaseDamage(wpn.dmgMin, wpn.dmgMax, src.attackPower, wpn.speedMs, rng.next(), {
462
+ totalPct: oh ? offhandDamageTotalPct(src.offhandDamagePct) : 1,
463
+ });
464
+ const amount = dealDamage(tgt, wd * seal.onSwingWeaponPct / 100, 'holy', src.spec.level, { attacker: src });
465
+ push({ t, kind: 'spell_hit', actor: src.side, target: tgt.side, spellId: seal.spellId, name: seal.name, result: 'hit', amount, school: 'holy' });
466
+ }
467
+ } else if (seal.seal === 'light' && seal.onSwingHeal) {
468
+ if (dead(src)) return;
469
+ const before = src.hp;
470
+ const heal = Number.isFinite(seal.onSwingHeal) ? seal.onSwingHeal : 0;
471
+ src.hp = Math.min(src.spec.maxHealth, Math.round(src.hp + heal));
472
+ if (src.hp !== before) push({ t, kind: 'heal', actor: src.side, amount: src.hp - before });
473
+ } else if (seal.seal === 'wisdom' && seal.onSwingMana) {
474
+ const before = src.power;
475
+ src.power = Math.min(src.spec.maxPower, src.power + seal.onSwingMana);
476
+ if (src.power !== before) push({ t, kind: 'resource', actor: src.side, power: src.spec.powerType, value: Math.round(src.power), delta: src.power - before });
477
+ }
478
+ };
479
+
480
+ /** Player-controlled: player / pet / totem sides, or explicit spec flag. */
481
+ const isPlayerOrPetSide = (f: Fighter): boolean =>
482
+ f.spec.isPlayerOrPet ?? (f.side === 'player' || f.side === 'pet' || isAllySide(f.side) || isTotemSide(f.side));
483
+
484
+ // CanDazeInCombat: only a creature that is not player-controlled dazes, only a PLAYER can be dazed,
485
+ // and the attacker must be facing the victim's back. Cite: Unit.cpp:2999 CanDaze, :3124.
486
+ const maybeDaze = (
487
+ src: Fighter,
488
+ tgt: Fighter,
489
+ atkCtx: { atkLevel: number; defLevel: number; attackerWeaponSkill?: number; defenderDefenseSkill?: number },
490
+ attackType: 'melee' | 'ranged',
491
+ ): void => {
492
+ if (attackType !== 'melee') return;
493
+ const spell = ctx.dazeSpell;
494
+ if (!spell) return;
495
+ if (isPlayerOrPetSide(src)) return;
496
+ if (!(tgt.side === 'player' || isAllySide(tgt.side))) return;
497
+ if (inFrontArc(tgt, src)) return;
498
+ if (!inFrontArc(src, tgt)) return;
499
+ const chance = calculateEffectiveDazeChance({
500
+ atkLevel: atkCtx.atkLevel,
501
+ defLevel: atkCtx.defLevel,
502
+ baseCrit: 0,
503
+ defenderParries: false,
504
+ defenderBlocks: false,
505
+ attackerWeaponSkill: atkCtx.attackerWeaponSkill,
506
+ defenderDefenseSkill: atkCtx.defenderDefenseSkill,
507
+ });
508
+ if (!rng.chance(chance)) return;
509
+ applySpell(src, tgt, spell);
510
+ };
511
+
512
+ const crushFields = (src: Fighter) => ({
513
+ noCrushingBlows: ((src.spec.staticFlags2 ?? 0) & CREATURE_STATIC_FLAGS2.NO_CRUSHING_BLOWS) !== 0,
514
+ attackerCharmerIsCreature: !!(
515
+ ctx.isCharmed?.(src) && src.charmedBy != null && isEnemySide(src.charmedBy)
516
+ ),
517
+ });
518
+
519
+ // Dual-wield active for attack tables / OH autos: gear flag + OH profile, not form override. source:
520
+ // hasOffhandWeaponForAttack; IsAttackSpeedOverridenShapeShift suppresses OH.
521
+ const fighterDualWield = (f: Fighter): boolean =>
522
+ !!f.spec.dualWield && !!f.spec.offhand && !isAttackSpeedOverriddenShapeShift(f.form);
523
+
524
+ // Shorten the defender's remaining MH/OH timer after a parry. Ranged mode overloads swingReadyAt as Auto
525
+ // Shot — leave it. Cite: Unit.cpp:2158–2195.
526
+ const applyParryHaste = (defender: Fighter): void => {
527
+ if (creatureSkipsParryHaste(defender.spec.extraFlags)) return;
528
+ if (defender.mode === 'ranged') return;
529
+ const now = ctx.t;
530
+ const mhPeriod = defender.disarmedUntil > now
531
+ ? BASE_ATTACK_TIME_MS
532
+ : scaleTime(defender.spec.weapon.speedMs, defender.haste, 0);
533
+ const mhRemain = defender.swingReadyAt - now;
534
+ if (fighterDualWield(defender) && defender.spec.offhand) {
535
+ const ohPeriod = scaleTime(defender.spec.offhand.speedMs, defender.haste, 0);
536
+ const ohRemain = defender.ohSwingReadyAt - now;
537
+ if (ohRemain < mhRemain) {
538
+ defender.ohSwingReadyAt = now + parryHasteRemaining(ohRemain, ohPeriod);
539
+ return;
540
+ }
541
+ }
542
+ defender.swingReadyAt = now + parryHasteRemaining(mhRemain, mhPeriod);
543
+ };
544
+
545
+ // Weapon/defense skill gain on melee-based outcomes. Cite: Unit.cpp ProcSkillsAndReactives →
546
+ // Player::UpdateCombatSkills (hit/crit/miss/dodge/parry/block vs non-player non-critter).
547
+ const SKILL_GAIN_RESULTS = new Set<HitResult>([
548
+ 'hit', 'crit', 'miss', 'dodge', 'parry', 'block', 'glancing', 'crushing',
549
+ ]);
550
+ /** GetSkillValue for a hand = pure trained + MOD_SKILL bonuses. */
551
+ const weaponSkillForHand = (
552
+ f: Fighter,
553
+ hand: 'mainhand' | 'offhand' | 'ranged',
554
+ ): number | undefined => {
555
+ if (hand === 'offhand') {
556
+ return f.spec.offhandWeaponSkill ?? f.spec.weaponSkill;
557
+ }
558
+ if (hand === 'ranged') {
559
+ return f.spec.rangedWeaponSkill ?? f.spec.weaponSkill;
560
+ }
561
+ return f.spec.weaponSkill;
562
+ };
563
+
564
+ /** Refresh live attack-table skill after pure skill gain (keep bonuses). */
565
+ const refreshWeaponSkillAfterGain = (
566
+ f: Fighter,
567
+ skillId: number,
568
+ pure: number,
569
+ hand: 'mainhand' | 'offhand' | 'ranged',
570
+ ): void => {
571
+ const bonus = f.spec.skillBonuses?.[skillId] ?? 0;
572
+ const effective = Math.max(0, pure + bonus);
573
+ if (hand === 'offhand') f.spec.offhandWeaponSkill = effective;
574
+ else if (hand === 'ranged') f.spec.rangedWeaponSkill = effective;
575
+ else f.spec.weaponSkill = effective;
576
+ };
577
+
578
+ const tryMeleeSkillGains = (
579
+ attacker: Fighter,
580
+ defender: Fighter,
581
+ result: HitResult,
582
+ hand: 'mainhand' | 'offhand' | 'ranged',
583
+ ): void => {
584
+ if (!SKILL_GAIN_RESULTS.has(result)) return;
585
+ // Player attacker vs NPC: weapon skill (pure map only; bonuses stay on skillBonuses).
586
+ if (
587
+ attacker.side === 'player'
588
+ && attacker.spec.combatSkills
589
+ && !isPlayerOrPetSide(defender)
590
+ && defender.creatureType !== CREATURE_TYPE.CRITTER
591
+ && !isAttackSpeedOverriddenShapeShift(attacker.form)
592
+ ) {
593
+ let skillId = 0;
594
+ if (hand === 'offhand') skillId = attacker.spec.offhandSkillId ?? 0;
595
+ else if (hand === 'ranged') skillId = attacker.spec.rangedSkillId ?? 0;
596
+ else skillId = attacker.spec.weaponSkillId ?? COMBAT_SKILL.UNARMED;
597
+ if (skillId) {
598
+ const r = tryCombatSkillGain(attacker.spec.combatSkills, skillId, attacker.spec.level, {
599
+ defence: false,
600
+ intellect: attacker.spec.intellect ?? 0,
601
+ roll: () => rng.next(),
602
+ });
603
+ attacker.spec.combatSkills = r.map;
604
+ if (r.gained) {
605
+ const row = r.map[skillId];
606
+ refreshWeaponSkillAfterGain(attacker, skillId, row?.current ?? 0, hand);
607
+ if (row) emitSimEffect(ctx, { kind: 'skillGain', skillId, current: row.current, max: row.max });
608
+ }
609
+ }
610
+ }
611
+ // Player defender vs NPC: defense skill
612
+ if (
613
+ defender.side === 'player'
614
+ && defender.spec.combatSkills
615
+ && !isPlayerOrPetSide(attacker)
616
+ && attacker.creatureType !== CREATURE_TYPE.CRITTER
617
+ ) {
618
+ const r = tryCombatSkillGain(
619
+ defender.spec.combatSkills,
620
+ COMBAT_SKILL.DEFENSE,
621
+ defender.spec.level,
622
+ {
623
+ defence: true,
624
+ intellect: defender.spec.intellect ?? 0,
625
+ roll: () => rng.next(),
626
+ },
627
+ );
628
+ defender.spec.combatSkills = r.map;
629
+ if (r.gained) {
630
+ const row = r.map[COMBAT_SKILL.DEFENSE];
631
+ const bonus = defender.spec.skillBonuses?.[COMBAT_SKILL.DEFENSE] ?? 0;
632
+ defender.spec.defenseSkill = Math.max(0, (row?.current ?? 0) + bonus);
633
+ if (row) {
634
+ emitSimEffect(ctx, {
635
+ kind: 'skillGain', skillId: COMBAT_SKILL.DEFENSE, current: row.current, max: row.max,
636
+ });
637
+ }
638
+ }
639
+ }
640
+ };
641
+
642
+ // Extra hit for Sweeping Strikes. White/yellow weapon hits call this from weaponStrike; melee-class
643
+ // school damage (Execute, Bloodthirst) calls it from applySpell. Cite: UnitAuraProcHandler.cpp:810-841.
644
+ const trySweepingStrikesExtra = (
645
+ src: Fighter,
646
+ tgt: Fighter,
647
+ amount: number,
648
+ spell: SpellDef | null,
649
+ isIgnoreMods: boolean,
650
+ ): void => {
651
+ if (src.sweepingStrikesCharges <= 0 || !isEnemySide(tgt.side) || amount <= 0) return;
652
+ const procId = spell?.id;
653
+ if (procId === SWEEPING_STRIKES_COPY_DAMAGE_SPELL_ID || procId === SWEEPING_STRIKES_NORMALIZED_SPELL_ID) return;
654
+ const combatPool = ctx.livingCombatEnemies?.() ?? livingEnemies();
655
+ const pool = combatPool.filter((e) => {
656
+ if (e.side === tgt.side || dead(e)) return false;
657
+ const d = distanceBetween(src, e);
658
+ // No geometry → allow (legacy multi-pack fixtures). Else ATTACK_DISTANCE (5 yd).
659
+ // Cite: Unit.cpp SelectRandomUnfriendlyTarget radius default ATTACK_DISTANCE.
660
+ if (d == null) return true;
661
+ return d <= MIN_AGGRO_DISTANCE;
662
+ });
663
+ if (pool.length === 0) return;
664
+ const other = pool[Math.floor(rng.next() * pool.length)]!;
665
+ src.sweepingStrikesCharges--;
666
+ const maxHp = other.spec.maxHealth || other.hp;
667
+ const hpFrac = maxHp > 0 ? other.hp / maxHp : 1;
668
+ const useNorm = sweepingStrikesUsesNormalized(procId, hpFrac);
669
+ let cleave: number;
670
+ if (useNorm) {
671
+ // 26654 NORMALIZED_WEAPON_DMG — roll MH normalized then armor-mitigate.
672
+ // Cite: Unit.cpp GetAPMultiplier; SpellEffects.cpp EffectWeaponDmg normalized.
673
+ const mh = src.spec.weapon;
674
+ const normSpeed = normalizedMeleeSpeedSec({
675
+ twoHand: mh.twoHand,
676
+ weaponSubclass: src.spec.mainhandWeaponSubclass,
677
+ });
678
+ const markAp = other.attackerAttackPowerBonus || 0;
679
+ const versusAp = sumVersusAp(src.attackPowerVersus, other.creatureType);
680
+ const rawNorm = meleeBaseDamage(
681
+ mh.dmgMin, mh.dmgMax, src.attackPower + markAp + versusAp, mh.speedMs, rng.next(),
682
+ {
683
+ normalized: true,
684
+ normalizedSpeedSec: normSpeed,
685
+ level: src.spec.level,
686
+ enchantFlat: (src.spec.enchantFlatMh ?? 0)
687
+ + (isIgnoreMods ? 0 : sumVersusForCreatureType(src.damageDoneCreature, other.creatureType)),
688
+ },
689
+ );
690
+ const physPct = src.damagePctBySchool.physical ?? 0;
691
+ const rawScaled = (!isIgnoreMods && physPct) ? rawNorm * (1 + physPct / 100) : rawNorm;
692
+ cleave = dealDamage(other, rawScaled, 'physical', src.spec.level, {
693
+ damageType: 'weapon', attacker: src, crit: false,
694
+ });
695
+ } else {
696
+ // 12723: basepoints[0] = damage already dealt (no second armor pass).
697
+ const beforeHp = other.hp;
698
+ other.hp = Math.max(0, Math.round(other.hp - amount));
699
+ cleave = beforeHp - other.hp;
700
+ onDamaged(other, cleave, 'weapon');
701
+ }
702
+ if (cleave > 0) applyDamageThreat(src, other, cleave, false, spell ?? undefined);
703
+ push({
704
+ t: ctx.t, kind: 'spell_hit', actor: src.side, target: other.side,
705
+ spellId: useNorm ? SWEEPING_STRIKES_NORMALIZED_SPELL_ID : SWEEPING_STRIKES_COPY_DAMAGE_SPELL_ID,
706
+ name: 'Sweeping Strikes', result: 'hit', amount: cleave, school: 'physical',
707
+ });
708
+ if (src.sweepingStrikesCharges === 0) {
709
+ for (const sid of [SWEEPING_STRIKES_PLAYER_SPELL_ID, SWEEPING_STRIKES_NPC_SPELL_ID]) {
710
+ src.buffActive.delete(sid);
711
+ }
712
+ for (const e of ctx.effectsOn(src.side)) {
713
+ if (e.kind === 'buff' && isSweepingStrikesAuraSpell(e.spellId)) {
714
+ e.expiresAt = Math.min(e.expiresAt, ctx.t);
715
+ touchEffect(e);
716
+ }
717
+ }
718
+ }
719
+ };
720
+ ctx.trySweepingStrikesExtra = trySweepingStrikesExtra;
721
+
722
+ type AbilityGates = ReturnType<typeof abilityDefense>;
723
+ // One attack context for every melee land die: the white swing, the weapon ability and the melee-class
724
+ // spell with no weapon damage. `white` is null for an ability. Cite: Unit.cpp:2816 MeleeSpellHitResult.
725
+ const meleeAtkCtx = (
726
+ src: Fighter,
727
+ tgt: Fighter,
728
+ spell: SpellDef | null,
729
+ hand: 'mainhand' | 'offhand',
730
+ gates: AbilityGates,
731
+ white: { nextSwingQueued: boolean; magicWhite: boolean; magicWhiteResistPercent: number } | null,
732
+ ) => {
733
+ const isAbility = white === null;
734
+ return {
735
+ atkLevel: src.spec.level, defLevel: tgt.spec.level, baseCrit: src.spec.critChance + (src.castCritAdd ?? 0),
736
+ defenderParries: gates.parry && canParryNow(tgt),
737
+ defenderBlocks: (!isAbility || gates.completelyBlocked) && !!tgt.spec.canBlock && canReactNow(tgt),
738
+ defenderDodge: gates.dodge && canReactNow(tgt) ? tgt.spec.dodgeChance : 0,
739
+ defenderParry: tgt.spec.parryChance,
740
+ defenderBlock: tgt.spec.blockChance,
741
+ attackerHit: src.spec.hitChance,
742
+ attackerWeaponSkill: weaponSkillForHand(src, hand),
743
+ defenderDefenseSkill: tgt.spec.defenseSkill,
744
+ attackerIsPlayerOrPet: isPlayerOrPetSide(src),
745
+ defenderIsPlayerOrPet: isPlayerOrPetSide(tgt),
746
+ dualWield: fighterDualWield(src),
747
+ isAbility,
748
+ onNextSwing: white?.nextSwingQueued ?? false,
749
+ wandUser: src.spec.wandUser,
750
+ casterGlance: src.spec.casterGlance,
751
+ attackType: 'melee' as const,
752
+ // Victim aura 187: signed points on the attacker's crit. Cite: Unit.cpp:3746.
753
+ defenderCritTakenPp: tgt.attackerMeleeCrit,
754
+ // Ex3 NO_AVOIDANCE bypasses the whole hit-result switch; NO_ACTIVE_DEFENSE clears the
755
+ // three reaction sides; a totem victim has no inherent miss chance at all.
756
+ noAvoidance: hasAttrEx3(spell, SPELL_ATTR_EX3.NO_AVOIDANCE),
757
+ noActiveDefense: hasAttr(spell, SPELL_ATTR.NO_ACTIVE_DEFENSE),
758
+ defenderIsTotem: isTotemCreatureType(tgt.spec.creatureType),
759
+ // WHITE swings only: `CanBlockInCombat(weaponSchoolMask)` reads the melee damage school,
760
+ // while an ability goes through CanBlockAbility, which `abilityDefense` already owns.
761
+ attackSchoolIsMagic: white?.magicWhite ?? false,
762
+ // Unit.cpp:3795-3806: the 100% resist bin of the partial-resist table is extra miss.
763
+ ...((white?.magicWhiteResistPercent ?? 0) > 0 ? { magicWhiteResistPercent: white!.magicWhiteResistPercent } : {}),
764
+ // Parry and block need the attacker in the defender's front arc; dodge needs it only
765
+ // from a player. Cite: Unit.cpp:3051/:3070/:3100.
766
+ attackerInDefendersFront: inFrontArc(tgt, src),
767
+ abilityDeflects: gates.deflect,
768
+ completelyBlocked: gates.completelyBlocked,
769
+ ...crushFields(src),
770
+ };
771
+ };
772
+
773
+ // Land die of a melee-class spell with no weapon damage (Bash, Kidney Shot): the strike's miss, dodge
774
+ // and parry plus the mechanic-resist side; failPct is miss + resist. Cite: Unit.cpp:2816-2856; :3988.
775
+ const meleeSpellHitResult = (
776
+ src: Fighter,
777
+ tgt: Fighter,
778
+ spell: SpellDef,
779
+ mechanicResistPct: number,
780
+ ): { result: HitResult | 'resist'; failPct: number } => {
781
+ const table = meleeAttackTable(meleeAtkCtx(src, tgt, spell, 'mainhand', abilityDefense(spell, 'melee'), null));
782
+ const resist = Math.max(0, Math.min(1, mechanicResistPct / 100));
783
+ const roll = rng.next();
784
+ const failPct = Math.min(100, (table.miss + resist) * 100);
785
+ if (roll < table.miss) { tryMeleeSkillGains(src, tgt, 'miss', 'mainhand'); return { result: 'miss', failPct }; }
786
+ if (roll < table.miss + resist) return { result: 'resist', failPct };
787
+ const result = pickMeleeSpellLand(table, roll - resist);
788
+ tryMeleeSkillGains(src, tgt, result, 'mainhand');
789
+ if (result === 'dodge' || result === 'parry' || result === 'block') {
790
+ stampDefensiveAuraState(tgt, result, ctx.t);
791
+ triggerTakenProcs(tgt, src, result, 'melee_ability');
792
+ }
793
+ if (result === 'parry') applyParryHaste(tgt);
794
+ return { result, failPct };
795
+ };
796
+
797
+ // Melee weapon strike: main-hand white, off-hand white, or a special. The off hand uses the OH
798
+ // weapon at 50% total and never arms an on-next-swing.
799
+ const weaponStrike = (
800
+ src: Fighter,
801
+ tgt: Fighter,
802
+ spell: SpellDef | null,
803
+ hand: 'mainhand' | 'offhand' = 'mainhand',
804
+ ): boolean => {
805
+ const t = ctx.t;
806
+ // The swing's real school. Non-physical for the 100 creature_template.DamageSchool rows, so
807
+ // the combat log stops calling a Fire Elemental's hit physical.
808
+ const swingSchool = handProfile(src, hand).school;
809
+ const emit = (result: HitResult, amount: number, blocked?: number): void => {
810
+ const extra = blocked && blocked > 0 ? { blocked } : {};
811
+ if (spell) {
812
+ push({ t, kind: 'spell_hit', actor: src.side, target: tgt.side, spellId: spell.id, name: spell.name, result, amount, school: swingSchool, ...extra });
813
+ } else if (src.extraAttacksExecuting && src.extraAttackSource) {
814
+ // DoExtraAttacks from Windfury Totem / Weapon — name the row for DPSMate.
815
+ const srcSpell = src.extraAttackSource;
816
+ push({
817
+ t, kind: 'spell_hit', actor: src.side, target: tgt.side,
818
+ spellId: srcSpell.spellId, name: srcSpell.name, result, amount, school: swingSchool, ...extra,
819
+ });
820
+ } else {
821
+ push({ t, kind: 'melee', actor: src.side, target: tgt.side, result, amount, ...extra });
822
+ }
823
+ };
824
+ // White and on-next-swing roll the full melee table; instant weapon abilities take the spell hit
825
+ // result then the spell crit roll. The dual-wield miss penalty applies to autos only.
826
+ const onNextSwing = !!spell && isNextMeleeSwingSpell(spell);
827
+ const isAbility = !!spell && !onNextSwing;
828
+ // The off-hand also skips the +19% while a strike still sits in CURRENT_MELEE_SPELL, which is
829
+ // `pendingSwing` until the main hand consumes it. Cite: Unit.cpp:3767.
830
+ const nextSwingQueued = onNextSwing || src.pendingSwing != null;
831
+ const magicWhite = !isAbility && swingSchool !== 'physical';
832
+ const magicWhiteResistPercent = magicWhite
833
+ ? effectiveMagicResistancePercent({
834
+ school: swingSchool,
835
+ resistance: tgt.resistances[swingSchool] ?? 0,
836
+ penetration: src.spec.resistancePenetration?.[swingSchool] ?? 0,
837
+ attackerLevel: src.spec.level,
838
+ victimLevel: tgt.spec.level,
839
+ victimIsNpc: isEnemySide(tgt.side),
840
+ })
841
+ : 0;
842
+ const gates = isAbility ? abilityDefense(spell, 'melee') : { dodge: true, parry: true, deflect: false, partialBlock: false, completelyBlocked: false };
843
+ const atkCtx = meleeAtkCtx(src, tgt, spell, hand, gates, isAbility ? null : {
844
+ nextSwingQueued, magicWhite, magicWhiteResistPercent,
845
+ });
846
+ // White DRAW ORDER: (1) table (2) glance amount if glancing (3) damage.
847
+ // Ability DRAW ORDER: (1) land table (2) crit if land (3) damage (4) partial block.
848
+ const tableRoll = rng.next();
849
+ const table = meleeAttackTable(atkCtx);
850
+ let result: HitResult = isAbility
851
+ ? pickMeleeSpellLand(table, tableRoll)
852
+ : pickMeleeOutcome(table, tableRoll);
853
+ // Skill gain on melee-based outcomes (including miss/dodge/parry). Cite: ProcSkillsAndReactives
854
+ tryMeleeSkillGains(src, tgt, result, hand);
855
+ // Remember the defender's dodge: dodge-reactive attacks (Overpower) read this for 5s.
856
+ // The aura state the outcome sets is class-dependent (rogue dodge and hunter parry differ).
857
+ if (result === 'dodge' || result === 'parry' || result === 'block') {
858
+ stampDefensiveAuraState(tgt, result, t);
859
+ triggerTakenProcs(tgt, src, result, isAbility ? 'melee_ability' : 'melee_swing');
860
+ }
861
+ if (result === 'parry') applyParryHaste(tgt);
862
+ // Any non-immune melee outcome is an attack: AttackedBy + EngageInCombatWithAggressor fire
863
+ // before the damage is dealt, so a whiffed opener still pulls. Cite: Unit.cpp:2202–2212.
864
+ engageOnHostileSpell(src, tgt, spell, true, true);
865
+ let glanceRoll = 0.5;
866
+ if (isAbility) {
867
+ if (result !== 'hit') { refundPowerOnMiss(src, spell, result); emit(result, 0); return false; }
868
+ // RollSpellCritOutcome: CanCrit(entry) refuses a non-player-controlled caster and any
869
+ // spell with no crittable effect before the die is rolled.
870
+ if (canCritWithAbility(src, spell) && rng.chance(calculateEffectiveCritChance(atkCtx))) result = 'crit';
871
+ } else {
872
+ glanceRoll = result === 'glancing' ? rng.next() : 0.5;
873
+ }
874
+ // Aura 169 raises the crit MULTIPLIER vs a creature type. Cite: Unit.cpp:3561.
875
+ const isIgnoreMods = spell ? hasAttrEx3(spell, SPELL_ATTR_EX3.IGNORE_CASTER_MODIFIERS) : false;
876
+ const critMult = isIgnoreMods ? 1.0 : critMultiplierWithVersus(
877
+ TUNING.meleeCritMult, src.critDamageVersusPct, tgt.creatureType,
878
+ );
879
+ const mult = meleeOutcomeMultiplier(atkCtx, result, critMult, glanceRoll);
880
+ if (mult === 0) { emit(result, 0); return false; } // miss / dodge / parry
881
+
882
+ // Weapon + speed for this hand (OH never uses form weapon bands — forms suppress OH).
883
+ const formOverride = hand === 'mainhand' && isAttackSpeedOverriddenShapeShift(src.form);
884
+ const wpn = handProfile(src, hand);
885
+ // CalculateMinMaxDamage: attack power over 14 times speed, plus the weapon, off hand halved. Melee
886
+ // path only. Enchant flat damage and the creature-flat aura are added here. Cite: StatSystem.cpp:346.
887
+ const enchantFlat =
888
+ (hand === 'offhand'
889
+ ? (src.spec.enchantFlatOh ?? 0)
890
+ : (src.spec.enchantFlatMh ?? 0))
891
+ + (isIgnoreMods ? 0 : sumVersusForCreatureType(src.damageDoneCreature, tgt.creatureType));
892
+ // Hunter's Mark / aura 127: victim grants attackers +AP (not +AP on the mob).
893
+ const markAp = tgt.attackerAttackPowerBonus || 0;
894
+ // MOD_MELEE_ATTACK_POWER_VERSUS (102): AP only when target creature type matches mask.
895
+ // Cite: Unit.cpp:7475–7476 MeleeDamageBonusDone APbonus; :7559 APbonus/14×speed.
896
+ const versusAp = isIgnoreMods ? 0 : sumVersusAp(src.attackPowerVersus, tgt.creatureType);
897
+ let raw = meleeBaseDamage(wpn.dmgMin, wpn.dmgMax, src.attackPower + markAp + versusAp, wpn.speedMs, rng.next(), {
898
+ formOverride,
899
+ level: src.spec.level,
900
+ totalPct: hand === 'offhand' ? offhandDamageTotalPct(src.offhandDamagePct) : 1,
901
+ enchantFlat,
902
+ });
903
+ // SPELL_EFFECT_WEAPON_PERCENT_DAMAGE multiplies the weapon roll; strikeBonus is the DBC
904
+ // basePoints and stays additive AFTER it (Backstab r4 = pct 150 then flat 46).
905
+ const pctEff = spell?.effects?.find((e) => e.kind === 'weapon_percent_damage');
906
+ if (pctEff && ((pctEff as { pct?: number }).pct ?? 0) > 0) {
907
+ raw = resolveWeaponPercentDamage({ weaponDamage: raw, pct: (pctEff as { pct: number }).pct }).damage;
908
+ }
909
+ if (spell?.strikeBonus) raw += spell.strikeBonus;
910
+ // Outcome multiplier, then the victim's clean damage for taken rage: block is stripped from HP
911
+ // only, a glance adds the glanced-away amount back, and a crush leaves clean at the base.
912
+ const baseForOutcome = raw;
913
+ raw *= mult; // glancing (<1) / crit|crush (>1) / hit|block (1)
914
+ let cleanForRage =
915
+ result === 'glancing' ? 2 * baseForOutcome - raw
916
+ : result === 'crushing' ? baseForOutcome
917
+ : raw;
918
+ // White block: flat absorb on the land die. Yellow: independent partial-block roll after crit.
919
+ // Cite: Unit.cpp MELEE_HIT_BLOCK ~2033; RollAbilityPartialBlockOutcome ~3388.
920
+ let blockedAmt = 0;
921
+ if (result === 'block') {
922
+ raw = applyShieldBlockAbsorb(raw, tgt.spec.blockValue ?? 0, tgt.spec.blockValuePct ?? 0);
923
+ } else if (isAbility && gates.partialBlock && tgt.spec.canBlock && canReactNow(tgt)) {
924
+ const blockCtx = { ...atkCtx, defenderBlocks: true };
925
+ if (rng.chance(calculateEffectiveBlockChance(blockCtx))) {
926
+ const after = applyShieldBlockAbsorb(raw, tgt.spec.blockValue ?? 0, tgt.spec.blockValuePct ?? 0);
927
+ blockedAmt = raw - after;
928
+ raw = after;
929
+ cleanForRage = Math.max(0, cleanForRage - blockedAmt);
930
+ if (blockedAmt > 0) stampDefensiveAuraState(tgt, 'block', t);
931
+ }
932
+ }
933
+ const physPct = src.damagePctBySchool.physical ?? 0;
934
+ if (!isIgnoreMods && physPct) {
935
+ const f = 1 + physPct / 100;
936
+ raw *= f;
937
+ cleanForRage *= f;
938
+ }
939
+ // MOD_DAMAGE_DONE_VERSUS (168) multiplier. Cite: Unit.cpp:7503 DoneTotalMod *= …
940
+ const versusPct = sumVersusForCreatureType(src.damageDoneVersusPct, tgt.creatureType);
941
+ if (!isIgnoreMods && versusPct) {
942
+ const f = 1 + versusPct / 100;
943
+ raw *= f;
944
+ cleanForRage *= f;
945
+ }
946
+ // creature_template.DamageSchool: a white hit from 97 templates is MAGICAL, and dealDamage already
947
+ // branches on school, so passing it is the whole fix — resistance applies and armor does not.
948
+ const amount = dealDamage(tgt, raw, wpn.school, src.spec.level, {
949
+ damageType: 'weapon', attacker: src, spell, crit: result === 'crit',
950
+ cleanDamage: cleanForRage,
951
+ });
952
+ emit(result, amount, blockedAmt);
953
+ // An NPC white hit landing in a player's back arc can daze (spell 1604, -50% speed). Melee
954
+ // abilities do not go through DealMeleeDamage, so this is autos only. Cite: Unit.cpp:2225-2229.
955
+ if (amount > 0 && !isAbility) maybeDaze(src, tgt, atkCtx, 'melee');
956
+ // Attacker white-swing rage uses final HP damage (Unit.cpp:910 RewardRage(damage, true)).
957
+ // Full block → amount 0 → no dealer rage; victim still gets cleanDamage rage inside dealDamage.
958
+ if (amount > 0) {
959
+ // Rage from weapon damage dealt, and only for a white swing: spell-driven weapon hits are
960
+ // SPELL_DIRECT_DAMAGE and award none, and an on-next-swing replaces the white entirely.
961
+ if (!spell && src.spec.powerType === 'rage') {
962
+ gainRage(src, rewardRage(amount, src.spec.level, true));
963
+ }
964
+ if (spell) applySpellThreatBonus(src, spell, isEnemySide(tgt.side) ? tgt : undefined);
965
+ // An ability hit is SUCCESSFUL_MELEE_SPELL_HIT (0x10), not a white swing (0x04): hardcoding the
966
+ // swing flag failed every ability proc, Seal Fate included. Cite: Unit.cpp DealMeleeDamage.
967
+ triggerProcs(src, tgt, result === 'crit', isAbility ? 'melee_ability' : 'melee_swing', spell ?? null);
968
+ // Aura-42 self-buffs (Seal of Justice): proc on deal melee via session RNG + PPM.
969
+ // Gated on live proc_trigger_spell with DEAL flags — no aura ⇒ no RNG draw.
970
+ fireDealMeleeAura42(
971
+ src,
972
+ tgt,
973
+ result === 'crit',
974
+ spell && !isNextMeleeSwingSpell(spell) ? 'melee_ability' : 'melee_swing',
975
+ hand,
976
+ );
977
+ // Flurry haste charges: main target only, not during Windfury extra attacks.
978
+ // Cite: UnitAuraProcHandler.cpp:749–762 HandleHasteAuraProc.
979
+ {
980
+ const mainOk = flurryMayConsumeCharge({
981
+ victimIsMainTarget: true, // weaponStrike is always the selected foe
982
+ extraAttacksExecuting: !!src.extraAttacksExecuting,
983
+ });
984
+ if (mainOk) {
985
+ const mine = ctx.effectsOn(src.side);
986
+ for (let i = mine.length - 1; i >= 0; i--) {
987
+ const e = mine[i]!;
988
+ if (e.kind !== 'buff' || !isFlurryHasteRank(e.spellId)) continue;
989
+ if (e.charges == null || e.charges <= 0) continue;
990
+ const { remaining, remove } = consumeProcCharge(e.charges);
991
+ if (remove) {
992
+ reverseBuffMod(src, e.mod, e.amount ?? 0, e.school, {
993
+ creatureTypeMask: e.creatureTypeMask,
994
+ schoolMask: e.schoolMask,
995
+ });
996
+ src.buffActive.delete(e.spellId);
997
+ const idx = effects.lastIndexOf(e);
998
+ if (idx >= 0) effects.splice(idx, 1);
999
+ push({ t, kind: 'aura', actor: src.side, name: e.name, applied: false });
1000
+ } else {
1001
+ e.charges = remaining;
1002
+ }
1003
+ }
1004
+ }
1005
+ }
1006
+ rollGearProcs(src, tgt, result === 'crit' ? 'crit' : 'hit', spell ?? null);
1007
+ applySealOnSwing(src, tgt, hand); // paladin seal holy proc rides a landed swing
1008
+ applyWeaponImbueOnSwing(src, tgt, hand); // Rockbiter / WF / FT / poison TEMP enchant
1009
+ // Builder combo points on a landed weapon strike; an off-hand white generates none. Prefer the
1010
+ // stamped count and fall back to the effect row. Cite: SpellEffects.cpp:4213-4225.
1011
+ const cpAmt = spell?.comboPointsGenerated
1012
+ ?? (spell?.effects ?? []).find((e) => e.kind === 'combo_points')?.amount;
1013
+ if (cpAmt) addComboPoints(src, tgt, cpAmt);
1014
+ trySweepingStrikesExtra(src, tgt, amount, spell, isIgnoreMods);
1015
+ }
1016
+ return true;
1017
+ };
1018
+
1019
+ // A ranged attack with the equipped weapon: a bow rolls the full table with ranged attack type
1020
+ // (no parry), a wand keeps hit and crit. Bows and guns consume one ammo per shot; wands none.
1021
+ const rangedStrike = (src: Fighter, tgt: Fighter, spell: SpellDef | null): boolean => {
1022
+ const t = ctx.t;
1023
+ const rw = src.spec.ranged;
1024
+ if (!rw) return false;
1025
+ const emit = (result: HitResult, amount: number, blocked?: number): void => {
1026
+ const extra = blocked && blocked > 0 ? { blocked } : {};
1027
+ if (spell) {
1028
+ push({ t, kind: 'spell_hit', actor: src.side, target: tgt.side, spellId: spell.id, name: spell.name, result, amount, school: rw.school, ...extra });
1029
+ return;
1030
+ }
1031
+ // Tag free shoot-toggle id on whites (Auto Shot 75 / Shoot / Throw). Cite: KI-870; Unit.cpp ranged auto.
1032
+ let logId: number | undefined;
1033
+ let logName: string | undefined;
1034
+ for (const s of src.spec.spells ?? []) {
1035
+ if (roleOf(s) !== 'shoot') continue;
1036
+ if ((s.cost ?? 0) > 0 || (s.castTimeMs ?? 0) > 0 || (s.gcdMs ?? 0) > 0) continue;
1037
+ logId = s.id;
1038
+ logName = s.name;
1039
+ break;
1040
+ }
1041
+ if (logId != null) {
1042
+ push({ t, kind: 'melee', actor: src.side, target: tgt.side, result, amount, spellId: logId, name: logName, ...extra });
1043
+ } else {
1044
+ push({ t, kind: 'melee', actor: src.side, target: tgt.side, result, amount, ...extra });
1045
+ }
1046
+ };
1047
+
1048
+ // Physical bow/gun: skill-based attack table (parry forced 0 via attackType ranged).
1049
+ if (rw.school === 'physical') {
1050
+ // --- Ammo gate (before table roll so misses don't free-fire without ammo) ---
1051
+ const wSub = src.spec.rangedWeaponSubclass;
1052
+ const isThrown = wSub === 16 || src.spec.thrownRemaining != null;
1053
+ // Bow / gun / crossbow need projectile ammo (player only — NPC ranged has no bags).
1054
+ // Cite: Spell.cpp CheckCast NO_AMMO 6730–6793 (player branch).
1055
+ const needsProjectile =
1056
+ src.side === 'player'
1057
+ && (wSub === 2 || wSub === 3 || wSub === 18
1058
+ // Fail closed when subclass missing on physical non-thrown player ranged.
1059
+ || (wSub == null && !isThrown));
1060
+ if (needsProjectile) {
1061
+ const rem = src.spec.ammoRemaining ?? 0;
1062
+ const aSub = src.spec.ammoSubclass;
1063
+ const okEntry = src.spec.ammoEntry != null && rem >= 1;
1064
+ // Known subclass → arrow/bullet match; unknown → any live projectile with count.
1065
+ const okCompat =
1066
+ wSub == null
1067
+ ? aSub != null
1068
+ : aSub != null
1069
+ && ((wSub === 2 || wSub === 18) ? aSub === 2 : wSub === 3 ? aSub === 3 : false);
1070
+ if (!okEntry || !okCompat) {
1071
+ // SPELL_FAILED_NO_AMMO — no free shot
1072
+ emit('miss', 0);
1073
+ return false;
1074
+ }
1075
+ } else if (isThrown && src.side === 'player') {
1076
+ if (src.spec.thrownRemaining != null && src.spec.thrownRemaining < 1) {
1077
+ emit('miss', 0);
1078
+ return false;
1079
+ }
1080
+ }
1081
+
1082
+ const onNextSwing = !!spell?.onNextSwing;
1083
+ const isAbility = !!spell && !onNextSwing;
1084
+ const gates = isAbility ? abilityDefense(spell, 'ranged') : { dodge: true, parry: false, deflect: false, partialBlock: false, completelyBlocked: false };
1085
+ const atkCtx = {
1086
+ atkLevel: src.spec.level, defLevel: tgt.spec.level, baseCrit: src.spec.critChance + (src.castCritAdd ?? 0),
1087
+ defenderParries: gates.parry && canParryNow(tgt),
1088
+ defenderBlocks: (!isAbility || gates.completelyBlocked) && !!tgt.spec.canBlock && canReactNow(tgt),
1089
+ defenderDodge: gates.dodge && canReactNow(tgt) ? tgt.spec.dodgeChance : 0,
1090
+ defenderParry: tgt.spec.parryChance,
1091
+ defenderBlock: tgt.spec.blockChance,
1092
+ attackerHit: src.spec.hitChance,
1093
+ attackerWeaponSkill: weaponSkillForHand(src, 'ranged'),
1094
+ defenderDefenseSkill: tgt.spec.defenseSkill,
1095
+ attackerIsPlayerOrPet: isPlayerOrPetSide(src),
1096
+ defenderIsPlayerOrPet: isPlayerOrPetSide(tgt),
1097
+ dualWield: false,
1098
+ isAbility,
1099
+ onNextSwing,
1100
+ wandUser: src.spec.wandUser,
1101
+ casterGlance: src.spec.casterGlance,
1102
+ attackType: 'ranged' as const,
1103
+ // Victim auras 188 (crit) and 185 (hit). Cite: Unit.cpp:3746, :3794.
1104
+ defenderCritTakenPp: tgt.attackerRangedCrit,
1105
+ defenderHitTakenPp: tgt.attackerRangedHit,
1106
+ noAvoidance: hasAttrEx3(spell, SPELL_ATTR_EX3.NO_AVOIDANCE),
1107
+ noActiveDefense: hasAttr(spell, SPELL_ATTR.NO_ACTIVE_DEFENSE),
1108
+ defenderIsTotem: isTotemCreatureType(tgt.spec.creatureType),
1109
+ attackerInDefendersFront: inFrontArc(tgt, src),
1110
+ abilityDeflects: gates.deflect,
1111
+ completelyBlocked: gates.completelyBlocked,
1112
+ ...crushFields(src),
1113
+ };
1114
+ const tableRoll = rng.next();
1115
+ const table = meleeAttackTable(atkCtx);
1116
+ let result: HitResult = isAbility
1117
+ ? pickMeleeSpellLand(table, tableRoll)
1118
+ : remapRangedWhiteOutcome(pickMeleeOutcome(table, tableRoll));
1119
+ tryMeleeSkillGains(src, tgt, result, 'ranged');
1120
+ if (result === 'dodge' || result === 'parry' || result === 'block') {
1121
+ stampDefensiveAuraState(tgt, result, t);
1122
+ triggerTakenProcs(tgt, src, result, isAbility ? 'ranged_ability' : 'ranged_attack');
1123
+ }
1124
+ if (result === 'parry') applyParryHaste(tgt);
1125
+ engageOnHostileSpell(src, tgt, spell, true); // Unit.cpp:2202–2212 — a whiffed shot still pulls
1126
+ let glanceRoll = 0.5;
1127
+ if (isAbility) {
1128
+ if (result !== 'hit') { refundPowerOnMiss(src, spell, result); emit(result, 0); return false; }
1129
+ if (canCritWithAbility(src, spell) && rng.chance(calculateEffectiveCritChance(atkCtx))) result = 'crit';
1130
+ } else {
1131
+ glanceRoll = result === 'glancing' ? rng.next() : 0.5;
1132
+ }
1133
+ const isIgnoreMods = spell ? hasAttrEx3(spell, SPELL_ATTR_EX3.IGNORE_CASTER_MODIFIERS) : false;
1134
+ const critMult = isIgnoreMods ? 1.0 : critMultiplierWithVersus(
1135
+ TUNING.meleeCritMult, src.critDamageVersusPct, tgt.creatureType,
1136
+ );
1137
+ const mult = meleeOutcomeMultiplier(atkCtx, result, critMult, glanceRoll);
1138
+ if (mult === 0) { emit(result, 0); return false; }
1139
+ // MOD_RANGED_ATTACK_POWER_VERSUS (131). Cite: Unit.cpp:7471.
1140
+ const versusRap = isIgnoreMods ? 0 : sumVersusAp(src.rangedAttackPowerVersus, tgt.creatureType);
1141
+ const markRap = tgt.attackerAttackPowerBonus || 0;
1142
+ const ap = (src.spec.rangedAttackPower ?? src.attackPower) + versusRap + markRap;
1143
+ // Ammo DPS only while remaining count > 0 (zero after last arrow consumed).
1144
+ const ammoLive = needsProjectile && (src.spec.ammoRemaining ?? 0) > 0;
1145
+ let raw = meleeBaseDamage(rw.dmgMin, rw.dmgMax, ap, rw.speedMs, rng.next(), {
1146
+ enchantFlat: (src.spec.enchantFlatRanged ?? 0)
1147
+ + sumVersusForCreatureType(src.damageDoneCreature, tgt.creatureType),
1148
+ ammoDpsMin: ammoLive ? (src.spec.ammoDpsMin ?? 0) : 0,
1149
+ ammoDpsMax: ammoLive ? (src.spec.ammoDpsMax ?? 0) : 0,
1150
+ });
1151
+ // Aimed Shot ranks: SPELL_EFFECT_NORMALIZED_WEAPON_DMG flat bonus (bake strikeBonus).
1152
+ // Cite: SpellEffects.cpp EffectWeaponDmg :3445; map-spell-effects eff 121; KI-164.
1153
+ if (spell?.strikeBonus) raw += spell.strikeBonus;
1154
+ const baseForOutcome = raw;
1155
+ raw *= mult;
1156
+ let cleanForRage =
1157
+ result === 'glancing' ? 2 * baseForOutcome - raw
1158
+ : result === 'crushing' ? baseForOutcome
1159
+ : raw;
1160
+ let blockedAmt = 0;
1161
+ if (result === 'block') {
1162
+ raw = applyShieldBlockAbsorb(raw, tgt.spec.blockValue ?? 0, tgt.spec.blockValuePct ?? 0);
1163
+ } else if (isAbility && gates.partialBlock && tgt.spec.canBlock && canReactNow(tgt)) {
1164
+ const blockCtx = { ...atkCtx, defenderBlocks: true };
1165
+ if (rng.chance(calculateEffectiveBlockChance(blockCtx))) {
1166
+ const after = applyShieldBlockAbsorb(raw, tgt.spec.blockValue ?? 0, tgt.spec.blockValuePct ?? 0);
1167
+ blockedAmt = raw - after;
1168
+ raw = after;
1169
+ cleanForRage = Math.max(0, cleanForRage - blockedAmt);
1170
+ if (blockedAmt > 0) stampDefensiveAuraState(tgt, 'block', t);
1171
+ }
1172
+ }
1173
+ const dmgPct = src.damagePctBySchool.physical ?? 0;
1174
+ if (!isIgnoreMods && dmgPct) {
1175
+ const f = 1 + dmgPct / 100;
1176
+ raw *= f;
1177
+ cleanForRage *= f;
1178
+ }
1179
+ const amount = dealDamage(tgt, raw, 'physical', src.spec.level, {
1180
+ binary: false, damageType: 'spell', attacker: src, ranged: true,
1181
+ cleanDamage: cleanForRage,
1182
+ });
1183
+ emit(result, amount, blockedAmt);
1184
+ // TakeAmmo runs on the successful cast path, not per hit-table row, so consume one projectile
1185
+ // per attempt that passed the ammo gate.
1186
+ if (src.side === 'player') {
1187
+ if (needsProjectile && src.spec.ammoEntry != null && (src.spec.ammoRemaining ?? 0) > 0) {
1188
+ src.spec.ammoRemaining = (src.spec.ammoRemaining ?? 1) - 1;
1189
+ itemsSpent.set(src.spec.ammoEntry, (itemsSpent.get(src.spec.ammoEntry) ?? 0) + 1);
1190
+ if ((src.spec.ammoRemaining ?? 0) < 1) {
1191
+ src.spec.ammoDpsMin = 0;
1192
+ src.spec.ammoDpsMax = 0;
1193
+ }
1194
+ } else if (isThrown && src.spec.thrownRemaining != null && src.spec.thrownRemaining > 0) {
1195
+ src.spec.thrownRemaining -= 1;
1196
+ if (src.spec.thrownEntry != null) {
1197
+ itemsSpent.set(src.spec.thrownEntry, (itemsSpent.get(src.spec.thrownEntry) ?? 0) + 1);
1198
+ }
1199
+ }
1200
+ }
1201
+ if (amount > 0) {
1202
+ // A shot is a RANGED proc event, never a melee one: tagging it melee matched every melee
1203
+ // ability aura the shooter carried and no ranged aura at all.
1204
+ triggerProcs(src, tgt, result === 'crit', isAbility ? 'ranged_ability' : 'ranged_attack', spell);
1205
+ rollGearProcs(src, tgt, result === 'crit' ? 'crit' : 'hit', spell);
1206
+ }
1207
+ return true;
1208
+ }
1209
+
1210
+ // Wand / non-physical school: legacy simple miss + separate crit (not full table).
1211
+ engageOnHostileSpell(src, tgt, spell, true);
1212
+ if (!rng.chance(src.spec.hitChance)) { emit('miss', 0); return false; }
1213
+ // White wand/Shoot may pass spell=null (stepSession shootAuto). Local white-swing
1214
+ // contract after Shoot toggle — CMaNGOS re-casts 5019 each shot (Unit.cpp:4192).
1215
+ let spellCrit = fighterSpellCrit(src) * 100 + ((spell ? (spell as { _talentCritBonus?: number })._talentCritBonus : 0) || 0);
1216
+ if (spell) spellCrit = applyOwnedSpellMod(src, spell, SPELLMOD_CRITICAL_CHANCE, spellCrit);
1217
+ spellCrit = spellCrit / 100 + (src.castCritAdd ?? 0);
1218
+ // A white wand/Shoot swing passes spell=null and keeps its crit; a real spell must pass
1219
+ // IsSpellAbleToCrit. Cite: SpellMgr.h:327-339; Unit.cpp:3878.
1220
+ const crit = (!spell || spellCanCrit(spell)) && rng.chance(spellCritChance(
1221
+ spellCrit, src.spec.level, tgt.spec.level,
1222
+ spell?.dmgClass ?? SPELL_DAMAGE_CLASS.RANGED,
1223
+ ));
1224
+ let raw = meleeBaseDamage(rw.dmgMin, rw.dmgMax, 0, rw.speedMs, rng.next());
1225
+ const isIgnoreMods = spell ? hasAttrEx3(spell, SPELL_ATTR_EX3.IGNORE_CASTER_MODIFIERS) : false;
1226
+ if (crit && !isIgnoreMods) raw *= TUNING.meleeCritMult;
1227
+ const dmgPct = src.damagePctBySchool[rw.school] ?? 0;
1228
+ if (!isIgnoreMods && dmgPct) raw *= (1 + dmgPct / 100);
1229
+ const amount = dealDamage(tgt, raw, rw.school, src.spec.level, {
1230
+ binary: false, damageType: 'spell', attacker: src,
1231
+ });
1232
+ emit(crit ? 'crit' : 'hit', amount);
1233
+ if (amount > 0) {
1234
+ // A wand shot is a ranged attack, not a harmful spell cast — the harmful-spell flag fired every
1235
+ // spell proc the wielder had on each wand tick. Cite: Unit.cpp:4192 ranged auto-repeat.
1236
+ triggerProcs(src, tgt, !!crit, spell && !spell.onNextSwing ? 'ranged_ability' : 'ranged_attack', spell);
1237
+ rollGearProcs(src, tgt, crit ? 'crit' : 'hit', spell);
1238
+ }
1239
+ return true;
1240
+ };
1241
+
1242
+ ctx.weaponStrike = weaponStrike;
1243
+ ctx.meleeSpellHitResult = meleeSpellHitResult;
1244
+ ctx.rangedStrike = rangedStrike;
1245
+ ctx.tryMeleeSkillGains = tryMeleeSkillGains;
1246
+ ctx.isPlayerOrPetSide = isPlayerOrPetSide;
1247
+ ctx.fighterDualWield = fighterDualWield;
1248
+ }