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,824 @@
1
+ // Bridge between the live combat session and the pure rotation engine. Each tick the driver
2
+ // asks `pickCombatAbility` what to do; it assembles a RotationSnapshot from the session views +
3
+ // character/inventory and consults the rotation, returning a spell id, an item use, or null
4
+ // (auto-attack). PURE (given a session).
5
+
6
+ import { type CombatSession, type CombatAction, type AbilityStatus, DRAIN_SOUL_ID } from '../engine/combat.ts';
7
+ import { type AllySide } from '../engine/core/events.ts';
8
+ import type { CombatantId } from '../engine/combat/threat.ts';
9
+ import type { GameState } from '../engine/game.ts';
10
+ import type { Game } from '../engine/game/types.ts';
11
+ import type { DataStore } from '../data/dataStore.ts';
12
+ import { CLASS, type TotemSlot } from '../data/schema.ts';
13
+ import { maxHealth, maxPower, powerType, getTempEnchant, getActiveBuffIds, currentMana, equippedEntry } from '../domain/character/character.ts';
14
+ import type { EquippedValue } from '../domain/items/itemInstance.ts';
15
+ import { petMaxHealth, petMaxPower } from '../domain/pet/pet.ts';
16
+ import { openTempWeaponImbueHands } from '../domain/professions/weaponImbueHand.ts';
17
+ import { knows } from '../domain/character/spellbook.ts';
18
+ import { SOUL_SHARD_ITEM } from '../domain/core/ids.ts';
19
+ import { bestCarriedFamilyItem } from '../domain/items/consumableFamily.ts';
20
+ import { itemUseCooldownRemainingMs } from '../domain/items/itemUseCooldown.ts';
21
+ import { normalizeAutopilotConfig } from './config.ts';
22
+ import {
23
+ chooseCombatAction, chooseOocAction,
24
+ isGroundTargetedMulti,
25
+ isMultiTargetCombatSpell,
26
+ isNovaKiteSpell,
27
+ isPiercingHowlSpell,
28
+ piercingHowlRefused,
29
+ multiTargetNeedsPackDensity,
30
+ isPackSuppressedStFiller,
31
+ isThreatOrTauntUtility,
32
+ isInterruptSpell,
33
+ isPureInterruptSpell,
34
+ isDispelSpell,
35
+ haltDpsCoversEntry,
36
+ peekHaltDpsSchool,
37
+ haltDpsRefusesSchool,
38
+ FROST_NOVA_RANK_IDS,
39
+ multiTargetGateYards,
40
+ novaDiscOccupied,
41
+ type ItemRuleTarget, type Rotation, type RotationSnapshot, type SubjectState,
42
+ type RotationRule,
43
+ type RuleAction,
44
+ type Condition,
45
+ isLowestHpSpellTarget,
46
+ } from '../domain/rotation/rotation.ts';
47
+ import { preferAoeRules, spellIsHeal } from '../domain/bots/companionRotation.ts';
48
+ import { encodeAoeMode, type CompanionAoeMode } from '../domain/bots/squad.ts';
49
+ import { oocPetCallReviveFailReason } from '../engine/game/oocCast.ts';
50
+ import { keepUpAlreadyPresent } from '../engine/spells/auraStacking.ts';
51
+ import { inferRole } from '../engine/combat.ts';
52
+ import { formIdFromSpell, isFormSpell, isMobilityFormSpellId, isMobilityTravelForm } from '../domain/character/forms.ts';
53
+ import { DISPEL_TYPE } from '../domain/spells/dispel.ts';
54
+ import { spellAllowedAtLocation } from '../engine/spells/spellAttributes.ts';
55
+
56
+ /** Paperdoll entries for `itemEquipped`. Cite: itemInstance.ts equippedEntry; buildCombatant.ts:175. */
57
+ function equippedEntrySet(char: { equipped?: Partial<Record<string, EquippedValue>> }): Set<number> {
58
+ const out = new Set<number>();
59
+ for (const v of Object.values(char.equipped ?? {})) {
60
+ const e = equippedEntry(v);
61
+ if (e) out.add(e);
62
+ }
63
+ return out;
64
+ }
65
+
66
+ /** Emergency ally heal threshold — same triage band as off-heal pack gates. */
67
+ const PLAYER_OFF_HEAL_PCT = 40;
68
+ /** Cite: invent.ts HEALER_DRY_POWER_PCT. */
69
+ const HEALER_DRY_POWER_PCT = 5;
70
+
71
+ function spellHealsFromDef(spell: { effects?: ReadonlyArray<{ kind: string }> }): boolean {
72
+ return spellIsHeal(spell);
73
+ }
74
+
75
+ /** Effect kinds a `halt_dps` row is about — the damage a threat watch has to stop. */
76
+ const DAMAGE_EFFECT_KINDS: ReadonlySet<string> = new Set([
77
+ 'direct_damage', 'dot', 'weapon_percent_damage', 'weapon_damage', 'weapon_strike',
78
+ 'health_leech', 'holy_shock', 'judgement', 'power_burn',
79
+ ]);
80
+
81
+ /** True when casting this spell would deal damage (so an armed halt should refuse it). */
82
+ function spellDealsDamage(spell: { effects?: ReadonlyArray<{ kind: string }> | null }): boolean {
83
+ return (spell.effects ?? []).some((e) => DAMAGE_EFFECT_KINDS.has(e.kind));
84
+ }
85
+
86
+ const pct = (v: number, max: number): number => (max > 0 ? (v / max) * 100 : 0);
87
+
88
+
89
+ /**
90
+ * Auto-combat's Soul-Shard cap. Drain Soul is a channeled shard-farmer that's wasted once you're
91
+ * stocked, so the rotation stops offering it as "usable" when you already hold `keepShards`+ shards
92
+ * (the `keepSoulShards` autopilot setting). Returns true = suppress the auto-cast. `keepShards <= 0`
93
+ * means "never auto-farm shards". Only Drain Soul is affected; manual casts are never gated here.
94
+ */
95
+ export function shardFarmSuppressed(spellId: number, shardCount: number, keepShards: number): boolean {
96
+ return spellId === DRAIN_SOUL_ID && shardCount >= keepShards;
97
+ }
98
+
99
+ /** Assemble the rotation snapshot from the live session + character/inventory context. */
100
+ export function combatSnapshot(
101
+ session: CombatSession,
102
+ state: GameState,
103
+ ds: DataStore,
104
+ kit?: readonly AbilityStatus[],
105
+ ): RotationSnapshot {
106
+ const pv = session.playerView();
107
+ const usable = new Map<number, boolean>();
108
+ const multiSpellIds = new Set<number>();
109
+ const totemSummonIds = new Set<number>();
110
+ const tauntSpellIds = new Set<number>();
111
+ const controlSpellIds = new Set<number>();
112
+ const interruptSpellIds = new Set<number>();
113
+ const dispelSpellIds = new Set<number>();
114
+ const abilityById = new Map<number, {
115
+ name?: string;
116
+ aoe?: boolean;
117
+ aoeShape?: string;
118
+ radiusYd?: number;
119
+ rangeMax?: number;
120
+ /** SpellEntry.Attributes — read for SPELL_ATTR_ONLY_OUTDOORS. */
121
+ attributes?: number;
122
+ effects?: ReadonlyArray<{ kind: string; dispelType?: number }>;
123
+ }>();
124
+ let hasCleave = false;
125
+ let hasSwipe = false;
126
+ for (const a of kit ?? session.abilities()) {
127
+ usable.set(a.spell.id, a.usable);
128
+ abilityById.set(a.spell.id, a.spell);
129
+ if (isMultiTargetCombatSpell(a.spell)) multiSpellIds.add(a.spell.id);
130
+ const nm = (a.spell.name ?? '').toLowerCase();
131
+ if (nm === 'cleave') hasCleave = true;
132
+ if (nm === 'swipe') hasSwipe = true;
133
+ // Session stubs / thin ability rows may omit effects[] — only classify full shapes.
134
+ const sp = a.spell as { id: number; role?: string; effects?: ReadonlyArray<{ kind: string }>; name?: string };
135
+ if (sp.role === 'totem_summon'
136
+ || (Array.isArray(sp.effects) && sp.effects.some((e) => e.kind === 'summon_totem'))) {
137
+ totemSummonIds.add(sp.id);
138
+ }
139
+ if (Array.isArray(sp.effects)) {
140
+ if (isThreatOrTauntUtility(sp as never)) tauntSpellIds.add(sp.id);
141
+ const role = sp.role ?? inferRole(sp as never);
142
+ if (role === 'control' || isInterruptSpell(sp as never)) controlSpellIds.add(sp.id);
143
+ if (isInterruptSpell(sp as never)) interruptSpellIds.add(sp.id);
144
+ if (isDispelSpell(sp as never)) dispelSpellIds.add(sp.id);
145
+ } else if (sp.role === 'control') {
146
+ controlSpellIds.add(sp.id);
147
+ }
148
+ }
149
+ const usableItems = new Map<number, boolean>();
150
+ // Best carried rung per rank ladder — a family rotation row ("Healing Potion") resolves here.
151
+ const bestByFamily = new Map<string, { entry: number; rank: number }>();
152
+ for (const s of session.items()) {
153
+ usableItems.set(s.item.entry, s.usable);
154
+ const fam = s.item.family;
155
+ if (fam == null || s.count <= 0) continue;
156
+ const rank = s.item.rank ?? 0;
157
+ const cur = bestByFamily.get(fam);
158
+ if (!cur || rank > cur.rank) bestByFamily.set(fam, { entry: s.item.entry, rank });
159
+ }
160
+
161
+ /**
162
+ * World state comes from the ENGINE's snapshot — the same object a companion's provider is
163
+ * handed — with the player's own focus pinned as the foe.
164
+ *
165
+ * This function used to rebuild all of it, and the two copies had drifted. Measured over 100
166
+ * snapshot pairs in a real BRD fight (probe-snapdiff): 23 of 27 fields were already identical,
167
+ * and every difference was the player being BLIND to something companions saw — self and target
168
+ * `rooted`/`stunned`/`feared`/`snared` all undefined, `target.level` 0 instead of 52,
169
+ * `target.comboPoints` and `formId` undefined, both dispellable sets missing (so a player
170
+ * Cleanse/Purge rule could never gate on having something to strip), and every ally `distYd`
171
+ * null (so PoH / Chain Heal range gates could not fire). Two builders for one evaluator is
172
+ * also how the pull-spent scan came to exist twice and drift.
173
+ *
174
+ * Overlaid below: only what the engine cannot see — the real bag, the autopilot knobs, creature
175
+ * type, the totem-twisting nudge, char-side weapon enchants, and the player's usability policy.
176
+ */
177
+ // The ONE-argument form is the memoized facade read (simFacade MEMO_READS_1); the pinned-foe form
178
+ // falls through and, behind the Worker the live floor ticks in, comes back without live aura state
179
+ // — every `target.*` gate then read an empty debuff list and re-fired forever (Hunter's Mark
180
+ // measured 40 casts over 7 bodies). The engine resolves the same foe internally.
181
+ const base = session.rotationSnapshot('player');
182
+ if (!base) throw new Error('combatSnapshot: session has no player unit');
183
+
184
+ const itemCounts = new Map<number, number>();
185
+ for (const s of state.inventory.stacks) itemCounts.set(s.item, (itemCounts.get(s.item) ?? 0) + s.count);
186
+
187
+ // Warlock: once we hold enough Soul Shards, drop Drain Soul from the usable set so the rotation
188
+ // falls through to a real nuke instead of channelling a shard-farmer we don't need right now.
189
+ const autopilotCfg = normalizeAutopilotConfig(state.autopilot);
190
+ const keepShards = autopilotCfg.keepSoulShards;
191
+ const twisting = autopilotCfg.totemTwisting;
192
+ const shardCount = itemCounts.get(SOUL_SHARD_ITEM) ?? 0;
193
+
194
+ // Fight-wide pack size for enemyCount gates. Endless floor streams ~48 yd of passives
195
+ // into the session; counting them as "in the fight" made custom/legacy enemyCount>=2
196
+ // rules (and aoeMode prefer) fire multi tools while only one pack was engaged.
197
+ // Companions already use livingCombatEnemies via pickAction. Prefer engaged (threat);
198
+ // fall back to all living when the view has no engaged flags (non-endless / 1v1).
199
+ // Cite: createSession livingCombatEnemies; enemiesView engaged; injectEnemySpec passive.
200
+ // The removed else counted livingEnemiesView(), which includes the passives the engaged branch
201
+ // below deliberately excludes - a different number under the same name. E1.3.
202
+ let living = 0;
203
+ let livingSides: Array<{ side: string; hp: number; engaged: boolean }> = [];
204
+ {
205
+ session.enemiesView().forEach((v) => {
206
+ const hp = v.hp;
207
+ const engaged = v.engaged === true;
208
+ livingSides.push({ side: v.side, hp, engaged });
209
+ });
210
+ const combatN = livingSides.filter((s) => s.hp > 0 && s.engaged).length;
211
+ const allN = livingSides.filter((s) => s.hp > 0).length;
212
+ living = combatN > 0 ? combatN : allN;
213
+ }
214
+ // Explicit click target counts even before threat (setPlayerTarget on a passive).
215
+ if (typeof session.playerTargetId === 'function') {
216
+ const focusId = session.playerTargetId();
217
+ if (
218
+ typeof focusId === 'string'
219
+ && (focusId === 'enemy' || /^enemy_\d+$/.test(focusId))
220
+ && livingSides.some((s) => s.side === focusId && s.hp > 0 && !s.engaged)
221
+ ) {
222
+ // Focus is a living passive — still one free-fire hostile for pack size.
223
+ if (livingSides.filter((s) => s.hp > 0 && s.engaged).length === 0) {
224
+ living = Math.max(living, 1);
225
+ }
226
+ }
227
+ }
228
+
229
+ // Blast geometry for the multi/AoE gates below — engine-built, proven identical to the
230
+ // copy this replaced across 100 sampled ticks.
231
+ const enemyDists = base.enemyDists;
232
+ const enemyDistsFromTarget = base.enemyDistsFromTarget;
233
+ const packNearMelee = (): number => {
234
+ if (!enemyDists) return living;
235
+ let n = 0;
236
+ for (const d of enemyDists) if (d <= 8) n++;
237
+ return n;
238
+ };
239
+
240
+ // Occupied element slots: engine-built, then nudged for twisting below.
241
+ const totemSlots = new Set<TotemSlot>(base.totemSlots ?? []);
242
+ // TOTEM TWISTING (opt-in). Windfury's kit hands the party a 10s weapon enchant keyed to its own
243
+ // spell id, and UnSummon strips only GetSpell(), so the enchant survives the air slot being
244
+ // taken over. Twisting is just re-casting the air slot on cooldown to keep BOTH running.
245
+ //
246
+ // Two nudges, no bespoke rule syntax:
247
+ // 1. the air slot never reports as occupied, so `totemPresent` gates stop blocking it;
248
+ // 2. the air totem currently standing is marked unusable, so the rotation is forced onto the
249
+ // OTHER air totem — that alternation is what makes it a twist rather than a refresh.
250
+ // Cite: Entities/Totem.cpp UnSummon :131–158 / GetSpell() :169–174.
251
+ const twistSuppressed = new Set<number>();
252
+ if (twisting && typeof session.totemsView === 'function') {
253
+ totemSlots.delete('air');
254
+ for (const t of session.totemsView()) {
255
+ if (t.slot === 'air' && t.summonSpellId) twistSuppressed.add(t.summonSpellId);
256
+ }
257
+ }
258
+
259
+ // Weapon TEMP enchants: live session hands + char state (OOC writeback lag).
260
+ const weaponImbueHands = new Set<'mainhand' | 'offhand'>();
261
+ if (getTempEnchant(state.char, 'mainhand')) weaponImbueHands.add('mainhand');
262
+ if (getTempEnchant(state.char, 'offhand')) weaponImbueHands.add('offhand');
263
+ for (const h of pv.weaponImbueHands ?? []) weaponImbueHands.add(h);
264
+ // Open eligible hands (weapon only — not shield) for ref-2 keep-up / dual-wield fill.
265
+ const weaponImbueOpenHands = openTempWeaponImbueHands(state.char, ds);
266
+
267
+ // Taunt policy below needs this; the engine already computed it.
268
+ const targetAttackingMe = base.targetAttackingMe;
269
+
270
+ return {
271
+ // self/target/pet, both ally lists, triage buffs, threat flags, dispellable sets,
272
+ // knownSpells, combatTimeMs and pull-spent all come from the engine unchanged.
273
+ ...base,
274
+ // activeBuffs are char-side (food/elixirs applied out of combat, which the fighter may not
275
+ // carry) — without the union a `buffPresent` rule would re-cast something already up.
276
+ // Strictly additive to the engine's subject.
277
+ //
278
+ // creatureType is NOT overlaid: subjectFromView already sets it from the Fighter
279
+ // (pickAction.ts:1030), for self and target alike. The overlay that used to be here read a
280
+ // SECOND resolution of "who is the target" - focusCombatView, which fell back to
281
+ // ensurePlayerFocus while the engine's own `foeOf` may return the tank-peel or kill-order
282
+ // pick instead. When those disagree the row described one unit and typed another, so an
283
+ // undead-only rule read `humanoid` on the mob it was aimed at. Two resolvers for one
284
+ // question is what section 0c rule 4 forbids; the duplicate is deleted.
285
+ self: {
286
+ ...base.self,
287
+ buffs: new Set([...base.self.buffs, ...state.char.activeBuffs]),
288
+ },
289
+ target: base.target,
290
+ pet: base.pet ? { ...base.pet, happiness: state.char.pet?.happiness } : base.pet,
291
+ // Player pack-size policy: engaged-preferred with an explicit-click fallback, which the
292
+ // engine's combatEnemyCount does not do (b6e85611).
293
+ enemyCount: living,
294
+ // Twisting nudge applied above; the engine reports the raw occupied slots.
295
+ totemSlots,
296
+ // Char-side temp enchants too: the session hands only what it can see, and OOC writeback
297
+ // lags a tick, so this is a superset of the engine's.
298
+ weaponImbueHands,
299
+ weaponImbueOpenHands,
300
+ itemCounts,
301
+ equippedItemEntries: equippedEntrySet(state.char),
302
+ // Player S4C knobs from AutopilotConfig (companions use companionActionProvider).
303
+ aoeMode: encodeAoeMode(autopilotCfg.aoeMode as CompanionAoeMode),
304
+ offHealAllowed: autopilotCfg.offHeal ? 1 : 0,
305
+ usableSpell: (id) => {
306
+ if (shardFarmSuppressed(id, shardCount, keepShards)) return false;
307
+ if (twistSuppressed.has(id)) return false; // twisting: never re-cast the air totem already up
308
+ // Indoors (a WMO roof over this pull): Entangling Roots / Nature's Grasp / Inferno fail
309
+ // CheckCast, so the rule must not win the GCD. The engine gates its own kit the same way.
310
+ if (!spellAllowedAtLocation(abilityById.get(id), base.outdoors !== false, false)) return false;
311
+ // Hard suppress multi under 'single' even when rotation rules lack aoeMode gates.
312
+ if (autopilotCfg.aoeMode === 'single' && multiSpellIds.has(id)) return false;
313
+ // Pack swap: HS/Maul must not dump rage when Cleave/Swipe is known (see isPackSuppressedStFiller).
314
+ // Nearby melee-band count — a spread pack still wants HS on the mob you have.
315
+ if (
316
+ isPackSuppressedStFiller(abilityById.get(id), {
317
+ enemyCount: packNearMelee(),
318
+ hasCleave,
319
+ hasSwipe,
320
+ singleTargetMode: autopilotCfg.aoeMode === 'single',
321
+ })
322
+ ) {
323
+ return false;
324
+ }
325
+ // Multi/AoE damage dumps: 2+ hostiles in blast. Hostile-aura PBAoEs (Demo Roar)
326
+ // stay usable on 1v1 — multiTargetNeedsPackDensity (KI-836).
327
+ {
328
+ const ab = abilityById.get(id);
329
+ if (ab && multiTargetNeedsPackDensity(ab)) {
330
+ const yards = multiTargetGateYards(ab);
331
+ const dists = isGroundTargetedMulti(ab) ? enemyDistsFromTarget : enemyDists;
332
+ let near = living;
333
+ if (dists) {
334
+ near = 0;
335
+ for (const d of dists) if (d <= yards) near++;
336
+ }
337
+ if (near < 2) return false;
338
+ }
339
+ if (ab && isNovaKiteSpell(ab) && enemyDists && !novaDiscOccupied(enemyDists, ab)) {
340
+ return false;
341
+ }
342
+ if (ab && isPiercingHowlSpell(ab) && piercingHowlRefused({
343
+ inMelee: base.target?.inMelee === true,
344
+ snared: base.target?.snared === true,
345
+ snareImmune: base.target?.immuneSpellIds?.has(id) === true
346
+ || base.target?.immuneAuraSpellIds?.has(id) === true,
347
+ distanceYd: base.targetDistanceYd ?? null,
348
+ radiusYd: ab.radiusYd,
349
+ })) return false;
350
+ // Bodies in the disc but none the root can hold (immune boss / full DR). The engine
351
+ // already decided this per-hostile in allySnapshot — do not re-derive it here.
352
+ if (base.unholdableAoeCcSpellIds?.has(id)) return false;
353
+ }
354
+ // Totems only once the pull is on (twin of pickAction usableSpell). `living` is no use
355
+ // here — it counts unengaged floor passives — so read the engaged flags.
356
+ if (totemSummonIds.has(id)
357
+ && base.inCombat === false
358
+ && !livingSides.some((s) => s.hp > 0 && s.engaged)) {
359
+ return false;
360
+ }
361
+ // DPS never auto-taunts — tank/offtank keep threat tools.
362
+ if (autopilotCfg.combatRole === 'dps' && tauntSpellIds.has(id)) return false;
363
+ // Tank/offtank: only taunt when the focus is not already on you (peel / regain).
364
+ if (
365
+ (autopilotCfg.combatRole === 'tank' || autopilotCfg.combatRole === 'offtank')
366
+ && tauntSpellIds.has(id)
367
+ && targetAttackingMe === true
368
+ ) {
369
+ return false;
370
+ }
371
+ // Offtank peel: prefer control when something is on you (or target not on you — rule gates).
372
+ // Soft: do not hard-block CC here; conditions + ccPreferCasters handle freedom.
373
+ void controlSpellIds;
374
+ // Party interrupt: skip when a companion already owns this cast, or when the
375
+ // engine says this Kick cannot land (wrong target, OOR of the caster add).
376
+ if (interruptSpellIds.has(id)) {
377
+ const focusId =
378
+ typeof session.playerTargetId === 'function' ? session.playerTargetId() : 'enemy';
379
+ if (
380
+ typeof session.interruptClaimedByOther === 'function'
381
+ && session.interruptClaimedByOther(focusId, 'player')
382
+ ) {
383
+ return false;
384
+ }
385
+ if (!base.usableSpell(id)) return false;
386
+ }
387
+ // Party Tranq/Purge claim on the focus. Cure cats aim at friendlies and skip this overlay.
388
+ // Cite: AUTOPILOT-PLAN §4 row 21.
389
+ if (dispelSpellIds.has(id)) {
390
+ const focusId =
391
+ typeof session.playerTargetId === 'function' ? session.playerTargetId() : 'enemy';
392
+ const sp = abilityById.get(id);
393
+ if (typeof session.dispelClaimedByOther === 'function' && sp?.effects) {
394
+ for (const e of sp.effects) {
395
+ const cat = e.dispelType;
396
+ if (e.kind !== 'dispel' || cat == null) continue;
397
+ // Offensive cats on the focus. Cure cats aim at friendlies — do not grey from a boss claim.
398
+ if (
399
+ cat !== DISPEL_TYPE.MAGIC
400
+ && cat !== DISPEL_TYPE.STEALTH
401
+ && cat !== DISPEL_TYPE.INVISIBILITY
402
+ && cat !== DISPEL_TYPE.SPE_NPC_ONLY
403
+ && cat !== DISPEL_TYPE.ENRAGE
404
+ ) continue;
405
+ if (session.dispelClaimedByOther(focusId, 'player', cat)) return false;
406
+ }
407
+ }
408
+ }
409
+ return usable.get(id) ?? false;
410
+ },
411
+ usableItem: (entry) => usableItems.get(entry) ?? false,
412
+ bestFamilyEntry: (family) => bestByFamily.get(family)?.entry ?? null,
413
+ };
414
+ }
415
+
416
+ /**
417
+ * Resolve an item-rule target selector to a live friendly CombatantId.
418
+ * - `'self'` → always `'player'`
419
+ * - `'pet'` → `'pet'` if a living pet exists, else `null` (do not fall back to self)
420
+ * - `'lowest_ally'` → lowest HP% among living player/pet/allies; ties keep fixed order
421
+ * (player → pet → ally_0..). Returns `null` if no living friendly exists.
422
+ *
423
+ * Exported for unit tests.
424
+ */
425
+ export function resolveItemRuleTarget(session: CombatSession, sel: ItemRuleTarget): CombatantId | null {
426
+ if (sel === 'self') return 'player';
427
+
428
+ if (sel === 'pet') {
429
+ const petv = typeof session.petView === 'function' ? session.petView() : null;
430
+ return petv && petv.hp > 0 ? 'pet' : null;
431
+ }
432
+
433
+ // lowest_ally — fixed scan order so ties are deterministic (first wins on equal HP%).
434
+ type Cand = { id: CombatantId; hpPct: number };
435
+ const cands: Cand[] = [];
436
+ const pv = session.playerView();
437
+ if (pv.hp > 0) cands.push({ id: 'player', hpPct: pct(pv.hp, pv.maxHp) });
438
+ const petv = typeof session.petView === 'function' ? session.petView() : null;
439
+ if (petv && petv.hp > 0) cands.push({ id: 'pet', hpPct: pct(petv.hp, petv.maxHp) });
440
+ if (typeof session.alliesView === 'function') {
441
+ const allies = session.alliesView();
442
+ for (let i = 0; i < allies.length; i++) {
443
+ const av = allies[i]!;
444
+ if (av.hp > 0) {
445
+ const id = `ally_${i}` as AllySide;
446
+ cands.push({ id, hpPct: pct(av.hp, av.maxHp) });
447
+ }
448
+ }
449
+ }
450
+ if (!cands.length) return null;
451
+ let best = cands[0]!;
452
+ for (let i = 1; i < cands.length; i++) {
453
+ if (cands[i]!.hpPct < best.hpPct) best = cands[i]!;
454
+ }
455
+ return best.id;
456
+ }
457
+
458
+ /**
459
+ * What the rotation would do this tick: a spell id, an item use, or null (auto-attack).
460
+ * `targetEntry` selects a per-creature override list when the rotation defines one.
461
+ * Item rules resolve optional `target` selectors to a CombatantId; unresolvable targets
462
+ * (e.g. pet selector with no living pet) skip the rule rather than mis-targeting self.
463
+ *
464
+ * Player aoeMode `aoe` reorders multi-target combat rules first when ≥2 living enemies
465
+ * (same prefer path as companionActionProvider).
466
+ */
467
+ export function pickCombatAbility(
468
+ session: CombatSession,
469
+ rot: Rotation,
470
+ state: GameState,
471
+ ds: DataStore,
472
+ targetEntry?: number,
473
+ ): CombatAction {
474
+ // Ghost Wolf is a 3s cast: recast is dropped while walking in (combatView skip).
475
+ // Buff-cancel first so the snapshot sees humanoid and Lightning Bolt is usable.
476
+ {
477
+ const pv = session.playerView();
478
+ const wolfed = isMobilityTravelForm((pv.form ?? 0) | 0)
479
+ || (pv.buffs ?? []).some((b) => isMobilityFormSpellId(b.spellId));
480
+ if (wolfed) {
481
+ session.leaveForm?.('player');
482
+ const cf = (state.char.form ?? 0) | 0;
483
+ if (isMobilityTravelForm(cf) || (state.char.activeAuras ?? []).some((a) => isMobilityFormSpellId(a.spellId))) {
484
+ const mana = currentMana(state.char);
485
+ let next = { ...state.char, form: undefined as undefined, power: mana };
486
+ delete next.mana;
487
+ if (next.activeAuras) {
488
+ next = {
489
+ ...next,
490
+ activeAuras: next.activeAuras.filter((a) => !isMobilityFormSpellId(a.spellId)),
491
+ };
492
+ }
493
+ next.activeBuffs = getActiveBuffIds(next);
494
+ state.char = next;
495
+ }
496
+ }
497
+ }
498
+ const kit = session.abilities();
499
+ let snap = combatSnapshot(session, state, ds, kit);
500
+ const cfg = normalizeAutopilotConfig(state.autopilot);
501
+ let useRot = rot;
502
+ // No hidden role transform: the editor rotation is what runs. Healer/tank defaults are
503
+ // baked at reset via buildPlayerDefaultRotation (visible recovery/combat rules).
504
+ if (cfg.offHeal && state.squadRole !== 'healer') {
505
+ const hasGate = (['buffs', 'pull', 'combat', 'recovery'] as const).some((p) =>
506
+ (useRot.phases[p] ?? []).some(
507
+ (r) => r.action.kind === 'spell' && r.conditions.some(
508
+ (c) => c.metric === 'offHealAllowed' || c.metric === 'healerPowerPct',
509
+ ),
510
+ ),
511
+ );
512
+ if (!hasGate) {
513
+ const heal = kit.find((a) => spellHealsFromDef(a.spell));
514
+ if (heal) {
515
+ const rule: RotationRule = {
516
+ id: 'g-player-offheal',
517
+ action: { kind: 'spell', spellId: heal.spell.id },
518
+ conditions: [
519
+ { subject: 'self', metric: 'offHealAllowed', op: '==', value: 1 },
520
+ { subject: 'self', metric: 'healerPowerPct', op: '<', value: HEALER_DRY_POWER_PCT },
521
+ { subject: 'self', metric: 'allyLowestHpPct', op: '<', value: PLAYER_OFF_HEAL_PCT },
522
+ ],
523
+ enabled: true,
524
+ };
525
+ useRot = { ...useRot, phases: { ...useRot.phases, combat: [rule, ...useRot.phases.combat] } };
526
+ }
527
+ }
528
+ }
529
+ if (cfg.aoeMode === 'aoe' && snap.enemyCount >= 2) {
530
+ const multiIds = new Set<number>();
531
+ for (const a of kit) {
532
+ if (isMultiTargetCombatSpell(a.spell)) multiIds.add(a.spell.id);
533
+ }
534
+ if (multiIds.size) useRot = preferAoeRules(useRot, (id) => multiIds.has(id));
535
+ }
536
+ // CC/interrupt freedom: when the global prefer-casters knob is on, soft-inject
537
+ // "enemy is casting" on control rules that have no cast gate yet (sheep casters).
538
+ if (cfg.ccPreferCasters) {
539
+ useRot = injectCcCasterGates(useRot, session, kit);
540
+ }
541
+ // Threat watch: as a DPS, hold damage while an enemy is actually on you. One `halt_dps` rule
542
+ // gated on hasAggro — the same vocabulary a raid tactic row uses, injected per tick like the
543
+ // off-heal above. Tank/offtank are skipped: holding threat is their job. A hand-authored halt
544
+ // on hasAggro wins, so editing the rotation still beats the knob.
545
+ if (cfg.threatWatch && cfg.combatRole === 'dps') {
546
+ const hasHalt = (['combat', 'recovery'] as const).some((p) =>
547
+ (useRot.phases[p] ?? []).some(
548
+ (r) => r.action.kind === 'halt_dps' && r.conditions.some((c) => c.metric === 'hasAggro'),
549
+ ),
550
+ );
551
+ if (!hasHalt) {
552
+ const rule: RotationRule = {
553
+ id: 'g-player-threat-watch',
554
+ action: { kind: 'halt_dps' },
555
+ conditions: [{ subject: 'self', metric: 'hasAggro', op: '==', value: 1 }],
556
+ enabled: true,
557
+ };
558
+ useRot = { ...useRot, phases: { ...useRot.phases, combat: [rule, ...useRot.phases.combat] } };
559
+ }
560
+ }
561
+ // Arm the halt BEFORE evaluate, the same order pickAction uses for companions: a halt has to
562
+ // make the damaging casts unusable so a lower non-damaging rule can still take the tick.
563
+ // Only companions and raid units did this — the player's own rotation ignored every
564
+ // `halt_dps` row it had, including the raid tactic ones, because nothing on this path read
565
+ // it. `firstFiring` alone cannot do the job: it returns a halt only when nothing else fires.
566
+ //
567
+ // This stops ABILITIES, not white swings: a null action means auto-attack, which is what
568
+ // pickAction's halt does too. Cite: pickAction.ts:1427 usableSpell halt gate.
569
+ {
570
+ const peeked = peekHaltDpsSchool(useRot, snap, targetEntry);
571
+ // A scoped halt names the bodies it stops damage on; this filter is by spell id, so the body
572
+ // is the target the rotation is being evaluated against. Not covered -> no halt at all here.
573
+ const halt = peeked && haltDpsCoversEntry(peeked.entries, targetEntry) ? peeked.school : null;
574
+ if (halt) {
575
+ // The SESSION's ability, not ds.spell: the bake leaves plenty of nukes with an empty
576
+ // effects[] and the interpreter fills them at resolve, so a ds read would let those slip
577
+ // straight through the halt. Same object pickAction tests. Built only when a halt is armed.
578
+ const resolved = new Map<number, { school?: string; effects?: ReadonlyArray<{ kind: string }> }>();
579
+ for (const a of kit) resolved.set(a.spell.id, a.spell);
580
+ const baseUsable = snap.usableSpell;
581
+ snap = {
582
+ ...snap,
583
+ usableSpell: (id: number) => {
584
+ if (!baseUsable(id)) return false;
585
+ const sp = resolved.get(id) ?? ds.spell(id);
586
+ if (!sp || !spellDealsDamage(sp)) return true;
587
+ return !haltDpsRefusesSchool(halt, sp.school);
588
+ },
589
+ };
590
+ }
591
+ }
592
+ const action = chooseCombatAction(useRot, snap, targetEntry);
593
+ if (!action) return null;
594
+ if (action.kind === 'spell') {
595
+ if (action.after && typeof session.armRuleAfter === 'function') {
596
+ session.armRuleAfter('player', action.spellId, action.after);
597
+ }
598
+ session.consumeFollowUp?.('player', action.spellId);
599
+ const offTarget = action.target === 'other';
600
+ // A FILTERED aim is resolved by the engine — the same resolver the gate's `subject: 'ally'`
601
+ // read. An unfiltered one keeps the local scan every stored rotation has always used.
602
+ const triageAlly = isLowestHpSpellTarget(action.target)
603
+ ? (action.target.among
604
+ ? (snap.allyAimSide?.(action.target.among) as CombatantId | undefined) ?? undefined
605
+ : resolveItemRuleTarget(session, 'lowest_ally') ?? undefined)
606
+ : undefined;
607
+ if (isLowestHpSpellTarget(action.target) && action.target.among && !triageAlly) return null;
608
+ if (action.after || offTarget || triageAlly) {
609
+ return {
610
+ spell: action.spellId,
611
+ ...(action.after ? { after: action.after } : {}),
612
+ ...(offTarget ? { offTarget: true } : {}),
613
+ ...(triageAlly ? { ally: triageAlly } : {}),
614
+ };
615
+ }
616
+ return action.spellId;
617
+ }
618
+ // A macro rule is compiled to spell/item rules before the evaluator runs (macro/rotation.ts) and
619
+ // actionUsable refuses any that slipped through, so chooseCombatAction cannot return one.
620
+ if (action.kind !== 'item') return null;
621
+ const sel = action.target ?? 'self';
622
+ const targetId = resolveItemRuleTarget(session, sel);
623
+ if (!targetId) return null;
624
+ // Omit target for self so solo combat logs stay shape-compatible (M3 convention).
625
+ return targetId !== 'player'
626
+ ? { item: action.itemEntry, target: targetId }
627
+ : { item: action.itemEntry };
628
+ }
629
+
630
+ /**
631
+ * A snapshot for OUT-of-combat decisions (buffs / conjure / summon), built from the character +
632
+ * inventory (no session, no target). `usableSpell` gates on "known + affordable" so the buffs
633
+ * phase only picks castable spells.
634
+ */
635
+ export function oocSnapshot(state: GameState, ds: DataStore): RotationSnapshot {
636
+ const char = state.char;
637
+ const hpMax = maxHealth(char, ds);
638
+ const pt = powerType(char);
639
+ // Mana and energy are spendable OOC (buffs/utility); rage starts empty and is not spent OOC.
640
+ const spendsPower = pt === 'mana' || pt === 'energy';
641
+ const pMax = spendsPower ? maxPower(char, ds) : 0;
642
+ const self: SubjectState = {
643
+ alive: true,
644
+ healthPct: hpMax > 0 ? (char.health / hpMax) * 100 : 100,
645
+ powerPct: pMax > 0 ? (char.power / pMax) * 100 : 100,
646
+ level: char.level,
647
+ // Prefer timed auras (Lightning Shield, imbues mirrored as auras) over legacy list only.
648
+ buffs: new Set(getActiveBuffIds(char)),
649
+ debuffs: new Set(),
650
+ formId: (char.form ?? 0) | 0,
651
+ inMelee: false,
652
+ };
653
+ const pet: SubjectState | null = (() => {
654
+ const p = char.pet;
655
+ if (!p || p.dead || p.health <= 0) return null;
656
+ const hpMax = petMaxHealth(char, ds);
657
+ const cdef = ds.creature(p.creature);
658
+ const pw = cdef ? petMaxPower(char, cdef, ds) : { maxPower: 0 };
659
+ return {
660
+ alive: true,
661
+ healthPct: hpMax > 0 ? (p.health / hpMax) * 100 : 100,
662
+ powerPct: pw.maxPower > 0 ? ((p.power ?? pw.maxPower) / pw.maxPower) * 100 : 0,
663
+ level: char.level,
664
+ buffs: new Set<number>(),
665
+ debuffs: new Set<number>(),
666
+ happiness: p.happiness,
667
+ };
668
+ })();
669
+ const itemCounts = new Map<number, number>();
670
+ for (const s of state.inventory.stacks) itemCounts.set(s.item, (itemCounts.get(s.item) ?? 0) + s.count);
671
+ const equippedItemEntries = equippedEntrySet(char);
672
+
673
+ const isShardSpender = (sp: any): boolean =>
674
+ !!sp && (sp.effects?.some((e: any) => e.kind === 'summon_pet') || /create healthstone/i.test(sp.name || ''));
675
+
676
+ const weaponImbueHands = new Set<'mainhand' | 'offhand'>();
677
+ if (getTempEnchant(char, 'mainhand')) weaponImbueHands.add('mainhand');
678
+ if (getTempEnchant(char, 'offhand')) weaponImbueHands.add('offhand');
679
+ const weaponImbueOpenHands = openTempWeaponImbueHands(char, ds);
680
+
681
+ return {
682
+ self,
683
+ target: null,
684
+ pet,
685
+ combatTimeMs: 999_999,
686
+ enemyCount: 0,
687
+ totemSlots: new Set(), // totems are fight-scoped only
688
+ weaponImbueHands,
689
+ weaponImbueOpenHands,
690
+ itemCounts,
691
+ equippedItemEntries,
692
+ auraCovered: (ids, ref) => keepUpAlreadyPresent(ids, ref, {
693
+ spell: (id) => ds.spell(id),
694
+ chainOf: (id) => ds.spellChain(id),
695
+ }),
696
+ usableSpell: (id) => {
697
+ if (!knows(state.spellbook, id)) return false;
698
+ const sp = ds.spell(id);
699
+ if (!sp) return false;
700
+ if (spendsPower && char.power < sp.cost) return false;
701
+ if (sp.powerType === 'health' && char.health <= (sp.cost ?? 0)) return false;
702
+ // Respect baked cooldowns (per-spell + category) using the persisted world state.
703
+ const now = Date.now();
704
+ if ((state.spellCooldowns?.[id] ?? 0) > now) return false;
705
+ if (sp.category != null && (state.categoryCooldowns?.[sp.category] ?? 0) > now) return false;
706
+ // Basic shard gate for warlock spenders using real item count (rotation can require more via itemCount cond).
707
+ if (char.cls === CLASS.WARLOCK && isShardSpender(sp)) {
708
+ const have = itemCounts.get(SOUL_SHARD_ITEM) ?? 0;
709
+ if (have < 1) return false;
710
+ }
711
+ if (oocPetCallReviveFailReason(sp.effects, char)) return false;
712
+ return true;
713
+ },
714
+ usableItem: (entry) => {
715
+ const worn = equippedItemEntries.has(entry);
716
+ if ((itemCounts.get(entry) ?? 0) <= 0 && !worn) return false;
717
+ const it = ds.item(entry);
718
+ if (it?.slot != null && !worn) return false;
719
+ return itemUseCooldownRemainingMs(
720
+ it, state.spellCooldowns, state.categoryCooldowns, Date.now(),
721
+ ) <= 0;
722
+ },
723
+ bestFamilyEntry: (family) =>
724
+ bestCarriedFamilyItem(family, ds, char, state.inventory)?.entry ?? null,
725
+ };
726
+ }
727
+
728
+ /** Drop travel form so OOC buffs can fire, then pick the next buffs-phase action. */
729
+ function dropTravelFormForOoc(state: GameState, ds: DataStore): void {
730
+ const form = (state.char.form ?? 0) | 0;
731
+ if (!isMobilityTravelForm(form)) return;
732
+ const mana = currentMana(state.char);
733
+ let next = { ...state.char, form: undefined as undefined, power: mana };
734
+ delete next.mana;
735
+ if (next.activeAuras) {
736
+ next = {
737
+ ...next,
738
+ activeAuras: next.activeAuras.filter((a) => {
739
+ const sp = ds.spell(a.spellId);
740
+ return !sp || !isFormSpell(sp) || formIdFromSpell(sp) !== form;
741
+ }),
742
+ };
743
+ }
744
+ next.activeBuffs = getActiveBuffIds(next);
745
+ state.char = next;
746
+ }
747
+
748
+ /** Next out-of-combat prep action (spell, item imbue, …), or null. */
749
+ export function pickOocAction(rot: Rotation, state: GameState, ds: DataStore): RuleAction | null {
750
+ dropTravelFormForOoc(state, ds);
751
+ return chooseOocAction(rot, oocSnapshot(state, ds));
752
+ }
753
+
754
+ // One buffs-phase ITEM step, the way every prep loop runs it: an imbue row names its own hand
755
+ // (poison offhand), so ask for that slot first and fall back to the router's own pick. Without the
756
+ // hand the router answers `needsTarget` with mainhand first every time, and the offhand row
757
+ // re-coats the mainhand instead. Cite: weaponImbueItem.ts resolveTempWeaponImbueHand.
758
+ export function useOocPrepItem(
759
+ game: { consumeItem: Game['consumeItem'] },
760
+ action: { itemEntry: number; hand?: 'mainhand' | 'offhand' },
761
+ ): boolean {
762
+ const used = game.consumeItem(action.itemEntry, action.hand ? { slot: action.hand } : undefined);
763
+ if (used.ok) return true;
764
+ const pick = used.needsTarget?.slots?.[0];
765
+ return !!pick && game.consumeItem(action.itemEntry, { slot: pick }).ok;
766
+ }
767
+
768
+ /** The next out-of-combat prep spell (buff/conjure/summon) the rotation wants cast, or null. */
769
+ export function pickOocSpell(rot: Rotation, state: GameState, ds: DataStore): number | null {
770
+ const action = pickOocAction(rot, state, ds);
771
+ return action && action.kind === 'spell' ? action.spellId : null;
772
+ }
773
+
774
+ // Soft-inject `target.castingPresent == 1` on control/interrupt rules that lack any cast gate. Idempotent
775
+ // for rules that already express castingPresent / castingSchool / castingIsHeal. Exported for unit tests.
776
+ export function injectCcCasterGates(
777
+ rot: Rotation,
778
+ session: CombatSession,
779
+ kit?: readonly AbilityStatus[],
780
+ ): Rotation {
781
+ /**
782
+ * INTERRUPTS ONLY. This used to take every `role: 'control'` spell, which is all hard CC —
783
+ * Polymorph, Sap, Shackle, Hibernate, Banish, Fear, the AoE screams — and bolt
784
+ * `target.castingPresent == 1` onto their rules, defaulted ON.
785
+ *
786
+ * build.ts authors that same condition INTO a pure-CC rule and says in as many words "user
787
+ * can ✕ the condition for hard-CC any target". Re-adding it here whenever the rule no longer
788
+ * carried it made that ✕ do nothing: the player deleted the condition, the rotation editor
789
+ * showed it gone, and the rule still would not fire unless the target was mid-cast. Worse for
790
+ * a `target: 'other'` rule, where `subject: 'target'` is the FOCUS — so sheeping a second mob
791
+ * required the TANK'S mob to be casting, which against melee trash is never.
792
+ *
793
+ * A cast gate on an interrupt is not crowd control and is not a hidden decision (the engine
794
+ * already refuses a Kick outside an interruptible cast), so the knob keeps that half.
795
+ */
796
+ const controlIds = new Set<number>();
797
+ for (const a of kit ?? session.abilities()) {
798
+ const sp = a.spell as { id: number; role?: string; effects?: ReadonlyArray<{ kind: string }> };
799
+ if (isPureInterruptSpell(sp as never)) controlIds.add(sp.id);
800
+ }
801
+ if (!controlIds.size) return rot;
802
+ const castGate: Condition = { subject: 'target', metric: 'castingPresent', op: '==', value: 1 };
803
+ let changed = false;
804
+ const adapt = (rules: RotationRule[]): RotationRule[] =>
805
+ rules.map((r) => {
806
+ if (r.action.kind !== 'spell' || !controlIds.has(r.action.spellId)) return r;
807
+ if (FROST_NOVA_RANK_IDS.includes(r.action.spellId)) return r;
808
+ if (r.conditions.some((c) =>
809
+ c.metric === 'castingPresent' || c.metric === 'castingSchool' || c.metric === 'castingIsHeal',
810
+ )) {
811
+ return r;
812
+ }
813
+ changed = true;
814
+ return { ...r, conditions: [...r.conditions, { ...castGate }] };
815
+ });
816
+ const phases = {
817
+ prebuff: rot.phases.prebuff ?? [],
818
+ buffs: adapt(rot.phases.buffs),
819
+ pull: adapt(rot.phases.pull),
820
+ combat: adapt(rot.phases.combat),
821
+ recovery: adapt(rot.phases.recovery),
822
+ };
823
+ return changed ? { ...rot, phases } : rot;
824
+ }