azeroth-shell 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 +26 -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 +126 -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,1155 @@
1
+ // Travel, journey, taxi, hearth and mount facade methods plus the journey engine. The helpers are
2
+ // created early so the fight and dungeon paths share them; fight teardown is late-bound.
3
+
4
+ import type { CombatSession } from '../combat.ts';
5
+ import { unsummonTotemsOnLeaveWorld } from '../combat/sessionDeaths.ts';
6
+ import type { ItemDef, SpellDef } from '../../data/schema.ts';
7
+ import { AURA_INTERRUPT } from '../../domain/spells/stealth.ts';
8
+
9
+ // Player travel only: Teleport:/Portal: kit + eng. transporters (not quest "Teleport to …"). KI-900.
10
+ function isTravelTeleportSpell(spell: Pick<SpellDef, 'effects' | 'name' | 'rawEffects'>): boolean {
11
+ const hasTeleportFx =
12
+ !!spell.effects?.some((e) => e.kind === 'teleport' || e.kind === 'teleport_hearth') ||
13
+ !!(spell.rawEffects ?? []).some((r) => (r.effect | 0) === 5); // SPELL_EFFECT_TELEPORT_UNITS
14
+ if (!hasTeleportFx) return false;
15
+ const name = spell.name ?? '';
16
+ if (/^Teleport:\s/i.test(name)) return true;
17
+ if (/^Portal:\s/i.test(name)) return true;
18
+ if (/Transporter/i.test(name) || /Dimensional Ripper/i.test(name)) return true;
19
+ return false;
20
+ }
21
+ import {
22
+ trainRiding as doTrainRiding,
23
+ mount as doMount,
24
+ dismount as doDismount,
25
+ findRidingSpell,
26
+ type RidingSpellRow,
27
+ } from '../../domain/items/mounts.ts';
28
+ import { playerCombatFlagSet } from './oocCast.ts';
29
+ import {
30
+ planJourney,
31
+ departJourney,
32
+ journeyProgress,
33
+ addIdlePause,
34
+ completeJourneyNow as doCompleteJourneyNow,
35
+ journeyArrivalEpochMs,
36
+ setJourneyTimeScale,
37
+ normalizeTimeScale,
38
+ taxiFareCopper,
39
+ } from '../../domain/nav/travel.ts';
40
+ import type { Journey, JourneyLeg, HearthBind } from '../../domain/nav/travelTypes.ts';
41
+ import {
42
+ bestTravelSpeeds,
43
+ AURA_INCREASE_SPEED,
44
+ AURA_INCREASE_SWIM_SPEED,
45
+ type SpeedEffect,
46
+ } from '../../domain/nav/speed.ts';
47
+ import { formSpeedBoostSpellIds } from '../../domain/character/forms.ts';
48
+ import { permanentSpeedAuras } from '../../domain/character/characterStats.ts';
49
+ import { SHAPESHIFT_FORM } from '../../data/schema.ts';
50
+ import { pruneExpiredAuras } from '../../domain/character/character.ts';
51
+ import { isPlayerAlive, refuseIfDead as refuseIfDeadHelper } from '../../domain/run/death.ts';
52
+ import { addItem, countItem, removeItem } from '../../domain/items/inventory.ts';
53
+ import {
54
+ recordExplore,
55
+ recordSameZoneTalkOrDeliver,
56
+ } from '../../domain/quests/questlog.ts';
57
+ import { creditSquadZoneEnter } from './squadQuestShare.ts';
58
+ import { factionOf } from '../../domain/character/races.ts';
59
+ import { isCapitalTeleport } from '../../domain/nav/cityTeleports.ts';
60
+ import { isCityDefender } from '../../domain/run/honor.ts';
61
+ import { canInteractWithCreature } from '../../domain/creature/faction.ts';
62
+ import { vendorPriceMultiplierFor } from './vendorOffers.ts';
63
+ import { getSpellRecoveryTime } from '../spells/recovery.ts';
64
+ import { currentMana, powerType } from '../../domain/character/character.ts';
65
+ import { grantXp } from '../../progression/leveling.ts';
66
+ import {
67
+ CLASSIC_MAX_PLAYER_LEVEL,
68
+ explorationXp,
69
+ zoneAreaLevelProxy,
70
+ } from '../../progression/xpGain.ts';
71
+ import { pruneInstanceSaves, saveRunOnLeave } from '../../world/instances.ts';
72
+ import { restoreRaidPartySeating } from './raidParty.ts';
73
+ import { reconcileGamePartyAuras } from './partyAuras.ts';
74
+ import type { Rng } from '../core/rng.ts';
75
+ import type {
76
+ ActionResult,
77
+ BeginFight,
78
+ FightOutcome,
79
+ Game,
80
+ TravelPace,
81
+ } from './types.ts';
82
+ import type { GameCtx } from './context.ts';
83
+ import { moneyMarked } from '../../domain/economy/money.ts';
84
+
85
+ /** Hearthstone item - seed-hearthstone.mjs / item_template 6948. */
86
+ const HEARTHSTONE_ITEM = 6948;
87
+ /** Hearthstone spell - seed-hearthstone.mjs / spell_template 8690. */
88
+ const HEARTHSTONE_SPELL = 8690;
89
+
90
+ /** Pending-fight shape travel reads. Owned by combatSession (S14). */
91
+ export type TravelPendingFight = {
92
+ rng: Rng;
93
+ creatureEntry: number;
94
+ enemyLevel: number;
95
+ pack?: Array<{ entry: number; level: number }>;
96
+ encounter?: {
97
+ dungeonId: string;
98
+ encounterId: string;
99
+ phaseIndex?: number;
100
+ fusedIds?: string[];
101
+ };
102
+ withSquad?: boolean;
103
+ };
104
+
105
+ // Late-bound combat re-entry for travel-owned fight teardown. Filled after startFight / resolveFight / sync
106
+ // helpers exist in createGame.
107
+ export type TravelFightBridge = {
108
+ startFight: (
109
+ creatureEntry: number,
110
+ bossSpellIds: number[] | undefined,
111
+ encounter: undefined,
112
+ ) => ActionResult<BeginFight>;
113
+ resolveFight: (session: CombatSession) => FightOutcome;
114
+ syncCharFromSession: (session: CombatSession) => void;
115
+ persistSessionCooldowns: (session: CombatSession) => void;
116
+ getPendingFight: () => TravelPendingFight | null;
117
+ setPendingFight: (p: TravelPendingFight | null) => void;
118
+ getLiveFightSession: () => CombatSession | null;
119
+ setLiveFightSession: (s: CombatSession | null) => void;
120
+ };
121
+
122
+ export type TravelFightBridgeRef = { current: TravelFightBridge | null };
123
+
124
+ /** Late-bound: createTravelCore runs before outfitter module exists (game.ts). */
125
+ export type TravelOutfitterSyncRef = { current: (() => void) | null };
126
+
127
+ export type TravelCore = {
128
+ discoverTaxiNodesInZone: (zoneId: string) => void;
129
+ applyZoneEnterHooks: (zoneId: string) => void;
130
+ ownedMountItems: () => ItemDef[];
131
+ onFootSpeedCandidates: () => SpeedEffect[];
132
+ travelerFaction: () => 'alliance' | 'horde';
133
+ busyTravelling: () => boolean;
134
+ resetJourneyTrack: (journey: Journey | undefined) => void;
135
+ applyTravelPace: (nowMs: number) => void;
136
+ journeyTick: (nowMs?: number) => void;
137
+ journeyStatus: (nowMs?: number) => ReturnType<Game['journeyStatus']>;
138
+ /** Sim Pause: fold wall time into pausedMs so travel does not jump. */
139
+ setIdlePaused: (paused: boolean, nowMs?: number) => void;
140
+ /** Carry journeyTrackKey across pace/speed rebases (setJourneySpeed). */
141
+ setJourneyTrackKey: (key: number | undefined) => void;
142
+ /** Construction-time taxi backfill (run once at createGame). */
143
+ initTravelOnLoad: () => void;
144
+ };
145
+
146
+ function travelPaceOf(ctx: GameCtx): TravelPace {
147
+ const p = typeof ctx.opts.travelPace === 'function' ? ctx.opts.travelPace() : ctx.opts.travelPace;
148
+ return p === 2 || p === 4 || p === 'instant' ? p : 1;
149
+ }
150
+
151
+ // Journey engine state and helpers, created before the fight paths so they can share the busy check
152
+ // and the tick. Fight teardown and outfitter sync are late-bound through refs.
153
+ export function createTravelCore(
154
+ ctx: GameCtx,
155
+ fightRef: TravelFightBridgeRef,
156
+ outfitterSyncRef: TravelOutfitterSyncRef = { current: null },
157
+ ): TravelCore {
158
+ const { ds, state, opts, log, refreshQuests } = ctx;
159
+
160
+ // Journey engine state, engine-local and not persisted: position is derived from the wall clock, so
161
+ // this only tracks observation. The last leg whose completion hooks ran; -1 is none.
162
+ let journeyHooksThroughLeg = -1;
163
+ /** Zone already credited for enter hooks this journey (skip re-hook at departure origin). */
164
+ let journeyLastHookedZone: string | undefined;
165
+ /** departedAtEpochMs of the journey we've been tracking (reset on new depart). */
166
+ let journeyTrackKey: number | undefined;
167
+ /** Sim Pause anchor — in-memory, not saved. */
168
+ let idlePausedAt: number | null = null;
169
+
170
+ const foldIdlePause = (nowMs: number): void => {
171
+ if (idlePausedAt == null) return;
172
+ const journey = state.char.journey;
173
+ const dt = nowMs - idlePausedAt;
174
+ idlePausedAt = nowMs;
175
+ if (!journey || dt <= 0) return;
176
+ state.char = { ...state.char, journey: addIdlePause(journey, dt) };
177
+ };
178
+
179
+ const setIdlePaused = (paused: boolean, nowMs: number = Date.now()): void => {
180
+ if (paused) {
181
+ if (idlePausedAt == null) idlePausedAt = nowMs;
182
+ return;
183
+ }
184
+ foldIdlePause(nowMs);
185
+ idlePausedAt = null;
186
+ };
187
+
188
+ /** D8: unlock all taxi nodes whose zone matches (both factions - planner gates use). */
189
+ const discoverTaxiNodesInZone = (zoneId: string): void => {
190
+ const nodes = ds.taxiNodes().filter((n) => n.zone === zoneId);
191
+ if (!nodes.length) return;
192
+ const have = new Set(state.char.discoveredTaxiNodes ?? []);
193
+ let changed = false;
194
+ for (const n of nodes) {
195
+ if (!have.has(n.id)) {
196
+ have.add(n.id);
197
+ changed = true;
198
+ }
199
+ }
200
+ if (changed) state.char.discoveredTaxiNodes = [...have];
201
+ };
202
+
203
+ // First-visit and zone-enter hooks: quest exploration credit, exploration XP and taxi discovery.
204
+ // Idempotent — XP checks the explored list and discovery is a set union.
205
+ const applyZoneEnterHooks = (zoneId: string): void => {
206
+ if (state.char.health > 0) {
207
+ state.questlog = recordExplore(state.questlog, zoneId, (e) => ds.quest(e));
208
+ // Same-zone talk and deliver credit for an active quest whose ender zones include this one; a
209
+ // deliver also needs the item carried. Cite: docs/archive/QUEST-INTERPRETER.md §5.
210
+ state.questlog = recordSameZoneTalkOrDeliver(
211
+ state.questlog,
212
+ zoneId,
213
+ (e) => ds.quest(e),
214
+ state.inventory,
215
+ );
216
+ creditSquadZoneEnter(state, ds, zoneId);
217
+ refreshQuests();
218
+ }
219
+ const explored = state.char.exploredZones ?? [];
220
+ if (!explored.includes(zoneId)) {
221
+ state.char = { ...state.char, exploredZones: [...explored, zoneId] };
222
+ // D8 taxi discovery on first zone visit (exploredZones transition).
223
+ discoverTaxiNodesInZone(zoneId);
224
+ const zone = ds.zone(zoneId);
225
+ const baseRows = ds.explorationBasexp();
226
+ if (zone && baseRows.length > 0 && state.char.level < CLASSIC_MAX_PLAYER_LEVEL) {
227
+ const baseXpFor = (lvl: number): number =>
228
+ baseRows.find((r) => r.level === lvl)?.basexp ?? 0;
229
+ const areaLevel = zoneAreaLevelProxy(
230
+ zone.quests.map((q) => ds.quest(q)?.questLevel ?? 0),
231
+ );
232
+ const xp = explorationXp(state.char.level, baseXpFor(areaLevel ?? state.char.level), 1, {
233
+ areaLevel,
234
+ baseXpFor,
235
+ });
236
+ if (xp > 0) {
237
+ const lv = grantXp(state.char, xp, ds);
238
+ state.char = lv.char;
239
+ log(`Discovered ${zone.name}: +${xp} XP.`);
240
+ for (const newLevel of lv.leveledTo) log(`You have reached level ${newLevel}!`);
241
+ }
242
+ }
243
+ } else {
244
+ // Already explored: still union taxi nodes (e.g. backfill gaps).
245
+ discoverTaxiNodesInZone(zoneId);
246
+ }
247
+ };
248
+
249
+ // Owned mount items in bags + bank (I1 flow: item stays in bags; bank holds extras). Mount equip is via
250
+ // useMount, not paperdoll - mountedItem is the active entry only.
251
+ const ownedMountItems = (): ItemDef[] => {
252
+ const out: ItemDef[] = [];
253
+ const seen = new Set<number>();
254
+ const consider = (entry: number): void => {
255
+ if (seen.has(entry)) return;
256
+ const item = ds.item(entry);
257
+ if (!item) return;
258
+ if (item.mountSpeedPct != null || item.mountSpell != null) {
259
+ seen.add(entry);
260
+ out.push(item);
261
+ }
262
+ };
263
+ for (const s of state.inventory.stacks) if (s.count > 0) consider(s.item);
264
+ for (const s of state.bank.stacks) if (s.count > 0) consider(s.item);
265
+ return out;
266
+ };
267
+
268
+ // On-foot and swim speed candidates for auto-best: sustained ground and swim auras only, since a
269
+ // timed sprint stays combat-only. Travel Form's speed rides an unlearned passive, so it is linked.
270
+ const onFootSpeedCandidates = (): SpeedEffect[] => {
271
+ const out: SpeedEffect[] = [];
272
+ const seen = new Set<string>();
273
+ const harvest = (sid: number): void => {
274
+ const sp = ds.spell(sid);
275
+ if (sp?.effects) {
276
+ for (const eff of sp.effects) {
277
+ if (eff.kind !== 'speed' || (eff.durationMs ?? 0) !== 0) continue;
278
+ if (eff.aura !== AURA_INCREASE_SPEED && eff.aura !== AURA_INCREASE_SWIM_SPEED) continue;
279
+ const key = `${eff.aura}:${eff.pct}`;
280
+ if (seen.has(key)) continue;
281
+ seen.add(key);
282
+ out.push({ aura: eff.aura, pct: eff.pct });
283
+ }
284
+ } else if (sid === 5419) {
285
+ // Travel Form (Passive) 5419 — aura 31 +40% speed (bake hole fallback)
286
+ // Cite: SpellAuras.cpp HandleShapeshiftBoosts FORM_TRAVEL->5419 (+40% run speed).
287
+ const key = `${AURA_INCREASE_SPEED}:40`;
288
+ if (!seen.has(key)) {
289
+ seen.add(key);
290
+ out.push({ aura: AURA_INCREASE_SPEED, pct: 40 });
291
+ }
292
+ } else if (sid === 5421) {
293
+ // Aquatic Form (Passive) 5421 — aura 58 +50% swim speed (bake hole fallback)
294
+ // Cite: SpellAuras.cpp HandleShapeshiftBoosts FORM_AQUA->5421 (+50% swim speed).
295
+ const key = `${AURA_INCREASE_SWIM_SPEED}:50`;
296
+ if (!seen.has(key)) {
297
+ seen.add(key);
298
+ out.push({ aura: AURA_INCREASE_SWIM_SPEED, pct: 50 });
299
+ }
300
+ }
301
+ };
302
+ for (const sid of state.spellbook.known) {
303
+ harvest(sid);
304
+ // Travel Form only: its speed spell is never trained, so a known-form auto-best has to follow the
305
+ // boost link. The active-form path below covers casting in form for both.
306
+ const sp = ds.spell(sid);
307
+ const formEff = sp?.effects?.find((e) => e.kind === 'shapeshift');
308
+ if (formEff && formEff.kind === 'shapeshift' && formEff.form === SHAPESHIFT_FORM.TRAVEL) {
309
+ for (const boostId of formSpeedBoostSpellIds(formEff.form)) harvest(boostId);
310
+ }
311
+ }
312
+ const activeForm = state.char.form ?? SHAPESHIFT_FORM.NONE;
313
+ if (activeForm !== SHAPESHIFT_FORM.NONE) {
314
+ for (const boostId of formSpeedBoostSpellIds(activeForm)) harvest(boostId);
315
+ }
316
+ // Boots enchant and speed talents — one row each, so effectiveSpeeds maxes them against a form
317
+ // (and Pursuit of Justice's aura-32 half reaches a mounted trip).
318
+ for (const p of permanentSpeedAuras(state.char, ds)) {
319
+ const key = `${p.aura}:${p.pct}`;
320
+ if (seen.has(key)) continue;
321
+ seen.add(key);
322
+ out.push({ aura: p.aura, pct: p.pct });
323
+ }
324
+ return out;
325
+ };
326
+
327
+ const travelerFaction = (): 'alliance' | 'horde' => {
328
+ const f = factionOf(ds, state.char.race);
329
+ return f === 'horde' ? 'horde' : 'alliance';
330
+ };
331
+
332
+ const busyTravelling = (): boolean => !!state.char.journey;
333
+
334
+ const resetJourneyTrack = (journey: Journey | undefined): void => {
335
+ if (!journey) {
336
+ journeyTrackKey = undefined;
337
+ journeyHooksThroughLeg = -1;
338
+ journeyLastHookedZone = undefined;
339
+ return;
340
+ }
341
+ if (journeyTrackKey !== journey.departedAtEpochMs) {
342
+ journeyTrackKey = journey.departedAtEpochMs;
343
+ journeyHooksThroughLeg = -1;
344
+ // Traveler is already in leg[0].from - do not re-fire enter hooks for the origin
345
+ // (quest AreaExplored requires a real re-entry; MiscHandler explore order).
346
+ journeyLastHookedZone = journey.legs[0]?.from ?? state.char.zoneId;
347
+ }
348
+ };
349
+
350
+ // Apply the client's persistent travel pace to a just-departed journey — a settings mode, not a
351
+ // per-journey dial. Rebasing the departure changes identity, so the track key is carried over.
352
+ const applyTravelPace = (nowMs: number): void => {
353
+ const journey = state.char.journey;
354
+ if (!journey) return;
355
+ const pace = travelPaceOf(ctx);
356
+ if (pace === 'instant') {
357
+ state.char = { ...state.char, journey: doCompleteJourneyNow(journey, nowMs) };
358
+ journeyTrackKey = state.char.journey!.departedAtEpochMs;
359
+ } else if (pace === 2 || pace === 4) {
360
+ const next = setJourneyTimeScale(journey, pace, nowMs);
361
+ state.char = { ...state.char, journey: next };
362
+ journeyTrackKey = next.departedAtEpochMs;
363
+ }
364
+ };
365
+
366
+ const hookZoneIfNew = (zoneId: string): void => {
367
+ if (zoneId === journeyLastHookedZone) return;
368
+ applyZoneEnterHooks(zoneId);
369
+ journeyLastHookedZone = zoneId;
370
+ outfitterSyncRef.current?.();
371
+ };
372
+
373
+ // Single derive-and-apply step for wall-clock journeys (D5). Offline catch-up: zone hooks for legs
374
+ // completed while away.
375
+ const journeyTick = (nowMs: number = Date.now()): void => {
376
+ const journey = state.char.journey;
377
+ if (!journey) {
378
+ resetJourneyTrack(undefined);
379
+ return;
380
+ }
381
+ // Death interrupts travel in place. Do not change the zone or clear the corpse: that was a free
382
+ // corpse-run bypass mid-journey. Cite: domain/run/death.ts isPlayerAlive.
383
+ if (!isPlayerAlive(state.char.health, state.deathFlow)) {
384
+ state.char = { ...state.char, journey: undefined };
385
+ resetJourneyTrack(undefined);
386
+ return;
387
+ }
388
+ resetJourneyTrack(journey);
389
+
390
+ if (idlePausedAt != null) {
391
+ foldIdlePause(nowMs);
392
+ return;
393
+ }
394
+
395
+ const progress = journeyProgress(journey, nowMs);
396
+
397
+ // Apply first-visit hooks for every zone newly entered since the last observation. The zone id must
398
+ // land before the hook: the outfitter sync reads the character's zone, not the argument.
399
+ for (let i = journeyHooksThroughLeg + 1; i < progress.legIndex; i++) {
400
+ const leg = journey.legs[i];
401
+ if (!leg) continue;
402
+ if (state.char.zoneId !== leg.to) {
403
+ state.char.zoneId = leg.to;
404
+ state.corpse = null;
405
+ state.char.activeAuras = pruneExpiredAuras(state.char.activeAuras);
406
+ }
407
+ hookZoneIfNew(leg.to);
408
+ }
409
+ if (progress.arrived) {
410
+ if (state.char.zoneId !== progress.zone) {
411
+ state.char.zoneId = progress.zone;
412
+ state.corpse = null;
413
+ state.char.activeAuras = pruneExpiredAuras(state.char.activeAuras);
414
+ }
415
+ hookZoneIfNew(progress.zone);
416
+ journeyHooksThroughLeg = progress.legIndex;
417
+ const destName = ds.zone(progress.zone)?.name ?? progress.zone;
418
+ log(`You arrive in ${destName}.`);
419
+
420
+ // Commit cast costs only on successful arrival, never at departure or on cancel: an interrupt
421
+ // must not start the recovery or eat the reagents. Cite: Spell.cpp cast success path.
422
+ const arrivedAt = journeyArrivalEpochMs(journey);
423
+ let hearthCooldownUntil = state.char.hearthCooldownUntil;
424
+ if (journey.legs.some((l) => l.kind === 'hearth')) {
425
+ const hearthSpell = ds.spell(HEARTHSTONE_SPELL);
426
+ if (hearthSpell) {
427
+ // Recovery starts when the cast lands (wall-clock arrival), not press time.
428
+ hearthCooldownUntil = arrivedAt + getSpellRecoveryTime(hearthSpell);
429
+ }
430
+ }
431
+ if (journey.teleportSpellId != null) {
432
+ const tpId = journey.teleportSpellId;
433
+ const row = ds.spellTeleports().find((t) => t.spell === tpId);
434
+ // The City Defender grant is free at arrival too, or a mage who holds rank 15 would still
435
+ // burn the rune the cast gate waived. Cite: castTeleport `granted` below.
436
+ const free = row != null && isCapitalTeleport(row) && isCityDefender(state.char.honor);
437
+ if (row && !free) {
438
+ for (const r of row.reagents) {
439
+ state.inventory = removeItem(state.inventory, r.item, r.count)[0];
440
+ }
441
+ }
442
+ // TakePower + recovery on successful land (KI-643). Cite: Spell.cpp cast path.
443
+ const tpSpell = ds.spell(tpId);
444
+ if (tpSpell) {
445
+ const cost = free ? 0 : (tpSpell.cost ?? 0) | 0;
446
+ if (cost > 0) {
447
+ if (powerType(state.char) === 'mana') {
448
+ state.char.power = Math.max(0, (state.char.power ?? 0) - cost);
449
+ } else {
450
+ state.char.mana = Math.max(0, (state.char.mana ?? 0) - cost);
451
+ }
452
+ }
453
+ const rec = getSpellRecoveryTime(tpSpell);
454
+ if (rec > 0) {
455
+ if (!state.spellCooldowns) state.spellCooldowns = {};
456
+ const ready = arrivedAt + rec;
457
+ state.spellCooldowns[tpId] = Math.max(state.spellCooldowns[tpId] ?? 0, ready);
458
+ // Portals share a 60s category in 1.12; bake often omits Category — mirror via name.
459
+ if (/^Portal:/i.test(tpSpell.name ?? '')) {
460
+ for (const pr of ds.spellTeleports()) {
461
+ const ps = ds.spell(pr.spell);
462
+ if (ps && /^Portal:/i.test(ps.name ?? '') && getSpellRecoveryTime(ps) > 0) {
463
+ state.spellCooldowns[pr.spell] = Math.max(
464
+ state.spellCooldowns[pr.spell] ?? 0,
465
+ ready,
466
+ );
467
+ }
468
+ }
469
+ }
470
+ }
471
+ }
472
+ }
473
+
474
+ state.char = { ...state.char, journey: undefined, hearthCooldownUntil };
475
+ resetJourneyTrack(undefined);
476
+ return;
477
+ }
478
+
479
+ if (state.char.zoneId !== progress.zone) {
480
+ state.char.zoneId = progress.zone;
481
+ state.corpse = null;
482
+ state.char.activeAuras = pruneExpiredAuras(state.char.activeAuras);
483
+ }
484
+ // Mid-journey handoff into legs[legIndex].from (equals prior leg.to).
485
+ hookZoneIfNew(progress.zone);
486
+ journeyHooksThroughLeg = Math.max(journeyHooksThroughLeg, progress.legIndex - 1);
487
+ };
488
+
489
+ const journeyStatus = (nowMs: number = Date.now()) => {
490
+ journeyTick(nowMs);
491
+ const journey = state.char.journey;
492
+ if (!journey) return null;
493
+ const progress = journeyProgress(journey, nowMs);
494
+ return {
495
+ progress,
496
+ legs: journey.legs,
497
+ arrivalEpochMs: journeyArrivalEpochMs(journey),
498
+ destination: journey.destination,
499
+ timeScale: normalizeTimeScale(journey.timeScale),
500
+ };
501
+ };
502
+
503
+ const initTravelOnLoad = (): void => {
504
+ // D8 construction backfill: current zone's taxi nodes for pre-discovery saves.
505
+ discoverTaxiNodesInZone(state.char.zoneId);
506
+
507
+ // The race's default-known flight nodes, unioned at construction so new heroes AND existing saves
508
+ // know their racial nodes. Cite: Player.cpp:295-301 PlayerTaxi::InitTaxiNodes.
509
+ {
510
+ const starting = ds.race(state.char.race)?.startingTaxiNodes ?? [];
511
+ if (starting.length) {
512
+ const have = new Set(state.char.discoveredTaxiNodes ?? []);
513
+ let changed = false;
514
+ for (const id of starting) {
515
+ if (!have.has(id)) {
516
+ have.add(id);
517
+ changed = true;
518
+ }
519
+ }
520
+ if (changed) state.char.discoveredTaxiNodes = [...have];
521
+ }
522
+ }
523
+ };
524
+
525
+ const core: TravelCore = {
526
+ discoverTaxiNodesInZone,
527
+ applyZoneEnterHooks,
528
+ ownedMountItems,
529
+ onFootSpeedCandidates,
530
+ travelerFaction,
531
+ busyTravelling,
532
+ resetJourneyTrack,
533
+ applyTravelPace,
534
+ journeyTick,
535
+ journeyStatus,
536
+ setIdlePaused,
537
+ setJourneyTrackKey: (key) => { journeyTrackKey = key; },
538
+ initTravelOnLoad,
539
+ };
540
+ return core;
541
+ }
542
+
543
+ export type TravelMethodsExtras = {
544
+ core: TravelCore;
545
+ fightRef: TravelFightBridgeRef;
546
+ innkeeperHere: () => { entry: number; name: string } | undefined;
547
+ onOutfitterSync?: () => void;
548
+ };
549
+
550
+ export function createTravelMethods(
551
+ ctx: GameCtx,
552
+ extras: TravelMethodsExtras,
553
+ ): Pick<
554
+ Game,
555
+ | 'previewTravel'
556
+ | 'travelTo'
557
+ | 'journeyTick'
558
+ | 'journeyStatus'
559
+ | 'setIdlePaused'
560
+ | 'cancelJourney'
561
+ | 'setJourneySpeed'
562
+ | 'completeJourneyNow'
563
+ | 'abandonLiveFight'
564
+ | 'bindHearth'
565
+ | 'castHearth'
566
+ | 'classTravelOptions'
567
+ | 'castTeleport'
568
+ | 'taxiOptions'
569
+ | 'trainRiding'
570
+ | 'useMount'
571
+ | 'dismount'
572
+ > {
573
+ const { ds, state, log } = ctx;
574
+ const { core, fightRef, innkeeperHere, onOutfitterSync } = extras;
575
+ const {
576
+ journeyTick: doJourneyTick,
577
+ journeyStatus: doJourneyStatus,
578
+ setIdlePaused: doSetIdlePaused,
579
+ resetJourneyTrack,
580
+ applyTravelPace,
581
+ ownedMountItems,
582
+ onFootSpeedCandidates,
583
+ travelerFaction,
584
+ setJourneyTrackKey,
585
+ } = core;
586
+
587
+ // A dead or ghost player cannot start a road, taxi, boat, hearth or teleport. The depart paths clear
588
+ // the corpse and would relocate the body, so the gate is shared with every other dead-char refusal.
589
+ const refuseIfDead = (): { ok: false; reason: string } | null =>
590
+ refuseIfDeadHelper(state.char.health, state.deathFlow);
591
+
592
+ // Same 30-min / lockout hold as leaveInstance (KI-968). Cite MapPersistentStateMgr.h:41.
593
+ const dropOpenInstanceOnDepart = (nowMs: number): void => {
594
+ if (!state.instance) return;
595
+ // Same restore leaveInstance does — benched companions live only on raidParty.
596
+ restoreRaidPartySeating(state, ds);
597
+ const def = ds.instance(state.instance.dungeonId);
598
+ const saves = state.instanceSaves ?? {};
599
+ state.instanceSaves = def
600
+ ? saveRunOnLeave(saves, state.instance, def, nowMs, state.lockouts)
601
+ : pruneInstanceSaves(saves, nowMs);
602
+ state.instance = null;
603
+ reconcileGamePartyAuras(state, ds);
604
+ };
605
+
606
+ // Player.cpp:17118 npc ? GetReputationPriceDiscount(npc) : 0.0f — 0.0 is SEND_TAXI (free); no FM keeps 1.
607
+ const taxiDiscountMod = (): number => {
608
+ const fms = ds.zone(state.char.zoneId)?.flightMasters ?? [];
609
+ for (const fm of fms) {
610
+ if (!ds.creature(fm.entry) || canInteractWithCreature(ds, state.char.race, fm.entry)) {
611
+ return vendorPriceMultiplierFor(ds, state.char, fm.entry);
612
+ }
613
+ }
614
+ return 1;
615
+ };
616
+
617
+ return {
618
+ previewTravel(zoneId, nowMs = Date.now()) {
619
+ // Plan-only view (K5 map preview): same assembly as travelTo, NO state change.
620
+ // Do not journeyTick here - a pure read must not apply arrivals.
621
+ if (!ds.zone(zoneId)) return { ok: false, reason: 'Unknown zone' };
622
+ if (zoneId === state.char.zoneId) return { ok: false, reason: 'You are already here' };
623
+ const speeds = bestTravelSpeeds({
624
+ char: state.char,
625
+ ownedMounts: ownedMountItems(),
626
+ onFootCandidates: onFootSpeedCandidates(),
627
+ inCombat: false,
628
+ });
629
+ const plan = planJourney({
630
+ from: state.char.zoneId,
631
+ to: zoneId,
632
+ now: nowMs,
633
+ speeds,
634
+ faction: travelerFaction(),
635
+ discoveredTaxiNodes: state.char.discoveredTaxiNodes ?? [],
636
+ availableCopper: state.char.gold,
637
+ taxiDiscountMod: taxiDiscountMod(),
638
+ travelEdges: ds.travelEdges(),
639
+ taxiNodes: ds.taxiNodes(),
640
+ taxiRoutes: ds.taxiRoutes(),
641
+ transports: ds.transports(),
642
+ });
643
+ if (!plan.ok) {
644
+ return {
645
+ ok: false,
646
+ reason: plan.reason === 'same_zone' ? 'You are already here' : 'No route to that destination',
647
+ };
648
+ }
649
+ return { ok: true, legs: plan.legs, totalSeconds: plan.totalSeconds, totalCopper: plan.totalCopper };
650
+ },
651
+
652
+ travelTo(zoneId, nowMs = Date.now()) {
653
+ doJourneyTick(nowMs);
654
+ const dead = refuseIfDead();
655
+ if (dead) return dead;
656
+ if (zoneId === state.char.zoneId && !state.char.journey) {
657
+ return { ok: false, reason: 'You are already here' };
658
+ }
659
+ if (state.char.journey) {
660
+ return { ok: false, reason: 'You are already travelling' };
661
+ }
662
+ // Cannot depart mid-pull. Cite: Player::ActivateTaxiPathTo IsInCombat → ERR_TAXIPLAYERBUSY
663
+ // (cmangos Player.cpp:17026); same gate as castHearth/castTeleport via getPendingFight.
664
+ const bridge = fightRef.current;
665
+ if (bridge?.getPendingFight()) return { ok: false, reason: 'You are in combat' };
666
+ if (!ds.zone(zoneId)) {
667
+ return { ok: false, reason: 'Unknown zone' };
668
+ }
669
+
670
+ // D7 auto-best speeds: owned mounts (bags/bank) + riding rank + sustained on-foot aura-31 / swim
671
+ // aura-58 (incl. form-linked HandleShapeshiftBoosts passives). No tripConsumables in this wave.
672
+ const speeds = bestTravelSpeeds({
673
+ char: state.char,
674
+ ownedMounts: ownedMountItems(),
675
+ onFootCandidates: onFootSpeedCandidates(),
676
+ inCombat: false,
677
+ });
678
+ const plan = planJourney({
679
+ from: state.char.zoneId,
680
+ to: zoneId,
681
+ now: nowMs,
682
+ speeds,
683
+ faction: travelerFaction(),
684
+ discoveredTaxiNodes: state.char.discoveredTaxiNodes ?? [],
685
+ availableCopper: state.char.gold,
686
+ taxiDiscountMod: taxiDiscountMod(),
687
+ travelEdges: ds.travelEdges(),
688
+ taxiNodes: ds.taxiNodes(),
689
+ taxiRoutes: ds.taxiRoutes(),
690
+ transports: ds.transports(),
691
+ });
692
+ if (!plan.ok) {
693
+ const reason =
694
+ plan.reason === 'same_zone'
695
+ ? 'You are already here'
696
+ : 'No route to that destination';
697
+ return { ok: false, reason };
698
+ }
699
+
700
+ // Taxi fares prepaid at departure (roadmap K1).
701
+ if (plan.totalCopper > 0) {
702
+ if (state.char.gold < plan.totalCopper) {
703
+ return { ok: false, reason: 'Not enough money' };
704
+ }
705
+ state.char = { ...state.char, gold: state.char.gold - plan.totalCopper };
706
+ }
707
+
708
+ // Departing leaves any open instance (no separate "leave first" gate) — save like leaveInstance.
709
+ dropOpenInstanceOnDepart(nowMs);
710
+ unsummonTotemsOnLeaveWorld(bridge?.getLiveFightSession());
711
+
712
+ const journey = departJourney(plan.legs, nowMs);
713
+ state.char = { ...state.char, journey };
714
+ state.corpse = null;
715
+ state.char.activeAuras = pruneExpiredAuras(state.char.activeAuras);
716
+ resetJourneyTrack(journey);
717
+ applyTravelPace(nowMs);
718
+
719
+ const destName = ds.zone(zoneId)?.name ?? zoneId;
720
+ const etaMin = Math.max(1, Math.round(plan.totalSeconds / 60));
721
+ const fareNote = plan.totalCopper > 0 ? ` Fare: ${moneyMarked(plan.totalCopper)}.` : '';
722
+ log(`You set out for ${destName} (ETA ~${etaMin} min).${fareNote}`);
723
+
724
+ // Observe departure immediately so leg-0 zone hooks fire on the first tick.
725
+ doJourneyTick(nowMs);
726
+
727
+ return {
728
+ ok: true,
729
+ travelMs: plan.totalSeconds * 1000,
730
+ totalSeconds: plan.totalSeconds,
731
+ totalCopper: plan.totalCopper,
732
+ };
733
+ },
734
+
735
+ journeyTick(nowMs = Date.now()) {
736
+ doJourneyTick(nowMs);
737
+ },
738
+
739
+ journeyStatus(nowMs = Date.now()) {
740
+ return doJourneyStatus(nowMs);
741
+ },
742
+
743
+ setIdlePaused(paused, nowMs = Date.now()) {
744
+ doSetIdlePaused(paused, nowMs);
745
+ },
746
+
747
+ cancelJourney(nowMs = Date.now()) {
748
+ doJourneyTick(nowMs);
749
+ const dead = refuseIfDead();
750
+ if (dead) return dead;
751
+ const journey = state.char.journey;
752
+ if (!journey) return { ok: false, reason: 'You are not travelling' };
753
+ const progress = journeyProgress(journey, nowMs);
754
+ // Prepaid taxi (legs.costCopper after Player.cpp:17118 rep discount) — refund incomplete legs
755
+ // only; cancel undoes unrendered service (KI-901). Completed hops keep their fare.
756
+ let refundCopper = 0;
757
+ for (let i = progress.legIndex; i < journey.legs.length; i++) {
758
+ refundCopper += journey.legs[i]!.costCopper;
759
+ }
760
+ state.char.zoneId = progress.zone;
761
+ state.char = {
762
+ ...state.char,
763
+ journey: undefined,
764
+ gold: state.char.gold + refundCopper,
765
+ };
766
+ resetJourneyTrack(undefined);
767
+ state.corpse = null;
768
+ const zoneName = ds.zone(progress.zone)?.name ?? progress.zone;
769
+ const fareNote = refundCopper > 0 ? ` Fare refunded: ${moneyMarked(refundCopper)}.` : '';
770
+ log(`You abandon your journey in ${zoneName}.${fareNote}`);
771
+ return { ok: true };
772
+ },
773
+
774
+ setJourneySpeed(scale, nowMs = Date.now()) {
775
+ if (scale !== 1 && scale !== 2 && scale !== 4) {
776
+ return { ok: false, reason: 'Travel speed must be 1, 2 or 4' };
777
+ }
778
+ doJourneyTick(nowMs); // observe arrival first - never fast-forward a finished journey
779
+ const journey = state.char.journey;
780
+ if (!journey) return { ok: false, reason: 'You are not travelling' };
781
+ const next = setJourneyTimeScale(journey, scale, nowMs);
782
+ if (next !== journey) {
783
+ state.char = { ...state.char, journey: next };
784
+ // The rebase moves departedAtEpochMs, which resetJourneyTrack uses as the journey identity - carry
785
+ // the key over so a dial press never re-fires zone hooks on the same journey.
786
+ setJourneyTrackKey(next.departedAtEpochMs);
787
+ }
788
+ return { ok: true };
789
+ },
790
+
791
+ completeJourneyNow(nowMs = Date.now()) {
792
+ // Instant arrival, gated on the live travel pace (D3 amendment). Phase-6
793
+ // server path must never accept this call.
794
+ if (travelPaceOf(ctx) !== 'instant') {
795
+ return { ok: false, reason: 'Instant travel is disabled' };
796
+ }
797
+ // Observe first: death mid-journey aborts in place via journeyTick (KI-834).
798
+ doJourneyTick(nowMs);
799
+ const dead = refuseIfDead();
800
+ if (dead) return dead;
801
+ const journey = state.char.journey;
802
+ if (!journey) return { ok: false, reason: 'You are not travelling' };
803
+ state.char = {
804
+ ...state.char,
805
+ journey: doCompleteJourneyNow(journey, nowMs),
806
+ };
807
+ doJourneyTick(nowMs);
808
+ return { ok: true };
809
+ },
810
+
811
+ abandonLiveFight(nowMs = Date.now()) {
812
+ const bridge = fightRef.current;
813
+ if (!bridge) return { ok: false, reason: 'No fight in progress' };
814
+ const session = bridge.getLiveFightSession();
815
+ if (!session) return { ok: false, reason: 'No fight in progress' };
816
+ unsummonTotemsOnLeaveWorld(session);
817
+ // The live session never wrote back to state.char, so dropping it leaves the hero at
818
+ // pre-fight HP with no XP/loot/wear - the fight simply never counted.
819
+ bridge.setPendingFight(null);
820
+ bridge.setLiveFightSession(null);
821
+ return { ok: true };
822
+ },
823
+
824
+ bindHearth(nowMs = Date.now()) {
825
+ // Same busy guard as travelTo / castHearth — binding is an innkeeper dialog, impossible mid-flight
826
+ // (KI-546). Observe arrival first so a just-finished journey does not falsely block.
827
+ doJourneyTick(nowMs);
828
+ const dead = refuseIfDead();
829
+ if (dead) return dead;
830
+ if (state.char.journey) {
831
+ return { ok: false, reason: 'You are already travelling' };
832
+ }
833
+ if (state.instance) return { ok: false, reason: 'Cannot bind inside an instance' };
834
+ // Vanilla bind is innkeeper gossip only (Unit::CanInteract via innkeeperHere /
835
+ // KI-669). areatrigger_tavern rows are rest-XP markers, not bind points (KI-731).
836
+ const inn = innkeeperHere();
837
+ if (!inn) {
838
+ return { ok: false, reason: 'There is no inn here' };
839
+ }
840
+ const tav = ds.tavernTriggers().find((t) => t.zone === state.char.zoneId);
841
+ const bind: HearthBind = {
842
+ zone: state.char.zoneId,
843
+ name: inn.name,
844
+ ...(tav ? { trigger: tav.id } : {}),
845
+ };
846
+ state.char = { ...state.char, hearthBind: bind };
847
+
848
+ // Vanilla innkeepers replace a lost hearthstone (item 6948). Documented bridge:
849
+ // CharStartOutfit / inn bind grants the stone when missing; seed-hearthstone.mjs.
850
+ const inBags = countItem(state.inventory, HEARTHSTONE_ITEM);
851
+ const inBank = countItem(state.bank, HEARTHSTONE_ITEM);
852
+ if (inBags <= 0 && inBank <= 0) {
853
+ const idef = ds.item(HEARTHSTONE_ITEM);
854
+ if (idef) {
855
+ state.inventory = addItem(
856
+ state.inventory,
857
+ HEARTHSTONE_ITEM,
858
+ 1,
859
+ idef.stackable ?? 1,
860
+ );
861
+ }
862
+ }
863
+
864
+ const place = bind.name ?? ds.zone(bind.zone)?.name ?? bind.zone;
865
+ log(`You are now bound to ${place}.`);
866
+ return { ok: true, bind };
867
+ },
868
+
869
+ castHearth(nowMs = Date.now()) {
870
+ doJourneyTick(nowMs);
871
+ const dead = refuseIfDead();
872
+ if (dead) return dead;
873
+ if (state.char.journey) {
874
+ return { ok: false, reason: 'You are already travelling' };
875
+ }
876
+ const bridge = fightRef.current;
877
+ if (bridge?.getPendingFight()) return { ok: false, reason: 'You are in combat' };
878
+ const bind = state.char.hearthBind;
879
+ if (!bind) return { ok: false, reason: 'You have no hearthstone bind' };
880
+ if (countItem(state.inventory, HEARTHSTONE_ITEM) <= 0) {
881
+ return { ok: false, reason: 'You need a Hearthstone' };
882
+ }
883
+ const cdUntil = state.char.hearthCooldownUntil ?? 0;
884
+ if (nowMs < cdUntil) {
885
+ const remMin = Math.ceil((cdUntil - nowMs) / 60_000);
886
+ return { ok: false, reason: `Hearthstone is recharging (${remMin} min left)` };
887
+ }
888
+ // Timing from the baked hearthstone spell: the cast time drives the journey leg, while the
889
+ // recovery is applied only on successful arrival, so a cancel must not start the cooldown.
890
+ const spell = ds.spell(HEARTHSTONE_SPELL);
891
+ if (!spell) return { ok: false, reason: 'Hearthstone spell missing from data' };
892
+ const castTimeMs = spell.castTimeMs ?? 0;
893
+ const seconds = castTimeMs / 1000;
894
+ const leg: JourneyLeg = {
895
+ kind: 'hearth',
896
+ from: state.char.zoneId,
897
+ to: bind.zone,
898
+ seconds,
899
+ costCopper: 0,
900
+ };
901
+ // Departing leaves any open instance (no "leave first" gate — same as travelTo).
902
+ dropOpenInstanceOnDepart(nowMs);
903
+ unsummonTotemsOnLeaveWorld(bridge?.getLiveFightSession());
904
+ const journey = departJourney([leg], nowMs);
905
+ state.char = {
906
+ ...state.char,
907
+ journey,
908
+ };
909
+ resetJourneyTrack(journey);
910
+ applyTravelPace(nowMs);
911
+ const dest = bind.name ?? ds.zone(bind.zone)?.name ?? bind.zone;
912
+ log(`You cast Hearthstone - returning to ${dest}.`);
913
+ doJourneyTick(nowMs);
914
+ return { ok: true, totalSeconds: seconds };
915
+ },
916
+
917
+ classTravelOptions(nowMs = Date.now()) {
918
+ // Pure read with lazy tick (same pattern as journeyStatus) so arrival is observed.
919
+ doJourneyTick(nowMs);
920
+ const known = new Set(state.spellbook.known);
921
+ // The City Defender easter egg rides the mage's own rows: rank 15 grants the six capital
922
+ // teleports free of the rune and the mana. Cite: domain/run/honor.ts isCityDefender.
923
+ const defender = isCityDefender(state.char.honor);
924
+ const out: Array<{
925
+ spellId: number;
926
+ name: string;
927
+ toZone: string;
928
+ castSeconds: number;
929
+ free: boolean;
930
+ reagents: Array<{ item: number; count: number; have: number }>;
931
+ }> = [];
932
+ // Only real player travel teleports the character knows, which is what keeps non-travel junk and
933
+ // quest or script ports out. Cite: DataBundle.spellTeleports.
934
+ for (const row of ds.spellTeleports()) {
935
+ const granted = defender && isCapitalTeleport(row);
936
+ if (!known.has(row.spell) && !granted) continue;
937
+ const spell = ds.spell(row.spell);
938
+ if (!spell) continue;
939
+ if (!isTravelTeleportSpell(spell)) continue;
940
+ const castTimeMs = spell.castTimeMs ?? 0;
941
+ out.push({
942
+ spellId: row.spell,
943
+ name: spell.name,
944
+ toZone: row.zone,
945
+ castSeconds: castTimeMs / 1000,
946
+ free: granted,
947
+ reagents: granted ? [] : row.reagents.map((r) => ({
948
+ item: r.item,
949
+ count: r.count,
950
+ have: countItem(state.inventory, r.item),
951
+ })),
952
+ });
953
+ }
954
+ return out;
955
+ },
956
+
957
+ castTeleport(spellId, nowMs = Date.now()) {
958
+ doJourneyTick(nowMs);
959
+ const dead = refuseIfDead();
960
+ if (dead) return dead;
961
+ if (state.char.journey) {
962
+ return { ok: false, reason: 'You are already travelling' };
963
+ }
964
+ const bridge = fightRef.current;
965
+ if (bridge?.getPendingFight()) return { ok: false, reason: 'You are in combat' };
966
+ // Destination + reagents from bake only - never hardcode zones/reagents.
967
+ // Cite: enrich-class-travel.mjs; spell_target_position + spell_template Reagent1..8.
968
+ const row = ds.spellTeleports().find((t) => t.spell === spellId);
969
+ // A City Defender casts the six capital teleports without knowing them, without the rune and
970
+ // without the mana — the rank IS the spell. Everything else still needs the spellbook.
971
+ const granted = row != null && isCapitalTeleport(row) && isCityDefender(state.char.honor);
972
+ if (!granted && !state.spellbook.known.includes(spellId)) {
973
+ return { ok: false, reason: 'You do not know that spell' };
974
+ }
975
+ if (!row) {
976
+ return { ok: false, reason: 'That spell is not a teleport' };
977
+ }
978
+ const spell = ds.spell(spellId);
979
+ if (!spell) return { ok: false, reason: 'Spell missing from data' };
980
+ // Refuse non-player-travel spells that polluted spell_target_position (KI-705/KI-900).
981
+ if (!isTravelTeleportSpell(spell)) {
982
+ return { ok: false, reason: 'That spell is not a teleport' };
983
+ }
984
+ if (row.zone === state.char.zoneId) {
985
+ return { ok: false, reason: 'You are already here' };
986
+ }
987
+
988
+ // Mana gate (KI-643). TakePower deferred to arrival with reagents.
989
+ const manaCost = granted ? 0 : (spell.cost ?? 0) | 0;
990
+ if (manaCost > 0 && currentMana(state.char) < manaCost) {
991
+ return { ok: false, reason: 'Not enough mana' };
992
+ }
993
+
994
+ // Recovery gate (portal 60s, etc.). Cite: SpellMgr.h GetSpellRecoveryTime.
995
+ const rec = getSpellRecoveryTime(spell);
996
+ if (rec > 0) {
997
+ const readyAt = state.spellCooldowns?.[spellId] ?? 0;
998
+ if (nowMs < readyAt) {
999
+ return { ok: false, reason: 'On cooldown' };
1000
+ }
1001
+ if (/^Portal:/i.test(spell.name ?? '')) {
1002
+ for (const pr of ds.spellTeleports()) {
1003
+ const ps = ds.spell(pr.spell);
1004
+ if (!ps || !/^Portal:/i.test(ps.name ?? '')) continue;
1005
+ const pReady = state.spellCooldowns?.[pr.spell] ?? 0;
1006
+ if (nowMs < pReady) return { ok: false, reason: 'On cooldown' };
1007
+ }
1008
+ }
1009
+ }
1010
+
1011
+ // Reagents are checked at cast start and taken only on successful arrival, so an interrupt does
1012
+ // not consume them. Cite: Spell.cpp CheckItems; TakeReagents after the cast lands.
1013
+ for (const r of (granted ? [] : row.reagents)) {
1014
+ if (countItem(state.inventory, r.item) < r.count) {
1015
+ const idef = ds.item(r.item);
1016
+ return {
1017
+ ok: false,
1018
+ reason: idef
1019
+ ? `Requires ${r.count}× ${idef.name}`
1020
+ : `Missing reagents (item ${r.item})`,
1021
+ };
1022
+ }
1023
+ }
1024
+
1025
+ // Cast time from baked spell row - not a hardcoded constant.
1026
+ const castTimeMs = spell.castTimeMs ?? 0;
1027
+ const seconds = castTimeMs / 1000;
1028
+ const leg: JourneyLeg = {
1029
+ kind: 'portal',
1030
+ from: state.char.zoneId,
1031
+ to: row.zone,
1032
+ seconds,
1033
+ costCopper: 0,
1034
+ };
1035
+ // Channeling leaves any open instance (same as travelTo — no "leave first" gate).
1036
+ dropOpenInstanceOnDepart(nowMs);
1037
+ unsummonTotemsOnLeaveWorld(bridge?.getLiveFightSession());
1038
+ const journey: Journey = {
1039
+ ...departJourney([leg], nowMs),
1040
+ teleportSpellId: spellId,
1041
+ };
1042
+ state.char = { ...state.char, journey };
1043
+ state.corpse = null;
1044
+ state.char.activeAuras = pruneExpiredAuras(state.char.activeAuras);
1045
+ resetJourneyTrack(journey);
1046
+ applyTravelPace(nowMs);
1047
+ log(`You begin channeling ${spell.name}...`);
1048
+ doJourneyTick(nowMs);
1049
+ return { ok: true, totalSeconds: seconds };
1050
+ },
1051
+
1052
+ taxiOptions(_nowMs = Date.now()) {
1053
+ // Pure read: single-hop flights from discovered nodes in the current zone.
1054
+ const zoneId = state.char.zoneId;
1055
+ const discovered = new Set(state.char.discoveredTaxiNodes ?? []);
1056
+ const faction = travelerFaction();
1057
+ const discountMod = taxiDiscountMod();
1058
+ const nodeById = new Map(ds.taxiNodes().map((n) => [n.id, n]));
1059
+ const hereNodes = ds.taxiNodes().filter(
1060
+ (n) => n.zone === zoneId && discovered.has(n.id),
1061
+ );
1062
+ const out: Array<{ toZone: string; seconds: number; priceCopper: number; pathId: number }> = [];
1063
+ const factionOk = (f: string): boolean =>
1064
+ f === 'both' || f === faction;
1065
+
1066
+ for (const fromNode of hereNodes) {
1067
+ if (!factionOk(fromNode.faction)) continue;
1068
+ for (const route of ds.taxiRoutes()) {
1069
+ if (route.transportPath) continue;
1070
+ if (route.from !== fromNode.id) continue;
1071
+ if (!(route.seconds > 0)) continue;
1072
+ if (!discovered.has(route.to)) continue;
1073
+ const toNode = nodeById.get(route.to);
1074
+ if (!toNode?.zone) continue;
1075
+ if (!factionOk(toNode.faction)) continue;
1076
+ if (toNode.zone === zoneId) continue;
1077
+ out.push({
1078
+ toZone: toNode.zone,
1079
+ seconds: route.seconds,
1080
+ priceCopper: taxiFareCopper(route.priceCopper, discountMod),
1081
+ pathId: route.id,
1082
+ });
1083
+ }
1084
+ }
1085
+ return out;
1086
+ },
1087
+
1088
+ trainRiding(trainerNpcEntry, rankOrSpellId) {
1089
+ const spells = ds.ridingSpells() as RidingSpellRow[];
1090
+ const spell = findRidingSpell(spells, rankOrSpellId);
1091
+ if (!spell) return { ok: false, reason: 'Unknown riding rank' };
1092
+ // Zone presence: trainer must be listed on current zone.ridingTrainers.
1093
+ // Entry 0 skips the gate (tests / debug). Riding NPCs are not in `trainers` map.
1094
+ if (trainerNpcEntry !== 0) {
1095
+ const zone = ds.zone(state.char.zoneId);
1096
+ const listed = zone?.ridingTrainers?.includes(trainerNpcEntry);
1097
+ if (!listed) {
1098
+ return { ok: false, reason: 'No riding trainer here' };
1099
+ }
1100
+ // Optional faction refuse when we have a creature def (fail open if missing).
1101
+ if (ds.creature(trainerNpcEntry) && !canInteractWithCreature(ds, state.char.race, trainerNpcEntry)) {
1102
+ return { ok: false, reason: 'They refuse to deal with you.' };
1103
+ }
1104
+ }
1105
+ const goldBefore = state.char.gold;
1106
+ const discountMod = vendorPriceMultiplierFor(ds, state.char, trainerNpcEntry);
1107
+ const res = doTrainRiding(state.char, spell, discountMod);
1108
+ if (!res.ok) return { ok: false, reason: res.reason ?? 'Cannot train riding' };
1109
+ state.char = res.char;
1110
+ const spent = goldBefore - state.char.gold;
1111
+ const npcName = trainerNpcEntry
1112
+ ? (ds.creature(trainerNpcEntry)?.name ?? `trainer ${trainerNpcEntry}`)
1113
+ : 'riding instructor';
1114
+ log(`Trained Riding (${spell.rank}) with ${npcName} for ${moneyMarked(spent)}.`);
1115
+ return { ok: true, rank: spell.rank, spent };
1116
+ },
1117
+
1118
+ useMount(itemEntry) {
1119
+ const dead = refuseIfDead();
1120
+ if (dead) return dead;
1121
+ if (countItem(state.inventory, itemEntry) <= 0) {
1122
+ return { ok: false, reason: "You don't have that mount" };
1123
+ }
1124
+ const item = ds.item(itemEntry);
1125
+ if (!item) return { ok: false, reason: 'Unknown item' };
1126
+ // Mounting is a non-combat action: the flag is hot for the pull, so the bag button refuses the
1127
+ // same way a mount SPELL does. Cite: oocCast.playerCombatFlagSet; Spell.cpp:4598.
1128
+ const res = doMount(state.char, item, {
1129
+ inCombat: playerCombatFlagSet(fightRef.current?.getLiveFightSession()),
1130
+ });
1131
+ if (!res.ok) return { ok: false, reason: res.reason ?? 'Cannot mount' };
1132
+ state.char = res.char;
1133
+
1134
+ // Cite: Unit.cpp:7747, the first statement in Unit::Mount.
1135
+ const live = fightRef.current?.getLiveFightSession();
1136
+ if (live?.removeAurasWithInterruptFlags) {
1137
+ live.removeAurasWithInterruptFlags('player', AURA_INTERRUPT.MOUNTING, 'mounting');
1138
+ }
1139
+
1140
+ const pct = item.mountSpeedPct;
1141
+ log(`You mount ${item.name}${pct != null ? ` (+${pct}% travel speed)` : ''}.`);
1142
+ onOutfitterSync?.();
1143
+ return { ok: true };
1144
+ },
1145
+
1146
+ dismount() {
1147
+ const res = doDismount(state.char);
1148
+ if (!res.ok) return { ok: false, reason: res.reason ?? 'Not mounted' };
1149
+ state.char = res.char;
1150
+ log('You dismount.');
1151
+ onOutfitterSync?.();
1152
+ return { ok: true };
1153
+ },
1154
+ };
1155
+ }