1. From ce93f3d7456f57147b1abce63bd7a5de5bb7eb22 Mon Sep 17 00:00:00 2001
  2. From: trickerer <[email protected]>
  3. Date: Thu, 4 Apr 2013 17:59:34 +0700
  4. Subject: [PATCH] NPCBots
  5. ---
  6. sql/Bots/character_NPC_bots.sql | 12 +
  7. .../world_bot_giver_locales_gossip_menu_option.sql | 92 +
  8. sql/Bots/world_bots.sql | 524 ++++
  9. sql/Bots/world_script_bot_giver.sql | 13 +
  10. src/server/game/AI/NpcBots/bot_GridNotifiers.h | 462 +++
  11. src/server/game/AI/NpcBots/bot_ai.cpp | 3185 ++++++++++++++++++++
  12. src/server/game/AI/NpcBots/bot_ai.h | 355 +++
  13. src/server/game/AI/NpcBots/bot_druid_ai.cpp | 1092 +++++++
  14. src/server/game/AI/NpcBots/bot_hunter_ai.cpp | 340 +++
  15. src/server/game/AI/NpcBots/bot_mage_ai.cpp | 935 ++++++
  16. src/server/game/AI/NpcBots/bot_paladin_ai.cpp | 1014 +++++++
  17. src/server/game/AI/NpcBots/bot_priest_ai.cpp | 859 ++++++
  18. src/server/game/AI/NpcBots/bot_rogue_ai.cpp | 894 ++++++
  19. src/server/game/AI/NpcBots/bot_shaman_ai.cpp | 390 +++
  20. src/server/game/AI/NpcBots/bot_warlock_ai.cpp | 458 +++
  21. src/server/game/AI/NpcBots/bot_warrior_ai.cpp | 1192 ++++++++
  22. src/server/game/AI/NpcBots/botcommands.cpp | 851 ++++++
  23. src/server/game/AI/NpcBots/botgiver.cpp | 643 ++++
  24. src/server/game/AI/PlayerBots/bp_ai.cpp | 1888 ++++++++++++
  25. src/server/game/AI/PlayerBots/bp_ai.h | 611 ++++
  26. src/server/game/AI/PlayerBots/bp_cai.cpp | 14 +
  27. src/server/game/AI/PlayerBots/bp_cai.h | 118 +
  28. src/server/game/AI/PlayerBots/bp_dk_ai.cpp | 549 ++++
  29. src/server/game/AI/PlayerBots/bp_dk_ai.h | 159 +
  30. src/server/game/AI/PlayerBots/bp_dru_ai.cpp | 787 +++++
  31. src/server/game/AI/PlayerBots/bp_dru_ai.h | 223 ++
  32. src/server/game/AI/PlayerBots/bp_hun_ai.cpp | 407 +++
  33. src/server/game/AI/PlayerBots/bp_hun_ai.h | 122 +
  34. src/server/game/AI/PlayerBots/bp_mag_ai.cpp | 356 +++
  35. src/server/game/AI/PlayerBots/bp_mag_ai.h | 165 +
  36. src/server/game/AI/PlayerBots/bp_mgr.cpp | 1136 +++++++
  37. src/server/game/AI/PlayerBots/bp_mgr.h | 64 +
  38. src/server/game/AI/PlayerBots/bp_pal_ai.cpp | 673 +++++
  39. src/server/game/AI/PlayerBots/bp_pal_ai.h | 208 ++
  40. src/server/game/AI/PlayerBots/bp_pri_ai.cpp | 514 ++++
  41. src/server/game/AI/PlayerBots/bp_pri_ai.h | 166 +
  42. src/server/game/AI/PlayerBots/bp_rog_ai.cpp | 408 +++
  43. src/server/game/AI/PlayerBots/bp_rog_ai.h | 104 +
  44. src/server/game/AI/PlayerBots/bp_sha_ai.cpp | 1660 ++++++++++
  45. src/server/game/AI/PlayerBots/bp_sha_ai.h | 246 ++
  46. src/server/game/AI/PlayerBots/bp_warl_ai.cpp | 550 ++++
  47. src/server/game/AI/PlayerBots/bp_warl_ai.h | 254 ++
  48. src/server/game/AI/PlayerBots/bp_warr_ai.cpp | 551 ++++
  49. src/server/game/AI/PlayerBots/bp_warr_ai.h | 182 ++
  50. src/server/game/CMakeLists.txt | 2 +
  51. src/server/game/Entities/Creature/Creature.cpp | 128 +
  52. src/server/game/Entities/Creature/Creature.h | 38 +
  53. .../game/Entities/Creature/TemporarySummon.cpp | 8 +
  54. src/server/game/Entities/Player/Player.cpp | 937 ++++++
  55. src/server/game/Entities/Player/Player.h | 86 +
  56. src/server/game/Entities/Unit/Unit.cpp | 214 ++
  57. src/server/game/Groups/Group.cpp | 10 +
  58. src/server/game/Groups/Group.h | 3 +
  59. src/server/game/Handlers/CharacterHandler.cpp | 98 +
  60. src/server/game/Handlers/ChatHandler.cpp | 30 +
  61. src/server/game/Handlers/QuestHandler.cpp | 8 +
  62. src/server/game/Maps/Map.cpp | 3 +
  63. src/server/game/Scripting/ScriptLoader.cpp | 24 +
  64. src/server/game/Server/WorldSession.cpp | 100 +
  65. src/server/game/Server/WorldSession.h | 24 +
  66. src/server/scripts/Spells/spell_priest.cpp | 4 +
  67. .../Database/Implementation/CharacterDatabase.cpp | 8 +
  68. .../Database/Implementation/CharacterDatabase.h | 8 +
  69. .../Database/Implementation/WorldDatabase.cpp | 4 +
  70. .../shared/Database/Implementation/WorldDatabase.h | 4 +
  71. src/server/worldserver/worldserver.conf.dist | 193 ++
  72. 66 files changed, 27360 insertions(+), 0 deletions(-)
  73. create mode 100644 sql/Bots/character_NPC_bots.sql
  74. create mode 100644 sql/Bots/world_bot_giver_locales_gossip_menu_option.sql
  75. create mode 100644 sql/Bots/world_bots.sql
  76. create mode 100644 sql/Bots/world_script_bot_giver.sql
  77. create mode 100644 src/server/game/AI/NpcBots/bot_GridNotifiers.h
  78. create mode 100644 src/server/game/AI/NpcBots/bot_ai.cpp
  79. create mode 100644 src/server/game/AI/NpcBots/bot_ai.h
  80. create mode 100644 src/server/game/AI/NpcBots/bot_druid_ai.cpp
  81. create mode 100644 src/server/game/AI/NpcBots/bot_hunter_ai.cpp
  82. create mode 100644 src/server/game/AI/NpcBots/bot_mage_ai.cpp
  83. create mode 100644 src/server/game/AI/NpcBots/bot_paladin_ai.cpp
  84. create mode 100644 src/server/game/AI/NpcBots/bot_priest_ai.cpp
  85. create mode 100644 src/server/game/AI/NpcBots/bot_rogue_ai.cpp
  86. create mode 100644 src/server/game/AI/NpcBots/bot_shaman_ai.cpp
  87. create mode 100644 src/server/game/AI/NpcBots/bot_warlock_ai.cpp
  88. create mode 100644 src/server/game/AI/NpcBots/bot_warrior_ai.cpp
  89. create mode 100644 src/server/game/AI/NpcBots/botcommands.cpp
  90. create mode 100644 src/server/game/AI/NpcBots/botgiver.cpp
  91. create mode 100644 src/server/game/AI/PlayerBots/bp_ai.cpp
  92. create mode 100644 src/server/game/AI/PlayerBots/bp_ai.h
  93. create mode 100644 src/server/game/AI/PlayerBots/bp_cai.cpp
  94. create mode 100644 src/server/game/AI/PlayerBots/bp_cai.h
  95. create mode 100644 src/server/game/AI/PlayerBots/bp_dk_ai.cpp
  96. create mode 100644 src/server/game/AI/PlayerBots/bp_dk_ai.h
  97. create mode 100644 src/server/game/AI/PlayerBots/bp_dru_ai.cpp
  98. create mode 100644 src/server/game/AI/PlayerBots/bp_dru_ai.h
  99. create mode 100644 src/server/game/AI/PlayerBots/bp_hun_ai.cpp
  100. create mode 100644 src/server/game/AI/PlayerBots/bp_hun_ai.h
  101. create mode 100644 src/server/game/AI/PlayerBots/bp_mag_ai.cpp
  102. create mode 100644 src/server/game/AI/PlayerBots/bp_mag_ai.h
  103. create mode 100644 src/server/game/AI/PlayerBots/bp_mgr.cpp
  104. create mode 100644 src/server/game/AI/PlayerBots/bp_mgr.h
  105. create mode 100644 src/server/game/AI/PlayerBots/bp_pal_ai.cpp
  106. create mode 100644 src/server/game/AI/PlayerBots/bp_pal_ai.h
  107. create mode 100644 src/server/game/AI/PlayerBots/bp_pri_ai.cpp
  108. create mode 100644 src/server/game/AI/PlayerBots/bp_pri_ai.h
  109. create mode 100644 src/server/game/AI/PlayerBots/bp_rog_ai.cpp
  110. create mode 100644 src/server/game/AI/PlayerBots/bp_rog_ai.h
  111. create mode 100644 src/server/game/AI/PlayerBots/bp_sha_ai.cpp
  112. create mode 100644 src/server/game/AI/PlayerBots/bp_sha_ai.h
  113. create mode 100644 src/server/game/AI/PlayerBots/bp_warl_ai.cpp
  114. create mode 100644 src/server/game/AI/PlayerBots/bp_warl_ai.h
  115. create mode 100644 src/server/game/AI/PlayerBots/bp_warr_ai.cpp
  116. create mode 100644 src/server/game/AI/PlayerBots/bp_warr_ai.h
  117. diff --git a/sql/Bots/character_NPC_bots.sql b/sql/Bots/character_NPC_bots.sql
  118. new file mode 100644
  119. index 0000000..723043e
  120. --- /dev/null
  121. +++ b/sql/Bots/character_NPC_bots.sql
  122. @@ -0,0 +1,12 @@
  123. +DROP TABLE IF EXISTS `character_npcbot`;
  124. +CREATE TABLE `character_npcbot` (
  125. + `owner` int(10) default NULL,
  126. + `entry` int(10) default NULL,
  127. + `race` tinyint(3) default NULL,
  128. + `class` tinyint(3) default NULL,
  129. + `istank` tinyint(3) default NULL,
  130. + PRIMARY KEY (`owner`,`entry`)
  131. +) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  132. +
  133. +
  134. +
  135. diff --git a/sql/Bots/world_bot_giver_locales_gossip_menu_option.sql b/sql/Bots/world_bot_giver_locales_gossip_menu_option.sql
  136. new file mode 100644
  137. index 0000000..a9f5c6b
  138. --- /dev/null
  139. +++ b/sql/Bots/world_bot_giver_locales_gossip_menu_option.sql
  140. @@ -0,0 +1,92 @@
  141. +delete from `locales_gossip_menu_option` where `menu_id` = '60000';
  142. +
  143. +insert into `locales_gossip_menu_option` (`menu_id`, `id`, `option_text_loc1`, `option_text_loc2`, `option_text_loc3`, `option_text_loc4`, `option_text_loc5`, `option_text_loc6`, `option_text_loc7`, `option_text_loc8`, `box_text_loc1`, `box_text_loc2`, `box_text_loc3`, `box_text_loc4`, `box_text_loc5`, `box_text_loc6`, `box_text_loc7`, `box_text_loc8`) values('60000','1','Abandon my Player','Abandon my Player','Abandon my Player','Abandon my Player','Abandon my Player','Abandon my Player','Abandon my Player','Abandon my Player',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
  144. +insert into `locales_gossip_menu_option` (`menu_id`, `id`, `option_text_loc1`, `option_text_loc2`, `option_text_loc3`, `option_text_loc4`, `option_text_loc5`, `option_text_loc6`, `option_text_loc7`, `option_text_loc8`, `box_text_loc1`, `box_text_loc2`, `box_text_loc3`, `box_text_loc4`, `box_text_loc5`, `box_text_loc6`, `box_text_loc7`, `box_text_loc8`) values('60000','2','Recruit a Player','Recruit a Player','Recruit a Player','Recruit a Player','Recruit a Player','Recruit a Player','Recruit a Player','Recruit a Player',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
  145. +insert into `locales_gossip_menu_option` (`menu_id`, `id`, `option_text_loc1`, `option_text_loc2`, `option_text_loc3`, `option_text_loc4`, `option_text_loc5`, `option_text_loc6`, `option_text_loc7`, `option_text_loc8`, `box_text_loc1`, `box_text_loc2`, `box_text_loc3`, `box_text_loc4`, `box_text_loc5`, `box_text_loc6`, `box_text_loc7`, `box_text_loc8`) values('60000','3','Abandon my Minion','Abandon my Minion','Abandon my Minion','Abandon my Minion','Abandon my Minion','Abandon my Minion','Abandon my Minion','Abandon my Minion',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
  146. +insert into `locales_gossip_menu_option` (`menu_id`, `id`, `option_text_loc1`, `option_text_loc2`, `option_text_loc3`, `option_text_loc4`, `option_text_loc5`, `option_text_loc6`, `option_text_loc7`, `option_text_loc8`, `box_text_loc1`, `box_text_loc2`, `box_text_loc3`, `box_text_loc4`, `box_text_loc5`, `box_text_loc6`, `box_text_loc7`, `box_text_loc8`) values('60000','4','Recruit a Minion','Recruit a Minion','Recruit a Minion','Recruit a Minion','Recruit a Minion','Recruit a Minion','Recruit a Minion','Recruit a Minion',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
  147. +insert into `locales_gossip_menu_option` (`menu_id`, `id`, `option_text_loc1`, `option_text_loc2`, `option_text_loc3`, `option_text_loc4`, `option_text_loc5`, `option_text_loc6`, `option_text_loc7`, `option_text_loc8`, `box_text_loc1`, `box_text_loc2`, `box_text_loc3`, `box_text_loc4`, `box_text_loc5`, `box_text_loc6`, `box_text_loc7`, `box_text_loc8`) values('60000','5','Tell me about these bots','Tell me about these bots','Tell me about these bots','Tell me about these bots','Tell me about these bots','Tell me about these bots','Tell me about these bots','Tell me about these bots',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
  148. +insert into `locales_gossip_menu_option` (`menu_id`, `id`, `option_text_loc1`, `option_text_loc2`, `option_text_loc3`, `option_text_loc4`, `option_text_loc5`, `option_text_loc6`, `option_text_loc7`, `option_text_loc8`, `box_text_loc1`, `box_text_loc2`, `box_text_loc3`, `box_text_loc4`, `box_text_loc5`, `box_text_loc6`, `box_text_loc7`, `box_text_loc8`) values('60000','6','ADD ALL','ADD ALL','ADD ALL','ADD ALL','ADD ALL','ADD ALL','ADD ALL','ADD ALL',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
  149. +insert into `locales_gossip_menu_option` (`menu_id`, `id`, `option_text_loc1`, `option_text_loc2`, `option_text_loc3`, `option_text_loc4`, `option_text_loc5`, `option_text_loc6`, `option_text_loc7`, `option_text_loc8`, `box_text_loc1`, `box_text_loc2`, `box_text_loc3`, `box_text_loc4`, `box_text_loc5`, `box_text_loc6`, `box_text_loc7`, `box_text_loc8`) values('60000','7','REMOVE ALL','REMOVE ALL','REMOVE ALL','REMOVE ALL','REMOVE ALL','REMOVE ALL','REMOVE ALL','REMOVE ALL',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
  150. +insert into `locales_gossip_menu_option` (`menu_id`, `id`, `option_text_loc1`, `option_text_loc2`, `option_text_loc3`, `option_text_loc4`, `option_text_loc5`, `option_text_loc6`, `option_text_loc7`, `option_text_loc8`, `box_text_loc1`, `box_text_loc2`, `box_text_loc3`, `box_text_loc4`, `box_text_loc5`, `box_text_loc6`, `box_text_loc7`, `box_text_loc8`) values('60000','8','Recruit a Warrior ','Recruit a Warrior ','Recruit a Warrior ','Recruit a Warrior ','Recruit a Warrior ','Recruit a Warrior ','Recruit a Warrior ','Recruit a Warrior ',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
  151. +insert into `locales_gossip_menu_option` (`menu_id`, `id`, `option_text_loc1`, `option_text_loc2`, `option_text_loc3`, `option_text_loc4`, `option_text_loc5`, `option_text_loc6`, `option_text_loc7`, `option_text_loc8`, `box_text_loc1`, `box_text_loc2`, `box_text_loc3`, `box_text_loc4`, `box_text_loc5`, `box_text_loc6`, `box_text_loc7`, `box_text_loc8`) values('60000','9','Recruit a Hunter ','Recruit a Hunter ','Recruit a Hunter ','Recruit a Hunter ','Recruit a Hunter ','Recruit a Hunter ','Recruit a Hunter ','Recruit a Hunter ',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
  152. +insert into `locales_gossip_menu_option` (`menu_id`, `id`, `option_text_loc1`, `option_text_loc2`, `option_text_loc3`, `option_text_loc4`, `option_text_loc5`, `option_text_loc6`, `option_text_loc7`, `option_text_loc8`, `box_text_loc1`, `box_text_loc2`, `box_text_loc3`, `box_text_loc4`, `box_text_loc5`, `box_text_loc6`, `box_text_loc7`, `box_text_loc8`) values('60000','10','Recruit a Paladin ','Recruit a Paladin ','Recruit a Paladin ','Recruit a Paladin ','Recruit a Paladin ','Recruit a Paladin ','Recruit a Paladin ','Recruit a Paladin ',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
  153. +insert into `locales_gossip_menu_option` (`menu_id`, `id`, `option_text_loc1`, `option_text_loc2`, `option_text_loc3`, `option_text_loc4`, `option_text_loc5`, `option_text_loc6`, `option_text_loc7`, `option_text_loc8`, `box_text_loc1`, `box_text_loc2`, `box_text_loc3`, `box_text_loc4`, `box_text_loc5`, `box_text_loc6`, `box_text_loc7`, `box_text_loc8`) values('60000','11','Recruit a Shaman ','Recruit a Shaman ','Recruit a Shaman ','Recruit a Shaman ','Recruit a Shaman ','Recruit a Shaman ','Recruit a Shaman ','Recruit a Shaman ',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
  154. +insert into `locales_gossip_menu_option` (`menu_id`, `id`, `option_text_loc1`, `option_text_loc2`, `option_text_loc3`, `option_text_loc4`, `option_text_loc5`, `option_text_loc6`, `option_text_loc7`, `option_text_loc8`, `box_text_loc1`, `box_text_loc2`, `box_text_loc3`, `box_text_loc4`, `box_text_loc5`, `box_text_loc6`, `box_text_loc7`, `box_text_loc8`) values('60000','12','Recruit a Rogue ','Recruit a Rogue ','Recruit a Rogue ','Recruit a Rogue ','Recruit a Rogue ','Recruit a Rogue ','Recruit a Rogue ','Recruit a Rogue ',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
  155. +insert into `locales_gossip_menu_option` (`menu_id`, `id`, `option_text_loc1`, `option_text_loc2`, `option_text_loc3`, `option_text_loc4`, `option_text_loc5`, `option_text_loc6`, `option_text_loc7`, `option_text_loc8`, `box_text_loc1`, `box_text_loc2`, `box_text_loc3`, `box_text_loc4`, `box_text_loc5`, `box_text_loc6`, `box_text_loc7`, `box_text_loc8`) values('60000','13','Recruit a Druid ','Recruit a Druid ','Recruit a Druid ','Recruit a Druid ','Recruit a Druid ','Recruit a Druid ','Recruit a Druid ','Recruit a Druid ',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
  156. +insert into `locales_gossip_menu_option` (`menu_id`, `id`, `option_text_loc1`, `option_text_loc2`, `option_text_loc3`, `option_text_loc4`, `option_text_loc5`, `option_text_loc6`, `option_text_loc7`, `option_text_loc8`, `box_text_loc1`, `box_text_loc2`, `box_text_loc3`, `box_text_loc4`, `box_text_loc5`, `box_text_loc6`, `box_text_loc7`, `box_text_loc8`) values('60000','14','Recruit a Mage ','Recruit a Mage ','Recruit a Mage ','Recruit a Mage ','Recruit a Mage ','Recruit a Mage ','Recruit a Mage ','Recruit a Mage ',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
  157. +insert into `locales_gossip_menu_option` (`menu_id`, `id`, `option_text_loc1`, `option_text_loc2`, `option_text_loc3`, `option_text_loc4`, `option_text_loc5`, `option_text_loc6`, `option_text_loc7`, `option_text_loc8`, `box_text_loc1`, `box_text_loc2`, `box_text_loc3`, `box_text_loc4`, `box_text_loc5`, `box_text_loc6`, `box_text_loc7`, `box_text_loc8`) values('60000','15','Recruit a Priest ','Recruit a Priest ','Recruit a Priest ','Recruit a Priest ','Recruit a Priest ','Recruit a Priest ','Recruit a Priest ','Recruit a Priest ',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
  158. +insert into `locales_gossip_menu_option` (`menu_id`, `id`, `option_text_loc1`, `option_text_loc2`, `option_text_loc3`, `option_text_loc4`, `option_text_loc5`, `option_text_loc6`, `option_text_loc7`, `option_text_loc8`, `box_text_loc1`, `box_text_loc2`, `box_text_loc3`, `box_text_loc4`, `box_text_loc5`, `box_text_loc6`, `box_text_loc7`, `box_text_loc8`) values('60000','16','Recruit a Warlock ','Recruit a Warlock ','Recruit a Warlock ','Recruit a Warlock ','Recruit a Warlock ','Recruit a Warlock ','Recruit a Warlock ','Recruit a Warlock ',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
  159. +insert into `locales_gossip_menu_option` (`menu_id`, `id`, `option_text_loc1`, `option_text_loc2`, `option_text_loc3`, `option_text_loc4`, `option_text_loc5`, `option_text_loc6`, `option_text_loc7`, `option_text_loc8`, `box_text_loc1`, `box_text_loc2`, `box_text_loc3`, `box_text_loc4`, `box_text_loc5`, `box_text_loc6`, `box_text_loc7`, `box_text_loc8`) values('60000','17','Recruit a Death Knight ','Recruit a Death Knight ','Recruit a Death Knight ','Recruit a Death Knight ','Recruit a Death Knight ','Recruit a Death Knight ','Recruit a Death Knight ','Recruit a Death Knight ',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
  160. +insert into `locales_gossip_menu_option` (`menu_id`, `id`, `option_text_loc1`, `option_text_loc2`, `option_text_loc3`, `option_text_loc4`, `option_text_loc5`, `option_text_loc6`, `option_text_loc7`, `option_text_loc8`, `box_text_loc1`, `box_text_loc2`, `box_text_loc3`, `box_text_loc4`, `box_text_loc5`, `box_text_loc6`, `box_text_loc7`, `box_text_loc8`) values('60000','18','To see list of Playerbot commands whisper \'help\' to one of your playerbots','To see list of Playerbot commands whisper \'help\' to one of your playerbots','To see list of Playerbot commands whisper \'help\' to one of your playerbots','To see list of Playerbot commands whisper \'help\' to one of your playerbots','To see list of Playerbot commands whisper \'help\' to one of your playerbots','To see list of Playerbot commands whisper \'help\' to one of your playerbots','To see list of Playerbot commands whisper \'help\' to one of your playerbots','To see list of Playerbot commands whisper \'help\' to one of your playerbots',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
  161. +insert into `locales_gossip_menu_option` (`menu_id`, `id`, `option_text_loc1`, `option_text_loc2`, `option_text_loc3`, `option_text_loc4`, `option_text_loc5`, `option_text_loc6`, `option_text_loc7`, `option_text_loc8`, `box_text_loc1`, `box_text_loc2`, `box_text_loc3`, `box_text_loc4`, `box_text_loc5`, `box_text_loc6`, `box_text_loc7`, `box_text_loc8`) values('60000','19','To see list of available npcbot commands type .npcbot or .npcb','To see list of available npcbot commands type .npcbot or .npcb','To see list of available npcbot commands type .npcbot or .npcb','To see list of available npcbot commands type .npcbot or .npcb','To see list of available npcbot commands type .npcbot or .npcb','To see list of available npcbot commands type .npcbot or .npcb','To see list of available npcbot commands type .npcbot or .npcb','To see list of available npcbot commands type .npcbot or .npcb',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
  162. +insert into `locales_gossip_menu_option` (`menu_id`, `id`, `option_text_loc1`, `option_text_loc2`, `option_text_loc3`, `option_text_loc4`, `option_text_loc5`, `option_text_loc6`, `option_text_loc7`, `option_text_loc8`, `box_text_loc1`, `box_text_loc2`, `box_text_loc3`, `box_text_loc4`, `box_text_loc5`, `box_text_loc6`, `box_text_loc7`, `box_text_loc8`) values('60000','20','You can also use .maintank (or .mt or .main) command on any party member (even npcbot) so your bots will stick to your plan','You can also use .maintank (or .mt or .main) command on any party member (even npcbot) so your bots will stick to your plan','You can also use .maintank (or .mt or .main) command on any party member (even npcbot) so your bots will stick to your plan','You can also use .maintank (or .mt or .main) command on any party member (even npcbot) so your bots will stick to your plan','You can also use .maintank (or .mt or .main) command on any party member (even npcbot) so your bots will stick to your plan','You can also use .maintank (or .mt or .main) command on any party member (even npcbot) so your bots will stick to your plan','You can also use .maintank (or .mt or .main) command on any party member (even npcbot) so your bots will stick to your plan','You can also use .maintank (or .mt or .main) command on any party member (even npcbot) so your bots will stick to your plan',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
  163. +insert into `locales_gossip_menu_option` (`menu_id`, `id`, `option_text_loc1`, `option_text_loc2`, `option_text_loc3`, `option_text_loc4`, `option_text_loc5`, `option_text_loc6`, `option_text_loc7`, `option_text_loc8`, `box_text_loc1`, `box_text_loc2`, `box_text_loc3`, `box_text_loc4`, `box_text_loc5`, `box_text_loc6`, `box_text_loc7`, `box_text_loc8`) values('60000','21','If you want your npcbots to heal someone out of your party set any raid target icon on them','If you want your npcbots to heal someone out of your party set any raid target icon on them','If you want your npcbots to heal someone out of your party set any raid target icon on them','If you want your npcbots to heal someone out of your party set any raid target icon on them','If you want your npcbots to heal someone out of your party set any raid target icon on them','If you want your npcbots to heal someone out of your party set any raid target icon on them','If you want your npcbots to heal someone out of your party set any raid target icon on them','If you want your npcbots to heal someone out of your party set any raid target icon on them',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
  164. +insert into `locales_gossip_menu_option` (`menu_id`, `id`, `option_text_loc1`, `option_text_loc2`, `option_text_loc3`, `option_text_loc4`, `option_text_loc5`, `option_text_loc6`, `option_text_loc7`, `option_text_loc8`, `box_text_loc1`, `box_text_loc2`, `box_text_loc3`, `box_text_loc4`, `box_text_loc5`, `box_text_loc6`, `box_text_loc7`, `box_text_loc8`) values('60000','22','If you want your npcbots to heal someone out of your party set proper raid target icon on them, one of these: ','If you want your npcbots to heal someone out of your party set proper raid target icon on them, one of these: ','If you want your npcbots to heal someone out of your party set proper raid target icon on them, one of these: ','If you want your npcbots to heal someone out of your party set proper raid target icon on them, one of these: ','If you want your npcbots to heal someone out of your party set proper raid target icon on them, one of these: ','If you want your npcbots to heal someone out of your party set proper raid target icon on them, one of these: ','If you want your npcbots to heal someone out of your party set proper raid target icon on them, one of these: ','If you want your npcbots to heal someone out of your party set proper raid target icon on them, one of these: ',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
  165. +insert into `locales_gossip_menu_option` (`menu_id`, `id`, `option_text_loc1`, `option_text_loc2`, `option_text_loc3`, `option_text_loc4`, `option_text_loc5`, `option_text_loc6`, `option_text_loc7`, `option_text_loc8`, `box_text_loc1`, `box_text_loc2`, `box_text_loc3`, `box_text_loc4`, `box_text_loc5`, `box_text_loc6`, `box_text_loc7`, `box_text_loc8`) values('60000','23','star','star','star','star','star','star','star','star',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
  166. +insert into `locales_gossip_menu_option` (`menu_id`, `id`, `option_text_loc1`, `option_text_loc2`, `option_text_loc3`, `option_text_loc4`, `option_text_loc5`, `option_text_loc6`, `option_text_loc7`, `option_text_loc8`, `box_text_loc1`, `box_text_loc2`, `box_text_loc3`, `box_text_loc4`, `box_text_loc5`, `box_text_loc6`, `box_text_loc7`, `box_text_loc8`) values('60000','24','circle','circle','circle','circle','circle','circle','circle','circle',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
  167. +insert into `locales_gossip_menu_option` (`menu_id`, `id`, `option_text_loc1`, `option_text_loc2`, `option_text_loc3`, `option_text_loc4`, `option_text_loc5`, `option_text_loc6`, `option_text_loc7`, `option_text_loc8`, `box_text_loc1`, `box_text_loc2`, `box_text_loc3`, `box_text_loc4`, `box_text_loc5`, `box_text_loc6`, `box_text_loc7`, `box_text_loc8`) values('60000','25','diamond','diamond','diamond','diamond','diamond','diamond','diamond','diamond',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
  168. +insert into `locales_gossip_menu_option` (`menu_id`, `id`, `option_text_loc1`, `option_text_loc2`, `option_text_loc3`, `option_text_loc4`, `option_text_loc5`, `option_text_loc6`, `option_text_loc7`, `option_text_loc8`, `box_text_loc1`, `box_text_loc2`, `box_text_loc3`, `box_text_loc4`, `box_text_loc5`, `box_text_loc6`, `box_text_loc7`, `box_text_loc8`) values('60000','26','triangle','triangle','triangle','triangle','triangle','triangle','triangle','triangle',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
  169. +insert into `locales_gossip_menu_option` (`menu_id`, `id`, `option_text_loc1`, `option_text_loc2`, `option_text_loc3`, `option_text_loc4`, `option_text_loc5`, `option_text_loc6`, `option_text_loc7`, `option_text_loc8`, `box_text_loc1`, `box_text_loc2`, `box_text_loc3`, `box_text_loc4`, `box_text_loc5`, `box_text_loc6`, `box_text_loc7`, `box_text_loc8`) values('60000','27','moon','moon','moon','moon','moon','moon','moon','moon',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
  170. +insert into `locales_gossip_menu_option` (`menu_id`, `id`, `option_text_loc1`, `option_text_loc2`, `option_text_loc3`, `option_text_loc4`, `option_text_loc5`, `option_text_loc6`, `option_text_loc7`, `option_text_loc8`, `box_text_loc1`, `box_text_loc2`, `box_text_loc3`, `box_text_loc4`, `box_text_loc5`, `box_text_loc6`, `box_text_loc7`, `box_text_loc8`) values('60000','28','square','square','square','square','square','square','square','square',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
  171. +insert into `locales_gossip_menu_option` (`menu_id`, `id`, `option_text_loc1`, `option_text_loc2`, `option_text_loc3`, `option_text_loc4`, `option_text_loc5`, `option_text_loc6`, `option_text_loc7`, `option_text_loc8`, `box_text_loc1`, `box_text_loc2`, `box_text_loc3`, `box_text_loc4`, `box_text_loc5`, `box_text_loc6`, `box_text_loc7`, `box_text_loc8`) values('60000','29','cross','cross','cross','cross','cross','cross','cross','cross',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
  172. +insert into `locales_gossip_menu_option` (`menu_id`, `id`, `option_text_loc1`, `option_text_loc2`, `option_text_loc3`, `option_text_loc4`, `option_text_loc5`, `option_text_loc6`, `option_text_loc7`, `option_text_loc8`, `box_text_loc1`, `box_text_loc2`, `box_text_loc3`, `box_text_loc4`, `box_text_loc5`, `box_text_loc6`, `box_text_loc7`, `box_text_loc8`) values('60000','30','skull','skull','skull','skull','skull','skull','skull','skull',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
  173. +insert into `locales_gossip_menu_option` (`menu_id`, `id`, `option_text_loc1`, `option_text_loc2`, `option_text_loc3`, `option_text_loc4`, `option_text_loc5`, `option_text_loc6`, `option_text_loc7`, `option_text_loc8`, `box_text_loc1`, `box_text_loc2`, `box_text_loc3`, `box_text_loc4`, `box_text_loc5`, `box_text_loc6`, `box_text_loc7`, `box_text_loc8`) values('60000','31','unknown icon','unknown icon','unknown icon','unknown icon','unknown icon','unknown icon','unknown icon','unknown icon',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
  174. +insert into `locales_gossip_menu_option` (`menu_id`, `id`, `option_text_loc1`, `option_text_loc2`, `option_text_loc3`, `option_text_loc4`, `option_text_loc5`, `option_text_loc6`, `option_text_loc7`, `option_text_loc8`, `box_text_loc1`, `box_text_loc2`, `box_text_loc3`, `box_text_loc4`, `box_text_loc5`, `box_text_loc6`, `box_text_loc7`, `box_text_loc8`) values('60000','32','no more bots available','no more bots available','no more bots available','no more bots available','no more bots available','no more bots available','no more bots available','no more bots available',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
  175. +insert into `locales_gossip_menu_option` (`menu_id`, `id`, `option_text_loc1`, `option_text_loc2`, `option_text_loc3`, `option_text_loc4`, `option_text_loc5`, `option_text_loc6`, `option_text_loc7`, `option_text_loc8`, `box_text_loc1`, `box_text_loc2`, `box_text_loc3`, `box_text_loc4`, `box_text_loc5`, `box_text_loc6`, `box_text_loc7`, `box_text_loc8`) values('60000','33','more bot available','more bot available','more bot available','more bot available','more bot available','more bot available','more bot available','more bot available',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
  176. +insert into `locales_gossip_menu_option` (`menu_id`, `id`, `option_text_loc1`, `option_text_loc2`, `option_text_loc3`, `option_text_loc4`, `option_text_loc5`, `option_text_loc6`, `option_text_loc7`, `option_text_loc8`, `box_text_loc1`, `box_text_loc2`, `box_text_loc3`, `box_text_loc4`, `box_text_loc5`, `box_text_loc6`, `box_text_loc7`, `box_text_loc8`) values('60000','34','more bots available','more bots available','more bots available','more bots available','more bots available','more bots available','more bots available','more bots available',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
  177. +insert into `locales_gossip_menu_option` (`menu_id`, `id`, `option_text_loc1`, `option_text_loc2`, `option_text_loc3`, `option_text_loc4`, `option_text_loc5`, `option_text_loc6`, `option_text_loc7`, `option_text_loc8`, `box_text_loc1`, `box_text_loc2`, `box_text_loc3`, `box_text_loc4`, `box_text_loc5`, `box_text_loc6`, `box_text_loc7`, `box_text_loc8`) values('60000','35','bot available','bot available','bot available','bot available','bot available','bot available','bot available','bot available',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
  178. +insert into `locales_gossip_menu_option` (`menu_id`, `id`, `option_text_loc1`, `option_text_loc2`, `option_text_loc3`, `option_text_loc4`, `option_text_loc5`, `option_text_loc6`, `option_text_loc7`, `option_text_loc8`, `box_text_loc1`, `box_text_loc2`, `box_text_loc3`, `box_text_loc4`, `box_text_loc5`, `box_text_loc6`, `box_text_loc7`, `box_text_loc8`) values('60000','36','bots available','bots available','bots available','bots available','bots available','bots available','bots available','bots available',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
  179. +
  180. +
  181. +-- Custom section
  182. +-- to change text displayed in botgiver's dialog you shoul
  183. +-- 1) translate text to your language
  184. +-- 2) place translated text in empty quotes below, (id = original id in `locales_gossip_menu_option`)
  185. +-- 3) replace `option_text_loc1` with your locale index
  186. +-- LOCALE_koKR `option_text_loc1`
  187. +-- LOCALE_frFR `option_text_loc2`
  188. +-- LOCALE_deDE `option_text_loc3`
  189. +-- LOCALE_zhCN `option_text_loc4`
  190. +-- LOCALE_zhTW `option_text_loc5`
  191. +-- LOCALE_esES `option_text_loc6`
  192. +-- LOCALE_esMX `option_text_loc7`
  193. +-- LOCALE_ruRU `option_text_loc8`
  194. +-- 4) run the queue below lol
  195. +-- 5) you most likely need to save your translation for later
  196. +
  197. +-- UPDATE `locales_gossip_menu_option` SET `option_text_loc1` = ' ' WHERE `menu_id` = 60000 AND `id` = 1;
  198. +-- UPDATE `locales_gossip_menu_option` SET `option_text_loc1` = ' ' WHERE `menu_id` = 60000 AND `id` = 2;
  199. +-- UPDATE `locales_gossip_menu_option` SET `option_text_loc1` = ' ' WHERE `menu_id` = 60000 AND `id` = 3;
  200. +-- UPDATE `locales_gossip_menu_option` SET `option_text_loc1` = ' ' WHERE `menu_id` = 60000 AND `id` = 4;
  201. +-- UPDATE `locales_gossip_menu_option` SET `option_text_loc1` = ' ' WHERE `menu_id` = 60000 AND `id` = 5;
  202. +-- UPDATE `locales_gossip_menu_option` SET `option_text_loc1` = ' ' WHERE `menu_id` = 60000 AND `id` = 6;
  203. +-- UPDATE `locales_gossip_menu_option` SET `option_text_loc1` = ' ' WHERE `menu_id` = 60000 AND `id` = 7;
  204. +-- UPDATE `locales_gossip_menu_option` SET `option_text_loc1` = ' ' WHERE `menu_id` = 60000 AND `id` = 8;
  205. +-- UPDATE `locales_gossip_menu_option` SET `option_text_loc1` = ' ' WHERE `menu_id` = 60000 AND `id` = 9;
  206. +-- UPDATE `locales_gossip_menu_option` SET `option_text_loc1` = ' ' WHERE `menu_id` = 60000 AND `id` = 10;
  207. +-- UPDATE `locales_gossip_menu_option` SET `option_text_loc1` = ' ' WHERE `menu_id` = 60000 AND `id` = 11;
  208. +-- UPDATE `locales_gossip_menu_option` SET `option_text_loc1` = ' ' WHERE `menu_id` = 60000 AND `id` = 12;
  209. +-- UPDATE `locales_gossip_menu_option` SET `option_text_loc1` = ' ' WHERE `menu_id` = 60000 AND `id` = 13;
  210. +-- UPDATE `locales_gossip_menu_option` SET `option_text_loc1` = ' ' WHERE `menu_id` = 60000 AND `id` = 14;
  211. +-- UPDATE `locales_gossip_menu_option` SET `option_text_loc1` = ' ' WHERE `menu_id` = 60000 AND `id` = 15;
  212. +-- UPDATE `locales_gossip_menu_option` SET `option_text_loc1` = ' ' WHERE `menu_id` = 60000 AND `id` = 16;
  213. +-- UPDATE `locales_gossip_menu_option` SET `option_text_loc1` = ' ' WHERE `menu_id` = 60000 AND `id` = 17;
  214. +-- UPDATE `locales_gossip_menu_option` SET `option_text_loc1` = ' ' WHERE `menu_id` = 60000 AND `id` = 18;
  215. +-- UPDATE `locales_gossip_menu_option` SET `option_text_loc1` = ' ' WHERE `menu_id` = 60000 AND `id` = 19;
  216. +-- UPDATE `locales_gossip_menu_option` SET `option_text_loc1` = ' ' WHERE `menu_id` = 60000 AND `id` = 20;
  217. +-- UPDATE `locales_gossip_menu_option` SET `option_text_loc1` = ' ' WHERE `menu_id` = 60000 AND `id` = 21;
  218. +-- UPDATE `locales_gossip_menu_option` SET `option_text_loc1` = ' ' WHERE `menu_id` = 60000 AND `id` = 22;
  219. +-- UPDATE `locales_gossip_menu_option` SET `option_text_loc1` = ' ' WHERE `menu_id` = 60000 AND `id` = 23;
  220. +-- UPDATE `locales_gossip_menu_option` SET `option_text_loc1` = ' ' WHERE `menu_id` = 60000 AND `id` = 24;
  221. +-- UPDATE `locales_gossip_menu_option` SET `option_text_loc1` = ' ' WHERE `menu_id` = 60000 AND `id` = 25;
  222. +-- UPDATE `locales_gossip_menu_option` SET `option_text_loc1` = ' ' WHERE `menu_id` = 60000 AND `id` = 26;
  223. +-- UPDATE `locales_gossip_menu_option` SET `option_text_loc1` = ' ' WHERE `menu_id` = 60000 AND `id` = 27;
  224. +-- UPDATE `locales_gossip_menu_option` SET `option_text_loc1` = ' ' WHERE `menu_id` = 60000 AND `id` = 28;
  225. +-- UPDATE `locales_gossip_menu_option` SET `option_text_loc1` = ' ' WHERE `menu_id` = 60000 AND `id` = 29;
  226. +-- UPDATE `locales_gossip_menu_option` SET `option_text_loc1` = ' ' WHERE `menu_id` = 60000 AND `id` = 30;
  227. +-- UPDATE `locales_gossip_menu_option` SET `option_text_loc1` = ' ' WHERE `menu_id` = 60000 AND `id` = 31;
  228. +-- UPDATE `locales_gossip_menu_option` SET `option_text_loc1` = ' ' WHERE `menu_id` = 60000 AND `id` = 32;
  229. +-- UPDATE `locales_gossip_menu_option` SET `option_text_loc1` = ' ' WHERE `menu_id` = 60000 AND `id` = 33;
  230. +-- UPDATE `locales_gossip_menu_option` SET `option_text_loc1` = ' ' WHERE `menu_id` = 60000 AND `id` = 34;
  231. +-- UPDATE `locales_gossip_menu_option` SET `option_text_loc1` = ' ' WHERE `menu_id` = 60000 AND `id` = 35;
  232. +-- UPDATE `locales_gossip_menu_option` SET `option_text_loc1` = ' ' WHERE `menu_id` = 60000 AND `id` = 36;
  233. diff --git a/sql/Bots/world_bots.sql b/sql/Bots/world_bots.sql
  234. new file mode 100644
  235. index 0000000..abd7bdd
  236. --- /dev/null
  237. +++ b/sql/Bots/world_bots.sql
  238. @@ -0,0 +1,524 @@
  239. +
  240. +-- GENERAL --
  241. +
  242. +delete from `creature_template` where entry between 60001 and 60238;
  243. +
  244. +insert into `creature_template`
  245. +(`entry`, `difficulty_entry_1`, `difficulty_entry_2`, `difficulty_entry_3`, `KillCredit1`, `KillCredit2`, `modelid1`, `modelid2`, `modelid3`, `modelid4`, `name`, `subname`, `IconName`, `gossip_menu_id`,
  246. +`minlevel`, `maxlevel`, `exp`, `faction_A`, `faction_H`, `npcflag`, `speed_walk`, `speed_run`, `scale`, `rank`, `mindmg`, `maxdmg`, `dmgschool`, `attackpower`, `dmg_multiplier`, `baseattacktime`,
  247. +`rangeattacktime`, `unit_class`, `unit_flags`, `unit_flags2`, `dynamicflags`, `family`, `trainer_type`, `trainer_spell`, `trainer_class`, `trainer_race`, `minrangedmg`, `maxrangedmg`, `rangedattackpower`,
  248. +`type`, `type_flags`, `lootid`, `pickpocketloot`, `skinloot`, `resistance1`, `resistance2`, `resistance3`, `resistance4`, `resistance5`, `resistance6`, `spell1`, `spell2`, `spell3`, `spell4`, `spell5`, `spell6`,
  249. +`spell7`, `spell8`, `PetSpellDataId`, `VehicleId`, `mingold`, `maxgold`, `AIName`, `MovementType`, `InhabitType`, `HoverHeight`, `Health_mod`, `Mana_mod`, `Armor_mod`, `RacialLeader`,
  250. +`questItem1`, `questItem2`, `questItem3`, `questItem4`, `questItem5`, `questItem6`, `movementId`, `RegenHealth`, `mechanic_immune_mask`, `flags_extra`, `ScriptName`, `WDBVerified`)
  251. +values
  252. +('60001','0','0','0','0','0','5001','0','5001','0','Khelden','Mage Bot','','0','80','80','2','12','12','1','1.2','1.3','1','0','1','1','0','0','1','3800','2000','8','0','0','0','0','0','0','8','1','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','mage_bot','0'),
  253. +('60002','0','0','0','0','0','1294','0','1294','0','Zaldimar','Mage Bot','','0','80','80','2','12','12','1','1.2','1.3','1','0','1','1','0','0','1','3800','2000','8','0','0','0','0','0','0','8','1','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','mage_bot','0'),
  254. +('60003','0','0','0','0','0','1484','0','1484','0','Maginor','Mage Bot','','0','80','80','2','12','12','1','1.2','1.3','1','0','1','1','0','0','1','3800','2000','8','0','0','0','0','0','0','8','1','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','mage_bot','0'),
  255. +('60004','0','0','0','0','0','3344','0','3344','0','Anetta','Priest Bot','','0','80','80','2','12','12','1','1.2','1.3','1','0','1','1','0','0','1','3600','2000','8','0','0','0','0','0','0','5','1','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','priest_bot','0'),
  256. +('60005','0','0','0','0','0','1495','0','1495','0','Laurena','Priest Bot','','0','80','80','2','12','12','1','1.2','1.3','1','0','1','1','0','0','1','3600','2000','8','0','0','0','0','0','0','5','1','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','priest_bot','0'),
  257. +('60006','0','0','0','0','0','1295','0','1295','0','Josetta','Priest Bot','','0','80','80','2','12','12','1','1.2','1.3','1','0','1','1','0','0','1','3600','2000','8','0','0','0','0','0','0','5','1','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','priest_bot','0'),
  258. +('60007','0','0','0','0','0','3345','0','3345','0','Drusilla','Warlock Bot','','0','80','80','2','12','12','1','1.2','1.3','1','0','1','1','0','0','1','3500','2000','8','0','0','0','0','0','0','9','1','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','warlock_bot','0'),
  259. +('60008','0','0','0','0','0','1930','0','1930','0','Alamar','Warlock Bot','','0','80','80','2','875','875','1','1.2','1.3','1','0','1','1','0','0','1','3500','2000','8','0','0','0','0','0','0','9','7','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','warlock_bot','0'),
  260. +('60009','0','0','0','0','0','1469','0','1469','0','Demisette','Warlock Bot','','0','80','80','2','12','12','1','1.2','1.3','1','0','1','1','0','0','1','3500','2000','8','0','0','0','0','0','0','9','1','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','warlock_bot','0'),
  261. +('60010','0','0','0','0','0','12749','0','12749','0','Nalesette','Hunter Bot','','0','80','80','2','80','80','1','1.2','1.3','1','0','2','3','0','0','1','2800','2000','2','0','0','0','0','3','0','3','4','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','hunter_bot','0'),
  262. +('60011','0','0','0','0','0','3401','0','3401','0','Branstock','Priest Bot','','0','80','80','2','55','55','1','1.2','1.3','1','0','1','1','0','0','1','3600','2000','8','0','0','0','0','0','0','5','3','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','priest_bot','0'),
  263. +('60012','0','0','0','0','0','3395','0','3395','0','Thorgas','Hunter Bot','','0','80','80','2','55','55','1','1.2','1.3','1','0','2','3','0','0','1','2800','2000','2','0','0','0','0','0','0','3','3','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','hunter_bot','0'),
  264. +('60013','0','0','0','0','0','3343','0','3343','0','Llane','Warrior Bot','','0','80','80','2','12','12','1','1.2','1.3','1','0','3','5','0','0','1','3400','2000','1','0','0','0','0','0','0','1','1','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','warrior_bot','0'),
  265. +('60014','0','0','0','0','0','3399','0','3399','0','Thran','Warrior Bot','','0','80','80','2','55','55','1','1.2','1.3','1','0','3','5','0','0','1','3400','2000','1','0','0','0','0','0','0','1','3','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','warrior_bot','0'),
  266. +('60015','0','0','0','0','0','1300','0','1300','0','Lyria','Warrior Bot','','0','80','80','2','12','12','1','1.2','1.3','1','0','3','5','0','0','1','3400','2000','1','0','0','0','0','0','0','1','1','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','warrior_bot','0'),
  267. +('60016','0','0','0','0','0','3351','0','3351','0','Jorik','Rogue Bot','','0','80','80','2','12','12','1','1.2','1.3','1','0','1','2','0','0','1','1600','2000','4','0','0','0','0','0','0','4','1','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','rogue_bot','0'),
  268. +('60017','0','0','0','0','0','3407','0','3407','0','Solm','Rogue Bot','','0','80','80','2','55','55','1','1.2','1.3','1','0','1','2','0','0','1','1600','2000','4','0','0','0','0','0','0','4','3','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','rogue_bot','0'),
  269. +('60018','0','0','0','0','0','1297','0','1297','0','Keryn','Rogue Bot','','0','80','80','2','12','12','1','1.2','1.3','1','0','1','2','0','0','1','1600','2000','4','0','0','0','0','0','0','4','1','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','rogue_bot','0'),
  270. +('60019','0','0','0','0','0','1507','0','1507','0','Osborne','Rogue Bot','','0','80','80','2','12','12','1','1.2','1.3','1','0','1','2','0','0','1','1600','2000','4','0','0','0','0','0','0','4','1','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','rogue_bot','0'),
  271. +('60020','0','0','0','0','0','3346','0','3346','0','Sammuel','Paladin Bot','','0','80','80','2','12','12','1','1.2','1.3','1','0','2','4','0','0','1','2300','2000','2','0','0','0','0','0','0','2','1','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','paladin_bot','0'),
  272. +('60021','0','0','0','0','0','3393','0','3393','0','Bob','Paladin Bot','','0','80','80','2','55','55','1','1.2','1.3','1','0','2','4','0','0','1','2300','2000','2','0','0','0','0','0','0','2','3','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','paladin_bot','0'),
  273. +('60022','0','0','0','0','0','1299','0','1299','0','Wilhelm','Paladin Bot','','0','80','80','2','12','12','1','1.2','1.3','1','0','2','4','0','0','1','2300','2000','2','0','0','0','0','0','0','2','1','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','paladin_bot','0'),
  274. +('60023','0','0','0','0','0','1499','0','1499','0','Brisombre','Paladin Bot','','0','80','80','2','12','12','1','1.2','1.3','1','0','2','4','0','0','1','2300','2000','2','0','0','0','0','0','0','2','1','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','paladin_bot','0'),
  275. +('60024','0','0','0','0','0','10216','0','10216','0','Marry','Mage Bot','','0','80','80','2','875','875','1','1.2','1.3','1','0','1','1','0','0','1','3800','2000','8','0','0','0','0','0','0','8','7','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','mage_bot','0'),
  276. +('60025','0','0','0','0','0','4552','0','4552','0','Haromm','Shaman Bot','','0','80','80','2','29','29','1','1.2','1.3','1','0','1','2','0','0','1','2600','2000','2','0','0','0','0','0','0','7','2','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','shaman_bot','0'),
  277. +('60026','0','0','0','0','0','4567','0','4567','0','Kartosh','Warlock Bot','','0','80','80','2','29','29','1','1.2','1.3','1','0','1','1','0','0','1','3500','2000','8','0','0','0','0','0','0','9','2','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','warlock_bot','0'),
  278. +('60027','0','0','0','0','0','3429','0','3429','0','MaxanAnvol','Priest Bot','','0','80','80','2','55','55','1','1.2','1.3','1','0','1','1','0','0','1','3600','2000','8','0','0','0','0','0','0','5','3','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','priest_bot','0'),
  279. +('60028','0','0','0','0','0','10215','0','10215','0','Magis','Mage Bot','','0','80','80','2','875','875','1','1.2','1.3','1','0','1','1','0','0','1','3800','2000','8','0','0','0','0','0','0','8','7','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','mage_bot','0'),
  280. +('60029','0','0','0','0','0','3431','0','3431','0','GranVivehache','Warrior Bot','','0','80','80','2','55','55','1','1.2','1.3','1','0','3','5','0','0','1','3400','2000','1','0','0','0','0','0','0','1','3','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','warrior_bot','0'),
  281. +('60030','0','0','0','0','0','1622','0','1622','0','Azar','Paladin Bot','','0','80','80','2','55','55','1','1.2','1.3','1','0','2','4','0','0','1','2300','2000','2','0','0','0','0','0','0','2','3','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','paladin_bot','0'),
  282. +('60031','0','0','0','0','0','3436','0','3436','0','Hogral','Rogue Bot','','0','80','80','2','55','55','1','1.2','1.3','1','0','1','2','0','0','1','1600','2000','4','0','0','0','0','0','0','4','3','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','rogue_bot','0'),
  283. +('60032','0','0','0','0','0','3053','0','3053','0','Kelstrum','Warrior Bot','','0','80','80','2','55','55','1','1.2','1.3','1','0','3','5','0','0','1','3400','2000','1','0','0','0','0','0','0','1','3','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','warrior_bot','0'),
  284. +('60033','0','0','0','0','0','1578','0','1578','0','Dannal','Warrior Bot','','0','80','80','2','68','68','1','1.2','1.3','1','0','3','5','0','0','1','3400','2000','1','0','0','0','0','0','0','1','5','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','warrior_bot','0'),
  285. +('60034','0','0','0','0','0','1579','0','1579','0','SombreDuesten','Priest Bot','','0','80','80','2','68','68','1','1.2','1.3','1','0','1','1','0','0','1','3600','2000','8','0','0','0','0','0','0','5','5','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','priest_bot','0'),
  286. +('60035','0','0','0','0','0','1592','0','1592','0','Isabella','Mage Bot','','0','80','80','2','68','68','1','1.2','1.3','1','0','1','1','0','0','1','3800','2000','8','0','0','0','0','0','0','8','5','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','mage_bot','0'),
  287. +('60036','0','0','0','0','0','1581','0','1581','0','Maximillion','Warlock Bot','','0','80','80','2','68','68','1','1.2','1.3','1','0','1','1','0','0','1','3500','2000','8','0','0','0','0','0','0','9','5','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','warlock_bot','0'),
  288. +('60037','0','0','0','0','0','1604','0','1604','0','Rupert','Warlock Bot','','0','80','80','2','68','68','1','1.2','1.3','1','0','1','1','0','0','1','3500','2000','8','0','0','0','0','0','0','9','5','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','warlock_bot','0'),
  289. +('60038','0','0','0','0','0','1600','0','1600','0','Cain','Mage Bot','','0','80','80','2','68','68','1','1.2','1.3','1','0','1','1','0','0','1','3800','2000','8','0','0','0','0','0','0','8','5','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','mage_bot','0'),
  290. +('60039','0','0','0','0','0','1602','0','1602','0','SombreBeryl','Priest Bot','','0','80','80','2','68','68','1','1.2','1.3','1','0','1','1','0','0','1','3600','2000','8','0','0','0','0','0','0','5','5','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','priest_bot','0'),
  291. +('60041','0','0','0','0','0','10548','0','10548','0','Milituus','Mage Bot','','0','80','80','2','55','55','1','1.2','1.3','1','0','1','1','0','0','1','3800','2000','8','0','0','0','0','0','0','8','3','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','mage_bot','0'),
  292. +('60042','0','0','0','0','0','2810','0','2810','0','Lexington','Mage Bot','','0','80','80','2','68','68','1','1.2','1.3','1','0','1','1','0','0','1','3800','2000','8','0','0','0','0','0','0','8','5','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','mage_bot','0'),
  293. +('60043','0','0','0','0','0','2123','0','2123','0','Siln','Shaman Bot','','0','80','80','2','104','104','1','1.2','1.3','1','0','1','2','0','0','1','2600','2000','2','0','0','0','0','0','0','7','6','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','shaman_bot','0'),
  294. +('60044','0','0','0','0','0','19598','0','19598','0','Umbrua','Shaman Bot','','0','80','80','2','1640','1640','1','1.2','1.3','1','0','1','2','0','0','1','2600','2000','2','0','0','0','0','0','0','7','11','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','shaman_bot','0'),
  295. +('60045','0','0','0','0','0','2102','0','2102','0','Tigor','Shaman Bot','','0','80','80','2','104','104','1','1.2','1.3','1','0','1','2','0','0','1','2600','2000','2','0','0','0','0','0','0','7','6','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','shaman_bot','0'),
  296. +('60046','0','0','0','0','0','2082','0','2082','0','Beram','Shaman Bot','','0','80','80','2','104','104','1','1.2','1.3','1','0','1','2','0','0','1','2600','2000','2','0','0','0','0','0','0','7','6','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','shaman_bot','0'),
  297. +('60047','0','0','0','0','0','2106','0','2106','0','Turak','Druid Bot','','0','80','80','2','104','104','1','1.2','1.3','1','0','2','4','0','0','1','2200','2000','2','0','0','0','0','0','0','11','6','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','druid_bot','0'),
  298. +('60048','0','0','0','0','0','2121','0','2121','0','Sheal','Druid Bot','','0','80','80','2','104','104','1','1.2','1.3','1','0','2','4','0','0','1','2200','2000','2','0','0','0','0','0','0','11','6','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','druid_bot','0'),
  299. +('60049','0','0','0','0','0','2115','0','2115','0','Kym','Druid Bot','','0','80','80','2','104','104','1','1.2','1.3','1','0','2','4','0','0','1','2200','2000','2','0','0','0','0','0','0','11','6','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','druid_bot','0'),
  300. +('60050','0','0','0','0','0','2112','0','2112','0','Kary','Hunter Bot','','0','80','80','2','104','104','1','1.2','1.3','1','0','2','3','0','0','1','2800','2000','2','0','0','0','0','0','0','3','6','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','hunter_bot','0'),
  301. +('60051','0','0','0','0','0','2087','0','2087','0','Holt','Hunter Bot','','0','80','80','2','104','104','1','1.2','1.3','1','0','2','3','0','0','1','2800','2000','2','0','0','0','0','0','0','3','6','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','hunter_bot','0'),
  302. +('60052','0','0','0','0','0','2105','0','2105','0','Urek','Hunter Bot','','0','80','80','2','104','104','1','1.2','1.3','1','0','2','3','0','0','1','2800','2000','2','0','0','0','0','0','0','3','6','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','hunter_bot','0'),
  303. +('60053','0','0','0','0','0','2103','0','2103','0','Torm','Warrior Bot','','0','80','80','2','104','104','1','1.2','1.3','1','0','3','5','0','0','1','3400','2000','1','0','0','0','0','0','0','1','6','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','warrior_bot','0'),
  304. +('60054','0','0','0','0','0','2096','0','2096','0','Sark','Warrior Bot','','0','80','80','2','104','104','1','1.2','1.3','1','0','3','5','0','0','1','3400','2000','1','0','0','0','0','0','0','1','6','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','warrior_bot','0'),
  305. +('60055','0','0','0','0','0','17211','0','17211','0','Kerra','Warrior Bot','','0','80','80','2','104','104','1','1.2','1.3','1','0','3','5','0','0','1','3400','2000','1','0','0','0','0','0','0','1','6','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','warrior_bot','0'),
  306. +('60056','0','0','0','0','0','2139','0','2139','0','Miles Welsh','Priest Bot','','0','80','80','2','68','68','1','1.2','1.3','1','0','1','1','0','0','1','3600','2000','8','0','0','0','0','0','0','5','5','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','priest_bot','0'),
  307. +('60057','0','0','0','0','0','2138','0','2138','0','Malakai','Priest Bot','','0','80','80','2','68','68','1','1.2','1.3','1','0','1','1','0','0','1','3600','2000','8','0','0','0','0','0','0','5','5','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','priest_bot','0'),
  308. +('60058','0','0','0','0','0','2137','0','2137','0','Cobb','Priest Bot','','0','80','80','2','68','68','1','1.2','1.3','1','0','1','1','0','0','1','3600','2000','8','0','0','0','0','0','0','5','5','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','priest_bot','0'),
  309. +('60059','0','0','0','0','0','2134','0','2134','0','Shymm','Mage Bot','','0','80','80','2','68','68','1','1.2','1.3','1','0','1','1','0','0','1','3800','2000','8','0','0','0','0','0','0','8','5','0','0','0','7','1','0','0','0','0','0','0','0','0','0','0','143','145','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','mage_bot','0'),
  310. +('60060','0','0','0','0','0','6058','0','6058','0','Ursyn','Mage Bot','','0','80','80','2','68','68','1','1.2','1.3','1','0','1','1','0','0','1','3800','2000','8','0','0','0','0','0','0','8','5','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','mage_bot','0'),
  311. +('60061','0','0','0','0','0','2135','0','2135','0','Thurston','Mage Bot','','0','80','80','2','68','68','1','1.2','1.3','1','0','1','1','0','0','1','3800','2000','8','0','0','0','0','0','0','8','5','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','mage_bot','0'),
  312. +('60062','0','0','0','0','0','3793','0','3793','0','Harutt','Warrior Bot','','0','80','80','2','104','104','1','1.2','1.3','1','0','3','5','0','0','1','3400','2000','1','0','0','0','0','0','0','1','6','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','warrior_bot','0'),
  313. +('60063','0','0','0','0','0','3819','0','3819','0','Gart','Druid Bot','','0','80','80','2','104','104','1','1.2','1.3','1','0','2','4','0','0','1','2200','2000','2','0','0','0','0','0','0','11','6','0','0','0','7','8','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','druid_bot','0'),
  314. +('60064','0','0','0','0','0','3810','0','3810','0','Lanka','Hunter Bot','','0','80','80','2','104','104','1','1.2','1.3','1','0','2','3','0','0','1','2800','2000','2','0','0','0','0','0','0','3','6','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','hunter_bot','0'),
  315. +('60065','0','0','0','0','0','10180','0','10180','0','Meela','Shaman Bot','','0','80','80','2','104','104','1','1.2','1.3','1','0','1','2','0','0','1','2600','2000','2','0','0','0','0','0','0','7','6','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','shaman_bot','0'),
  316. +('60066','0','0','0','0','0','3794','0','3794','0','Krang','Warrior Bot','','0','80','80','2','104','104','1','1.2','1.3','1','0','3','5','0','0','1','3400','2000','1','0','0','0','0','0','0','1','6','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','warrior_bot','0'),
  317. +('60067','0','0','0','0','0','10734','0','10734','0','Gennia','Druid Bot','','0','80','80','2','104','104','1','1.2','1.3','1','0','2','4','0','0','1','2200','2000','2','0','0','0','0','0','0','11','6','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','druid_bot','0'),
  318. +('60068','0','0','0','0','0','3811','0','3811','0','Yaw','Hunter Bot','','0','80','80','2','104','104','1','1.2','1.3','1','0','2','3','0','0','1','2800','2000','2','0','0','0','0','0','0','3','6','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','hunter_bot','0'),
  319. +('60069','0','0','0','0','0','3816','0','3816','0','Narm','Shaman Bot','','0','80','80','2','104','104','1','1.2','1.3','1','0','1','2','0','0','1','2600','2000','2','0','0','0','0','0','0','7','6','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','shaman_bot','0'),
  320. +('60070','0','0','0','0','0','1880','0','1880','0','Frang','Warrior Bot','','0','80','80','2','29','29','1','1.2','1.3','1','0','3','5','0','0','1','3400','2000','1','0','0','0','0','0','0','1','2','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','warrior_bot','0'),
  321. +('60071','0','0','0','0','0','1882','0','1882','0','Jenshan','Hunter Bot','','0','80','80','2','126','126','1','1.2','1.3','1','0','2','3','0','0','1','2800','2000','2','0','0','0','0','0','0','3','8','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','hunter_bot','0'),
  322. +('60072','0','0','0','0','0','1884','0','1884','0','Nartok','Warlock Bot','','0','80','80','2','29','29','1','1.2','1.3','1','0','1','1','0','0','1','3500','2000','8','0','0','0','0','0','0','9','2','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','warlock_bot','0'),
  323. +('60073','0','0','0','0','0','1878','0','1878','0','Shikrik','Shaman Bot','','0','80','80','2','29','29','1','1.2','1.3','1','0','1','2','0','0','1','2600','2000','2','0','0','0','0','0','0','7','2','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','shaman_bot','0'),
  324. +('60074','0','0','0','0','0','3743','0','3743','0','Tarshaw','Warrior Bot','','0','80','80','2','29','29','1','1.2','1.3','1','0','3','5','0','0','1','3400','2000','1','0','0','0','0','0','0','1','2','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','warrior_bot','0'),
  325. +('60075','0','0','0','0','0','3744','0','3744','0','Thotar','Hunter Bot','','0','80','80','2','29','29','1','1.2','1.3','1','0','2','3','0','0','1','2800','2000','2','0','0','0','0','0','0','3','2','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','hunter_bot','0'),
  326. +('60076','0','0','0','0','0','3745','0','3745','0','Dhugru','Warlock Bot','','0','80','80','2','29','29','1','1.2','1.3','1','0','1','1','0','0','1','3500','2000','8','0','0','0','0','0','0','9','2','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','warlock_bot','0'),
  327. +('60077','0','0','0','0','0','3746','0','3746','0','Swart','Shaman Bot','','0','80','80','2','29','29','1','1.2','1.3','1','0','1','2','0','0','1','2600','2000','2','0','0','0','0','0','0','7','2','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','shaman_bot','0'),
  328. +('60078','0','0','0','0','0','1324','0','1324','0','Groldar','Warlock Bot','','0','80','80','2','29','29','1','1.2','1.3','1','0','1','1','0','0','1','3500','2000','8','0','0','0','0','0','0','9','2','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','warlock_bot','0'),
  329. +('60079','0','0','0','0','0','1325','0','1325','0','Mirket','Warlock Bot','','0','80','80','2','29','29','1','1.2','1.3','1','0','1','1','0','0','1','3500','2000','8','0','0','0','0','0','0','9','2','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','warlock_bot','0'),
  330. +('60080','0','0','0','0','0','1326','0','1326','0','Zevrost','Warlock Bot','','0','80','80','2','29','29','1','1.2','1.3','1','0','1','1','0','0','1','3500','2000','8','0','0','0','0','0','0','9','2','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','warlock_bot','0'),
  331. +('60081','0','0','0','0','0','1360','0','1360','0','Kardris','Shaman Bot','','0','80','80','2','29','29','1','1.2','1.3','1','0','1','2','0','0','1','2600','2000','2','0','0','0','0','0','0','7','2','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','shaman_bot','0'),
  332. +('60082','0','0','0','0','0','1373','0','1373','0','Ormak','Hunter Bot','','0','80','80','2','29','29','1','1.2','1.3','1','0','2','3','0','0','1','2800','2000','2','0','0','0','0','0','0','3','2','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','hunter_bot','0'),
  333. +('60083','0','0','0','0','0','1374','0','1374','0','Grezz','Warrior Bot','','0','80','80','2','29','29','1','1.2','1.3','1','0','3','5','0','0','1','3400','2000','1','0','0','0','0','0','0','1','2','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','warrior_bot','0'),
  334. +('60084','0','0','0','0','0','1375','0','1375','0','Sorek','Warrior Bot','','0','80','80','2','29','29','1','1.2','1.3','1','0','3','5','0','0','1','3400','2000','1','0','0','0','0','0','0','1','2','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','warrior_bot','0'),
  335. +('60085','0','0','0','0','0','4231','0','4231','0','Siantsu','Shaman Bot','','0','80','80','2','29','29','1','1.2','1.3','1','0','1','2','0','0','1','2600','2000','2','0','0','0','0','0','0','7','2','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','shaman_bot','0'),
  336. +('60086','0','0','0','0','0','4239','0','4239','0','Xorjuul','Hunter Bot','','0','80','80','2','29','29','1','1.2','1.3','1','0','2','3','0','0','1','2800','2000','2','0','0','0','0','0','0','3','2','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','hunter_bot','0'),
  337. +('60087','0','0','0','0','0','4241','0','4241','0','Siandur','Hunter Bot','','0','80','80','2','29','29','1','1.2','1.3','1','0','2','3','0','0','1','2800','2000','2','0','0','0','0','0','0','3','2','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','hunter_bot','0'),
  338. +('60088','0','0','0','0','0','4242','0','4242','0','Zelmak','Warrior Bot','','0','80','80','2','29','29','1','1.2','1.3','1','0','3','5','0','0','1','3400','2000','1','0','0','0','0','0','0','1','2','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','warrior_bot','0'),
  339. +('60089','0','0','0','0','0','7915','0','7915','0','ClaudeErksine','Hunter Bot','','0','80','80','2','55','55','1','1.2','1.3','1','0','2','3','0','0','1','2800','2000','2','0','0','0','0','3','0','3','3','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','hunter_bot','0'),
  340. +('60090','0','0','0','0','0','1721','0','1721','0','Alyissia','Warrior Bot','','0','80','80','2','80','80','1','1.2','1.3','1','0','3','5','0','0','1','3400','2000','1','0','0','0','0','0','0','1','4','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','warrior_bot','0'),
  341. +('60091','0','0','0','0','0','1725','0','1725','0','FrahunMurmombre','Rogue Bot','','0','80','80','2','80','80','1','1.2','1.3','1','0','1','2','0','0','1','1600','2000','4','0','0','0','0','0','0','4','4','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','rogue_bot','0'),
  342. +('60092','0','0','0','0','0','1733','0','1733','0','Shanda','Priest Bot','','0','80','80','2','80','80','1','1.2','1.3','1','0','1','1','0','0','1','3600','2000','8','0','0','0','0','0','0','5','4','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','priest_bot','0'),
  343. +('60093','0','0','0','0','0','1732','0','1732','0','Mardant','Druid Bot','','0','80','80','2','80','80','1','1.2','1.3','1','0','2','4','0','0','1','2200','2000','2','0','0','0','0','0','0','11','4','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','druid_bot','0'),
  344. +('60094','0','0','0','0','0','1707','0','1707','0','Kyra','Warrior Bot','','0','80','80','2','80','80','1','1.2','1.3','1','0','3','5','0','0','1','3400','2000','1','0','0','0','0','0','0','1','4','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','warrior_bot','0'),
  345. +('60095','0','0','0','0','0','1704','0','1704','0','Jannok','Rogue Bot','','0','80','80','2','80','80','1','1.2','1.3','1','0','1','2','0','0','1','1600','2000','4','0','0','0','0','0','0','4','4','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','rogue_bot','0'),
  346. +('60096','0','0','0','0','0','1708','0','1708','0','Laurna','Priest Bot','','0','80','80','2','80','80','1','1.2','1.3','1','0','1','1','0','0','1','3600','2000','8','0','0','0','0','0','0','5','4','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','priest_bot','0'),
  347. +('60097','0','0','0','0','0','1706','0','1706','0','Kal','Druid Bot','','0','80','80','2','80','80','1','1.2','1.3','1','0','2','4','0','0','1','2200','2000','2','0','0','0','0','0','0','11','4','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','druid_bot','0'),
  348. +('60098','0','0','0','0','0','4296','0','4296','0','Harruk','Hunter Bot','','0','80','80','2','29','29','1','1.2','1.3','1','0','2','3','0','0','1','2800','2000','2','0','0','0','0','3','0','3','2','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','hunter_bot','0'),
  349. +('60099','0','0','0','0','0','4299','0','4299','0','Reban','Hunter bot','','0','80','80','2','104','104','1','1.2','1.3','1','0','2','3','0','0','1','2800','2000','2','0','0','0','0','3','0','3','6','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','hunter_bot','0'),
  350. +('60100','0','0','0','0','0','4304','0','4304','0','Bolyun','Hunter Bot','','0','80','80','2','80','80','1','1.2','1.3','1','0','2','3','0','0','1','2800','2000','2','0','0','0','0','3','0','3','4','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','hunter_bot','0'),
  351. +('60101','0','0','0','0','0','1897','0','1897','0','Taijin','Priest Bot','','0','80','80','2','126','126','1','1.2','1.3','1','0','1','1','0','0','1','3600','2000','8','0','0','0','0','0','0','5','8','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','priest_bot','0'),
  352. +('60102','0','0','0','0','0','4068','0','4068','0','Kenjai','Priest Bot','','0','80','80','2','126','126','1','1.2','1.3','1','0','1','1','0','0','1','3600','2000','8','0','0','0','0','0','0','5','8','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','priest_bot','0'),
  353. +('60103','0','0','0','0','0','2066','0','2066','0','Danlaar','Hunter Bot','','0','80','80','2','80','80','1','1.2','1.3','1','0','2','3','0','0','1','2800','2000','2','0','0','0','0','0','0','3','4','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','hunter_bot','0'),
  354. +('60104','0','0','0','0','0','2196','0','2196','0','Ariasta','Warrior Bot','','0','80','80','2','80','80','1','1.2','1.3','1','0','3','5','0','0','1','3400','2000','1','0','0','0','0','0','0','1','4','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','warrior_bot','0'),
  355. +('60105','0','0','0','0','0','2198','0','2198','0','Sildanair','Warrior Bot','','0','80','80','2','80','80','1','1.2','1.3','1','0','3','5','0','0','1','3400','2000','1','0','0','0','0','0','0','1','4','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','warrior_bot','0'),
  356. +('60106','0','0','0','0','0','2200','0','2200','0','Astarii','Priest Bot','','0','80','80','2','80','80','1','1.2','1.3','1','0','1','1','0','0','1','3600','2000','8','0','0','0','0','0','0','5','4','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','priest_bot','0'),
  357. +('60107','0','0','0','0','0','2201','0','2201','0','Jandria','Priest Bot','','0','80','80','2','80','80','1','1.2','1.3','1','0','1','1','0','0','1','3600','2000','8','0','0','0','0','0','0','5','4','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','priest_bot','0'),
  358. +('60108','0','0','0','0','0','2202','0','2202','0','Lariia','Priest Bot','','0','80','80','2','80','80','1','1.2','1.3','1','0','1','1','0','0','1','3600','2000','8','0','0','0','0','0','0','5','4','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','priest_bot','0'),
  359. +('60109','0','0','0','0','0','2231','0','2231','0','Syurna','Rogue Bot','','0','80','80','2','80','80','1','1.2','1.3','1','0','1','2','0','0','1','1600','2000','4','0','0','0','0','0','0','4','4','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','rogue_bot','0'),
  360. +('60110','0','0','0','0','0','7669','0','7669','0','Elissa','Mage Bot','','0','80','80','2','80','80','1','1.2','1.3','1','0','1','1','0','0','1','3800','2000','8','0','0','0','0','0','0','8','4','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','mage_bot','0'),
  361. +('60111','0','0','0','0','0','2252','0','2252','0','Erion','Rogue Bot','','0','80','80','2','80','80','1','1.2','1.3','1','0','1','2','0','0','1','1600','2000','4','0','0','0','0','0','0','4','4','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','rogue_bot','0'),
  362. +('60112','0','0','0','0','0','2243','0','2243','0','Anishar','Rogue Bot','','0','80','80','2','80','80','1','1.2','1.3','1','0','1','2','0','0','1','1600','2000','4','0','0','0','0','0','0','4','4','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','rogue_bot','0'),
  363. +('60113','0','0','0','0','0','2250','0','2250','0','Denatharion','Druid Bot','','0','80','80','2','80','80','1','1.2','1.3','1','0','2','4','0','0','1','2200','2000','2','0','0','0','0','0','0','11','4','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','druid_bot','0'),
  364. +('60114','0','0','0','0','0','2255','0','2255','0','Fylerian','Druid Bot','','0','80','80','2','80','80','1','1.2','1.3','1','0','2','4','0','0','1','2200','2000','2','0','0','0','0','0','0','11','4','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','druid_bot','0'),
  365. +('60115','0','0','0','0','0','2416','0','2416','0','Caelyb','Hunter Bot','','0','80','80','2','80','80','1','1.2','1.3','1','0','2','3','0','0','1','2800','2000','2','0','0','0','0','3','0','3','4','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','hunter_bot','0'),
  366. +('60116','0','0','0','0','0','2675','0','2675','0','Kaal','Warlock Bot','','0','80','80','2','68','68','1','1.2','1.3','1','0','1','1','0','0','1','3500','2000','8','0','0','0','0','0','0','9','5','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','warlock_bot','0'),
  367. +('60117','0','0','0','0','0','16800','0','16800','0','Lana','Warlock Bot','','0','80','80','2','68','68','1','1.2','1.3','1','0','1','1','0','0','1','3500','2000','8','0','0','0','0','0','0','9','5','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','warlock_bot','0'),
  368. +('60118','0','0','0','0','0','2646','0','2646','0','Richard','Warlock Bot','','0','80','80','2','68','68','1','1.2','1.3','1','0','1','1','0','0','1','3500','2000','8','0','0','0','0','0','0','9','5','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','warlock_bot','0'),
  369. +('60119','0','0','0','0','0','10214','0','10214','0','Kaelystia','Mage Bot','','0','80','80','2','68','68','1','1.2','1.3','1','0','1','1','0','0','1','3800','2000','8','0','0','0','0','0','0','8','5','0','0','0','6','1','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','mage_bot','0'),
  370. +('60120','0','0','0','0','0','2644','0','2644','0','Pierce','Mage Bot','','0','80','80','2','68','68','1','1.2','1.3','1','0','1','1','0','0','1','3800','2000','8','0','0','0','0','0','0','8','5','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','mage_bot','0'),
  371. +('60121','0','0','0','0','0','2657','0','2657','0','Anastasia','Mage Bot','','0','80','80','2','68','68','1','1.2','1.3','1','0','1','1','0','0','1','3800','2000','8','0','0','0','0','0','0','8','5','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','mage_bot','0'),
  372. +('60122','0','0','0','0','0','2620','0','2620','0','Chris','Warrior Bot','','0','80','80','2','68','68','1','1.2','1.3','1','0','3','5','0','0','1','3400','2000','1','0','0','0','0','0','0','1','5','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','warrior_bot','0'),
  373. +('60123','0','0','0','0','0','2658','0','2658','0','Angela','Warrior Bot','','0','80','80','2','68','68','1','1.2','1.3','1','0','3','5','0','0','1','3400','2000','1','0','0','0','0','0','0','1','5','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','warrior_bot','0'),
  374. +('60124','0','0','0','0','0','2614','0','2614','0','Baltus','Warrior Bot','','0','80','80','2','68','68','1','1.2','1.3','1','0','3','5','0','0','1','3400','2000','1','0','0','0','0','0','0','1','5','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','warrior_bot','0'),
  375. +('60125','0','0','0','0','0','3054','0','3054','0','Kelv','Warrior Bot','','0','80','80','2','55','55','1','1.2','1.3','1','0','3','5','0','0','1','3400','2000','1','0','0','0','0','0','0','1','3','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','warrior_bot','0'),
  376. +('60126','0','0','0','0','0','3055','0','3055','0','Bilban','Warrior Bot','','0','80','80','2','875','875','1','1.2','1.3','1','0','3','5','0','0','1','3400','2000','1','0','0','0','0','0','0','1','7','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','warrior_bot','0'),
  377. +('60127','0','0','0','0','0','3056','0','3056','0','Daera','Hunter Bot','','0','80','80','2','55','55','1','1.2','1.3','1','0','2','3','0','0','1','2800','2000','2','0','0','0','0','0','0','3','3','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','hunter_bot','0'),
  378. +('60128','0','0','0','0','0','3072','0','3072','0','Olmin','Hunter Bot','','0','80','80','2','55','55','1','1.2','1.3','1','0','2','3','0','0','1','2800','2000','2','0','0','0','0','0','0','3','3','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','hunter_bot','0'),
  379. +('60129','0','0','0','0','0','3073','0','3073','0','Regnus','Hunter Bot','','0','80','80','2','55','55','1','1.2','1.3','1','0','2','3','0','0','1','2800','2000','2','0','0','0','0','0','0','3','3','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','hunter_bot','0'),
  380. +('60130','0','0','0','0','0','3086','0','3086','0','Theodrus','Priest Bot','','0','80','80','2','55','55','1','1.2','1.3','1','0','1','1','0','0','1','3600','2000','8','0','0','0','0','0','0','5','3','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','priest_bot','0'),
  381. +('60131','0','0','0','0','0','3066','0','3066','0','Braenna','Priest Bot','','0','80','80','2','55','55','1','1.2','1.3','1','0','1','1','0','0','1','3600','2000','8','0','0','0','0','0','0','5','3','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','priest_bot','0'),
  382. +('60132','0','0','0','0','0','3085','0','3085','0','Toldren','Priest Bot','','0','80','80','2','55','55','1','1.2','1.3','1','0','1','1','0','0','1','3600','2000','8','0','0','0','0','0','0','5','3','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','priest_bot','0'),
  383. +('60134','0','0','0','0','0','3108','0','3108','0','Bink','Mage Bot','','0','80','80','2','875','875','1','1.2','1.3','1','0','1','1','0','0','1','3800','2000','8','0','0','0','0','0','0','8','7','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','mage_bot','0'),
  384. +('60135','0','0','0','0','0','10214','0','10214','0','Juli','Mage Bot','','0','80','80','2','875','875','1','1.2','1.3','1','0','1','1','0','0','1','3800','2000','8','0','0','0','0','0','0','8','7','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','mage_bot','0'),
  385. +('60136','0','0','0','0','0','3109','0','3109','0','Nittegousse','Mage Bot','','0','80','80','2','55','55','1','1.2','1.3','1','0','1','1','0','0','1','3800','2000','8','0','0','0','0','0','0','8','3','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','mage_bot','0'),
  386. +('60137','0','0','0','0','0','3089','0','3089','0','Valgar','Paladin Bot','','0','80','80','2','55','55','1','1.2','1.3','1','0','2','4','0','0','1','2300','2000','2','0','0','0','0','0','0','2','3','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','paladin_bot','0'),
  387. +('60138','0','0','0','0','0','3088','0','3088','0','Beldruk','Paladin Bot','','0','80','80','2','55','55','1','1.2','1.3','1','0','2','4','0','0','1','2300','2000','2','0','0','0','0','0','0','2','3','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','paladin_bot','0'),
  388. +('60139','0','0','0','0','0','3087','0','3087','0','Brandur','Paladin Bot','','0','80','80','2','55','55','1','1.2','1.3','1','0','2','4','0','0','1','2300','2000','2','0','0','0','0','0','0','2','3','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','paladin_bot','0'),
  389. +('60140','0','0','0','0','0','3101','0','3101','0','Hulfdan','Rogue Bot','','0','80','80','2','55','55','1','1.2','1.3','1','0','1','2','0','0','1','1600','2000','4','0','0','0','0','0','0','4','3','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','rogue_bot','0'),
  390. +('60141','0','0','0','0','0','3100','0','3100','0','Ormyr','Rogue Bot','','0','80','80','2','55','55','1','1.2','1.3','1','0','1','2','0','0','1','1600','2000','4','0','0','0','0','0','0','4','3','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','rogue_bot','0'),
  391. +('60142','0','0','0','0','0','3113','0','3113','0','Phenwick','Rogue Bot','','0','80','80','2','875','875','1','1.2','1.3','1','0','1','2','0','0','1','1600','2000','4','0','0','0','0','0','0','4','7','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','rogue_bot','0'),
  392. +('60143','0','0','0','0','0','3115','0','3115','0','Coeurdechardon','Warlock Bot','','0','80','80','2','55','55','1','1.2','1.3','1','0','1','1','0','0','1','3500','2000','8','0','0','0','0','0','0','9','3','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','warlock_bot','0'),
  393. +('60144','0','0','0','0','0','3116','0','3116','0','Eglantin','Warlock Bot','','0','80','80','2','875','875','1','1.2','1.3','1','0','1','1','0','0','1','3500','2000','8','0','0','0','0','0','0','9','7','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','warlock_bot','0'),
  394. +('60145','0','0','0','0','0','3122','0','3122','0','Alexander','Warlock Bot','','0','80','80','2','55','55','1','1.2','1.3','1','0','1','1','0','0','1','3500','2000','8','0','0','0','0','0','0','9','3','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','warlock_bot','0'),
  395. +('60146','0','0','0','0','0','3280','0','3280','0','Wu','Warrior Bot','','0','80','80','2','12','12','1','1.2','1.3','1','0','3','5','0','0','1','3400','2000','1','0','0','0','0','0','0','1','1','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','warrior_bot','0'),
  396. +('60147','0','0','0','0','0','3287','0','3287','0','Ilsa','Warrior Bot','','0','80','80','2','12','12','1','1.2','1.3','1','0','3','5','0','0','1','3400','2000','1','0','0','0','0','0','0','1','1','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','warrior_bot','0'),
  397. +('60148','0','0','0','0','0','3283','0','3283','0','Joshua','Priest Bot','','0','80','80','2','12','12','1','1.2','1.3','1','0','1','1','0','0','1','3600','2000','8','0','0','0','0','0','0','5','1','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','priest_bot','0'),
  398. +('60149','0','0','0','0','0','3284','0','3284','0','Arthur','Paladin Bot','','0','80','80','2','12','12','1','1.2','1.3','1','0','2','4','0','0','1','2300','2000','2','0','0','0','0','0','0','2','1','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','paladin_bot','0'),
  399. +('60150','0','0','0','0','0','3289','0','3289','0','Katherine','Paladin Bot','','0','80','80','2','12','12','1','1.2','1.3','1','0','2','4','0','0','1','2300','2000','2','0','0','0','0','0','0','2','1','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','paladin_bot','0'),
  400. +('60151','0','0','0','0','0','3291','0','3291','0','Deline','Warlock Bot','','0','80','80','2','12','12','1','1.2','1.3','1','0','1','1','0','0','1','3500','2000','8','0','0','0','0','0','0','9','1','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','warlock_bot','0'),
  401. +('60152','0','0','0','0','0','3286','0','3286','0','Sandahl','Warlock Bot','','0','80','80','2','12','12','1','1.2','1.3','1','0','1','1','0','0','1','3500','2000','8','0','0','0','0','0','0','9','1','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','warlock_bot','0'),
  402. +('60153','0','0','0','0','0','3292','0','3292','0','Jennea','Mage Bot','','0','80','80','2','12','12','1','1.2','1.3','1','0','1','1','0','0','1','3800','2000','8','0','0','0','0','0','0','8','1','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','mage_bot','0'),
  403. +('60154','0','0','0','0','0','19803','0','19803','0','Elsharin','Mage Bot','','0','80','80','2','12','12','1','1.2','1.3','1','0','1','1','0','0','1','3800','2000','8','0','0','0','0','0','0','8','1','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','mage_bot','0'),
  404. +('60155','0','0','0','0','0','3299','0','3299','0','Kaerbrus','Hunter Bot','','0','80','80','2','80','80','1','1.2','1.3','1','0','2','3','0','0','1','2800','2000','2','0','0','0','0','0','0','3','4','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','hunter_bot','0'),
  405. +('60156','0','0','0','0','0','3300','0','3300','0','Sheldras','Druid Bot','','0','80','80','2','12','12','1','1.2','1.3','1','0','2','4','0','0','1','2200','2000','2','0','0','0','0','0','0','11','4','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','druid_bot','0'),
  406. +('60157','0','0','0','0','0','3301','0','3301','0','Theridran','Druid Bot','','0','80','80','2','80','80','1','1.2','1.3','1','0','2','4','0','0','1','2200','2000','2','0','0','0','0','0','0','11','4','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','druid_bot','0'),
  407. +('60158','0','0','0','0','0','3312','0','3312','0','Einris','Hunter Bot','','0','80','80','2','12','12','1','1.2','1.3','1','0','2','3','0','0','1','2800','2000','2','0','0','0','0','0','0','3','1','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','hunter_bot','0'),
  408. +('60159','0','0','0','0','0','3309','0','3309','0','Ulfir','Hunter Bot','','0','80','80','2','12','12','1','1.2','1.3','1','0','2','3','0','0','1','2800','2000','2','0','0','0','0','0','0','3','1','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','hunter_bot','0'),
  409. +('60160','0','0','0','0','0','3310','0','3310','0','Thorfin','Hunter Bot','','0','80','80','2','12','12','1','1.2','1.3','1','0','2','3','0','0','1','2800','2000','2','0','0','0','0','0','0','3','1','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','hunter_bot','0'),
  410. +('60161','0','0','0','0','0','10171','0','10171','0','UnThuwa','Mage Bot','','0','80','80','2','126','126','1','1.2','1.3','1','0','1','1','0','0','1','3800','2000','8','0','0','0','0','0','0','8','8','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','mage_bot','0'),
  411. +('60162','0','0','0','0','0','4524','0','4524','0','Pephredo','Mage Bot','','0','80','80','2','29','29','1','1.2','1.3','1','0','1','1','0','0','1','3800','2000','8','0','0','0','0','0','0','8','2','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','mage_bot','0'),
  412. +('60163','0','0','0','0','0','4522','0','4522','0','Enyo','Mage Bot','','0','80','80','2','29','29','1','1.2','1.3','1','0','1','1','0','0','1','3800','2000','8','0','0','0','0','0','0','8','2','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','mage_bot','0'),
  413. +('60164','0','0','0','0','0','4526','0','4526','0','Mai','Mage Bot','','0','80','80','2','126','126','1','1.2','1.3','1','0','1','1','0','0','1','3800','2000','8','0','0','0','0','0','0','8','8','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','mage_bot','0'),
  414. +('60165','0','0','0','0','0','4523','0','4523','0','Deino','Mage Bot','','0','80','80','2','29','29','1','1.2','1.3','1','0','1','1','0','0','1','3800','2000','8','0','0','0','0','0','0','8','2','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','mage_bot','0'),
  415. +('60166','0','0','0','0','0','4665','0','4665','0','Birgitte','Mage Bot','','0','80','80','2','68','68','1','1.2','1.3','1','0','1','1','0','0','1','3800','2000','8','0','0','0','0','0','0','8','5','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','mage_bot','0'),
  416. +('60167','0','0','0','0','0','12849','0','12849','0','Thuul','Mage Bot','','0','80','80','2','29','29','1','1.2','1.3','1','0','1','1','0','0','1','3800','2000','8','0','0','0','0','0','0','8','2','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','mage_bot','0'),
  417. +('60168','0','0','0','0','0','4690','0','4690','0','Zayus','Priest Bot','','0','80','80','2','29','29','1','1.2','1.3','1','0','1','1','0','0','1','3600','2000','8','0','0','0','0','0','0','5','2','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','priest_bot','0'),
  418. +('60169','0','0','0','0','0','10473','0','10473','0','Xyera','Priest Bot','','0','80','80','2','29','29','1','1.2','1.3','1','0','1','1','0','0','1','3600','2000','8','0','0','0','0','0','0','5','2','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','priest_bot','0'),
  419. +('60170','0','0','0','0','0','4711','0','4711','0','Urkyo','Priest Bot','','0','80','80','2','29','29','1','1.2','1.3','1','0','1','1','0','0','1','3600','2000','8','0','0','0','0','0','0','5','2','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','priest_bot','0'),
  420. +('60171','0','0','0','0','0','6060','0','6060','0','Uthelnay','Mage Bot','','0','80','80','2','126','126','1','1.2','1.3','1','0','1','1','0','0','1','3800','2000','8','0','0','0','0','0','0','8','8','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','mage_bot','0'),
  421. +('60172','0','0','0','0','0','6072','0','6072','0','Dink','Mage Bot','','0','80','80','2','875','875','1','1.2','1.3','1','0','1','1','0','0','1','3800','2000','8','0','0','0','0','0','0','8','7','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','mage_bot','0'),
  422. +('60173','0','0','0','0','0','6071','0','6071','0','Darnath','Warrior Bot','','0','80','80','2','80','80','1','1.2','1.3','1','0','3','5','0','0','1','3400','2000','1','0','0','0','0','0','0','1','4','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','warrior_bot','0'),
  423. +('60174','0','0','0','0','0','7356','0','7356','0','Karman','Paladin Bot','','0','80','80','2','894','894','1','1.2','1.3','1','0','2','4','0','0','1','2300','2000','2','0','0','0','0','0','0','2','1','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','paladin_bot','0'),
  424. +('60175','0','0','0','0','0','11037','0','11037','0','Evencane','Warrior Bot','','0','80','80','2','894','894','1','1.2','1.3','1','0','3','5','0','0','1','3400','2000','1','0','0','0','0','0','0','1','1','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','warrior_bot','0'),
  425. +('60176','0','0','0','0','0','7357','0','7357','0','Jannos','Druid Bot','','0','80','80','2','104','104','1','1.2','1.3','1','0','2','4','0','0','1','2200','2000','2','0','0','0','0','0','0','11','6','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','druid_bot','0'),
  426. +('60177','0','0','0','0','0','7538','0','7538','0','Alenndaar','Hunter Bot','','0','80','80','2','1076','1076','1','1.2','1.3','1','0','2','3','0','0','1','2800','2000','2','0','0','0','0','0','0','3','4','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','hunter_bot','0'),
  427. +('60178','0','0','0','0','0','10738','0','10738','0','Golhine','Druid Bot','','0','80','80','2','80','80','1','1.2','1.3','1','0','2','4','0','0','1','2200','2000','2','0','0','0','0','0','0','11','4','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','druid_bot','0'),
  428. +('60179','0','0','0','0','0','9337','0','9337','0','Hesuwa','Hunter Bot','','0','80','80','2','104','104','1','1.2','1.3','1','0','2','3','0','0','1','2800','2000','2','0','0','0','0','3','0','3','6','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','hunter_bot','0'),
  429. +('60180','0','0','0','0','0','9336','0','9336','0','Xao\'tsu','Hunter Bot','','0','80','80','2','29','29','1','1.2','1.3','1','0','2','3','0','0','1','2800','2000','2','0','0','0','0','3','0','3','2','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','hunter_bot','0'),
  430. +('60181','0','0','0','0','0','9338','0','9338','0','Belia','Hunter Bot','','0','80','80','2','55','55','1','1.2','1.3','1','0','2','3','0','0','1','2800','2000','2','0','0','0','0','3','0','3','3','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','hunter_bot','0'),
  431. +('60182','0','0','0','0','0','10245','0','10245','0','Dargh','Hunter Bot','','0','80','80','2','55','55','1','1.2','1.3','1','0','2','3','0','0','1','2800','2000','2','0','0','0','0','0','0','3','3','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','hunter_bot','0'),
  432. +('60183','0','0','0','0','0','11044','0','11044','0','Meideros','Priest Bot','','0','80','80','2','80','80','1','1.2','1.3','1','0','1','1','0','0','1','3600','2000','8','0','0','0','0','0','0','5','4','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','priest_bot','0'),
  433. +('60184','0','0','0','0','0','11048','0','11048','0','Presse','Priest Bot','','0','80','80','2','1076','1076','1','1.2','1.3','1','0','1','1','0','0','1','3600','2000','8','0','0','0','0','0','0','5','4','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','priest_bot','0'),
  434. +('60185','0','0','0','0','0','11053','0','11053','0','Rohan','Priest Bot','','0','80','80','2','122','122','1','1.2','1.3','1','0','1','1','0','0','1','3600','2000','8','0','0','0','0','0','0','5','3','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','priest_bot','0'),
  435. +('60186','0','0','0','0','0','12053','0','12053','0','Loganaar','Druid Bot','','0','80','80','2','994','994','1','1.2','1.3','1','0','2','4','0','0','1','2200','2000','2','0','0','0','0','0','0','11','4','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','druid_bot','0'),
  436. +('60187','0','0','0','0','0','13171','0','13171','0','Romano','Rogue Bot','','0','80','80','2','12','12','1','1.2','1.3','1','0','1','2','0','0','1','1600','2000','4','0','0','0','0','0','0','4','1','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','rogue_bot','0'),
  437. +('60188','0','0','0','0','0','13341','0','13341','0','Sagorne','Shaman Bot','','0','80','80','2','104','104','1','1.2','1.3','1','0','1','2','0','0','1','2600','2000','2','0','0','0','0','0','0','7','6','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','shaman_bot','0'),
  438. +('60189','0','0','0','0','0','15522','0','15522','0','Julia','Mage Bot','','0','80','80','2','1604','1604','1','1.2','1.3','1','0','1','1','0','0','1','3800','2000','8','0','0','0','0','0','0','8','10','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','mage_bot','0'),
  439. +('60190','0','0','0','0','0','15511','0','15511','0','Jesthenis','Paladin Bot','','0','80','80','2','1604','1604','1','1.2','1.3','1','0','2','4','0','0','1','2300','2000','2','0','0','0','0','0','0','2','10','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','paladin_bot','0'),
  440. +('60191','0','0','0','0','0','15524','0','15524','0','Invocateur','Warlock Bot','','0','80','80','2','1604','1604','1','1.2','1.3','1','0','1','1','0','0','1','3500','2000','8','0','0','0','0','0','0','9','10','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','warlock_bot','0'),
  441. +('60192','0','0','0','0','0','15518','0','15518','0','Matrone','Priest Bot','','0','80','80','2','1604','1604','1','1.2','1.3','1','0','1','1','0','0','1','3600','2000','8','0','0','0','0','0','0','5','10','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','priest_bot','0'),
  442. +('60193','0','0','0','0','0','2659','0','2659','0','Eclaireur','Rogue Bot','','0','80','80','2','68','68','1','1.2','1.3','1','0','1','2','0','0','1','1600','2000','4','0','0','0','0','0','0','4','5','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','rogue_bot','0'),
  443. +('60194','0','0','0','0','0','15520','0','15520','0','Sallina','Hunter Bot','','0','80','80','2','1604','1604','1','1.2','1.3','1','0','2','3','0','0','1','2800','2000','2','0','0','0','0','0','0','3','10','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','hunter_bot','0'),
  444. +('60195','0','0','0','0','0','16685','0','16685','0','Noellene','Paladin Bot','','0','80','80','2','1604','1604','1','1.2','1.3','1','0','2','4','0','0','1','2300','2000','2','0','0','0','0','0','0','2','10','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','paladin_bot','0'),
  445. +('60196','0','0','0','0','0','16707','0','16707','0','Ponaris','Priest Bot','','0','80','80','2','1604','1604','1','1.2','1.3','1','0','1','1','0','0','1','3600','2000','8','0','0','0','0','0','0','5','10','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','priest_bot','0'),
  446. +('60197','0','0','0','0','0','16222','0','16222','0','Keilnei','Hunter Bot','','0','80','80','2','1638','1638','1','1.2','1.3','1','0','2','3','0','0','1','2800','2000','2','0','0','0','0','0','0','3','11','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','hunter_bot','0'),
  447. +('60198','0','0','0','0','0','16223','0','16223','0','Valaatu','Mage Bot','','0','80','80','2','1638','1638','1','1.2','1.3','1','0','1','1','0','0','1','3800','2000','8','0','0','0','0','0','0','8','11','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','mage_bot','0'),
  448. +('60199','0','0','0','0','0','16224','0','16224','0','Aurelon','Paladin Bot','','0','80','80','2','1638','1638','1','1.2','1.3','1','0','2','4','0','0','1','2300','2000','2','0','0','0','0','0','0','2','11','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','paladin_bot','0'),
  449. +('60200','0','0','0','0','0','16225','0','16225','0','Zalduun','Priest Bot','','0','80','80','2','1638','1638','1','1.2','1.3','1','0','1','1','0','0','1','3600','2000','8','0','0','0','0','0','0','5','11','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','priest_bot','0'),
  450. +('60201','0','0','0','0','0','16226','0','16226','0','Kore','Warrior Bot','','0','80','80','2','1638','1638','1','1.2','1.3','1','0','3','5','0','0','1','3400','2000','1','0','0','0','0','0','0','1','11','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','warrior_bot','0'),
  451. +('60202','0','0','0','0','0','16787','0','16787','0','Alamma','Warlock Bot','','0','80','80','2','1604','1604','1','1.2','1.3','1','0','1','1','0','0','1','3500','2000','8','0','0','0','0','0','0','9','10','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','warlock_bot','0'),
  452. +('60203','0','0','0','0','0','16800','0','16800','0','Talionia','Warlock Bot','','0','80','80','2','1604','1604','1','1.2','1.3','1','0','1','1','0','0','1','3500','2000','8','0','0','0','0','0','0','9','10','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','warlock_bot','0'),
  453. +('60204','0','0','0','0','0','16831','0','16831','0','Zanien','Hunter Bot','','0','80','80','2','1604','1604','1','1.2','1.3','1','0','2','3','0','0','1','2800','2000','2','0','0','0','0','0','0','9','10','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','hunter_bot','0'),
  454. +('60205','0','0','0','0','0','16781','0','16781','0','Zaedana','Mage Bot','','0','80','80','2','1604','1604','1','1.2','1.3','1','0','1','1','0','0','1','3800','2000','8','0','0','0','0','0','0','8','10','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','mage_bot','0'),
  455. +('60206','0','0','0','0','0','16824','0','16824','0','Quithas','Mage Bot','','0','80','80','2','1604','1604','1','1.2','1.3','1','0','1','1','0','0','1','3800','2000','8','0','0','0','0','0','0','8','10','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','mage_bot','0'),
  456. +('60207','0','0','0','0','0','16739','0','16739','0','Harene','Druid Bot','','0','80','80','2','1604','1604','1','1.2','1.3','1','0','2','4','0','0','1','2200','2000','2','0','0','0','0','0','0','11','6','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','druid_bot','0'),
  457. +('60208','0','0','0','0','0','16778','0','16778','0','Tana','Hunter Bot','','0','80','80','2','1604','1604','1','1.2','1.3','1','0','2','3','0','0','1','2800','2000','2','0','0','0','0','0','0','3','10','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','hunter_bot','0'),
  458. +('60209','0','0','0','0','0','16816','0','16816','0','Oninath','Hunter Bot','','0','80','80','2','1604','1604','1','1.2','1.3','1','0','2','3','0','0','1','2800','2000','2','0','0','0','0','0','0','3','10','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','hunter_bot','0'),
  459. +('60210','0','0','0','0','0','16829','0','16829','0','Bachi','Paladin Bot','','0','80','80','2','1604','1604','1','1.2','1.3','1','0','2','4','0','0','1','2300','2000','2','0','0','0','0','0','0','2','10','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','paladin_bot','0'),
  460. +('60211','0','0','0','0','0','16767','0','16767','0','Zelanis','Rogue Bot','','0','80','80','2','1604','1604','1','1.2','1.3','1','0','1','2','0','0','1','1600','2000','4','0','0','0','0','0','0','4','10','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','rogue_bot','0'),
  461. +('60212','0','0','0','0','0','16798','0','16798','0','Elara','Rogue Bot','','0','80','80','2','1604','1604','1','1.2','1.3','1','0','1','2','0','0','1','1600','2000','4','0','0','0','0','0','0','4','10','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','rogue_bot','0'),
  462. +('60213','0','0','0','0','0','16858','0','16858','0','Shalannius','Druid Bot','','0','80','80','2','1638','1638','1','1.2','1.3','1','0','2','4','0','0','1','2200','2000','2','0','0','0','0','0','0','11','6','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','druid_bot','0'),
  463. +('60214','0','0','0','0','0','17434','0','17434','0','Deremiis','Hunter Bot','','0','80','80','2','1638','1638','1','1.2','1.3','1','0','2','3','0','0','1','2800','2000','2','0','0','0','0','0','0','3','11','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','hunter_bot','0'),
  464. +('60215','0','0','0','0','0','17247','0','17247','0','Caedmos','Priest Bot','','0','80','80','2','1638','1638','1','1.2','1.3','1','0','1','1','0','0','1','3600','2000','8','0','0','0','0','0','0','5','11','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','priest_bot','0'),
  465. +('60216','0','0','0','0','0','17225','0','17225','0','Baatun','Paladin Bot','','0','80','80','2','1638','1638','1','1.2','1.3','1','0','2','4','0','0','1','2300','2000','2','0','0','0','0','0','0','2','11','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','paladin_bot','0'),
  466. +('60217','0','0','0','0','0','17212','0','17212','0','Ahonan','Warrior Bot','','0','80','80','2','1638','1638','1','1.2','1.3','1','0','3','5','0','0','1','3400','2000','1','0','0','0','0','0','0','1','11','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','warrior_bot','0'),
  467. +('60218','0','0','0','0','0','17598','0','17598','0','Firmanvaar','Shaman Bot','','0','80','80','2','1638','1638','1','1.2','1.3','1','0','1','2','0','0','1','2600','2000','2','0','0','0','0','0','0','7','11','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','shaman_bot','0'),
  468. +('60219','0','0','0','0','0','16860','0','16860','0','Actron','Hunter Bot','','0','80','80','2','1638','1638','1','1.2','1.3','1','0','2','3','0','0','1','2800','2000','2','0','0','0','0','0','0','3','11','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','hunter_bot','0'),
  469. +('60220','0','0','0','0','0','17213','0','17213','0','Behomat','Warrior Bot','','0','80','80','2','1638','1638','1','1.2','1.3','1','0','3','5','0','0','1','3400','2000','1','0','0','0','0','0','0','1','11','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','warrior_bot','0'),
  470. +('60221','0','0','0','0','0','17600','0','17600','0','Nobundo','Shaman Bot','','0','80','80','2','1638','1638','1','1.2','1.3','1','0','1','2','0','0','1','2600','2000','2','0','0','0','0','0','0','7','11','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','shaman_bot','0'),
  471. +('60222','0','0','0','0','0','17599','0','17599','0','Tuluun','Shaman Bot','','0','80','80','2','1638','1638','1','1.2','1.3','1','0','1','2','0','0','1','2600','2000','2','0','0','0','0','0','0','7','11','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','shaman_bot','0'),
  472. +('60223','0','0','0','0','0','16914','0','16914','0','Sulaa','Shaman Bot','','0','80','80','2','1638','1638','1','1.2','1.3','1','0','1','2','0','0','1','2600','2000','2','0','0','0','0','0','0','7','11','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','shaman_bot','0'),
  473. +('60224','0','0','0','0','0','17215','0','17215','0','Ruada','Warrior Bot','','0','80','80','2','1638','1638','1','1.2','1.3','1','0','3','5','0','0','1','3400','2000','1','0','0','0','0','0','0','1','11','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','warrior_bot','0'),
  474. +('60225','0','0','0','0','0','17233','0','17233','0','Semid','Mage Bot','','0','80','80','2','1638','1638','1','1.2','1.3','1','0','1','1','0','0','1','3800','2000','8','0','0','0','0','0','0','8','11','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','mage_bot','0'),
  475. +('60226','0','0','0','0','0','17232','0','17232','0','Guvan','Priest Bot','','0','80','80','2','1638','1638','1','1.2','1.3','1','0','1','1','0','0','1','3600','2000','8','0','0','0','0','0','0','5','11','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','priest_bot','0'),
  476. +('60227','0','0','0','0','0','17234','0','17234','0','Tullas','Paladin Bot','','0','80','80','2','1638','1638','1','1.2','1.3','1','0','2','4','0','0','1','2300','2000','2','0','0','0','0','0','0','2','11','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','paladin_bot','0'),
  477. +('60228','0','0','0','0','0','17488','0','17488','0','Killac','Hunter bot','','0','80','80','2','1638','1638','1','1.2','1.3','1','0','2','3','0','0','1','2800','2000','2','0','0','0','0','0','0','3','11','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','hunter_bot','0'),
  478. +('60229','0','0','0','0','0','17226','0','17226','0','Jol','Paladin Bot','','0','80','80','2','1638','1638','1','1.2','1.3','1','0','2','4','0','0','1','2300','2000','2','0','0','0','0','0','0','2','11','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','paladin_bot','0'),
  479. +('60230','0','0','0','0','0','17248','0','17248','0','Fallat','Priest Bot','','0','80','80','2','1638','1638','1','1.2','1.3','1','0','1','1','0','0','1','3600','2000','8','0','0','0','0','0','0','5','11','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','priest_bot','0'),
  480. +('60231','0','0','0','0','0','17243','0','17243','0','Harnan','Mage Bot','','0','80','80','2','1638','1638','1','1.2','1.3','1','0','1','1','0','0','1','3800','2000','8','0','0','0','0','0','0','8','11','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','mage_bot','0'),
  481. +('60232','0','0','0','0','0','17241','0','17241','0','Bati','Mage Bot','','0','80','80','2','1638','1638','1','1.2','1.3','1','0','1','1','0','0','1','3800','2000','8','0','0','0','0','0','0','8','11','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','mage_bot','0'),
  482. +('60233','0','0','0','0','0','17792','0','17792','0','Hobahken','Shaman Bot','','0','80','80','2','1638','1638','1','1.2','1.3','1','0','1','2','0','0','1','2600','2000','2','0','0','0','0','0','0','7','11','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','shaman_bot','0'),
  483. +('60234','0','0','0','0','0','6820','0','6820','0','Gurrag','Shaman Bot','','0','80','80','2','1638','1638','1','1.2','1.3','1','0','1','2','0','0','1','2600','2000','2','0','0','0','0','0','0','7','11','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','shaman_bot','0'),
  484. +('60235','0','0','0','0','0','19596','0','19596','0','Auberose','Paladin Bot','','0','80','80','2','1602','1602','1','1.2','1.3','1','0','2','4','0','0','1','2300','2000','2','0','0','0','0','0','0','2','10','0','0','0','7','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','0','1','1048688','paladin_bot','0'),
  485. +('60236','0','0','0','0','0','10335','10335','10335','10335','Afina','Priest Bot','','0','80','80','2','35','35','1','1.2','1.3','1','0','1','1','0','0','1','3600','2000','8','0','0','0','0','0','0','5','2','0','0','0','7','1','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','1','1','1048688','priest_bot','0'),
  486. +('60237','0','0','0','0','0','1132','0','1132','0','Voidwalker','Warlock\'s Pet Bot',NULL,'0','80','80','2','14','14','0','1.2','1.3','1','0','2','3','0','0','1','2000','2000','1','0','0','0','16','0','0','1','0','0','0','0','3','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','0','1','1','1048688','voidwalker_bot','0'),
  487. +('60238','0','0','0','0','0','1105','0','0','0','Hunter\'s Pet',NULL,NULL,'0','80','80','0','14','14','0','1.1','1.14286','1','0','87','117','0','214','1','2000','0','1','0','0','0','7','0','0','1','0','61','90','21','1','1','0','0','0','0','0','0','0','0','0','5708','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','1','1','1','0','0','0','0','0','0','0','149','1','0','0','','0');
  488. +
  489. +-- EQUIPS --
  490. +
  491. +delete from `creature_equip_template` where entry between 60001 and 60238;
  492. +
  493. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60001','1','18842','0','0');
  494. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60002','1','18842','0','0');
  495. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60003','1','18842','0','0');
  496. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60004','1','31289','0','0');
  497. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60005','1','31289','0','0');
  498. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60006','1','31289','0','0');
  499. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60007','1','31186','0','0');
  500. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60008','1','31186','0','0');
  501. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60009','1','31186','0','0');
  502. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60010','1','2291','0','2825');
  503. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60011','1','31289','0','0');
  504. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60012','1','2291','0','2825');
  505. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60013','1','18002','0','0');
  506. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60014','1','27903','0','0');
  507. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60015','1','7723','0','0');
  508. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60016','1','13984','6448','0');
  509. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60017','1','13984','6448','0');
  510. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60018','1','6633','820','0');
  511. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60019','1','13984','6448','0');
  512. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60020','1','12584','18825','0');
  513. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60021','1','18876','0','0');
  514. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60022','1','12584','18825','0');
  515. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60023','1','18876','0','0');
  516. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60024','1','18842','0','0');
  517. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60025','1','18203','18202','0');
  518. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60026','1','31186','0','0');
  519. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60027','1','31289','0','0');
  520. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60028','1','18842','0','0');
  521. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60029','1','28367','0','0');
  522. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60030','1','12584','18825','0');
  523. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60031','1','13984','6448','0');
  524. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60032','1','27903','0','0');
  525. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60033','1','18002','0','0');
  526. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60034','1','31289','0','0');
  527. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60035','1','18842','0','0');
  528. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60036','1','31186','0','0');
  529. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60037','1','31186','0','0');
  530. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60038','1','18842','0','0');
  531. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60039','1','31289','0','0');
  532. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60041','1','18842','0','0');
  533. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60042','1','18842','0','0');
  534. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60043','1','18203','18202','0');
  535. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60044','1','18203','18202','0');
  536. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60045','1','18203','18202','0');
  537. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60046','1','18203','18202','0');
  538. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60047','1','25622','0','0');
  539. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60048','1','25622','0','0');
  540. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60049','1','25622','0','0');
  541. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60050','1','2291','0','2825');
  542. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60051','1','2291','0','2825');
  543. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60052','1','2291','0','2825');
  544. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60053','1','18002','0','0');
  545. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60054','1','27903','0','0');
  546. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60055','1','28367','0','0');
  547. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60056','1','31289','0','0');
  548. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60057','1','31289','0','0');
  549. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60058','1','31289','0','0');
  550. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60059','1','18842','0','0');
  551. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60060','1','18842','0','0');
  552. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60061','1','18842','0','0');
  553. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60062','1','28367','0','0');
  554. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60063','1','25622','0','0');
  555. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60064','1','2291','0','2825');
  556. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60065','1','18203','18202','0');
  557. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60066','1','18002','0','0');
  558. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60067','1','25622','0','0');
  559. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60068','1','2291','0','2825');
  560. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60069','1','18203','18202','0');
  561. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60070','1','27903','0','0');
  562. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60071','1','2291','0','2825');
  563. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60072','1','31186','0','0');
  564. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60073','1','18203','18202','0');
  565. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60074','1','18002','0','0');
  566. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60075','1','2291','0','2825');
  567. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60076','1','31186','0','0');
  568. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60077','1','18203','18202','0');
  569. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60078','1','31186','0','0');
  570. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60079','1','31186','0','0');
  571. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60080','1','31186','0','0');
  572. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60081','1','18203','18202','0');
  573. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60082','1','2291','0','2825');
  574. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60083','1','7723','0','0');
  575. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60084','1','18002','0','0');
  576. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60085','1','18203','18202','0');
  577. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60086','1','2291','0','2825');
  578. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60087','1','2291','0','2825');
  579. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60088','1','27903','0','0');
  580. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60089','1','2291','0','2825');
  581. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60090','1','28367','0','0');
  582. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60091','1','6633','820','0');
  583. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60092','1','31289','0','0');
  584. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60093','1','25622','0','0');
  585. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60094','1','18002','0','0');
  586. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60095','1','13984','6448','0');
  587. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60096','1','31289','0','0');
  588. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60097','1','25622','0','0');
  589. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60098','1','2291','0','2825');
  590. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60099','1','2291','0','2825');
  591. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60100','1','2291','0','2825');
  592. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60101','1','31289','0','0');
  593. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60102','1','31289','0','0');
  594. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60103','1','2291','0','2825');
  595. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60104','1','7723','0','0');
  596. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60105','1','18002','0','0');
  597. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60106','1','31289','0','0');
  598. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60107','1','31289','0','0');
  599. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60108','1','31289','0','0');
  600. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60109','1','13984','6448','0');
  601. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60110','1','18842','0','0');
  602. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60111','1','6633','820','0');
  603. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60112','1','13984','6448','0');
  604. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60113','1','25622','0','0');
  605. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60114','1','25622','0','0');
  606. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60115','1','2291','0','2825');
  607. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60116','1','31186','0','0');
  608. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60117','1','31186','0','0');
  609. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60118','1','31186','0','0');
  610. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60119','1','18842','0','0');
  611. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60120','1','18842','0','0');
  612. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60121','1','18842','0','0');
  613. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60122','1','27903','0','0');
  614. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60123','1','18002','0','0');
  615. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60124','1','7723','0','0');
  616. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60125','1','18002','0','0');
  617. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60126','1','28367','0','0');
  618. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60127','1','2291','0','2825');
  619. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60128','1','2291','0','2825');
  620. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60129','1','2291','0','2825');
  621. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60130','1','31289','0','0');
  622. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60131','1','31289','0','0');
  623. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60132','1','31289','0','0');
  624. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60134','1','18842','0','0');
  625. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60135','1','18842','0','0');
  626. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60136','1','18842','0','0');
  627. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60137','1','18876','0','0');
  628. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60138','1','12584','18825','0');
  629. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60139','1','18876','0','0');
  630. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60140','1','6633','820','0');
  631. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60141','1','13984','6448','0');
  632. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60142','1','6633','820','0');
  633. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60143','1','31186','0','0');
  634. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60144','1','31186','0','0');
  635. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60145','1','31186','0','0');
  636. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60146','1','27903','0','0');
  637. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60147','1','18002','0','0');
  638. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60148','1','31289','0','0');
  639. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60149','1','12584','18825','0');
  640. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60150','1','18876','0','0');
  641. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60151','1','31186','0','0');
  642. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60152','1','31186','0','0');
  643. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60153','1','18842','0','0');
  644. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60154','1','18842','0','0');
  645. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60155','1','2291','0','2825');
  646. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60156','1','25622','0','0');
  647. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60157','1','25622','0','0');
  648. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60158','1','2291','0','2825');
  649. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60159','1','2291','0','2825');
  650. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60160','1','2291','0','2825');
  651. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60161','1','18842','0','0');
  652. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60162','1','18842','0','0');
  653. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60163','1','18842','0','0');
  654. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60164','1','18842','0','0');
  655. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60165','1','18842','0','0');
  656. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60166','1','18842','0','0');
  657. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60167','1','18842','0','0');
  658. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60168','1','31289','0','0');
  659. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60169','1','31289','0','0');
  660. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60170','1','31289','0','0');
  661. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60171','1','18842','0','0');
  662. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60172','1','18842','0','0');
  663. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60173','1','28367','0','0');
  664. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60174','1','12584','18825','0');
  665. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60175','1','7723','0','0');
  666. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60176','1','25622','0','0');
  667. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60177','1','2291','0','2825');
  668. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60178','1','25622','0','0');
  669. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60179','1','2291','0','2825');
  670. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60180','1','2291','0','2825');
  671. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60181','1','2291','0','2825');
  672. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60182','1','2291','0','2825');
  673. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60183','1','31289','0','0');
  674. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60184','1','31289','0','0');
  675. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60185','1','31289','0','0');
  676. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60186','1','25622','0','0');
  677. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60187','1','13984','6448','0');
  678. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60188','1','18203','18202','0');
  679. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60189','1','18842','0','0');
  680. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60191','1','31186','0','0');
  681. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60192','1','31289','0','0');
  682. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60193','1','13984','6448','0');
  683. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60194','1','2291','0','2825');
  684. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60195','1','12584','18826','0');
  685. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60196','1','31289','0','0');
  686. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60197','1','2291','0','2825');
  687. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60198','1','18842','0','0');
  688. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60199','1','18876','0','0');
  689. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60200','1','31289','0','0');
  690. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60201','1','27903','0','0');
  691. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60202','1','31186','0','0');
  692. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60203','1','31186','0','0');
  693. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60204','1','31186','0','0');
  694. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60205','1','18842','0','0');
  695. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60206','1','18842','0','0');
  696. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60207','1','25622','0','0');
  697. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60208','1','2291','0','2825');
  698. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60209','1','2291','0','2825');
  699. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60210','1','12584','18826','0');
  700. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60211','1','6633','820','0');
  701. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60212','1','13984','6448','0');
  702. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60213','1','25622','0','0');
  703. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60214','1','2291','0','2825');
  704. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60215','1','31289','0','0');
  705. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60216','1','18876','0','0');
  706. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60217','1','28367','0','0');
  707. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60218','1','18203','18202','0');
  708. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60219','1','2291','0','2825');
  709. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60220','1','18002','0','0');
  710. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60221','1','18203','18202','0');
  711. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60222','1','18203','18202','0');
  712. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60223','1','18203','18202','0');
  713. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60224','1','27903','0','0');
  714. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60225','1','18842','0','0');
  715. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60226','1','31289','0','0');
  716. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60227','1','12584','18825','0');
  717. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60229','1','18876','0','0');
  718. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60230','1','31289','0','0');
  719. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60231','1','18842','0','0');
  720. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60232','1','18842','0','0');
  721. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60233','1','18203','18202','0');
  722. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60234','1','18203','18202','0');
  723. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60235','1','29175','18826','0');
  724. +insert into `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) values('60236','1','31289','0','0');
  725. +
  726. +
  727. +
  728. +-- Customize section
  729. +-- You can create your own values to be in line with your own server if these are not acceptable.
  730. +
  731. +-- Add flags_extra
  732. +SET @EX_NO_BLOCK = 16;
  733. +SET @EX_NO_CRUSH = 32;
  734. +SET @EX_NO_XP = 64;
  735. +SET @EX_DIMINISH = 1048576;
  736. +SET @FLAGS_EX = @EX_NO_BLOCK | @EX_NO_CRUSH | @EX_NO_XP | @EX_DIMINISH;
  737. +
  738. +-- minions
  739. +UPDATE `creature_template` SET exp:=2, dmg_multiplier:=1.0, attackpower:=0, mindmg:=2, maxdmg:=4, minlevel:=80, maxlevel:=80, baseattacktime:=2200, rangeattacktime:=2000, minrangedmg:=0, maxrangedmg:=0, rangedattackpower:=0, dynamicflags:=0, speed_walk:=1.2, speed_run:=1.3, InhabitType:=3, health_mod:=1, mana_mod:=1, armor_mod:=1, mechanic_immune_mask:=1, flags_extra:=@FLAGS_EX, AIName='' where entry >= 60000 && entry < 60239 and subname='Druid Bot';
  740. +UPDATE `creature_template` SET exp:=2, dmg_multiplier:=1.0, attackpower:=0, mindmg:=2, maxdmg:=3, minlevel:=80, maxlevel:=80, baseattacktime:=2800, rangeattacktime:=2000, minrangedmg:=0, maxrangedmg:=0, rangedattackpower:=0, dynamicflags:=0, speed_walk:=1.2, speed_run:=1.3, InhabitType:=3, health_mod:=1, mana_mod:=1, armor_mod:=1, mechanic_immune_mask:=1, flags_extra:=@FLAGS_EX, AIName='' where entry >= 60000 && entry < 60239 and subname='Hunter Bot';
  741. +UPDATE `creature_template` SET exp:=2, dmg_multiplier:=1.0, attackpower:=0, mindmg:=1, maxdmg:=1, minlevel:=80, maxlevel:=80, baseattacktime:=3800, rangeattacktime:=2000, minrangedmg:=0, maxrangedmg:=0, rangedattackpower:=0, dynamicflags:=0, speed_walk:=1.2, speed_run:=1.3, InhabitType:=3, health_mod:=1, mana_mod:=1, armor_mod:=1, mechanic_immune_mask:=1, flags_extra:=@FLAGS_EX, AIName='' where entry >= 60000 && entry < 60239 and subname='Mage Bot';
  742. +UPDATE `creature_template` SET exp:=2, dmg_multiplier:=1.0, attackpower:=0, mindmg:=2, maxdmg:=4, minlevel:=80, maxlevel:=80, baseattacktime:=2300, rangeattacktime:=2000, minrangedmg:=0, maxrangedmg:=0, rangedattackpower:=0, dynamicflags:=0, speed_walk:=1.2, speed_run:=1.3, InhabitType:=3, health_mod:=1, mana_mod:=1, armor_mod:=1, mechanic_immune_mask:=1, flags_extra:=@FLAGS_EX, AIName='' where entry >= 60000 && entry < 60239 and subname='Paladin Bot';
  743. +UPDATE `creature_template` SET exp:=2, dmg_multiplier:=1.0, attackpower:=0, mindmg:=1, maxdmg:=1, minlevel:=80, maxlevel:=80, baseattacktime:=3600, rangeattacktime:=2000, minrangedmg:=0, maxrangedmg:=0, rangedattackpower:=0, dynamicflags:=0, speed_walk:=1.2, speed_run:=1.3, InhabitType:=3, health_mod:=1, mana_mod:=1, armor_mod:=1, mechanic_immune_mask:=1, flags_extra:=@FLAGS_EX, AIName='' where entry >= 60000 && entry < 60239 and subname='Priest Bot';
  744. +UPDATE `creature_template` SET exp:=2, dmg_multiplier:=1.0, attackpower:=0, mindmg:=1, maxdmg:=2, minlevel:=80, maxlevel:=80, baseattacktime:=1600, rangeattacktime:=2000, minrangedmg:=0, maxrangedmg:=0, rangedattackpower:=0, dynamicflags:=0, speed_walk:=1.2, speed_run:=1.3, InhabitType:=3, health_mod:=1, mana_mod:=1, armor_mod:=1, mechanic_immune_mask:=1, flags_extra:=@FLAGS_EX, AIName='' where entry >= 60000 && entry < 60239 and subname='Rogue Bot';
  745. +UPDATE `creature_template` SET exp:=2, dmg_multiplier:=1.0, attackpower:=0, mindmg:=1, maxdmg:=2, minlevel:=80, maxlevel:=80, baseattacktime:=2600, rangeattacktime:=2000, minrangedmg:=0, maxrangedmg:=0, rangedattackpower:=0, dynamicflags:=0, speed_walk:=1.2, speed_run:=1.3, InhabitType:=3, health_mod:=1, mana_mod:=1, armor_mod:=1, mechanic_immune_mask:=1, flags_extra:=@FLAGS_EX, AIName='' where entry >= 60000 && entry < 60239 and subname='Shaman Bot';
  746. +UPDATE `creature_template` SET exp:=2, dmg_multiplier:=1.0, attackpower:=0, mindmg:=1, maxdmg:=1, minlevel:=80, maxlevel:=80, baseattacktime:=3500, rangeattacktime:=2000, minrangedmg:=0, maxrangedmg:=0, rangedattackpower:=0, dynamicflags:=0, speed_walk:=1.2, speed_run:=1.3, InhabitType:=3, health_mod:=1, mana_mod:=1, armor_mod:=1, mechanic_immune_mask:=1, flags_extra:=@FLAGS_EX, AIName='' where entry >= 60000 && entry < 60239 and subname='Warlock Bot';
  747. +UPDATE `creature_template` SET exp:=2, dmg_multiplier:=1.0, attackpower:=0, mindmg:=3, maxdmg:=5, minlevel:=80, maxlevel:=80, baseattacktime:=3400, rangeattacktime:=2000, minrangedmg:=0, maxrangedmg:=0, rangedattackpower:=0, dynamicflags:=0, speed_walk:=1.2, speed_run:=1.3, InhabitType:=3, health_mod:=1, mana_mod:=1, armor_mod:=1, mechanic_immune_mask:=1, flags_extra:=@FLAGS_EX, AIName='' where entry >= 60000 && entry < 60239 and subname='Warrior Bot';
  748. +
  749. +-- Druid -- Druid -- Druid -- Druid -- Druid -- Druid -- Druid -- Druid -- Druid -- Druid -- Druid -- Druid -- Druid -- Druid -- Druid -- Druid -- Druid -- Druid -- Druid -- Druid -- Druid -- Druid -- Druid -- Druid -- Druid -- Druid -- Druid -- Druid -- Druid -- Druid -- Druid -- Druid -- Druid -- Druid -- Druid
  750. +-- Hunter -- Hunter -- Hunter -- Hunter -- Hunter -- Hunter -- Hunter -- Hunter -- Hunter -- Hunter -- Hunter -- Hunter -- Hunter -- Hunter -- Hunter -- Hunter -- Hunter -- Hunter -- Hunter -- Hunter -- Hunter -- Hunter -- Hunter -- Hunter -- Hunter -- Hunter -- Hunter -- Hunter -- Hunter -- Hunter -- Hunter -- Hunter -- Hunter -- Hunter -- Hunter
  751. +-- Mage -- Mage -- Mage -- Mage -- Mage -- Mage -- Mage -- Mage -- Mage -- Mage -- Mage -- Mage -- Mage -- Mage -- Mage -- Mage -- Mage -- Mage -- Mage -- Mage -- Mage -- Mage -- Mage -- Mage -- Mage -- Mage -- Mage -- Mage -- Mage -- Mage -- Mage -- Mage -- Mage -- Mage -- Mage
  752. +-- Paladin -- Paladin -- Paladin -- Paladin -- Paladin -- Paladin -- Paladin -- Paladin -- Paladin -- Paladin -- Paladin -- Paladin -- Paladin -- Paladin -- Paladin -- Paladin -- Paladin -- Paladin -- Paladin -- Paladin -- Paladin -- Paladin -- Paladin -- Paladin -- Paladin -- Paladin -- Paladin -- Paladin -- Paladin -- Paladin -- Paladin -- Paladin -- Paladin -- Paladin -- Paladin
  753. +-- Priest -- Priest -- Priest -- Priest -- Priest -- Priest -- Priest -- Priest -- Priest -- Priest -- Priest -- Priest -- Priest -- Priest -- Priest -- Priest -- Priest -- Priest -- Priest -- Priest -- Priest -- Priest -- Priest -- Priest -- Priest -- Priest -- Priest -- Priest -- Priest -- Priest -- Priest -- Priest -- Priest -- Priest -- Priest
  754. +-- Rogue -- Rogue -- Rogue -- Rogue -- Rogue -- Rogue -- Rogue -- Rogue -- Rogue -- Rogue -- Rogue -- Rogue -- Rogue -- Rogue -- Rogue -- Rogue -- Rogue -- Rogue -- Rogue -- Rogue -- Rogue -- Rogue -- Rogue -- Rogue -- Rogue -- Rogue -- Rogue -- Rogue -- Rogue -- Rogue -- Rogue -- Rogue -- Rogue -- Rogue -- Rogue
  755. +-- Shaman -- Shaman -- Shaman -- Shaman -- Shaman -- Shaman -- Shaman -- Shaman -- Shaman -- Shaman -- Shaman -- Shaman -- Shaman -- Shaman -- Shaman -- Shaman -- Shaman -- Shaman -- Shaman -- Shaman -- Shaman -- Shaman -- Shaman -- Shaman -- Shaman -- Shaman -- Shaman -- Shaman -- Shaman -- Shaman -- Shaman -- Shaman -- Shaman -- Shaman -- Shaman
  756. +-- Warlock -- Warlock -- Warlock -- Warlock -- Warlock -- Warlock -- Warlock -- Warlock -- Warlock -- Warlock -- Warlock -- Warlock -- Warlock -- Warlock -- Warlock -- Warlock -- Warlock -- Warlock -- Warlock -- Warlock -- Warlock -- Warlock -- Warlock -- Warlock -- Warlock -- Warlock -- Warlock -- Warlock -- Warlock -- Warlock -- Warlock -- Warlock -- Warlock -- Warlock -- Warlock
  757. +-- Warrior -- Warrior -- Warrior -- Warrior -- Warrior -- Warrior -- Warrior -- Warrior -- Warrior -- Warrior -- Warrior -- Warrior -- Warrior -- Warrior -- Warrior -- Warrior -- Warrior -- Warrior -- Warrior -- Warrior -- Warrior -- Warrior -- Warrior -- Warrior -- Warrior -- Warrior -- Warrior -- Warrior -- Warrior -- Warrior -- Warrior -- Warrior -- Warrior -- Warrior -- Warrior
  758. +
  759. +-- pets
  760. +UPDATE `creature_template` SET exp:=2, dmg_multiplier:=1.0, attackpower:=0, mindmg:= 2, maxdmg:= 3, minlevel:=80, maxlevel:=80, baseattacktime:=2000, rangeattacktime:=2000, minrangedmg:=0, maxrangedmg:=0, rangedattackpower:=0, dynamicflags:=0, speed_walk:=1.2, speed_run:=1.3, InhabitType:=3, health_mod:=1, mana_mod:=1, armor_mod:=1, mechanic_immune_mask:=1, flags_extra:=@FLAGS_EX, AIName='' where entry between 60001 and 60239 and name='Voidwalker';
  761. +
  762. +
  763. diff --git a/sql/Bots/world_script_bot_giver.sql b/sql/Bots/world_script_bot_giver.sql
  764. new file mode 100644
  765. index 0000000..20d0ecd
  766. --- /dev/null
  767. +++ b/sql/Bots/world_script_bot_giver.sql
  768. @@ -0,0 +1,13 @@
  769. +delete from `creature_template` where entry = 60000;
  770. +
  771. +insert into `creature_template`
  772. +(`entry`, `difficulty_entry_1`, `difficulty_entry_2`, `difficulty_entry_3`, `KillCredit1`, `KillCredit2`, `modelid1`, `modelid2`, `modelid3`, `modelid4`, `name`, `subname`, `IconName`, `gossip_menu_id`,
  773. +`minlevel`, `maxlevel`, `exp`, `faction_A`, `faction_H`, `npcflag`, `speed_walk`, `speed_run`, `scale`, `rank`, `mindmg`, `maxdmg`, `dmgschool`, `attackpower`, `dmg_multiplier`, `baseattacktime`,
  774. +`rangeattacktime`, `unit_class`, `unit_flags`, `unit_flags2`, `dynamicflags`, `family`, `trainer_type`, `trainer_spell`, `trainer_class`, `trainer_race`, `minrangedmg`, `maxrangedmg`, `rangedattackpower`,
  775. +`type`, `type_flags`, `lootid`, `pickpocketloot`, `skinloot`, `resistance1`, `resistance2`, `resistance3`, `resistance4`, `resistance5`, `resistance6`, `spell1`, `spell2`, `spell3`, `spell4`, `spell5`, `spell6`,
  776. +`spell7`, `spell8`, `PetSpellDataId`, `VehicleId`, `mingold`, `maxgold`, `AIName`, `MovementType`, `InhabitType`, `HoverHeight`, `Health_mod`, `Mana_mod`, `Armor_mod`, `RacialLeader`,
  777. +`questItem1`, `questItem2`, `questItem3`, `questItem4`, `questItem5`, `questItem6`, `movementId`, `RegenHealth`, `mechanic_immune_mask`, `flags_extra`, `ScriptName`, `WDBVerified`)
  778. +values
  779. +('60000','0','0','0','0','0','27541','0','27541','0','Lagretta','Bot Officer','','0','83','83','2','35','35','1','1.4','1.14286','0.4','4','228','298','0','500','1','1500','0','1','0','2048','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','','0','3','1','4.8','1','1','0','0','0','0','0','0','0','0','1','0','0','script_bot_giver','0');
  780. +
  781. +
  782. diff --git a/src/server/game/AI/NpcBots/bot_GridNotifiers.h b/src/server/game/AI/NpcBots/bot_GridNotifiers.h
  783. new file mode 100644
  784. index 0000000..c2dda46
  785. --- /dev/null
  786. +++ b/src/server/game/AI/NpcBots/bot_GridNotifiers.h
  787. @@ -0,0 +1,462 @@
  788. +/*
  789. +Name: bot_GridNotifiers
  790. +%Complete: 91+
  791. +Comment: Custom grid notifiers for Bot system by Graff ([email protected])
  792. +Category: creature_cripts/custom/bots/grids
  793. +*/
  794. +
  795. +#ifndef _BOT_GRIDNOTIFIERS_H
  796. +#define _BOT_GRIDNOTIFIERS_H
  797. +
  798. +#include "Group.h"
  799. +#include "Player.h"
  800. +#include "SpellAuras.h"
  801. +#include "bot_ai.h"
  802. +#include "bp_ai.h"
  803. +
  804. +class NearestHostileUnitCheck
  805. +{
  806. + public:
  807. + explicit NearestHostileUnitCheck(Unit const* unit, float dist, bool magic, bot_ai const* m_ai, bool targetCCed = false) :
  808. + me(unit), m_range(dist), byspell(magic), ai(m_ai), AttackCCed(targetCCed) { }
  809. + bool operator()(Unit* u)
  810. + {
  811. + if (!me->IsWithinDistInMap(u, m_range))
  812. + return false;
  813. + if (!u->isInCombat())
  814. + return false;
  815. + if (ai ? !ai->CanBotAttack(u, byspell) : !PlayerbotAI::CanPlayerbotAttack(const_cast<Player*>(me->ToPlayer()), u, byspell))
  816. + return false;
  817. + if (ai ? ai->InDuel(u) : PlayerbotAI::IsUnitInDuel(u, const_cast<Player*>(me->ToPlayer())->GetPlayerbotAI()->GetMaster()))
  818. + return false;
  819. + if (!AttackCCed && (u->HasUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_STUNNED | UNIT_STATE_FLEEING | UNIT_STATE_DISTRACTED | UNIT_STATE_CONFUSED_MOVE | UNIT_STATE_FLEEING_MOVE)))
  820. + return false;//do not allow CCed units if checked
  821. + //if (u->HasUnitState(UNIT_STATE_CASTING) && (u->GetTypeId() == TYPEID_PLAYER || u->isPet()))
  822. + // for (uint8 i = 0; i != CURRENT_MAX_SPELL; ++i)
  823. + // if (Spell* spell = u->GetCurrentSpell(i))
  824. + // if (ai->IsInBotParty(spell->m_targets.GetUnitTarget()))
  825. + // return true;
  826. + if (!((ai ? ai->IsInBotParty(u->getVictim()) : PlayerbotAI::IsUnitInPlayersParty(u->getVictim(), const_cast<Player*>(me->ToPlayer())->GetPlayerbotAI()->GetMaster())) || (u->getThreatManager().getThreat(const_cast<Unit*>(me)) > 0.f && u->HasUnitState(UNIT_STATE_FLEEING))))
  827. + return false;
  828. +
  829. + m_range = me->GetDistance(u); // use found unit range as new range limit for next check
  830. + return true;
  831. + }
  832. + private:
  833. + Unit const* me;
  834. + float m_range;
  835. + bool byspell;
  836. + bot_ai const* ai;
  837. + bool AttackCCed;
  838. + NearestHostileUnitCheck(NearestHostileUnitCheck const&);
  839. +};
  840. +
  841. +class HostileDispelTargetCheck
  842. +{
  843. + public:
  844. + explicit HostileDispelTargetCheck(Unit const* unit, float dist = 30, bool stealable = false, bot_ai const* m_ai = NULL) :
  845. + me(unit), m_range(dist), checksteal(stealable), ai(m_ai) { }
  846. + bool operator()(Unit* u)
  847. + {
  848. + if (u->IsWithinDistInMap(me, m_range) &&
  849. + u->isAlive() &&
  850. + u->InSamePhase(me) &&
  851. + u->isInCombat() &&
  852. + u->isTargetableForAttack() &&
  853. + u->IsVisible() &&
  854. + u->GetReactionTo(me) < REP_NEUTRAL &&
  855. + ((ai ? ai->IsInBotParty(u->getVictim()) : PlayerbotAI::IsUnitInPlayersParty(u->getVictim(), const_cast<Player*>(me->ToPlayer())->GetPlayerbotAI()->GetMaster())) || u->getThreatManager().getThreat(const_cast<Unit*>(me)) > 0.f))
  856. + {
  857. + if (checksteal && u->IsImmunedToSpell(sSpellMgr->GetSpellInfo(30449))) return false;//immune to steal
  858. + if (!checksteal)
  859. + {
  860. + if (me->getLevel() >= 70 && u->IsImmunedToSpell(sSpellMgr->GetSpellInfo(32375))) return false;//immune to mass dispel
  861. + if (me->getLevel() < 70 && u->IsImmunedToSpell(sSpellMgr->GetSpellInfo(527))) return false;//immune to direct dispel
  862. + }
  863. + Unit::AuraMap const &Auras = u->GetOwnedAuras();
  864. + for (Unit::AuraMap::const_iterator itr = Auras.begin(); itr != Auras.end(); ++itr)
  865. + {
  866. + Aura* aura = itr->second;
  867. + SpellInfo const* Info = aura->GetSpellInfo();
  868. + if (Info->Dispel != DISPEL_MAGIC) continue;
  869. + if (Info->Attributes & (SPELL_ATTR0_PASSIVE | SPELL_ATTR0_HIDDEN_CLIENTSIDE)) continue;
  870. + if (checksteal && (Info->AttributesEx4 & SPELL_ATTR4_NOT_STEALABLE)) continue;
  871. + AuraApplication* aurApp = aura->GetApplicationOfTarget(u->GetGUID());
  872. + if (aurApp && aurApp->IsPositive())
  873. + {
  874. + const std::string name = Info->SpellName[0];
  875. + if (name == "Vengeance" || name == "Bloody Vengeance")
  876. + continue;
  877. + return true;
  878. + }
  879. + }
  880. + }
  881. + return false;
  882. + }
  883. + private:
  884. + Unit const* me;
  885. + float m_range;
  886. + bool checksteal;
  887. + bot_ai const* ai;
  888. + HostileDispelTargetCheck(HostileDispelTargetCheck const&);
  889. +};
  890. +
  891. +class AffectedTargetCheck
  892. +{
  893. + public:
  894. + explicit AffectedTargetCheck(uint64 casterguid, float dist, uint32 spellId, Player const* groupCheck = 0, uint8 hostileCheckType = 0) :
  895. + caster(casterguid), m_range(dist), spell(spellId), checker(groupCheck), needhostile(hostileCheckType)
  896. + { if (checker->GetTypeId() != TYPEID_PLAYER) return; gr = checker->GetGroup(); }
  897. + bool operator()(Unit* u)
  898. + {
  899. + if (caster && u->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE))
  900. + return false;
  901. + if (needhostile == 0 && !u->IsHostileTo(checker)) return false;
  902. + if (needhostile == 1 && !(gr && gr->IsMember(u->GetGUID()) && u->GetTypeId() == TYPEID_PLAYER)) return false;
  903. + if (needhostile == 2 && !(gr && gr->IsMember(u->GetGUID()))) return false;
  904. + if (needhostile == 3 && !u->IsFriendlyTo(checker)) return false;
  905. +
  906. + if (u->isAlive() && checker->IsWithinDistInMap(u, m_range))
  907. + {
  908. + Unit::AuraMap const &Auras = u->GetOwnedAuras();
  909. + for (Unit::AuraMap::const_iterator itr = Auras.begin(); itr != Auras.end(); ++itr)
  910. + {
  911. + Aura* aura = itr->second;
  912. + if (aura->GetId() == spell)
  913. + if (caster == 0 || aura->GetCasterGUID() == caster)
  914. + return true;
  915. + }
  916. + }
  917. + return false;
  918. + }
  919. + private:
  920. + uint64 const caster;
  921. + float m_range;
  922. + uint32 const spell;
  923. + Player const* checker;
  924. + uint8 needhostile;
  925. + Group const* gr;
  926. + AffectedTargetCheck(AffectedTargetCheck const&);
  927. +};
  928. +
  929. +class PolyUnitCheck
  930. +{
  931. + public:
  932. + explicit PolyUnitCheck(Unit const* unit, float dist, Unit const* currTarget) : me(unit), m_range(dist), mytar(currTarget) {}
  933. + bool operator()(Unit* u)
  934. + {
  935. + if (u == mytar)
  936. + return false;
  937. + if (!me->IsWithinDistInMap(u, m_range))
  938. + return false;
  939. + if (!u->isInCombat() || !u->isAlive() || !u->getVictim())
  940. + return false;
  941. + if (u->GetCreatureType() != CREATURE_TYPE_HUMANOID &&
  942. + u->GetCreatureType() != CREATURE_TYPE_BEAST)
  943. + return false;
  944. + if (me->GetDistance(u) < 6 || mytar->GetDistance(u) < 5 || u->GetHealthPct() < 70)
  945. + return false;
  946. + if (!u->InSamePhase(me))
  947. + return false;
  948. + if (!u->isTargetableForAttack())
  949. + return false;
  950. + if (!u->IsVisible())
  951. + return false;
  952. + if (!u->getAttackers().empty())
  953. + return false;
  954. + if (!u->IsHostileTo(me))
  955. + return false;
  956. + if (u->IsPolymorphed() ||
  957. + u->isFrozen() ||
  958. + u->isInRoots() ||
  959. + u->HasAura(51514)/*hex*/ ||
  960. + u->HasAura(20066)/*repentance*/ ||
  961. + //u->HasAuraTypeWithAffectMask(SPELL_AURA_PERIODIC_DAMAGE, sSpellMgr->GetSpellInfo(339)) || //entangling roots
  962. + //u->HasAuraTypeWithAffectMask(SPELL_AURA_PERIODIC_DAMAGE, sSpellMgr->GetSpellInfo(16914)) || //hurricane
  963. + //u->HasAuraTypeWithAffectMask(SPELL_AURA_PERIODIC_DAMAGE, sSpellMgr->GetSpellInfo(10)) || //blizzard
  964. + //u->HasAuraTypeWithAffectMask(SPELL_AURA_PERIODIC_DAMAGE, sSpellMgr->GetSpellInfo(2121)) || //flamestrike
  965. + //u->HasAuraTypeWithAffectMask(SPELL_AURA_PERIODIC_DAMAGE, sSpellMgr->GetSpellInfo(20116)) || //consecration
  966. + u->HasAuraType(SPELL_AURA_PERIODIC_DAMAGE))
  967. + return false;
  968. + if (!u->IsImmunedToSpell(sSpellMgr->GetSpellInfo(12826)))//Polymorph rank 4
  969. + return true;
  970. +
  971. + return false;
  972. + }
  973. + private:
  974. + Unit const* me;
  975. + float m_range;
  976. + Unit const* mytar;
  977. + PolyUnitCheck(PolyUnitCheck const&);
  978. +};
  979. +
  980. +class FearUnitCheck
  981. +{
  982. + public:
  983. + explicit FearUnitCheck(Unit const* unit, float dist = 30) : me(unit), m_range(dist) {}
  984. + bool operator()(Unit* u)
  985. + {
  986. + if (!me->IsWithinDistInMap(u, m_range))
  987. + return false;
  988. + if (!u->InSamePhase(me))
  989. + return false;
  990. + if (!u->isInCombat())
  991. + return false;
  992. + if (u->GetCreatureType() == CREATURE_TYPE_UNDEAD)
  993. + return false;
  994. + if (!u->isAlive())
  995. + return false;
  996. + if (!u->isTargetableForAttack())
  997. + return false;
  998. + if (!u->IsVisible())
  999. + return false;
  1000. + if (u->getAttackers().size() > 1 && u->getVictim() != me)
  1001. + return false;
  1002. + if (u->HasUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_STUNNED | UNIT_STATE_FLEEING | UNIT_STATE_DISTRACTED | UNIT_STATE_CONFUSED_MOVE | UNIT_STATE_FLEEING_MOVE))
  1003. + return false;
  1004. + if (u->isFeared())
  1005. + return false;
  1006. + if (u->GetReactionTo(me) > REP_NEUTRAL)
  1007. + return false;
  1008. +
  1009. + if (!u->IsImmunedToSpell(sSpellMgr->GetSpellInfo(5782)))//fear rank1
  1010. + return true;
  1011. +
  1012. + return false;
  1013. + }
  1014. + private:
  1015. + Unit const* me;
  1016. + float m_range;
  1017. + FearUnitCheck(FearUnitCheck const&);
  1018. +};
  1019. +
  1020. +class StunUnitCheck
  1021. +{
  1022. + public:
  1023. + explicit StunUnitCheck(Unit const* unit, float dist = 20) : me(unit), m_range(dist) {}
  1024. + bool operator()(Unit* u)
  1025. + {
  1026. + if (!me->IsWithinDistInMap(u, m_range))
  1027. + return false;
  1028. + if (!u->isInCombat())
  1029. + return false;
  1030. + if (me->getVictim() == u)
  1031. + return false;
  1032. + if (me->GetTypeId() == TYPEID_UNIT)
  1033. + if (Player* mymaster = me->ToCreature()->GetBotOwner())
  1034. + if (mymaster->getVictim() == u)
  1035. + return false;
  1036. + if (!u->InSamePhase(me))
  1037. + return false;
  1038. + if (u->GetReactionTo(me) > REP_NEUTRAL)
  1039. + return false;
  1040. + if (!u->isAlive())
  1041. + return false;
  1042. + if (!u->IsVisible())
  1043. + return false;
  1044. + if (!u->isTargetableForAttack())
  1045. + return false;
  1046. + if (!u->getAttackers().empty())
  1047. + return false;
  1048. + if (u->HasAuraType(SPELL_AURA_PERIODIC_DAMAGE))
  1049. + return false;
  1050. + if (!(u->GetCreatureType() == CREATURE_TYPE_HUMANOID ||
  1051. + u->GetCreatureType() == CREATURE_TYPE_DEMON ||
  1052. + u->GetCreatureType() == CREATURE_TYPE_DRAGONKIN ||
  1053. + u->GetCreatureType() == CREATURE_TYPE_GIANT ||
  1054. + u->GetCreatureType() == CREATURE_TYPE_UNDEAD))
  1055. + return false;
  1056. + if (me->GetDistance(u) < 10)//do not allow close cast to prevent break due to consecration
  1057. + return false;
  1058. + if (u->IsPolymorphed() ||
  1059. + u->HasAura(51514)/*hex*/ ||
  1060. + u->HasAura(20066)/*repentance*/ ||
  1061. + u->HasAuraWithMechanic(1<<MECHANIC_SHACKLE)/*shackleundead*/)
  1062. + return false;
  1063. + if (!u->IsImmunedToSpell(sSpellMgr->GetSpellInfo(20066)))//repentance
  1064. + return true;
  1065. +
  1066. + return false;
  1067. + }
  1068. + private:
  1069. + Unit const* me;
  1070. + float m_range;
  1071. + StunUnitCheck(StunUnitCheck const&);
  1072. +};
  1073. +
  1074. +class UndeadCCUnitCheck
  1075. +{
  1076. + public:
  1077. + explicit UndeadCCUnitCheck(Unit const* unit, float dist = 30, uint32 spell = 0) : me(unit), m_range(dist), m_spellId(spell) { if (!spell) return; }
  1078. + bool operator()(Unit* u)
  1079. + {
  1080. + if (!me->IsWithinDistInMap(u, m_range))
  1081. + return false;
  1082. + if (!u->InSamePhase(me))
  1083. + return false;
  1084. + if (!u->isInCombat())
  1085. + return false;
  1086. + if (u->GetReactionTo(me) > REP_NEUTRAL)
  1087. + return false;
  1088. + if (!u->isAlive())
  1089. + return false;
  1090. + if (!u->isTargetableForAttack())
  1091. + return false;
  1092. + if (!u->IsVisible())
  1093. + return false;
  1094. + if (me->getVictim() == u && u->getVictim() == me)
  1095. + return false;
  1096. + if (!u->getAttackers().empty())
  1097. + return false;
  1098. + if (u->GetCreatureType() != CREATURE_TYPE_UNDEAD &&
  1099. + (m_spellId == 9484 || m_spellId == 9485 || m_spellId == 10955))//shackle undead
  1100. + return false;
  1101. + //most horrible hacks
  1102. + if (u->GetCreatureType() != CREATURE_TYPE_UNDEAD &&
  1103. + u->GetCreatureType() != CREATURE_TYPE_DEMON &&
  1104. + (m_spellId == 2812 || m_spellId == 10318 || //holy
  1105. + m_spellId == 27139 || m_spellId == 48816 || //wra
  1106. + m_spellId == 48817 || //th or
  1107. + m_spellId == 10326)) //turn evil
  1108. + return false;
  1109. + if (u->HasUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_STUNNED | UNIT_STATE_FLEEING | UNIT_STATE_DISTRACTED | UNIT_STATE_CONFUSED_MOVE | UNIT_STATE_FLEEING_MOVE))
  1110. + return false;
  1111. + if (u->HasAuraType(SPELL_AURA_PERIODIC_DAMAGE) &&
  1112. + (m_spellId == 9484 || m_spellId == 9485 || m_spellId == 10955))//shackle undead
  1113. + return false;
  1114. + if (!u->IsImmunedToSpell(sSpellMgr->GetSpellInfo(m_spellId)))
  1115. + return true;
  1116. +
  1117. + return false;
  1118. + }
  1119. + private:
  1120. + Unit const* me;
  1121. + float m_range;
  1122. + uint32 m_spellId;
  1123. + UndeadCCUnitCheck(UndeadCCUnitCheck const&);
  1124. +};
  1125. +
  1126. +class RootUnitCheck
  1127. +{
  1128. + public:
  1129. + explicit RootUnitCheck(Unit const* unit, Unit const* mytarget, float dist = 30, uint32 spell = 0) : me(unit), curtar(mytarget), m_range(dist), m_spellId(spell)
  1130. + { if (!spell) return; }
  1131. + bool operator()(Unit* u)
  1132. + {
  1133. + if (u == curtar)
  1134. + return false;
  1135. + if (!me->IsWithinDistInMap(u, m_range))
  1136. + return false;
  1137. + if (!u->isAlive())
  1138. + return false;
  1139. + if (!u->isInCombat())
  1140. + return false;
  1141. + if (me->GetDistance(u) < 8)
  1142. + return false;
  1143. + if (!u->InSamePhase(me))
  1144. + return false;
  1145. + if (!u->IsVisible())
  1146. + return false;
  1147. + if (!u->isTargetableForAttack())
  1148. + return false;
  1149. + if (u->GetReactionTo(me) > REP_NEUTRAL)
  1150. + return false;
  1151. + if (u->isFrozen() || u->isInRoots())
  1152. + return false;
  1153. + if (!u->getAttackers().empty())
  1154. + return false;
  1155. + if (u->IsPolymorphed() ||
  1156. + u->HasAura(51514)/*hex*/ ||
  1157. + u->HasAura(20066)/*repentance*/ ||
  1158. + u->HasAuraWithMechanic(1<<MECHANIC_SHACKLE)/*shackleundead*/)
  1159. + return false;
  1160. + if (!u->IsImmunedToSpell(sSpellMgr->GetSpellInfo(m_spellId)))
  1161. + return true;
  1162. +
  1163. + return false;
  1164. + }
  1165. + private:
  1166. + Unit const* me;
  1167. + Unit const* curtar;
  1168. + float m_range;
  1169. + uint32 m_spellId;
  1170. + RootUnitCheck(RootUnitCheck const&);
  1171. +};
  1172. +
  1173. +class CastingUnitCheck
  1174. +{
  1175. + public:
  1176. + explicit CastingUnitCheck(Unit const* unit, float dist = 30, bool friendly = false, uint32 spell = 0) : me(unit), m_range(dist), m_friend(friendly), m_spell(spell) { if (!m_spell) return; }
  1177. + bool operator()(Unit* u)
  1178. + {
  1179. + if (!me->IsWithinDistInMap(u, m_range))
  1180. + return false;
  1181. + if (!u->isAlive())
  1182. + return false;
  1183. + if (!u->InSamePhase(me))
  1184. + return false;
  1185. + if (!u->IsVisible())
  1186. + return false;
  1187. + if (!m_friend && !u->isTargetableForAttack())
  1188. + return false;
  1189. + //if (!m_friend && u->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED))//prevent double silence
  1190. + // return false;
  1191. + if (!u->IsNonMeleeSpellCasted(false))
  1192. + return false;
  1193. + if (m_friend == (u->GetReactionTo(me) < REP_FRIENDLY))
  1194. + return false;
  1195. + if (m_spell == 10326 && //turn evil
  1196. + u->GetCreatureType() != CREATURE_TYPE_UNDEAD &&
  1197. + u->GetCreatureType() != CREATURE_TYPE_DEMON)
  1198. + return false;
  1199. + if (m_spell == 20066 && //repentance
  1200. + !(u->GetCreatureType() == CREATURE_TYPE_HUMANOID ||
  1201. + u->GetCreatureType() == CREATURE_TYPE_DEMON ||
  1202. + u->GetCreatureType() == CREATURE_TYPE_DRAGONKIN ||
  1203. + u->GetCreatureType() == CREATURE_TYPE_GIANT ||
  1204. + u->GetCreatureType() == CREATURE_TYPE_UNDEAD))
  1205. + return false;
  1206. + if (!m_spell || !u->IsImmunedToSpell(sSpellMgr->GetSpellInfo(m_spell)))
  1207. + return true;
  1208. +
  1209. + return false;
  1210. + }
  1211. + private:
  1212. + Unit const* me;
  1213. + float m_range;
  1214. + bool m_friend;
  1215. + uint32 m_spell;
  1216. + CastingUnitCheck(CastingUnitCheck const&);
  1217. +};
  1218. +
  1219. +class SecondEnemyCheck
  1220. +{
  1221. + public:
  1222. + explicit SecondEnemyCheck(Unit const* unit, float dist, Unit const* currtarget, bot_ai const* m_ai) : me(unit), m_range(dist), mytar(currtarget), ai(m_ai) {}
  1223. + bool operator()(Unit* u)
  1224. + {
  1225. + if (u == mytar)
  1226. + return false;//We need to find SECONDARY target
  1227. + if (!u->isInCombat())
  1228. + return false;
  1229. + if (u->isMoving() != mytar->isMoving())//only when both targets idle or both moving
  1230. + return false;
  1231. + if (!me->IsWithinDistInMap(u, m_range + 1.f))//distance check
  1232. + return false;
  1233. + if (mytar->GetDistance(u) > 4)//not close enough to each other
  1234. + return false;
  1235. +
  1236. + if (ai ? ai->CanBotAttack(u) : PlayerbotAI::CanPlayerbotAttack(const_cast<Player*>(me->ToPlayer()), u))
  1237. + return true;
  1238. +
  1239. + return false;
  1240. + }
  1241. + private:
  1242. + Unit const* me;
  1243. + float m_range;
  1244. + Unit const* mytar;
  1245. + bot_ai const* ai;
  1246. + SecondEnemyCheck(SecondEnemyCheck const&);
  1247. +};
  1248. +
  1249. +#endif
  1250. diff --git a/src/server/game/AI/NpcBots/bot_ai.cpp b/src/server/game/AI/NpcBots/bot_ai.cpp
  1251. new file mode 100644
  1252. index 0000000..72c4c73
  1253. --- /dev/null
  1254. +++ b/src/server/game/AI/NpcBots/bot_ai.cpp
  1255. @@ -0,0 +1,3185 @@
  1256. +/*
  1257. +NpcBot System by Graff ([email protected])
  1258. +Original patch (npcbot part only) from: LordPsyan https://bitbucket.org/lordpsyan/trinitycore-patches/src/3b8b9072280e/Individual/11185-BOTS-NPCBots.patch
  1259. +TODO:
  1260. +Convert doCast events (CD etc.) into SpellHit()- and SpellHitTarget()-based
  1261. +Implement heal/tank/DD modes
  1262. +Implement Racial Abilities
  1263. +Implement Equipment Change (maybe)
  1264. +I NEED MORE
  1265. +*/
  1266. +
  1267. +#include "bot_ai.h"
  1268. +#include "bot_GridNotifiers.h"
  1269. +#include "CellImpl.h"
  1270. +#include "Chat.h"
  1271. +#include "Config.h"
  1272. +#include "GridNotifiers.h"
  1273. +#include "GridNotifiersImpl.h"
  1274. +#include "ScriptedGossip.h"
  1275. +#include "SpellAuraEffects.h"
  1276. +
  1277. +const uint8 GroupIconsFlags[TARGETICONCOUNT] =
  1278. +{
  1279. + /*STAR = */0x001,
  1280. + /*CIRCLE = */0x002,
  1281. + /*DIAMOND = */0x004,
  1282. + /*TRIANGLE = */0x008,
  1283. + /*MOON = */0x010,
  1284. + /*SQUARE = */0x020,
  1285. + /*CROSS = */0x040,
  1286. + /*SKULL = */0x080
  1287. +};
  1288. +
  1289. +bot_minion_ai::bot_minion_ai(Creature* creature): bot_ai(creature)
  1290. +{
  1291. + Potion_cd = 0;
  1292. + pvpTrinket_cd = 30000;
  1293. + rezz_cd = 0;
  1294. + evade_cd = 0;
  1295. + myangle = 0.f;
  1296. + classinfo = new PlayerClassLevelInfo;
  1297. +}
  1298. +bot_minion_ai::~bot_minion_ai()
  1299. +{
  1300. + delete classinfo;
  1301. +}
  1302. +
  1303. +bot_pet_ai::bot_pet_ai(Creature* creature): bot_ai(creature)
  1304. +{
  1305. + m_creatureOwner = me->GetCreatureOwner();
  1306. + basearmor = 0;
  1307. +}
  1308. +bot_pet_ai::~bot_pet_ai(){}
  1309. +
  1310. +bot_ai::bot_ai(Creature* creature) : ScriptedAI(creature)
  1311. +{
  1312. + master = me->GetBotOwner();
  1313. + m_spellpower = 0;
  1314. + haste = 0;
  1315. + hit = 0.f;
  1316. + regen_mp5 = 0.f;
  1317. + m_TankGuid = 0;
  1318. + tank = NULL;
  1319. + extank = NULL;
  1320. + info = NULL;
  1321. + clear_cd = 2;
  1322. + temptimer = 0;
  1323. + wait = 15;
  1324. + GC_Timer = 0;
  1325. + checkAurasTimer = 20;
  1326. + cost = 0;
  1327. + doHealth = false;
  1328. + doMana = false;
  1329. + //shouldUpdateStats = true;
  1330. + pos.m_positionX = 0.f;
  1331. + pos.m_positionY = 0.f;
  1332. + pos.m_positionZ = 0.f;
  1333. + aftercastTargetGuid = 0;
  1334. + currentSpell = 0;
  1335. + dmgmult_melee = ConfigMgr::GetFloatDefault("Bot.DamageMult.Melee", 1.0);
  1336. + dmgmult_spell = ConfigMgr::GetFloatDefault("Bot.DamageMult.Spell", 1.0);
  1337. + dmgmult_melee = std::max(dmgmult_melee, 0.01f);
  1338. + dmgmult_spell = std::max(dmgmult_spell, 0.01f);
  1339. + dmgmult_melee = std::min(dmgmult_melee, 10.f);
  1340. + dmgmult_spell = std::min(dmgmult_spell, 10.f);
  1341. + dmgmod_melee = Creature::_GetDamageMod(me->GetCreatureTemplate()->rank);
  1342. + dmgmod_spell = me->GetSpellDamageMod(me->GetCreatureTemplate()->rank);
  1343. + healTargetIconFlags = ConfigMgr::GetIntDefault("Bot.HealTargetIconsMask", 8);
  1344. +}
  1345. +bot_ai::~bot_ai(){}
  1346. +
  1347. +SpellCastResult bot_ai::checkBotCast(Unit* victim, uint32 spellId, uint8 botclass) const
  1348. +{
  1349. + if (spellId == 0) return SPELL_FAILED_DONT_REPORT;
  1350. + if (!CheckImmunities(spellId, victim)) return SPELL_FAILED_DONT_REPORT;
  1351. + if (InDuel(victim)) return SPELL_FAILED_DONT_REPORT;
  1352. +
  1353. + switch (botclass)
  1354. + {
  1355. + case CLASS_MAGE:
  1356. + case CLASS_PRIEST:
  1357. + case CLASS_DRUID:
  1358. + case CLASS_WARLOCK:
  1359. + case CLASS_SHAMAN:
  1360. + if (Feasting() && !master->isInCombat() && !master->HasAuraType(SPELL_AURA_PERIODIC_DAMAGE))
  1361. + return SPELL_FAILED_DONT_REPORT;
  1362. + return SPELL_CAST_OK;
  1363. + case CLASS_PALADIN:
  1364. + //Crusader Strike
  1365. + if (spellId != 35395 && spellId != MANAPOTION && spellId != HEALINGPOTION && me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED))
  1366. + return SPELL_FAILED_DONT_REPORT;
  1367. + return SPELL_CAST_OK;
  1368. + case CLASS_WARRIOR:
  1369. + //BladeStorm
  1370. + if (me->HasAura(46924/*67541*/))
  1371. + return SPELL_FAILED_DONT_REPORT;
  1372. + return SPELL_CAST_OK;
  1373. + case CLASS_ROGUE:
  1374. + case CLASS_HUNTER:
  1375. + case CLASS_DEATH_KNIGHT:
  1376. + default:
  1377. + return SPELL_CAST_OK;
  1378. + }
  1379. +}
  1380. +
  1381. +bool bot_ai::doCast(Unit* victim, uint32 spellId, bool triggered, uint64 originalCaster)
  1382. +{
  1383. + if (spellId == 0) return false;
  1384. + if (me->IsMounted()) return false;
  1385. + if (IsCasting()) return false;
  1386. + if (!victim || !victim->IsInWorld() || me->GetMap() != victim->FindMap()) return false;
  1387. +
  1388. + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
  1389. + if (!spellInfo)
  1390. + return false;
  1391. + info = spellInfo;
  1392. +
  1393. + if (spellId == MANAPOTION)
  1394. + {
  1395. + value = urand(me->GetMaxPower(POWER_MANA)/4, me->GetMaxPower(POWER_MANA)/2);
  1396. + me->CastCustomSpell(victim, spellId, &value, 0, 0, true);
  1397. + return true;
  1398. + }
  1399. +
  1400. + if (me->GetShapeshiftForm() != FORM_NONE && info->CheckShapeshift(me->GetShapeshiftForm()) != SPELL_CAST_OK)
  1401. + removeFeralForm(true, true);
  1402. +
  1403. + if (spellId != HEALINGPOTION && spellId != MANAPOTION)
  1404. + me->SetStandState(UNIT_STAND_STATE_STAND);
  1405. +
  1406. + if (!victim->IsWithinLOSInMap(me) && IsInBotParty(victim))
  1407. + {
  1408. + //std::ostringstream msg;
  1409. + //msg << "casting " << spellInfo->SpellName[0] << " on " << victim->GetName();
  1410. + //me->MonsterWhisper(msg.str().c_str(), master->GetGUID());
  1411. + me->Relocate(victim);
  1412. + }
  1413. +
  1414. + TriggerCastFlags flags = triggered ? TRIGGERED_FULL_MASK : TRIGGERED_NONE;
  1415. + SpellCastTargets targets;
  1416. + targets.SetUnitTarget(victim);
  1417. + Spell* spell = new Spell(me, info, flags, originalCaster);
  1418. + spell->prepare(&targets);//sets current spell if succeed
  1419. +
  1420. + bool casted = triggered;//triggered casts are casted immediately
  1421. + for (uint8 i = 0; i != CURRENT_MAX_SPELL; ++i)
  1422. + if (Spell* curspell = me->GetCurrentSpell(i))
  1423. + if (curspell == spell)
  1424. + casted = true;
  1425. +
  1426. + if (!casted)
  1427. + {
  1428. + //failed to cast
  1429. + //delete spell;//crash due to invalid event added to master's eventmap
  1430. + return false;
  1431. + }
  1432. +
  1433. + currentSpell = spellId;
  1434. +
  1435. + switch (me->GetBotClass())
  1436. + {
  1437. + case CLASS_ROGUE:
  1438. + case CAT:
  1439. + value = int32(1000.f - 1000.f*(float(haste) / 100.f));
  1440. + break;
  1441. + default:
  1442. + value = int32(1500.f - 1500.f*(float(haste) / 100.f));
  1443. + break;
  1444. + }
  1445. + GC_Timer = std::max<uint32>(value, 500);
  1446. +
  1447. + return true;
  1448. +}
  1449. +//Follow point calculation
  1450. +void bot_minion_ai::CalculatePos(Position & pos)
  1451. +{
  1452. + uint8 followdist = master->GetBotFollowDist();
  1453. + Unit* followTarget = master;
  1454. + float mydist, angle;
  1455. + if (master->GetBotTankGuid() == me->GetGUID())
  1456. + {
  1457. + mydist = frand(3.5f, 6.5f);
  1458. + angle = (M_PI/2.f) / 16.f * frand(-3.f, 3.f);
  1459. + }
  1460. + else
  1461. + {
  1462. + switch (me->GetBotClass())
  1463. + {
  1464. + case CLASS_WARRIOR: case CLASS_DEATH_KNIGHT: case CLASS_PALADIN: case BEAR:
  1465. + mydist = frand(0.2f, 1.f);//(1.f, 3.f);//RAND(1.f,1.5f,2.f,2.5f,3.f,3.5f);
  1466. + angle = (M_PI/2.f) / 8.f * RAND(frand(5.f, 10.f), frand(-10.f, -5.f));//RAND(6.5f,7.f,7.5f,8.f,8.5f,9.f,9.5f,-6.5f,-7.f,-7.5f,-8.f,-8.5f,-9.f,-9.5f);
  1467. + break;
  1468. + case CLASS_WARLOCK: case CLASS_PRIEST: case CLASS_MAGE: case CAT:
  1469. + mydist = frand(0.15f, 0.8f);//(0.5f, 2.f);//RAND(0.5f,1.f,1.5f,2.f);
  1470. + angle = (M_PI/2.f) / 6.f * frand(10.5f, 13.5f);//RAND(10.5f,11.f,11.5f,12.f,12.5f,13.f,13.5f);
  1471. + break;
  1472. + default:
  1473. + mydist = frand(0.3f, 1.2f);//(2.5f, 4.f);//RAND(2.5f,3.f,3.5f,4.f);
  1474. + angle = (M_PI/2.f) / 6.f * frand(9.f, 15.f);//RAND(9.f,10.f,11.f,12.f,13.f,14.f,15.f);
  1475. + break;
  1476. + }
  1477. + }
  1478. + if (abs(abs(myangle) - abs(angle)) > M_PI/3.f)
  1479. + myangle = angle;
  1480. + else
  1481. + angle = myangle;
  1482. + mydist += followdist > 10 ? float(followdist - 10)/4.f : 0.f;
  1483. + mydist = std::min<float>(mydist, 35.f);
  1484. + angle += followTarget->GetOrientation();
  1485. + float x(0),y(0),z(0);
  1486. + float size = me->GetObjectSize()/3.f;
  1487. + bool over = false;
  1488. + for (uint8 i = 0; i != 5 + over; ++i)
  1489. + {
  1490. + if (over)
  1491. + {
  1492. + mydist *= 0.2f;
  1493. + break;
  1494. + }
  1495. + followTarget->GetNearPoint(me, x, y, z, size, mydist, angle);
  1496. + if (!master->IsWithinLOS(x,y,z))
  1497. + {
  1498. + mydist *= 0.4f - float(i*0.07f);
  1499. + size *= 0.1f;
  1500. + if (size < 0.1)
  1501. + size = 0.f;
  1502. + if (size == 0.f && me->GetPositionZ() < followTarget->GetPositionZ())
  1503. + z += 0.25f;
  1504. + }
  1505. + else
  1506. + over = true;
  1507. + }
  1508. + pos.m_positionX = x;
  1509. + pos.m_positionY = y;
  1510. + pos.m_positionZ = z;
  1511. +
  1512. + // T
  1513. + // TTT
  1514. + // mmmmmmmm mmmmmmmm
  1515. + // mmmmmmm MMM mmmmmmm
  1516. + // mmmmm rrrrrrr mmmmm
  1517. + // ddd rrrrrrrrr ddd
  1518. + // ddddddddddddddd
  1519. + // ddddddddddd
  1520. + //
  1521. + //MMM - player
  1522. + //TTT - bot tank
  1523. + //m - melee (warrior, paladin, deathknight)
  1524. + //d - default (druid, shaman, rogue, hunter)
  1525. + //r - ranged/support (priest, warlock, mage)
  1526. +}
  1527. +// Movement set
  1528. +void bot_minion_ai::SetBotCommandState(CommandStates st, bool force, Position* newpos)
  1529. +{
  1530. + if (me->isDead() || IAmDead())
  1531. + return;
  1532. + if (st == COMMAND_FOLLOW && ((!me->isMoving() && !IsCasting() && master->isAlive()) || force))
  1533. + {
  1534. + if (CCed(me, true)/* || master->HasUnitState(UNIT_STATE_FLEEING)*/) return;
  1535. + if (!newpos)
  1536. + CalculatePos(pos);
  1537. + else
  1538. + {
  1539. + pos.m_positionX = newpos->m_positionX;
  1540. + pos.m_positionY = newpos->m_positionY;
  1541. + pos.m_positionZ = newpos->m_positionZ;
  1542. + }
  1543. + if (me->getStandState() == UNIT_STAND_STATE_SIT && !Feasting())
  1544. + me->SetStandState(UNIT_STAND_STATE_STAND);
  1545. + me->GetMotionMaster()->MovePoint(master->GetMapId(), pos);
  1546. + //me->GetMotionMaster()->MoveFollow(master, mydist, angle);
  1547. + }
  1548. + else if (st == COMMAND_STAY)
  1549. + {
  1550. + me->StopMoving();
  1551. + me->GetMotionMaster()->Clear();
  1552. + me->GetMotionMaster()->MoveIdle();
  1553. + }
  1554. + else if (st == COMMAND_ATTACK)
  1555. + { }
  1556. + m_botCommandState = st;
  1557. + if (Creature* m_botsPet = me->GetBotsPet())
  1558. + m_botsPet->SetBotCommandState(st, force);
  1559. +}
  1560. +
  1561. +void bot_pet_ai::SetBotCommandState(CommandStates st, bool force, Position* /*newpos*/)
  1562. +{
  1563. + if (me->isDead() || IAmDead())
  1564. + return;
  1565. + if (st == COMMAND_FOLLOW && ((!me->isMoving() && !IsCasting() && master->isAlive()) || force))
  1566. + {
  1567. + if (CCed(me, true)) return;
  1568. + Unit* followtarget = m_creatureOwner;
  1569. + if (CCed(m_creatureOwner))
  1570. + followtarget = master;
  1571. + if (followtarget == m_creatureOwner)
  1572. + {
  1573. + if (!me->HasUnitState(UNIT_STATE_FOLLOW) || me->GetDistance(master)*0.75f < me->GetDistance(m_creatureOwner))
  1574. + me->GetMotionMaster()->MoveFollow(m_creatureOwner, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE);
  1575. + }
  1576. + else
  1577. + if (!me->HasUnitState(UNIT_STATE_FOLLOW) || me->GetDistance(m_creatureOwner)*0.75f < me->GetDistance(master))
  1578. + me->GetMotionMaster()->MoveFollow(master, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE);
  1579. + }
  1580. + else if (st == COMMAND_STAY)//NUY
  1581. + {
  1582. + me->StopMoving();
  1583. + me->GetMotionMaster()->Clear();
  1584. + me->GetMotionMaster()->MoveIdle();
  1585. + }
  1586. + else if (st == COMMAND_ATTACK)
  1587. + { }
  1588. + m_botCommandState = st;
  1589. +}
  1590. +// Get Maintank
  1591. +void bot_ai::FindTank()
  1592. +{
  1593. + if (tank == me)
  1594. + extank = me;
  1595. + //check group flags in DB
  1596. + tank = _GetBotGroupMainTank(master->GetGroup());
  1597. + //check if master has set tank
  1598. + if (!tank)
  1599. + tank = master->GetBotTankGuid() != 0 ? sObjectAccessor->GetObjectInWorld(master->GetBotTankGuid(), (Unit*)NULL) : NULL;
  1600. + //check if we have tank flag in master's motmap
  1601. + if (!tank)
  1602. + tank = master->GetBotTank(me->GetEntry());
  1603. + //at last try to find tank by class if master is too lazy to set it
  1604. + if (!tank)
  1605. + {
  1606. + Player* owner = master;
  1607. + uint8 Class = owner->getClass();
  1608. + if (owner->isAlive() &&
  1609. + (Class == CLASS_WARRIOR || Class == CLASS_PALADIN || Class == CLASS_DEATH_KNIGHT))
  1610. + tank = owner;
  1611. + else if (owner != master && master->isAlive())
  1612. + {
  1613. + Class = master->getClass();
  1614. + if (Class == CLASS_WARRIOR || Class == CLASS_PALADIN || Class == CLASS_DEATH_KNIGHT)
  1615. + tank = master;
  1616. + }
  1617. + }
  1618. + //it happens to every bot so they all will know who the tank is
  1619. + if (tank != extank)
  1620. + me->SetBotTank(tank);
  1621. + if (tank == me)
  1622. + {
  1623. + //if tank set by entry let master get right guid and set tank in botmap
  1624. + if (master->GetBotTankGuid() != me->GetGUID())
  1625. + master->SetBotTank(me->GetGUID());
  1626. + }
  1627. +}
  1628. +//Get Group maintank
  1629. +Unit* bot_ai::_GetBotGroupMainTank(Group* group)
  1630. +{
  1631. + if (!group)
  1632. + return NULL;
  1633. +
  1634. + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_MAINTANK);
  1635. + stmt->setUInt32(0, group->GetGUID());
  1636. + PreparedQueryResult result = CharacterDatabase.Query(stmt);
  1637. + //QueryResult result = CharacterDatabase.PQuery("SELECT memberGuid, memberFlags FROM `group_member` WHERE `guid`='%u'", group->GetGUID());
  1638. + if (!result)
  1639. + return NULL;
  1640. + Unit* unit = NULL;
  1641. + do
  1642. + {
  1643. + Field* field = result->Fetch();
  1644. + uint32 lowGuid = field[0].GetInt32();
  1645. + uint8 flags = field[1].GetInt8();
  1646. + if (flags & MEMBER_FLAG_MAINTANK)
  1647. + {
  1648. + Group::MemberSlotList const &members = group->GetMemberSlots();
  1649. + for (Group::MemberSlotList::const_iterator itr = members.begin(); itr != members.end(); ++itr)
  1650. + if (GUID_LOPART(itr->guid) == lowGuid)
  1651. + unit = ObjectAccessor::FindUnit(itr->guid);
  1652. + }
  1653. + } while (result->NextRow() && !unit);
  1654. + return unit;
  1655. +}
  1656. +// Buffs And Heal (really)
  1657. +void bot_minion_ai::BuffAndHealGroup(Player* gPlayer, uint32 diff)
  1658. +{
  1659. + if (GC_Timer > diff) return;
  1660. + if (me->IsMounted()) return;
  1661. + if (IsCasting() || Feasting()) return; // if I'm already casting
  1662. +
  1663. + Group* pGroup = gPlayer->GetGroup();
  1664. + if (!pGroup)
  1665. + {
  1666. + if (!master->IsInWorld() || master->IsBeingTeleported())
  1667. + return;
  1668. + if (HealTarget(master, GetHealthPCT(master), diff))
  1669. + return;
  1670. + if (BuffTarget(master, diff))
  1671. + return;
  1672. + for (Unit::ControlList::const_iterator itr = master->m_Controlled.begin(); itr != master->m_Controlled.end(); ++itr)
  1673. + {
  1674. + Unit* u = *itr;
  1675. + if (!u || u->isDead()) continue;
  1676. + if (HealTarget(u, GetHealthPCT(u), diff))
  1677. + return;
  1678. + if (Creature* cre = u->ToCreature())
  1679. + if (cre->GetIAmABot() || cre->isPet())
  1680. + if (BuffTarget(u, diff))
  1681. + return;
  1682. + }
  1683. + return;
  1684. + }
  1685. + bool Bots = false;
  1686. + for (GroupReference* itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
  1687. + {
  1688. + Player* tPlayer = itr->getSource();
  1689. + if (tPlayer == NULL) continue;
  1690. + if (me->GetMap() != tPlayer->FindMap()) continue;
  1691. + if (!tPlayer->m_Controlled.empty())
  1692. + Bots = true;
  1693. + if (tPlayer->isDead()) continue;
  1694. + if (HealTarget(tPlayer, GetHealthPCT(tPlayer), diff))
  1695. + return;
  1696. + if (BuffTarget(tPlayer, diff))
  1697. + return;
  1698. + }
  1699. + if (Bots)
  1700. + {
  1701. + for (GroupReference* itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
  1702. + {
  1703. + Player* tPlayer = itr->getSource();
  1704. + if (tPlayer == NULL || tPlayer->m_Controlled.empty()) continue;
  1705. + if (me->GetMap() != tPlayer->FindMap()) continue;
  1706. + for (Unit::ControlList::const_iterator itr = tPlayer->m_Controlled.begin(); itr != tPlayer->m_Controlled.end(); ++itr)
  1707. + {
  1708. + Unit* u = *itr;
  1709. + if (!u || u->isDead()) continue;
  1710. + if (HealTarget(u, GetHealthPCT(u), diff))
  1711. + return;
  1712. + if (Creature* cre = u->ToCreature())
  1713. + if (cre->GetIAmABot() || cre->isPet())
  1714. + if (BuffTarget(u, diff))
  1715. + return;
  1716. + }
  1717. + }
  1718. + }
  1719. + //check if we have pointed heal target
  1720. + for (uint8 i = 0; i != TARGETICONCOUNT; ++i)
  1721. + {
  1722. + if (healTargetIconFlags & GroupIconsFlags[i])
  1723. + {
  1724. + if (uint64 guid = pGroup->GetTargetIcons()[i])//check this one
  1725. + {
  1726. + if (Unit* unit = sObjectAccessor->FindUnit(guid))
  1727. + {
  1728. + if (unit->isAlive() && me->GetMap() == unit->FindMap() &&
  1729. + master->getVictim() != unit && unit->getVictim() != master &&
  1730. + unit->GetReactionTo(master) >= REP_NEUTRAL)
  1731. + {
  1732. + HealTarget(unit, GetHealthPCT(unit), diff);
  1733. + //CureTarget(unit, getCureSpell(), diff);
  1734. + }
  1735. + }
  1736. + }
  1737. + }
  1738. + }
  1739. +}
  1740. +// Attempt to resurrect dead players using class spells
  1741. +// Targets either player or its corpse
  1742. +void bot_minion_ai::RezGroup(uint32 REZZ, Player* gPlayer)
  1743. +{
  1744. + if (!REZZ || !gPlayer || me->IsMounted()) return;
  1745. + if (IsCasting()) return; // if I'm already casting
  1746. + if (rezz_cd > 0) return;
  1747. +
  1748. + //sLog->outBasic("RezGroup by %s", me->GetName().c_str());
  1749. + Group* pGroup = gPlayer->GetGroup();
  1750. + if (!pGroup)
  1751. + {
  1752. + Unit* target = master;
  1753. + if (master->isAlive()) return;
  1754. + if (master->isRessurectRequested()) return; //ressurected
  1755. + if (master->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST))
  1756. + target = (Unit*)master->GetCorpse();
  1757. + if (me->GetMap() != target->FindMap()) return;
  1758. + if (me->GetDistance(target) > 30)
  1759. + {
  1760. + me->GetMotionMaster()->MovePoint(master->GetMapId(), *target);
  1761. + rezz_cd = 3;//6-9 sec reset
  1762. + return;
  1763. + }
  1764. + else if (!target->IsWithinLOSInMap(me))
  1765. + me->Relocate(*target);
  1766. +
  1767. + if (doCast(target, REZZ))//rezzing it
  1768. + {
  1769. + me->MonsterWhisper("Rezzing You", master->GetGUID());
  1770. + rezz_cd = 60;
  1771. + }
  1772. + return;
  1773. + }
  1774. + for (GroupReference* itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
  1775. + {
  1776. + Player* tPlayer = itr->getSource();
  1777. + Unit* target = tPlayer;
  1778. + if (!tPlayer || tPlayer->isAlive()) continue;
  1779. + if (tPlayer->isRessurectRequested()) continue; //ressurected
  1780. + if (Rand() > 5) continue;
  1781. + if (tPlayer->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST))
  1782. + target = (Unit*)tPlayer->GetCorpse();
  1783. + if (master->GetMap() != target->FindMap()) continue;
  1784. + if (me->GetDistance(target) > 30)
  1785. + {
  1786. + me->GetMotionMaster()->MovePoint(master->GetMapId(), *target);
  1787. + rezz_cd = 3;//6-9 sec reset
  1788. + return;
  1789. + }
  1790. + else if (!target->IsWithinLOSInMap(me))
  1791. + me->Relocate(*target);
  1792. +
  1793. + if (doCast(target, REZZ))//rezzing it
  1794. + {
  1795. + me->MonsterWhisper("Rezzing You", tPlayer->GetGUID());
  1796. + if (tPlayer != master)
  1797. + {
  1798. + std::string rezstr = "Rezzing ";
  1799. + rezstr += tPlayer->GetName();
  1800. + me->MonsterWhisper(rezstr.c_str(), master->GetGUID());
  1801. + }
  1802. + rezz_cd = 60;
  1803. + return;
  1804. + }
  1805. + }
  1806. +}
  1807. +// CURES
  1808. +//cycle through the group sending members for cure
  1809. +void bot_minion_ai::CureGroup(Player* pTarget, uint32 cureSpell, uint32 diff)
  1810. +{
  1811. + if (!cureSpell || GC_Timer > diff) return;
  1812. + if (me->getLevel() < 10 || pTarget->getLevel() < 10) return;
  1813. + if (me->IsMounted()) return;
  1814. + if (IsCasting() || Feasting()) return;
  1815. + if (!master->GetMap()->IsRaid() && Rand() > 75) return;
  1816. + //sLog->outBasic("%s: CureGroup() on %s", me->GetName().c_str(), pTarget->GetName().c_str());
  1817. + Group* pGroup = pTarget->GetGroup();
  1818. + if (!pGroup)
  1819. + {
  1820. + if (CureTarget(master, cureSpell, diff))
  1821. + return;
  1822. + for (uint8 i = 0; i != master->GetMaxNpcBots(); ++i)
  1823. + {
  1824. + Creature* cre = master->GetBotMap(i)->_Cre();
  1825. + if (!cre || !cre->IsInWorld() || me->GetDistance(cre) > 30) continue;
  1826. + if (CureTarget(cre, cureSpell, diff))
  1827. + return;
  1828. + }
  1829. + }
  1830. + else
  1831. + {
  1832. + bool Bots = false;
  1833. + for (GroupReference* itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
  1834. + {
  1835. + Player* tPlayer = itr->getSource();
  1836. + if (!tPlayer || (tPlayer->isDead() && !tPlayer->HaveBot())) continue;
  1837. + if (!Bots && tPlayer->HaveBot())
  1838. + Bots = true;
  1839. + if (!tPlayer->IsInWorld() || tPlayer->IsBeingTeleported()) continue;
  1840. + if (me->GetMap() != tPlayer->FindMap()) continue;
  1841. + if (me->GetDistance(tPlayer) > 30) continue;
  1842. + if (CureTarget(tPlayer, cureSpell, diff))
  1843. + return;
  1844. + }
  1845. + if (!Bots) return;
  1846. + for (GroupReference* itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
  1847. + {
  1848. + Player* tPlayer = itr->getSource();
  1849. + if (tPlayer == NULL || !tPlayer->HaveBot()) continue;
  1850. + if (!tPlayer->IsInWorld() || tPlayer->IsBeingTeleported()) continue;
  1851. + if (me->GetMap() != tPlayer->FindMap()) continue;
  1852. + for (uint8 i = 0; i != tPlayer->GetMaxNpcBots(); ++i)
  1853. + {
  1854. + Creature* cre = tPlayer->GetBotMap(i)->_Cre();
  1855. + if (!cre || !cre->IsInWorld() || me->GetDistance(cre) > 30) continue;
  1856. + if (CureTarget(cre, cureSpell, diff))
  1857. + return;
  1858. + }
  1859. + }
  1860. + }
  1861. +}
  1862. +
  1863. +bool bot_minion_ai::CureTarget(Unit* target, uint32 cureSpell, uint32 diff)
  1864. +{
  1865. + return CanCureTarget(target, cureSpell, diff) ? doCast(target, cureSpell) : false;
  1866. +}
  1867. +// determines if unit has something to cure
  1868. +bool bot_minion_ai::CanCureTarget(Unit* target, uint32 cureSpell, uint32 diff) const
  1869. +{
  1870. + if (!cureSpell || GC_Timer > diff) return false;
  1871. + if (!target || target->isDead()) return false;
  1872. + if (me->getLevel() < 10 || target->getLevel() < 10) return false;
  1873. + if (me->IsMounted()) return false;
  1874. + if (IsCasting() || Feasting()) return false;
  1875. + if (me->GetDistance(target) > 30) return false;
  1876. + if (!IsInBotParty(target)) return false;
  1877. +
  1878. + SpellInfo const* info = sSpellMgr->GetSpellInfo(cureSpell);
  1879. + if (!info) return false;
  1880. +
  1881. + uint32 dispelMask = 0;
  1882. + for (uint8 i = 0; i != MAX_SPELL_EFFECTS; ++i)
  1883. + if (info->Effects[i].Effect == SPELL_EFFECT_DISPEL)
  1884. + dispelMask |= SpellInfo::GetDispelMask(DispelType(info->Effects[i].MiscValue));
  1885. +
  1886. + if (dispelMask == 0)
  1887. + return false;
  1888. +
  1889. + DispelChargesList dispel_list;
  1890. + target->GetDispellableAuraList(me, dispelMask, dispel_list);
  1891. + if (dispel_list.empty())
  1892. + return false;
  1893. + return true;
  1894. +}
  1895. +
  1896. +bool bot_ai::HasAuraName(Unit* unit, uint32 spellId, uint64 casterGuid, bool exclude) const
  1897. +{
  1898. + if (!spellId) return false;
  1899. + SpellInfo const* pSpellInfo = sSpellMgr->GetSpellInfo(spellId);
  1900. + if (!pSpellInfo) return false;
  1901. +
  1902. + uint8 loc = master->GetSession()->GetSessionDbcLocale();
  1903. + const std::string name = pSpellInfo->SpellName[loc];
  1904. + if (name.length() == 0) return false;
  1905. +
  1906. + return HasAuraName(unit, name, casterGuid, exclude);
  1907. +}
  1908. +
  1909. +bool bot_ai::HasAuraName(Unit* unit, const std::string spell, uint64 casterGuid, bool exclude) const
  1910. +{
  1911. + if (spell.length() == 0) return false;
  1912. +
  1913. + uint8 loc = master->GetSession()->GetSessionDbcLocale();
  1914. + if (!unit || unit->isDead()) return false;
  1915. +
  1916. + Unit::AuraMap const& vAuras = unit->GetOwnedAuras();
  1917. + for (Unit::AuraMap::const_iterator itr = vAuras.begin(); itr != vAuras.end(); ++itr)
  1918. + {
  1919. + SpellInfo const* spellInfo = itr->second->GetSpellInfo();
  1920. + const std::string name = spellInfo->SpellName[loc];
  1921. + if (spell == name)
  1922. + if (casterGuid == 0 || (casterGuid != 0 && exclude == (casterGuid != itr->second->GetCasterGUID())))
  1923. + return true;
  1924. + }
  1925. + return false;
  1926. +}
  1927. +//LIST AURAS
  1928. +// Debug: Returns bot's info to called player
  1929. +void bot_ai::listAuras(Player* player, Unit* unit) const
  1930. +{
  1931. + if (!IsInBotParty(player)) return;
  1932. + if (!IsInBotParty(unit)) return;
  1933. + ChatHandler ch(player->GetSession());
  1934. + std::ostringstream botstring;
  1935. + if (unit->GetTypeId() == TYPEID_PLAYER)
  1936. + botstring << "player";
  1937. + else if (unit->GetTypeId() == TYPEID_UNIT)
  1938. + {
  1939. + if (unit->ToCreature()->GetIAmABot())
  1940. + {
  1941. + botstring << "minion bot, master: ";
  1942. + std::string const& ownername = unit->ToCreature()->GetBotOwner()->GetName();
  1943. + botstring << ownername;
  1944. + }
  1945. + else if (unit->ToCreature()->GetIAmABotsPet())
  1946. + {
  1947. + Player* owner = unit->ToCreature()->GetBotOwner();
  1948. + Creature* creowner = unit->ToCreature()->GetBotPetAI()->GetCreatureOwner();
  1949. + std::string const& ownername = owner ? owner->GetName() : "none";
  1950. + std::string const& creownername = creowner ? creowner->GetName() : "none";
  1951. + botstring << "pet bot, master: ";
  1952. + botstring << ownername;
  1953. + botstring << ", creature owner: ";
  1954. + botstring << creownername;
  1955. + if (creowner)
  1956. + botstring << " (" << creowner->GetGUIDLow() << ')';
  1957. + }
  1958. + }
  1959. + ch.PSendSysMessage("ListAuras for %s, %s", unit->GetName().c_str(), botstring.str().c_str());
  1960. + uint8 locale = player->GetSession()->GetSessionDbcLocale();
  1961. + Unit::AuraMap const &vAuras = unit->GetOwnedAuras();
  1962. + for (Unit::AuraMap::const_iterator itr = vAuras.begin(); itr != vAuras.end(); ++itr)
  1963. + {
  1964. + SpellInfo const* spellInfo = itr->second->GetSpellInfo();
  1965. + if (!spellInfo)
  1966. + continue;
  1967. + uint32 id = spellInfo->Id;
  1968. + SpellInfo const* learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  1969. + const std::string name = spellInfo->SpellName[locale];
  1970. + std::ostringstream spellmsg;
  1971. + spellmsg << id << " - |cffffffff|Hspell:" << id << "|h[" << name;
  1972. + spellmsg << ' ' << localeNames[locale] << "]|h|r";
  1973. + uint32 talentcost = GetTalentSpellCost(id);
  1974. + uint32 rank = 0;
  1975. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  1976. + rank = talentcost;
  1977. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  1978. + rank = spellInfo->GetRank();
  1979. + if (rank > 0)
  1980. + spellmsg << " Rank " << rank;
  1981. + if (talentcost > 0)
  1982. + spellmsg << " [talent]";
  1983. + if (spellInfo->IsPassive())
  1984. + spellmsg << " [passive]";
  1985. + if (unit->GetTypeId() == TYPEID_PLAYER && unit->ToPlayer()->HasSpell(id))
  1986. + spellmsg << " [known]";
  1987. +
  1988. + ch.PSendSysMessage(spellmsg.str().c_str());
  1989. + }
  1990. + for (uint8 i = STAT_STRENGTH; i != MAX_STATS; ++i)
  1991. + {
  1992. + std::string mystat;
  1993. + switch (i)
  1994. + {
  1995. + case STAT_STRENGTH: mystat = "str"; break;
  1996. + case STAT_AGILITY: mystat = "agi"; break;
  1997. + case STAT_STAMINA: mystat = "sta"; break;
  1998. + case STAT_INTELLECT: mystat = "int"; break;
  1999. + case STAT_SPIRIT: mystat = "spi"; break;
  2000. + default: mystat = "unk stat"; break;
  2001. + }
  2002. + ch.PSendSysMessage("%s: %f", mystat.c_str(), unit->GetTotalStatValue(Stats(i)));
  2003. + }
  2004. + ch.PSendSysMessage("Melee AP: %f", unit->GetTotalAttackPowerValue(BASE_ATTACK));
  2005. + ch.PSendSysMessage("Ranged AP: %f", unit->GetTotalAttackPowerValue(RANGED_ATTACK));
  2006. + ch.PSendSysMessage("armor: %u", unit->GetArmor());
  2007. + ch.PSendSysMessage("crit: %f pct", unit->GetUnitCriticalChance(BASE_ATTACK, me));
  2008. + ch.PSendSysMessage("dodge: %f pct", unit->GetUnitDodgeChance());
  2009. + ch.PSendSysMessage("parry: %f pct", unit->GetUnitParryChance());
  2010. + ch.PSendSysMessage("Damage taken melee: %f", unit->GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN, SPELL_SCHOOL_MASK_NORMAL));
  2011. + ch.PSendSysMessage("Damage taken spell: %f", unit->GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN, SPELL_SCHOOL_MASK_MAGIC));
  2012. + ch.PSendSysMessage("Damage range mainhand: min: %f, max: %f", unit->GetFloatValue(UNIT_FIELD_MINDAMAGE), unit->GetFloatValue(UNIT_FIELD_MAXDAMAGE));
  2013. + ch.PSendSysMessage("Damage range offhand: min: %f, max: %f", unit->GetFloatValue(UNIT_FIELD_MINOFFHANDDAMAGE), unit->GetFloatValue(UNIT_FIELD_MAXOFFHANDDAMAGE));
  2014. + ch.PSendSysMessage("Damage range ranged: min: %f, max: %f", unit->GetFloatValue(UNIT_FIELD_MINRANGEDDAMAGE), unit->GetFloatValue(UNIT_FIELD_MAXRANGEDDAMAGE));
  2015. + ch.PSendSysMessage("Damage mult mainhand: %f", unit->GetModifierValue(UNIT_MOD_DAMAGE_MAINHAND, BASE_PCT)*unit->GetModifierValue(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_PCT));
  2016. + ch.PSendSysMessage("Damage mult offhand: %f", unit->GetModifierValue(UNIT_MOD_DAMAGE_OFFHAND, BASE_PCT)*unit->GetModifierValue(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_PCT));
  2017. + ch.PSendSysMessage("Damage mult ranged: %f", unit->GetModifierValue(UNIT_MOD_DAMAGE_RANGED, BASE_PCT)*unit->GetModifierValue(UNIT_MOD_DAMAGE_RANGED, TOTAL_PCT));
  2018. + ch.PSendSysMessage("Attack time mainhand: %f", float(unit->GetAttackTime(BASE_ATTACK))/1000.f);
  2019. + ch.PSendSysMessage("Attack time offhand: %f", float(unit->GetAttackTime(OFF_ATTACK))/1000.f);
  2020. + ch.PSendSysMessage("Attack time ranged: %f", float(unit->GetAttackTime(RANGED_ATTACK))/1000.f);
  2021. + if (unit == me)
  2022. + ch.PSendSysMessage("melee damage mult: %f", dmgmult_melee);
  2023. + ch.PSendSysMessage("base hp: %u", unit->GetCreateHealth());
  2024. + ch.PSendSysMessage("total hp: %u", unit->GetMaxHealth());
  2025. + ch.PSendSysMessage("base mana: %u", unit->GetCreateMana());
  2026. + ch.PSendSysMessage("total mana: %u", unit->GetMaxPower(POWER_MANA));
  2027. + //DEBUG1
  2028. + //ch.PSendSysMessage("STATS: ");
  2029. + //ch.PSendSysMessage("Health");
  2030. + //ch.PSendSysMessage("base value: %f", unit->GetModifierValue(UNIT_MOD_HEALTH, BASE_VALUE));
  2031. + //ch.PSendSysMessage("base pct: %f", unit->GetModifierValue(UNIT_MOD_HEALTH, BASE_PCT));
  2032. + //ch.PSendSysMessage("total value: %f", unit->GetModifierValue(UNIT_MOD_HEALTH, TOTAL_VALUE));
  2033. + //ch.PSendSysMessage("total pct: %f", unit->GetModifierValue(UNIT_MOD_HEALTH, TOTAL_PCT));
  2034. + //ch.PSendSysMessage("Mana");
  2035. + //ch.PSendSysMessage("base value: %f", unit->GetModifierValue(UNIT_MOD_MANA, BASE_VALUE));
  2036. + //ch.PSendSysMessage("base pct: %f", unit->GetModifierValue(UNIT_MOD_MANA, BASE_PCT));
  2037. + //ch.PSendSysMessage("total value: %f", unit->GetModifierValue(UNIT_MOD_MANA, TOTAL_VALUE));
  2038. + //ch.PSendSysMessage("total pct: %f", unit->GetModifierValue(UNIT_MOD_MANA, TOTAL_PCT));
  2039. + //ch.PSendSysMessage("Stamina");
  2040. + //ch.PSendSysMessage("base value: %f", unit->GetModifierValue(UNIT_MOD_STAT_STAMINA, BASE_VALUE));
  2041. + //ch.PSendSysMessage("base pct: %f", unit->GetModifierValue(UNIT_MOD_STAT_STAMINA, BASE_PCT));
  2042. + //ch.PSendSysMessage("total value: %f", unit->GetModifierValue(UNIT_MOD_STAT_STAMINA, TOTAL_VALUE));
  2043. + //ch.PSendSysMessage("total pct: %f", unit->GetModifierValue(UNIT_MOD_STAT_STAMINA, TOTAL_PCT));
  2044. + //ch.PSendSysMessage("Intellect");
  2045. + //ch.PSendSysMessage("base value: %f", unit->GetModifierValue(UNIT_MOD_STAT_INTELLECT, BASE_VALUE));
  2046. + //ch.PSendSysMessage("base pct: %f", unit->GetModifierValue(UNIT_MOD_STAT_INTELLECT, BASE_PCT));
  2047. + //ch.PSendSysMessage("total value: %f", unit->GetModifierValue(UNIT_MOD_STAT_INTELLECT, TOTAL_VALUE));
  2048. + //ch.PSendSysMessage("total pct: %f", unit->GetModifierValue(UNIT_MOD_STAT_INTELLECT, TOTAL_PCT));
  2049. + //ch.PSendSysMessage("Spirit");
  2050. + //ch.PSendSysMessage("base value: %f", unit->GetModifierValue(UNIT_MOD_STAT_SPIRIT, BASE_VALUE));
  2051. + //ch.PSendSysMessage("base pct: %f", unit->GetModifierValue(UNIT_MOD_STAT_SPIRIT, BASE_PCT));
  2052. + //ch.PSendSysMessage("total value: %f", unit->GetModifierValue(UNIT_MOD_STAT_SPIRIT, TOTAL_VALUE));
  2053. + //ch.PSendSysMessage("total pct: %f", unit->GetModifierValue(UNIT_MOD_STAT_SPIRIT, TOTAL_PCT));
  2054. + //END DEBUG1
  2055. + if (unit == me)
  2056. + {
  2057. + ch.PSendSysMessage("spellpower: %u", m_spellpower - m_spellpower % 50);
  2058. + ch.PSendSysMessage("spell damage mult: %f", dmgmult_spell);
  2059. + ch.PSendSysMessage("mana regen: %f", regen_mp5 - (int32(regen_mp5) % 45));
  2060. + ch.PSendSysMessage("haste: %u *10 pct", haste);
  2061. + for (uint8 i = SPELL_SCHOOL_HOLY; i != MAX_SPELL_SCHOOL; ++i)
  2062. + {
  2063. + const char* resist = NULL;
  2064. + switch (i)
  2065. + {
  2066. + case 1: resist = "holy"; break;
  2067. + case 2: resist = "fire"; break;
  2068. + case 3: resist = "nature"; break;
  2069. + case 4: resist = "frost"; break;
  2070. + case 5: resist = "shadow"; break;
  2071. + case 6: resist = "arcane"; break;
  2072. + }
  2073. + ch.PSendSysMessage("Resistance %s: %u", resist, me->GetResistance(SpellSchools(i)));
  2074. + }
  2075. + ch.PSendSysMessage("BotCommandState: %s", m_botCommandState == COMMAND_FOLLOW ? "Follow" : m_botCommandState == COMMAND_ATTACK ? "Attack" : m_botCommandState == COMMAND_STAY ? "Stay" : m_botCommandState == COMMAND_ABANDON ? "Reset" : "none");
  2076. + ch.PSendSysMessage("Follow distance: %u", master->GetBotFollowDist());
  2077. + //ch.PSendSysMessage("healTargetIconFlags: %u", healTargetIconFlags);
  2078. + if (tank != NULL && tank->IsInWorld())
  2079. + {
  2080. + if (tank == me)
  2081. + ch.PSendSysMessage("Is a MainTank!");
  2082. + else
  2083. + ch.PSendSysMessage("Maintank is %s", tank->GetName().c_str());
  2084. + }
  2085. + //debug
  2086. + //if (IsPetAI()) GetPetAI()->ListSpells(&ch);
  2087. + }
  2088. +}
  2089. +//SETSTATS
  2090. +// Health, Armor, Powers, Combat Ratings, and global update setup
  2091. +void bot_minion_ai::setStats(uint8 myclass, uint8 myrace, uint8 mylevel, bool force)
  2092. +{
  2093. + if (myrace == 0 || myclass == 0) return;
  2094. + if (myclass != BEAR && myclass != CAT && (master->isDead() || (!shouldUpdateStats && !force))) return;
  2095. + /*sLog->outBasic("setStats(): Updating bot %s, class: %u, race: %u, level %u, master: %s",
  2096. + me->GetName().c_str(), myclass, myrace, mylevel, master->GetName().c_str());*/
  2097. +
  2098. + mylevel = std::min<uint8>(mylevel, 80);
  2099. +
  2100. + //LEVEL
  2101. + if (me->getLevel() != mylevel)
  2102. + {
  2103. + me->SetLevel(mylevel);
  2104. + force = true; //restore powers on lvl update
  2105. + }
  2106. + if (force)
  2107. + InitSpells();
  2108. +
  2109. + //PHASE
  2110. + if (!me->InSamePhase(master))
  2111. + me->SetPhaseMask(master->GetPhaseMask(), true);
  2112. + //INIT STATS
  2113. + //partially receive master's stats and get base class stats, we'll need all this later
  2114. + uint8 tempclass = myclass == BEAR || myclass == CAT ? CLASS_DRUID : myclass;
  2115. + sObjectMgr->GetPlayerClassLevelInfo(tempclass, mylevel, classinfo);
  2116. + const CreatureBaseStats* const classstats = sObjectMgr->GetCreatureBaseStats(mylevel, me->getClass());//use creature class
  2117. + float value;
  2118. + if (force)
  2119. + for (uint8 i = STAT_STAMINA; i < MAX_STATS; i++)
  2120. + me->SetCreateStat(Stats(i), master->GetCreateStat(Stats(i)));
  2121. +
  2122. + //MAXSTAT
  2123. + for (uint8 i = 0; i < MAX_STATS; ++i)
  2124. + {
  2125. + value = master->GetTotalStatValue(Stats(i));
  2126. + if (i == 0 || value > stat)
  2127. + stat = value;//Get Hightest stat (on first cycle just set base value)
  2128. + }
  2129. + stat = std::max(stat - 18.f, 0.f);
  2130. +
  2131. + //INIT CLASS MODIFIERS
  2132. + switch (myclass)
  2133. + {
  2134. + case CLASS_WARRIOR: ap_mod = 1.3f; spp_mod = 0.0f; armor_mod = 1.4f; crit_mod = 1.0f; haste_mod = 0.75f; dodge_mod = 0.75f; parry_mod = 1.75f; break;
  2135. + case CLASS_DEATH_KNIGHT: ap_mod = 1.2f; spp_mod = 1.0f; armor_mod = 1.15f; crit_mod = 0.9f; haste_mod = 0.65f; dodge_mod = 0.8f; parry_mod = 2.0f; break;//NYI
  2136. + case CLASS_PALADIN: ap_mod = 1.0f; spp_mod = 0.8f; armor_mod = 1.2f; crit_mod = 0.8f; haste_mod = 0.85f; dodge_mod = 0.7f; parry_mod = 1.5f; break;
  2137. + case CLASS_ROGUE: ap_mod = 1.5f; spp_mod = 0.0f; armor_mod = 0.7f; crit_mod = 1.5f; haste_mod = 1.35f; dodge_mod = 1.5f; parry_mod = 0.8f; break;//NYI
  2138. + case CLASS_HUNTER: ap_mod = 1.15f; spp_mod = 0.0f; armor_mod = 0.85f; crit_mod = 1.2f; haste_mod = 1.25f; dodge_mod = 1.2f; parry_mod = 1.2f; break;//NYI
  2139. + case CLASS_SHAMAN: ap_mod = 0.9f; spp_mod = 1.0f; armor_mod = 0.9f; crit_mod = 1.2f; haste_mod = 1.65f; dodge_mod = 0.8f; parry_mod = 0.5f; break;//NYI
  2140. + case CLASS_DRUID: ap_mod = 0.0f; spp_mod = 1.3f; armor_mod = 0.7f; crit_mod = 0.7f; haste_mod = 1.95f; dodge_mod = 0.5f; parry_mod = 0.0f; break;
  2141. + case CLASS_MAGE: ap_mod = 0.0f; spp_mod = 0.8f; armor_mod = 0.5f; crit_mod = 0.7f; haste_mod = 1.75f; dodge_mod = 0.5f; parry_mod = 0.0f; break;
  2142. + case CLASS_PRIEST: ap_mod = 0.0f; spp_mod = 1.2f; armor_mod = 0.5f; crit_mod = 0.7f; haste_mod = 1.75f; dodge_mod = 0.5f; parry_mod = 0.0f; break;
  2143. + case CLASS_WARLOCK: ap_mod = 0.0f; spp_mod = 1.0f; armor_mod = 0.5f; crit_mod = 0.7f; haste_mod = 1.75f; dodge_mod = 0.5f; parry_mod = 0.0f; break;
  2144. + case BEAR: ap_mod = 2.0f; spp_mod = 1.3f; armor_mod = 2.25f; crit_mod = 1.0f; haste_mod = 0.75f; dodge_mod = 2.5f; parry_mod = 0.0f; break;
  2145. + case CAT: ap_mod = 1.5f; spp_mod = 1.3f; armor_mod = 1.1f; crit_mod = 1.5f; haste_mod = 2.25f; dodge_mod = 1.35f; parry_mod = 0.0f; break;
  2146. + default: ap_mod = 0.0f; spp_mod = 0.0f; armor_mod = 0.0f; crit_mod = 0.0f; haste_mod = 0.00f; dodge_mod = 0.0f; parry_mod = 0.0f; break;
  2147. + }
  2148. + if (spp_mod != 0.f && mylevel > 39)
  2149. + spp_mod *= (float(mylevel - 39))/41.f;// gain spell power slowly
  2150. +
  2151. + //DAMAGE
  2152. + _OnMeleeDamageUpdate(myclass);
  2153. +
  2154. + //ARMOR
  2155. + //sLog->outBasic("Unpdating %s's ARMOR: ", me->GetName().c_str());
  2156. + //sLog->outBasic("armor mod: %f", armor_mod);
  2157. + armor_mod *= (master->GetModifierValue(UNIT_MOD_ARMOR, BASE_PCT) + master->GetModifierValue(UNIT_MOD_ARMOR, TOTAL_PCT))/2.f;
  2158. + //sLog->outBasic("armor mod * master's modifier: %f", armor_mod);
  2159. + value = float(classstats->BaseArmor);
  2160. + //sLog->outBasic("base armor: %f", value);
  2161. + value += float(master->GetArmor())/5.f;
  2162. + //sLog->outBasic("base armor + 1/5 of master's armor: %f", value);
  2163. + value *= armor_mod;
  2164. + //sLog->outBasic("multiplied by armor mod (total base armor): %f", value);
  2165. + me->SetModifierValue(UNIT_MOD_ARMOR, BASE_VALUE, value);
  2166. + me->UpdateArmor();//buffs will be took in consideration here
  2167. +
  2168. + //RESISTANCES
  2169. + //sLog->outBasic("Unpdating %s's RESISTANCES: ", me->GetName().c_str());
  2170. + for (uint8 i = SPELL_SCHOOL_HOLY; i != MAX_SPELL_SCHOOL; ++i)
  2171. + {
  2172. + value = float(master->GetResistance(SpellSchools(i)));
  2173. + //sLog->outBasic("master's resistance %u: %f, setting %f (triple) to bot", uint32(UNIT_MOD_RESISTANCE_START + i), value, value*3);
  2174. + me->SetModifierValue(UnitMods(UNIT_MOD_RESISTANCE_START + i), BASE_VALUE, value*2.5f + float(mylevel*2));
  2175. + //me->SetModifierValue(UnitMods(UNIT_MOD_RESISTANCE_START + i), BASE_PCT, 1.f);
  2176. + me->UpdateResistances(i);
  2177. + }
  2178. + //DAMAGE TAKEN
  2179. + float directReduction = master->GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN, SPELL_SCHOOL_MASK_NORMAL);
  2180. + float magicReduction = master->GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN, SPELL_SCHOOL_MASK_MAGIC);
  2181. + value = (directReduction + magicReduction)/2.f;// average
  2182. + if (value > 1.f)
  2183. + value -= 1.f;
  2184. + else
  2185. + value = 1.f - value;//get reduction even if master's is 1.0+
  2186. + value = std::min(0.42f, value);
  2187. + value/= 0.01f; //here we get percent like 0.42/0.01 = 42% (value * 100.f)
  2188. + if (mylevel > 77)
  2189. + value += float(mylevel - 77)*6.f;// + 3 stacks for high level
  2190. + RefreshAura(DMG_TAKEN, int8(value/6.f));//so max aura count = 10
  2191. +
  2192. + //HEALTH
  2193. + _OnHealthUpdate(myclass, mylevel);
  2194. +
  2195. + //HASTE
  2196. + value = 0.f;
  2197. + for (uint8 i = CR_HASTE_MELEE; i != CR_HASTE_SPELL + 1; ++i)
  2198. + if (float rating = master->GetRatingBonusValue(CombatRating(i)))
  2199. + if (rating > value)//master got some haste
  2200. + value = rating;//get hightest pct
  2201. + for (uint8 i = EQUIPMENT_SLOT_BACK; i < EQUIPMENT_SLOT_END; ++i)
  2202. + if (Item* item = master->GetItemByPos(0, i))//inventory weapons
  2203. + if (item->GetTemplate()->ItemLevel >= 277)//bears ICC 25H LK items or Wrathful items
  2204. + value += 10.f;//only weapons so we can add 1 to 3 stacks (rogue, warr, sham...)
  2205. + value *= haste_mod;
  2206. + if (isMeleeClass(myclass))
  2207. + value *= 0.67f;//nerf melee haste by 1/3
  2208. + value = value/10.f + float(mylevel/39);//get bonus at 78
  2209. + if (myclass == CAT)//give cat lots of haste
  2210. + value += float(mylevel/16);//or 20 (+ 4-5 stacks);
  2211. + RefreshAura(HASTE, uint8(value));//spell haste
  2212. + RefreshAura(HASTE2, uint8(value + 1*(myclass == CLASS_ROGUE)));//melee haste
  2213. + haste = uint8(value);//for show only
  2214. +
  2215. + //HIT
  2216. + int32 melee_hit = master->GetTotalAuraModifier(SPELL_AURA_MOD_HIT_CHANCE) + master->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_MELEE_HIT_CHANCE);
  2217. + int32 spell_hit = master->GetTotalAuraModifier(SPELL_AURA_MOD_SPELL_HIT_CHANCE) + master->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_INCREASES_SPELL_PCT_TO_HIT, SPELL_SCHOOL_MASK_SPELL);
  2218. + value = float(melee_hit > spell_hit ? melee_hit : spell_hit)*1.5f;//hightest, buff hit chance for bots
  2219. + hit = value/3.f;
  2220. + RefreshAura(PRECISION, int8(hit) + mylevel/39);//melee
  2221. + RefreshAura(PRECISION2, int8(hit) + mylevel/39);//spell
  2222. +
  2223. + //CRIT
  2224. + //chose melee or ranged cuz crit rating increases melee/spell, and hunter benefits from agility
  2225. + value = master->GetUnitCriticalChance((master->getClass() == CLASS_HUNTER ? RANGED_ATTACK : BASE_ATTACK), me);
  2226. + value = value > 5.f ? value - 5.f : 0.f;//remove base chance if can
  2227. + value *= crit_mod;
  2228. + RefreshAura(CRITS, int8(value/5.f) + mylevel/39);
  2229. + if (myclass == CLASS_PRIEST)
  2230. + RefreshAura(HOLYCRIT, int8(value/7.f));//add holy crit to healers
  2231. +
  2232. + //PARRY
  2233. + value = master->GetFloatValue(PLAYER_PARRY_PERCENTAGE);
  2234. + value = value > 5.f ? value - 5.f : 0.f;//remove base chance if possible
  2235. + value *= parry_mod;
  2236. + if (master->GetBotTankGuid() == me->GetGUID() && myclass != CAT && myclass != BEAR)//feral cannot parry so let it be base 5%
  2237. + value += 10.f;
  2238. + if (value > 55.f)
  2239. + value = 55.f;
  2240. + float parryAndDodge = value;//set temp value, this is needed to keep total avoidance within 65%
  2241. + RefreshAura(PARRY, int8(value/5.f));
  2242. +
  2243. + //DODGE
  2244. + value = master->GetUnitDodgeChance();
  2245. + value = value > 5.f ? value - 5.f : 0.f;//remove base chance if possible
  2246. + value *= dodge_mod;
  2247. + if (master->GetBotTankGuid() == me->GetGUID())
  2248. + value += 10.f;
  2249. + if (value > 55.f)
  2250. + value = 55.f;
  2251. + if (parryAndDodge + value > 55.f)
  2252. + value = 55.f - parryAndDodge;//do not allow avoidance to be more than 65% (base 5+5)
  2253. + if (myclass == CLASS_ROGUE)
  2254. + value += 6.f;
  2255. + RefreshAura(DODGE, int8(value/5.f));
  2256. +
  2257. + //MANA
  2258. + _OnManaUpdate(myclass, mylevel);
  2259. +
  2260. + //MANA REGEN
  2261. + if (mylevel >= 40 && me->getPowerType() == POWER_MANA)
  2262. + {
  2263. + regen_mp5 = master->GetFloatValue(UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER);
  2264. + //regen_mp5 = (master->GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_POWER_REGEN, POWER_MANA) + sqrt(master->GetStat(STAT_INTELLECT)) * master->OCTRegenMPPerSpirit()) / 5.f;
  2265. + //Unit::AuraEffectList const& regenAura = master->GetAuraEffectsByType(SPELL_AURA_MOD_MANA_REGEN_FROM_STAT);
  2266. + //for (Unit::AuraEffectList::const_iterator i = regenAura.begin(); i != regenAura.end(); ++i)
  2267. + // regen_mp5 += master->GetStat(Stats((*i)->GetMiscValue())) * (*i)->GetAmount() / 500.f;
  2268. + //regen_mp5 *= 0.8f;//custom modifier
  2269. + float regen_mp5_a = stat * 0.2f;
  2270. + //regen_mp5 += master->GetTotalStatValue(STAT_SPIRIT) * 0.1f;
  2271. + regen_mp5 = regen_mp5 > regen_mp5_a ? regen_mp5 : regen_mp5_a;
  2272. + if (regen_mp5 >= 45.f)
  2273. + {
  2274. + me->RemoveAurasDueToSpell(MANAREGEN100);
  2275. + me->RemoveAurasDueToSpell(MANAREGEN45);
  2276. + if (regen_mp5 > 200.f) RefreshAura(MANAREGEN100,int8(regen_mp5/100.f) + mylevel/20);
  2277. + else/*if (regen_mp5 > 150.f)*/RefreshAura(MANAREGEN45, int8(regen_mp5/45.f) + mylevel/20);
  2278. + }
  2279. + }
  2280. +
  2281. + //SPELL POWER
  2282. + if (mylevel >= 40 && spp_mod != 0.f)
  2283. + {
  2284. + //sLog->outBasic("Updating spellpower for %s:", me->GetName().c_str());
  2285. + //sLog->outBasic("spp_mod: %f", spp_mod);
  2286. + for (uint8 i = SPELL_SCHOOL_HOLY; i != MAX_SPELL_SCHOOL; ++i)
  2287. + {
  2288. + int32 power = master->SpellBaseDamageBonusDone(SpellSchoolMask(1 << i));
  2289. + if (power > sppower || i == SPELL_SCHOOL_HOLY)
  2290. + sppower = power;
  2291. + }
  2292. + //sppower = master->SpellBaseDamageBonusDone(SPELL_SCHOOL_MASK_MAGIC);//"Spell Power" stat
  2293. + //sLog->outBasic("Master's spell power: %i", sppower);
  2294. + atpower = master->GetTotalAttackPowerValue(master->getClass() == CLASS_HUNTER ? RANGED_ATTACK : BASE_ATTACK);
  2295. + atpower *= 0.67f;
  2296. + //sLog->outBasic("Master's 2/3 of attack power: %f", atpower);
  2297. + m_spellpower = sppower > atpower ? sppower : atpower;
  2298. + //sLog->outBasic("Chosen stat value: %i", m_spellpower);
  2299. + m_spellpower = int32(float(m_spellpower)*spp_mod);
  2300. + //sLog->outBasic("spellpower * mod: %i", m_spellpower);
  2301. + if (myclass == CLASS_MAGE)
  2302. + RefreshAura(FIREDAM_86, m_spellpower/4/86 + (mylevel >= 78)*2); //(86,172,258,344,430,516,602,688...) // fire spp, do not touch this
  2303. + me->RemoveAurasDueToSpell(SPELL_BONUS_250);
  2304. + me->RemoveAurasDueToSpell(SPELL_BONUS_150);
  2305. + me->RemoveAurasDueToSpell(SPELL_BONUS_50);
  2306. + if (mylevel < 60) RefreshAura(SPELL_BONUS_50, m_spellpower/50);
  2307. + else if (mylevel < 80) RefreshAura(SPELL_BONUS_150, m_spellpower/150 + 1);
  2308. + else RefreshAura(SPELL_BONUS_250, m_spellpower/250 + 2);
  2309. + }
  2310. +
  2311. + if (force)
  2312. + {
  2313. + me->SetFullHealth();
  2314. + me->SetPower(POWER_MANA, me->GetMaxPower(POWER_MANA));
  2315. + }
  2316. +
  2317. + //SetStats for pet
  2318. + if (Creature* pet = me->GetBotsPet())
  2319. + if (bot_pet_ai* petai = pet->GetBotPetAI())
  2320. + petai->setStats(mylevel, bot_pet_ai::GetPetType(pet), force);
  2321. +
  2322. + shouldUpdateStats = false;
  2323. +}
  2324. +void bot_pet_ai::setStats(uint8 mylevel, uint8 petType, bool force)
  2325. +{
  2326. + if (petType == PET_TYPE_NONE || petType >= MAX_PET_TYPES) return;
  2327. + if (!shouldUpdateStats && !force) return;
  2328. + //sLog->outError(LOG_FILTER_PLAYER, "setStats(): Updating pet bot %s, type: %u, level %u, owner: %s, master: %s", me->GetName().c_str(), petType, mylevel, m_creatureOwner->GetName().c_str(), master->GetName().c_str());
  2329. +
  2330. + //LEVEL
  2331. + if (me->getLevel() != mylevel)
  2332. + {
  2333. + me->SetLevel(mylevel);
  2334. + force = true; //restore powers on lvl update
  2335. + }
  2336. + if (force)
  2337. + InitSpells();
  2338. +
  2339. + //PHASE
  2340. + if (!me->InSamePhase(master))
  2341. + me->SetPhaseMask(master->GetPhaseMask(), true);
  2342. +
  2343. + ////INIT STATS
  2344. + uint8 botclass = m_creatureOwner->GetBotClass();
  2345. + if (botclass == BEAR || botclass == CAT)
  2346. + botclass = CLASS_DRUID;
  2347. + //sObjectMgr->GetPlayerClassLevelInfo(botclass, m_creatureOwner->getLevel(), &classinfo);
  2348. + //const CreatureBaseStats* const classstats = sObjectMgr->GetCreatureBaseStats(mylevel, me->GetBotClass());//use creature class
  2349. + //if (force)
  2350. + // for (uint8 i = STAT_STRENGTH; i < MAX_STATS; i++)
  2351. + // me->SetCreateStat(Stats(i), master->GetCreateStat(Stats(i))*0.5f);
  2352. +
  2353. + //MAXSTAT
  2354. + float value;
  2355. + for (uint8 i = 0; i < MAX_STATS; ++i)
  2356. + {
  2357. + value = master->GetTotalStatValue(Stats(i));
  2358. + if (i == 0 || value > stat)
  2359. + stat = value;//Get Hightest stat (on first cycle just set base value)
  2360. + }
  2361. + stat = std::max(stat - 18.f, 0.f);//remove base
  2362. +
  2363. + //INIT CLASS MODIFIERS
  2364. + //STAT -- 'mod' -- used stat values to apply
  2365. + //WARLOCK
  2366. + //Stamina x0.3 -- health
  2367. + //Armor x0.35 -- armor
  2368. + //Int x0.3 -- crit/mana
  2369. + //Spd x0.15 -- spd (if has mana)
  2370. + //AP x0.57 -- attack power (if melee pet)
  2371. + //Resist x0.4 -- resistances
  2372. + //MAGE
  2373. + //
  2374. + //SHAMAN
  2375. + //
  2376. + //HUNTER
  2377. + //Other x1.0 -- use as default
  2378. + switch (petType)
  2379. + {
  2380. + case PET_TYPE_VOIDWALKER: ap_mod = 0.57f; spp_mod = 0.15f; crit_mod = 1.0f; break;
  2381. + //case PET_TYPE_FELHUNTER: ap_mod = 0.57f; spp_mod = 0.15f; crit_mod = 1.0f; break;//NYI
  2382. + //case PET_TYPE_FELGUARD: ap_mod = 0.57f; spp_mod = 0.15f; crit_mod = 1.0f; break;//NYI
  2383. + //case PET_TYPE_SUCCUBUS: ap_mod = 0.57f; spp_mod = 0.15f; crit_mod = 1.0f; break;//NYI
  2384. + //case PET_TYPE_IMP: ap_mod = 0.f; spp_mod = 0.15f; crit_mod = 1.0f; break;//NYI
  2385. +
  2386. + //case PET_TYPE_WATER_ELEMENTAL: ap_mod = 0.0f; spp_mod = 0.0f; crit_mod = 0.0f; break;//NYI
  2387. +
  2388. + //case PET_TYPE_FIRE_ELEMENTAL: ap_mod = 0.0f; spp_mod = 0.0f; crit_mod = 0.0f; break;//NYI
  2389. + //case PET_TYPE_EARTH_ELEMENTAL: ap_mod = 0.0f; spp_mod = 0.0f; crit_mod = 0.0f; break;//NYI
  2390. +
  2391. + //case PET_TYPE_VULTURE: ap_mod = 0.9f; spp_mod = 1.0f; crit_mod = 1.2f; break;//NYI
  2392. + default: ap_mod = 0.0f; spp_mod = 0.0f; crit_mod = 0.0f; break;
  2393. + }
  2394. + //case CLASS_WARRIOR: ap_mod = 1.3f; spp_mod = 0.0f; armor_mod = 1.4f; crit_mod = 1.0f; haste_mod = 0.75f; dodge_mod = 0.75f; parry_mod = 1.75f; break;
  2395. + //case CLASS_DEATH_KNIGHT: ap_mod = 1.2f; spp_mod = 1.0f; armor_mod = 1.15f; crit_mod = 0.9f; haste_mod = 0.65f; dodge_mod = 0.8f; parry_mod = 2.0f; break;//NYI
  2396. + //case CLASS_PALADIN: ap_mod = 1.0f; spp_mod = 0.8f; armor_mod = 1.2f; crit_mod = 0.8f; haste_mod = 0.85f; dodge_mod = 0.7f; parry_mod = 1.5f; break;
  2397. + //case CLASS_ROGUE: ap_mod = 1.5f; spp_mod = 0.0f; armor_mod = 0.7f; crit_mod = 1.5f; haste_mod = 1.35f; dodge_mod = 1.5f; parry_mod = 0.8f; break;//NYI
  2398. + //case CLASS_HUNTER: ap_mod = 1.15f; spp_mod = 0.0f; armor_mod = 0.85f; crit_mod = 1.2f; haste_mod = 1.25f; dodge_mod = 1.2f; parry_mod = 1.2f; break;//NYI
  2399. + //case CLASS_SHAMAN: ap_mod = 0.9f; spp_mod = 1.0f; armor_mod = 0.9f; crit_mod = 1.2f; haste_mod = 1.65f; dodge_mod = 0.8f; parry_mod = 0.5f; break;//NYI
  2400. + //case CLASS_DRUID: ap_mod = 0.0f; spp_mod = 1.3f; armor_mod = 0.7f; crit_mod = 0.7f; haste_mod = 1.95f; dodge_mod = 0.5f; parry_mod = 0.0f; break;
  2401. + //case CLASS_MAGE: ap_mod = 0.0f; spp_mod = 0.8f; armor_mod = 0.5f; crit_mod = 0.7f; haste_mod = 1.75f; dodge_mod = 0.5f; parry_mod = 0.0f; break;
  2402. + //case CLASS_PRIEST: ap_mod = 0.0f; spp_mod = 1.2f; armor_mod = 0.5f; crit_mod = 0.7f; haste_mod = 1.75f; dodge_mod = 0.5f; parry_mod = 0.0f; break;
  2403. + //case CLASS_WARLOCK: ap_mod = 0.0f; spp_mod = 1.0f; armor_mod = 0.5f; crit_mod = 0.7f; haste_mod = 1.75f; dodge_mod = 0.5f; parry_mod = 0.0f; break;
  2404. + //case BEAR: ap_mod = 2.0f; spp_mod = 1.3f; armor_mod = 2.25f; crit_mod = 1.0f; haste_mod = 0.75f; dodge_mod = 2.5f; parry_mod = 0.0f; break;
  2405. + //case CAT: ap_mod = 1.5f; spp_mod = 1.3f; armor_mod = 1.1f; crit_mod = 1.5f; haste_mod = 2.25f; dodge_mod = 1.35f; parry_mod = 0.0f; break;
  2406. +
  2407. + if (spp_mod != 0.f && mylevel > 39)
  2408. + spp_mod *= (float(mylevel - 39))/41.f;// gain spell power slowly
  2409. +
  2410. + //DAMAGE
  2411. + if (ap_mod > 0.f)//do not bother casters
  2412. + {
  2413. + switch (m_creatureOwner->GetBotClass())
  2414. + {
  2415. + case CLASS_WARLOCK:
  2416. + value = float(m_creatureOwner->GetBotAI()->GetSpellPower());
  2417. + break;
  2418. + default://some weird class or NYI
  2419. + value = 0.f;
  2420. + break;
  2421. + }
  2422. + //Calculate ap
  2423. + //set base strength
  2424. + me->SetModifierValue(UNIT_MOD_STAT_STRENGTH, BASE_VALUE, me->GetCreateStat(STAT_STRENGTH) - 9.f);
  2425. + //calc attack power (strength and minion's spd)
  2426. + atpower = me->GetTotalAuraModValue(UNIT_MOD_STAT_STRENGTH)*2.f + value*ap_mod;
  2427. + //set value
  2428. + me->SetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE, atpower);
  2429. + me->UpdateAttackPowerAndDamage();
  2430. + }
  2431. +
  2432. + //ARMOR
  2433. + value = float(basearmor);
  2434. + //get minion's armor and give 35% to pet (just as for real pets)
  2435. + value += m_creatureOwner->GetModifierValue(UNIT_MOD_ARMOR, BASE_VALUE)*0.35f;
  2436. + me->SetModifierValue(UNIT_MOD_ARMOR, BASE_VALUE, value);
  2437. + me->UpdateArmor();//buffs will be took in consideration here
  2438. +
  2439. + //RESISTANCES
  2440. + //based on minion's resistances gain x0.4
  2441. + for (uint8 i = SPELL_SCHOOL_HOLY; i != MAX_SPELL_SCHOOL; ++i)
  2442. + {
  2443. + value = float(master->GetResistance(SpellSchools(i)));
  2444. + me->SetModifierValue(UnitMods(UNIT_MOD_RESISTANCE_START + i), BASE_VALUE, 0.4f*(value*2.5f + float(mylevel*2)));
  2445. + me->UpdateResistances(i);
  2446. + }
  2447. +
  2448. + //DAMAGE TAKEN
  2449. + //just get minion's reduction and apply to pet
  2450. + value = m_creatureOwner->GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN, SPELL_SCHOOL_MASK_NORMAL);
  2451. + if (value > 1.f)
  2452. + value -= 1.f;
  2453. + else
  2454. + value = 1.f - value;//get reduction even if owner's is 1.0+
  2455. + value = std::min(0.42f, value);
  2456. + value/= 0.01f; //here we get percent like 0.42/0.01 = 42% (value * 100.f)
  2457. + RefreshAura(DMG_TAKEN, int8(value/6.f));//so max aura count = 10
  2458. +
  2459. + //HEALTH
  2460. + _OnHealthUpdate(petType, mylevel);
  2461. +
  2462. +//////RATINGS//////
  2463. + //ok now, pet receives 100% of its master's ratings
  2464. +
  2465. + //HASTE
  2466. + haste = m_creatureOwner->GetBotAI()->GetHaste();
  2467. + RefreshAura(HASTE, haste);//spell haste
  2468. + RefreshAura(HASTE2, haste);//melee haste
  2469. +
  2470. + //HIT
  2471. + hit = m_creatureOwner->GetBotAI()->GetHitRating();
  2472. + RefreshAura(PRECISION, int8(hit) + mylevel/39);//melee
  2473. + RefreshAura(PRECISION2, int8(hit) + mylevel/39);//spell
  2474. +
  2475. + //CRIT
  2476. + //chose melee or ranged cuz crit rating increases melee/spell, and hunter benefits from agility
  2477. + value = master->GetUnitCriticalChance((master->getClass() == CLASS_HUNTER ? RANGED_ATTACK : BASE_ATTACK), me);
  2478. + if (crit_mod != 1.0f)
  2479. + value *= crit_mod;
  2480. + RefreshAura(CRITS, int8(value/5.f) + mylevel/39);
  2481. +
  2482. + //PARRY
  2483. + value = master->GetFloatValue(PLAYER_PARRY_PERCENTAGE);
  2484. + if (master->GetBotTankGuid() == me->GetGUID())//feral cannot parry so let it be base 5%
  2485. + value += 10.f;
  2486. + if (value > 65.f)
  2487. + value = 65.f;
  2488. + float parryAndDodge = value;//set temp value, this is needed to keep total avoidance within 75%
  2489. + RefreshAura(PARRY, int8(value/5.f));
  2490. +
  2491. + //DODGE
  2492. + value = master->GetUnitDodgeChance();
  2493. + value = value > 5.f ? value - 5.f : 0.f;//remove base chance if possible
  2494. + if (master->GetBotTankGuid() == me->GetGUID())
  2495. + value += 10.f;
  2496. + if (value > 65.f)
  2497. + value = 65.f;
  2498. + if (parryAndDodge + value > 65.f)
  2499. + value = 65.f - parryAndDodge;//do not allow avoidance to be more than 75% (base 5+5)
  2500. + RefreshAura(DODGE, int8(value/5.f));
  2501. +
  2502. + //MANA
  2503. + _OnManaUpdate(petType, mylevel);
  2504. +
  2505. + //MANA REGEN
  2506. + if (mylevel >= 40 && me->getPowerType() == POWER_MANA)
  2507. + {
  2508. + //let regen rate be same as stats rate x0.3
  2509. + regen_mp5 = m_creatureOwner->GetBotAI()->GetManaRegen()*0.3f;
  2510. + if (regen_mp5 >= 45.f)
  2511. + {
  2512. + me->RemoveAurasDueToSpell(MANAREGEN100);
  2513. + me->RemoveAurasDueToSpell(MANAREGEN45);
  2514. + if (regen_mp5 > 200.f) RefreshAura(MANAREGEN100,int8(regen_mp5/100.f) + mylevel/20);
  2515. + else/*if (regen_mp5 > 150.f)*/RefreshAura(MANAREGEN45, int8(regen_mp5/45.f) + mylevel/20);
  2516. + }
  2517. + }
  2518. +
  2519. + //SPELL POWER
  2520. + if (mylevel >= 40 && spp_mod != 0.f)
  2521. + {
  2522. + switch (m_creatureOwner->GetBotClass())
  2523. + {
  2524. + case CLASS_WARLOCK:
  2525. + value = float(m_creatureOwner->GetBotAI()->GetSpellPower());
  2526. + break;
  2527. + default://some weird class or NYI
  2528. + value = 0.f;
  2529. + break;
  2530. + }
  2531. + m_spellpower = int32(value*spp_mod);
  2532. + me->RemoveAurasDueToSpell(SPELL_BONUS_250);
  2533. + me->RemoveAurasDueToSpell(SPELL_BONUS_150);
  2534. + me->RemoveAurasDueToSpell(SPELL_BONUS_50);
  2535. + if (mylevel < 60) RefreshAura(SPELL_BONUS_50, m_spellpower/50);
  2536. + else if (mylevel < 80) RefreshAura(SPELL_BONUS_150, m_spellpower/150 + 1);
  2537. + else RefreshAura(SPELL_BONUS_250, m_spellpower/250 + 2);
  2538. + }
  2539. +
  2540. + if (force)
  2541. + {
  2542. + me->SetFullHealth();
  2543. + me->SetPower(POWER_MANA, me->GetMaxPower(POWER_MANA));
  2544. + }
  2545. +
  2546. + shouldUpdateStats = false;
  2547. +}
  2548. +//Emotion-based action
  2549. +void bot_ai::ReceiveEmote(Player* player, uint32 emote)
  2550. +{
  2551. + switch (emote)
  2552. + {
  2553. + case TEXT_EMOTE_BONK:
  2554. + listAuras(player, me);
  2555. + break;
  2556. + case TEXT_EMOTE_SALUTE:
  2557. + listAuras(player, player);
  2558. + break;
  2559. + case TEXT_EMOTE_STAND:
  2560. + if (!IsMinionAI())
  2561. + return;
  2562. + if (master != player)
  2563. + {
  2564. + me->HandleEmoteCommand(EMOTE_ONESHOT_RUDE);
  2565. + return;
  2566. + }
  2567. + SetBotCommandState(COMMAND_STAY);
  2568. + me->MonsterWhisper("Standing Still.", player->GetGUID());
  2569. + break;
  2570. + case TEXT_EMOTE_WAVE:
  2571. + if (!IsMinionAI())
  2572. + return;
  2573. + if (master != player)
  2574. + {
  2575. + me->HandleEmoteCommand(EMOTE_ONESHOT_RUDE);
  2576. + return;
  2577. + }
  2578. + SetBotCommandState(COMMAND_FOLLOW, true);
  2579. + me->MonsterWhisper("Following!", player->GetGUID());
  2580. + break;
  2581. + default:
  2582. + break;
  2583. + }
  2584. +}
  2585. +
  2586. +//ISINBOTPARTY
  2587. +//Returns group members (and their npcbots too)
  2588. +//For now all your puppets are in your group automatically
  2589. +bool bot_ai::IsInBotParty(Unit* unit) const
  2590. +{
  2591. + if (!unit) return false;
  2592. + if (unit == me || unit == master) return true;
  2593. +
  2594. + //cheap check
  2595. + if (Group* gr = master->GetGroup())
  2596. + {
  2597. + //group member case
  2598. + if (gr->IsMember(unit->GetGUID()))
  2599. + return true;
  2600. + //pointed target case
  2601. + for (uint8 i = 0; i != TARGETICONCOUNT; ++i)
  2602. + if (healTargetIconFlags & GroupIconsFlags[i])
  2603. + if (uint64 guid = gr->GetTargetIcons()[i])//check this one
  2604. + if (guid == unit->GetGUID())
  2605. + if (unit->GetReactionTo(master) >= REP_NEUTRAL &&
  2606. + master->getVictim() != unit &&
  2607. + unit->getVictim() != master)
  2608. + return true;
  2609. + }
  2610. +
  2611. + //Player-controlled creature case
  2612. + if (Creature* cre = unit->ToCreature())
  2613. + {
  2614. + //npcbot/npcbot's pet case
  2615. + if (Player* owner = cre->GetBotOwner())
  2616. + {
  2617. + if (owner == master)
  2618. + return true;
  2619. + }
  2620. + //pets, minions, guardians etc.
  2621. + else
  2622. + {
  2623. + uint64 ownerGuid = unit->GetOwnerGUID();
  2624. + //controlled by group member
  2625. + if (Group* gr = master->GetGroup())
  2626. + if (gr->IsMember(ownerGuid))
  2627. + return true;
  2628. + }
  2629. + }
  2630. +
  2631. + return false;
  2632. +}
  2633. +
  2634. +//REFRESHAURA
  2635. +//Applies/reapplies aura stacks
  2636. +bool bot_ai::RefreshAura(uint32 spell, int8 count, Unit* target) const
  2637. +{
  2638. + if (!spell)
  2639. + return false;
  2640. + if (!target)
  2641. + target = me;
  2642. + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spell);
  2643. + if (!spellInfo)
  2644. + return false;
  2645. + //if (!spellInfo->IsPassive())
  2646. + //{
  2647. + // sLog->outError(LOG_FILTER_PLAYER, "bot_ai::RefreshAura(): %s received spell %u (%s) which is not a passive spell!", target->GetName().c_str(), spell, spellInfo->SpellName[0]);
  2648. + // //return false;
  2649. + //}
  2650. + if (target->HasAura(spell))
  2651. + target->RemoveAurasDueToSpell(spell);
  2652. + if (count > 0)
  2653. + for (uint8 i = 0; i < count; ++i)
  2654. + target->AddAura(spellInfo, MAX_EFFECT_MASK, target);
  2655. + return true;
  2656. +}
  2657. +//CHECKAURAS
  2658. +//Updates bot's condition once a while
  2659. +void bot_minion_ai::CheckAuras(bool force)
  2660. +{
  2661. + if (checkAurasTimer > 0 && !force) return;
  2662. + if (checkAurasTimer == 0)
  2663. + {
  2664. + checkAurasTimer = 10 + master->GetNpcBotsCount()/2;
  2665. + if (m_botCommandState != COMMAND_FOLLOW && m_botCommandState != COMMAND_STAY)
  2666. + {
  2667. + opponent = me->getVictim();
  2668. + if (opponent)
  2669. + {
  2670. + switch (me->GetBotClass())
  2671. + {
  2672. + case CLASS_MAGE: case CLASS_DRUID: case CLASS_WARLOCK: case CLASS_PRIEST:/* case CLASS_SHAMAN:*/
  2673. + CalculateAttackPos(opponent, attackpos);
  2674. + if (me->GetDistance(attackpos) > 8)
  2675. + GetInPosition(true, true, opponent, &attackpos);
  2676. + break;
  2677. + default:
  2678. + if (me->GetDistance(opponent) > 1.5f)
  2679. + GetInPosition(true, false);
  2680. + break;
  2681. + }
  2682. + }
  2683. + }
  2684. + if (shouldUpdateStats)
  2685. + setStats(me->GetBotClass(), me->getRace(), master->getLevel());
  2686. + else
  2687. + {
  2688. + UpdateHealth();
  2689. + UpdateMana();
  2690. + }
  2691. + if (rezz_cd > 0)
  2692. + --rezz_cd;
  2693. + if (clear_cd > 0)
  2694. + --clear_cd;
  2695. + else
  2696. + {
  2697. + FindTank();
  2698. + clear_cd = 15;
  2699. + }
  2700. + return;
  2701. + }
  2702. + else if (force)
  2703. + {
  2704. + if (!opponent)
  2705. + {
  2706. + if (master->isDead())
  2707. + {
  2708. + //If ghost move to corpse, else move to dead player
  2709. + if (master->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST))
  2710. + {
  2711. + Corpse* corpse = master->GetCorpse();
  2712. + if (corpse && me->GetMap() == corpse->FindMap() && !me->isInCombat() && !me->HasUnitState(UNIT_STATE_MOVING) && !IsCasting() && !CCed(me) && me->GetDistance(corpse) > 5)
  2713. + me->GetMotionMaster()->MovePoint(corpse->GetMapId(), *corpse);
  2714. + }
  2715. + else
  2716. + {
  2717. + if (m_botCommandState != COMMAND_FOLLOW || me->GetDistance(master) > 30 - 20 * (!me->IsWithinLOSInMap(master)))
  2718. + Follow(true);
  2719. + }
  2720. + }
  2721. + else if (m_botCommandState != COMMAND_STAY && !IsCasting())
  2722. + {
  2723. + CalculatePos(pos);
  2724. + uint8 followdist = master->GetBotFollowDist();
  2725. + if (me->GetExactDist(&pos) > (followdist > 8 ? 4 + followdist/2*(!master->isMoving()) : 8))
  2726. + Follow(true, &pos); // check if doing nothing
  2727. + }
  2728. + }
  2729. + if (!IsCasting())
  2730. + {
  2731. + if (me->isInCombat())
  2732. + {
  2733. + if (me->GetSheath() != SHEATH_STATE_MELEE)
  2734. + me->SetSheath(SHEATH_STATE_MELEE);
  2735. + }
  2736. + else if (me->IsStandState() && me->GetSheath() != SHEATH_STATE_UNARMED && Rand() < 50)
  2737. + me->SetSheath(SHEATH_STATE_UNARMED);
  2738. + }
  2739. + UpdateMountedState();
  2740. + UpdateStandState();
  2741. + UpdateRations();
  2742. + }
  2743. +}
  2744. +void bot_pet_ai::CheckAuras(bool /*force*/)
  2745. +{
  2746. + if (checkAurasTimer > 0) return;
  2747. + checkAurasTimer = 10 + master->GetNpcBotsCount()/2;
  2748. + if (m_botCommandState != COMMAND_FOLLOW && m_botCommandState != COMMAND_STAY)
  2749. + {
  2750. + opponent = me->getVictim();
  2751. + if (opponent)
  2752. + {
  2753. + switch (GetPetType(me))
  2754. + {
  2755. + case PET_TYPE_IMP:
  2756. + CalculateAttackPos(opponent, attackpos);
  2757. + if (me->GetDistance(attackpos) > 8)
  2758. + GetInPosition(true, true, opponent, &attackpos);
  2759. + break;
  2760. + default:
  2761. + if (me->GetDistance(opponent) > 1.5f)
  2762. + GetInPosition(true, false);
  2763. + break;
  2764. + }
  2765. + }
  2766. + }
  2767. + if (clear_cd > 0)
  2768. + --clear_cd;
  2769. + else
  2770. + {
  2771. + FindTank();
  2772. + clear_cd = 15;
  2773. + }
  2774. + return;
  2775. +}
  2776. +
  2777. +bool bot_ai::CanBotAttack(Unit* target, int8 byspell) const
  2778. +{
  2779. + if (!target) return false;
  2780. + uint8 followdist = master->GetBotFollowDist();
  2781. + float foldist = _getAttackDistance(float(followdist));
  2782. + return
  2783. + (target->isAlive() &&
  2784. + target->IsVisible() &&
  2785. + (master->isDead() || target->GetTypeId() == TYPEID_PLAYER || target->isPet() ||
  2786. + (target->GetDistance(master) < foldist && me->GetDistance(master) < followdist)) &&//if master is killed pursue to the end
  2787. + target->isTargetableForAttack() &&
  2788. + !IsInBotParty(target) &&
  2789. + (target->IsHostileTo(master) ||
  2790. + (target->GetReactionTo(master) < REP_FRIENDLY && master->getVictim() == target && (master->isInCombat() || target->isInCombat())) ||//master has pointed this target
  2791. + target->IsHostileTo(me)) &&//if master is controlled
  2792. + //target->IsWithinLOSInMap(me) &&
  2793. + (byspell == -1 || !target->IsImmunedToDamage(byspell ? SPELL_SCHOOL_MASK_MAGIC : SPELL_SCHOOL_MASK_NORMAL)));
  2794. +}
  2795. +//GETTARGET
  2796. +//Returns attack target or 'no target'
  2797. +//uses follow distance if range isn't set
  2798. +Unit* bot_ai::getTarget(bool byspell, bool ranged, bool &reset) const
  2799. +{
  2800. + //check if no need to change target
  2801. + Unit* u = master->getVictim();
  2802. + Unit* mytar = me->getVictim();
  2803. + if (!mytar && IsMinionAI())
  2804. + if (Creature* pet = me->GetBotsPet())
  2805. + mytar = pet->getVictim();
  2806. +
  2807. + if (u && u == mytar)
  2808. + {
  2809. + //sLog->outError(LOG_FILTER_PLAYER, "bot %s continues attack common target %s", me->GetName().c_str(), u->GetName().c_str());
  2810. + return u;//forced
  2811. + }
  2812. + //Follow if...
  2813. + uint8 followdist = master->GetBotFollowDist();
  2814. + float foldist = _getAttackDistance(float(followdist));
  2815. + if (!u && master->isAlive() && (me->GetDistance(master) > foldist || (mytar && master->GetDistance(mytar) > foldist && me->GetDistance(master) > foldist)))
  2816. + {
  2817. + //sLog->outError(LOG_FILTER_PLAYER, "bot %s cannot attack target %s, too far away", me->GetName().c_str(), mytar ? mytar->GetName().c_str() : "");
  2818. + return NULL;
  2819. + }
  2820. +
  2821. + if (u && (master->isInCombat() || u->isInCombat()) && !InDuel(u) && !IsInBotParty(u))
  2822. + {
  2823. + //sLog->outError(LOG_FILTER_PLAYER, "bot %s starts attack master's target %s", me->GetName().c_str(), u->GetName().c_str());
  2824. + return u;
  2825. + }
  2826. +
  2827. + if (CanBotAttack(mytar, byspell) && !InDuel(mytar))
  2828. + {
  2829. + //sLog->outError(LOG_FILTER_PLAYER, "bot %s continues attack its target %s", me->GetName().c_str(), mytar->GetName().c_str());
  2830. + if (me->GetDistance(mytar) > (ranged ? 20.f : 5.f) && m_botCommandState != COMMAND_STAY && m_botCommandState != COMMAND_FOLLOW)
  2831. + reset = true;
  2832. + return mytar;
  2833. + }
  2834. +
  2835. + if (followdist == 0 && master->isAlive())
  2836. + return NULL; //do not bother
  2837. +
  2838. + //check group
  2839. + Group* gr = master->GetGroup();
  2840. + if (!gr)
  2841. + {
  2842. + for (uint8 i = 0; i != master->GetMaxNpcBots(); ++i)
  2843. + {
  2844. + Creature* bot = master->GetBotMap(i)->_Cre();
  2845. + if (!bot || !bot->InSamePhase(me) || bot == me) continue;
  2846. + u = bot->getVictim();
  2847. + if (u && CanBotAttack(u, byspell) &&
  2848. + (bot->isInCombat() || u->isInCombat()) &&
  2849. + (master->isDead() || master->GetDistance(u) < foldist))
  2850. + {
  2851. + //sLog->outError(LOG_FILTER_PLAYER, "bot %s hooked %s's victim %s", me->GetName().c_str(), bot->GetName().c_str(), u->GetName().c_str());
  2852. + return u;
  2853. + }
  2854. + Creature* pet = bot->GetIAmABot() ? bot->GetBotsPet() : NULL;
  2855. + if (!pet || !pet->InSamePhase(me)) continue;
  2856. + u = pet->getVictim();
  2857. + if (u && CanBotAttack(u, byspell) &&
  2858. + (pet->isInCombat() || u->isInCombat()) &&
  2859. + (master->isDead() || master->GetDistance(u) < foldist))
  2860. + {
  2861. + //sLog->outError(LOG_FILTER_PLAYER, "bot %s hooked %s's victim %s", me->GetName().c_str(), pet->GetName().c_str(), u->GetName().c_str());
  2862. + return u;
  2863. + }
  2864. + }
  2865. + }
  2866. + else
  2867. + {
  2868. + for (GroupReference* ref = gr->GetFirstMember(); ref != NULL; ref = ref->next())
  2869. + {
  2870. + Player* pl = ref->getSource();
  2871. + if (!pl || !pl->IsInWorld() || pl->IsBeingTeleported()) continue;
  2872. + if (me->GetMap() != pl->FindMap() || !pl->InSamePhase(me)) continue;
  2873. + u = pl->getVictim();
  2874. + if (u && pl != master && CanBotAttack(u, byspell) &&
  2875. + (pl->isInCombat() || u->isInCombat()) &&
  2876. + (master->isDead() || master->GetDistance(u) < foldist))
  2877. + {
  2878. + //sLog->outError(LOG_FILTER_PLAYER, "bot %s hooked %s's victim %s", me->GetName().c_str(), pl->GetName().c_str(), u->GetName().c_str());
  2879. + return u;
  2880. + }
  2881. + if (!pl->HaveBot()) continue;
  2882. + for (uint8 i = 0; i != pl->GetMaxNpcBots(); ++i)
  2883. + {
  2884. + Creature* bot = pl->GetBotMap(i)->_Cre();
  2885. + if (!bot || !bot->InSamePhase(me) || bot == me) continue;
  2886. + if (!bot->IsInWorld()) continue;
  2887. + if (me->GetMap() != bot->FindMap()) continue;
  2888. + u = bot->getVictim();
  2889. + if (u && CanBotAttack(u, byspell) &&
  2890. + (bot->isInCombat() || u->isInCombat()) &&
  2891. + (master->isDead() || master->GetDistance(u) < foldist))
  2892. + {
  2893. + //sLog->outError(LOG_FILTER_PLAYER, "bot %s hooked %s's victim %s", me->GetName().c_str(), bot->GetName().c_str(), u->GetName().c_str());
  2894. + return u;
  2895. + }
  2896. + Creature* pet = bot->GetIAmABot() ? bot->GetBotsPet() : NULL;
  2897. + if (!pet || !pet->InSamePhase(me)) continue;
  2898. + if (!pet->IsInWorld()) continue;
  2899. + if (me->GetMap() != pet->FindMap()) continue;
  2900. + u = pet->getVictim();
  2901. + if (u && CanBotAttack(u, byspell) &&
  2902. + (pet->isInCombat() || u->isInCombat()) &&
  2903. + (master->isDead() || master->GetDistance(u) < foldist))
  2904. + {
  2905. + //sLog->outError(LOG_FILTER_PLAYER, "bot %s hooked %s's victim %s", me->GetName().c_str(), pet->GetName().c_str(), u->GetName().c_str());
  2906. + return u;
  2907. + }
  2908. + }
  2909. + }
  2910. + }
  2911. +
  2912. + //check targets around
  2913. + Unit* t = NULL;
  2914. + float maxdist = InitAttackRange(float(followdist), ranged);
  2915. + //first cycle we search non-cced target, then, if not found, check all
  2916. + for (uint8 i = 0; i != 2; ++i)
  2917. + {
  2918. + if (!t)
  2919. + {
  2920. + bool attackCC = i;
  2921. +
  2922. + CellCoord p(Trinity::ComputeCellCoord(me->GetPositionX(), me->GetPositionY()));
  2923. + Cell cell(p);
  2924. + cell.SetNoCreate();
  2925. +
  2926. + NearestHostileUnitCheck check(me, maxdist, byspell, this, attackCC);
  2927. + Trinity::UnitLastSearcher <NearestHostileUnitCheck> searcher(master, t, check);
  2928. + me->VisitNearbyWorldObject(maxdist, searcher);
  2929. +
  2930. + TypeContainerVisitor<Trinity::UnitLastSearcher <NearestHostileUnitCheck>, WorldTypeMapContainer > world_unit_searcher(searcher);
  2931. + TypeContainerVisitor<Trinity::UnitLastSearcher <NearestHostileUnitCheck>, GridTypeMapContainer > grid_unit_searcher(searcher);
  2932. + cell.Visit(p, world_unit_searcher, *master->GetMap(), *master, maxdist);
  2933. + cell.Visit(p, grid_unit_searcher, *master->GetMap(), *master, maxdist);
  2934. + }
  2935. + }
  2936. +
  2937. + if (t && opponent && t != opponent)
  2938. + {
  2939. + //sLog->outError(LOG_FILTER_PLAYER, "bot %s has Found new target %s", me->GetName().c_str(), t->GetName().c_str());
  2940. + reset = true;
  2941. + }
  2942. + return t;
  2943. +}
  2944. +//'CanAttack' function
  2945. +bool bot_ai::CheckAttackTarget(uint8 botOrPetType)
  2946. +{
  2947. + bool byspell = false, ranged = false, reset = false;
  2948. + if (IsMinionAI())
  2949. + {
  2950. + switch (botOrPetType)
  2951. + {
  2952. + case CLASS_DRUID:
  2953. + byspell = me->GetShapeshiftForm() == FORM_NONE ||
  2954. + me->GetShapeshiftForm() == FORM_TREE ||
  2955. + me->GetShapeshiftForm() == FORM_MOONKIN;
  2956. + ranged = byspell;
  2957. + break;
  2958. + case CLASS_PRIEST:
  2959. + case CLASS_MAGE:
  2960. + case CLASS_WARLOCK:
  2961. + case CLASS_SHAMAN:
  2962. + byspell = true;
  2963. + ranged = true;
  2964. + break;
  2965. + case CLASS_HUNTER:
  2966. + ranged = true;
  2967. + break;
  2968. + default:
  2969. + break;
  2970. + }
  2971. + }
  2972. + else
  2973. + {
  2974. + switch (botOrPetType)
  2975. + {
  2976. + case PET_TYPE_IMP:
  2977. + byspell = true;
  2978. + ranged = true;
  2979. + break;
  2980. + default:
  2981. + break;
  2982. + }
  2983. + }
  2984. +
  2985. + opponent = getTarget(byspell, ranged, reset);
  2986. + if (!opponent)
  2987. + {
  2988. + me->AttackStop();
  2989. + return false;
  2990. + }
  2991. +
  2992. + if (reset)
  2993. + m_botCommandState = COMMAND_ABANDON;//reset AttackStart()
  2994. +
  2995. + if (opponent != me->getVictim())
  2996. + me->Attack(opponent, !ranged);
  2997. + return true;
  2998. +}
  2999. +//POSITION
  3000. +void bot_ai::CalculateAttackPos(Unit* target, Position& pos) const
  3001. +{
  3002. + uint8 followdist = master->GetBotFollowDist();
  3003. + float x(0),y(0),z(0),
  3004. + dist = float(6 + urand(followdist/4, followdist/3)),
  3005. + angle = target->GetAngle(me);
  3006. + dist = std::min(dist, 20.f);
  3007. + if (me->GetIAmABotsPet())
  3008. + dist *= 0.5f;
  3009. + float clockwise = RAND(1.f,-1.f);
  3010. + for (uint8 i = 0; i != 5; ++i)
  3011. + {
  3012. + target->GetNearPoint(me, x, y, z, me->GetObjectSize()/2.f, dist, angle);
  3013. + bool toofaraway = master->GetDistance(x,y,z) > (followdist > 30 ? 30.f : followdist < 20 ? 20.f : float(followdist));
  3014. + bool outoflos = !target->IsWithinLOS(x,y,z);
  3015. + if (toofaraway || outoflos)
  3016. + {
  3017. + if (toofaraway)
  3018. + angle = target->GetAngle(master) + frand(0.f, M_PI*0.5f) * clockwise;
  3019. + if (outoflos)
  3020. + dist *= 0.5f;
  3021. + }
  3022. + else
  3023. + {
  3024. + dist *= 0.75f;
  3025. + break;
  3026. + }
  3027. + }
  3028. + pos.m_positionX = x;
  3029. + pos.m_positionY = y;
  3030. + pos.m_positionZ = z;
  3031. +}
  3032. +// Forces bot to chase opponent (if ranged then distance depends on follow distance)
  3033. +void bot_ai::GetInPosition(bool force, bool ranged, Unit* newtarget, Position* mypos)
  3034. +{
  3035. + if (me->HasUnitState(UNIT_STATE_ROOT)) return;
  3036. + if (!newtarget)
  3037. + newtarget = me->getVictim();
  3038. + if (!newtarget)
  3039. + return;
  3040. + if ((!newtarget->isInCombat() || m_botCommandState == COMMAND_STAY) && !force)
  3041. + return;
  3042. + if (IsCasting())
  3043. + return;
  3044. + uint8 followdist = master->GetBotFollowDist();
  3045. + if (ranged)
  3046. + {
  3047. + if (newtarget->GetTypeId() == TYPEID_PLAYER &&
  3048. + me->GetDistance(newtarget) < 6 + urand(followdist/4, followdist/3)) return;//do not allow constant runaway from player
  3049. + if (!mypos)
  3050. + CalculateAttackPos(newtarget, attackpos);
  3051. + else
  3052. + {
  3053. + attackpos.m_positionX = mypos->m_positionX;
  3054. + attackpos.m_positionY = mypos->m_positionY;
  3055. + attackpos.m_positionZ = mypos->m_positionZ;
  3056. + }
  3057. + if (me->GetDistance(attackpos) > 8)
  3058. + me->GetMotionMaster()->MovePoint(newtarget->GetMapId(), attackpos);
  3059. + }
  3060. + else
  3061. + me->GetMotionMaster()->MoveChase(newtarget);
  3062. + if (newtarget != me->getVictim())
  3063. + me->Attack(newtarget, !ranged);
  3064. +}
  3065. +
  3066. +bool bot_ai::MoveBehind(Unit& target) const
  3067. +{
  3068. + if (me->HasUnitState(UNIT_STATE_ROOT)) return false;
  3069. + if (target.IsWithinCombatRange(me, ATTACK_DISTANCE) &&
  3070. + target.HasInArc(M_PI, me) &&
  3071. + tank != me &&
  3072. + (me->GetBotClass() == CLASS_ROGUE ? target.getVictim() != me || CCed(&target) : target.getVictim() != me && !CCed(&target)))
  3073. + {
  3074. + float x(0),y(0),z(0);
  3075. + target.GetNearPoint(me, x, y, z, me->GetObjectSize()/3, 0.1f, me->GetAngle(&target));
  3076. + me->GetMotionMaster()->MovePoint(target.GetMapId(), x, y, z);
  3077. + return true;
  3078. + }
  3079. + return false;
  3080. +}
  3081. +//MOUNT SUPPORT
  3082. +void bot_minion_ai::UpdateMountedState()
  3083. +{
  3084. + //DEBUG
  3085. + if (master->IsMounted() && me->IsMounted())
  3086. + {
  3087. + if ((master->HasAuraType(SPELL_AURA_FLY) || master->HasUnitMovementFlag(MOVEMENTFLAG_CAN_FLY) || master->HasUnitMovementFlag(MOVEMENTFLAG_FLYING)))
  3088. + {
  3089. + //creature don't benefit from mount flight speed, so force it
  3090. + if (me->GetSpeed(MOVE_FLIGHT) != master->GetSpeed(MOVE_FLIGHT)/2)
  3091. + me->SetSpeed(MOVE_FLIGHT, master->GetSpeed(MOVE_FLIGHT)/2);
  3092. + }
  3093. + return;
  3094. + }
  3095. + bool aura = me->HasAuraType(SPELL_AURA_MOUNTED);
  3096. + bool mounted = me->IsMounted();
  3097. + if ((!master->IsMounted() || aura != mounted || (me->isInCombat() && opponent)) && (aura || mounted))
  3098. + {
  3099. + me->RemoveAurasByType(SPELL_AURA_MOUNTED);
  3100. + me->Dismount();
  3101. + return;
  3102. + }
  3103. + //END DEBUG
  3104. + if (me->isInCombat() || IsCasting() || me->HasUnitMovementFlag(MOVEMENTFLAG_SWIMMING)) //IsInWater() is too much calculations
  3105. + return;
  3106. + //fly
  3107. + //if ((master->IsMounted() && master->HasAuraType(SPELL_AURA_FLY))/* || master->HasUnitMovementFlag(MOVEMENTFLAG_CAN_FLY) || master->HasUnitMovementFlag(MOVEMENTFLAG_FLYING)*/)
  3108. + //{
  3109. + // if (!me->IsMounted() || !me->HasAuraType(SPELL_AURA_FLY))
  3110. + // {
  3111. + // //if (me->GetBotClass() == CLASS_DRUID && InitSpell(FLY_FORM))//TODO
  3112. + // //{
  3113. + // //}
  3114. + // //else
  3115. + // {
  3116. + // uint32 mount = 0;
  3117. + // Unit::AuraEffectList const &mounts = master->GetAuraEffectsByType(SPELL_AURA_MOUNTED);
  3118. + // if (!mounts.empty())
  3119. + // mount = mounts.front()->GetId();
  3120. + // if (mount)
  3121. + // {
  3122. + // if (me->HasAuraType(SPELL_AURA_MOUNTED))
  3123. + // me->RemoveAurasByType(SPELL_AURA_MOUNTED);
  3124. + // if (doCast(me, mount))
  3125. + // {
  3126. + // if (Feasting())
  3127. + // {
  3128. + // me->RemoveAurasDueToSpell(DRINK);
  3129. + // me->RemoveAurasDueToSpell(EAT);
  3130. + // }
  3131. + // }
  3132. + // }
  3133. + // }
  3134. + // }
  3135. + //}
  3136. + ////ground
  3137. + /*else */
  3138. + if (master->IsMounted() && !me->IsMounted() && !master->isInCombat() && !me->isInCombat() && !me->getVictim())
  3139. + {
  3140. + uint32 mount = 0;
  3141. + Unit::AuraEffectList const &mounts = master->GetAuraEffectsByType(SPELL_AURA_MOUNTED);
  3142. + if (!mounts.empty())
  3143. + mount = mounts.front()->GetId();
  3144. + if (mount)
  3145. + {
  3146. + if (me->HasAuraType(SPELL_AURA_MOUNTED))
  3147. + me->RemoveAurasByType(SPELL_AURA_MOUNTED);
  3148. + if (Feasting())
  3149. + {
  3150. + me->RemoveAurasDueToSpell(DRINK);
  3151. + me->RemoveAurasDueToSpell(EAT);
  3152. + }
  3153. + if (doCast(me, mount))
  3154. + {
  3155. + return;
  3156. + }
  3157. + }
  3158. + }
  3159. +}
  3160. +//STANDSTATE
  3161. +void bot_minion_ai::UpdateStandState() const
  3162. +{
  3163. + if (master->getStandState() == UNIT_STAND_STATE_STAND &&
  3164. + me->getStandState() == UNIT_STAND_STATE_SIT &&
  3165. + !(me->GetInterruptMask() & AURA_INTERRUPT_FLAG_NOT_SEATED))
  3166. + me->SetStandState(UNIT_STAND_STATE_STAND);
  3167. + if ((master->getStandState() == UNIT_STAND_STATE_SIT || Feasting()) && !me->isInCombat() && !me->isMoving() &&
  3168. + me->getStandState() == UNIT_STAND_STATE_STAND)
  3169. + me->SetStandState(UNIT_STAND_STATE_SIT);
  3170. +
  3171. +}
  3172. +//RATIONS
  3173. +void bot_minion_ai::UpdateRations() const
  3174. +{
  3175. + if (me->isInCombat() || CCed(me))
  3176. + {
  3177. + if (me->HasAura(EAT)) me->RemoveAurasDueToSpell(EAT);
  3178. + if (me->HasAura(DRINK)) me->RemoveAurasDueToSpell(DRINK);
  3179. + }
  3180. +
  3181. + //drink
  3182. + if (me->getPowerType() == POWER_MANA && !me->IsMounted() && !me->isMoving() && !CCed(me) &&
  3183. + !me->isInCombat() && !IsCasting() && urand(0, 100) < 20 && GetManaPCT(me) < 80 &&
  3184. + !me->HasAura(DRINK))
  3185. + {
  3186. + me->CastSpell(me, DRINK);
  3187. + me->SetStandState(UNIT_STAND_STATE_SIT);
  3188. + }
  3189. + if (me->GetPower(POWER_MANA) < me->GetMaxPower(POWER_MANA) && me->HasAura(DRINK))
  3190. + me->ModifyPower(POWER_MANA, me->GetCreateMana()/20);
  3191. +
  3192. + //eat
  3193. + if (!me->IsMounted() && !me->isMoving() && !CCed(me) &&
  3194. + !me->isInCombat() && !IsCasting() && urand(0, 100) < 20 && GetHealthPCT(me) < 80 &&
  3195. + !me->HasAura(EAT))
  3196. + {
  3197. + me->CastSpell(me, EAT);
  3198. + me->SetStandState(UNIT_STAND_STATE_SIT);
  3199. + }
  3200. + if (me->GetHealth() < me->GetMaxHealth() && me->HasAura(EAT))
  3201. + me->SetHealth(me->GetHealth() + me->GetCreateHealth()/20);
  3202. +
  3203. + //check
  3204. + if (me->GetHealth() >= me->GetMaxHealth() && me->HasAura(EAT))
  3205. + me->RemoveAurasDueToSpell(EAT);
  3206. +
  3207. + if (me->getPowerType() == POWER_MANA &&
  3208. + me->GetPower(POWER_MANA) >= me->GetMaxPower(POWER_MANA) &&
  3209. + me->HasAura(DRINK))
  3210. + me->RemoveAurasDueToSpell(DRINK);
  3211. +}
  3212. +//PASSIVES
  3213. +// Used to apply common passives (run once)
  3214. +void bot_ai::ApplyPassives(uint8 botOrPetType) const
  3215. +{
  3216. + //me->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_TAUNT, true);
  3217. + //me->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_ATTACK_ME, true);
  3218. +
  3219. + //apply +healing taken
  3220. + if (master->getLevel() >= 60) RefreshAura(BOR);//+40%
  3221. + if (IsMinionAI())
  3222. + {
  3223. + //apply -threat mod
  3224. + switch (botOrPetType)
  3225. + {
  3226. + case CLASS_WARRIOR:
  3227. + RefreshAura(RCP,1);//-27%
  3228. + break;
  3229. + case CLASS_PRIEST:
  3230. + case CLASS_MAGE:
  3231. + case CLASS_ROGUE:
  3232. + RefreshAura(RCP,3);//-87%
  3233. + break;
  3234. + default:
  3235. + RefreshAura(RCP,2);//-54%
  3236. + break;
  3237. + }
  3238. + }
  3239. + else
  3240. + {
  3241. + switch (botOrPetType)
  3242. + {
  3243. + case PET_TYPE_VOIDWALKER:
  3244. + RefreshAura(DEFENSIVE_STANCE_PASSIVE,2);
  3245. + break;
  3246. + default:
  3247. + break;
  3248. + }
  3249. + }
  3250. +}
  3251. +//check if our party players are in duel. if so - ignore them, their opponents and any bots they have
  3252. +bool bot_ai::InDuel(Unit* target) const
  3253. +{
  3254. + if (!target) return false;
  3255. + bool isbot = target->GetTypeId() == TYPEID_UNIT && (target->ToCreature()->GetIAmABot() || target->ToCreature()->GetIAmABotsPet());
  3256. + Player* player = target->GetTypeId() == TYPEID_PLAYER ? target->ToPlayer() : isbot ? target->ToCreature()->GetBotOwner() : NULL;
  3257. + if (!player)
  3258. + {
  3259. + if (!target->IsControlledByPlayer())
  3260. + return false;
  3261. + player = target->GetCharmerOrOwnerPlayerOrPlayerItself();
  3262. + }
  3263. +
  3264. + return (player && player->duel && (IsInBotParty(player) || IsInBotParty(player->duel->opponent)));
  3265. +}
  3266. +//Used to find target for priest's dispels and mage's spellsteal (also shaman's purge in future)
  3267. +//Returns dispellable/stealable 'Any Hostile Unit Attacking BotParty'
  3268. +Unit* bot_minion_ai::FindHostileDispelTarget(float dist, bool stealable) const
  3269. +{
  3270. + CellCoord p(Trinity::ComputeCellCoord(me->GetPositionX(), me->GetPositionY()));
  3271. + Cell cell(p);
  3272. + cell.SetNoCreate();
  3273. +
  3274. + Unit* unit = NULL;
  3275. +
  3276. + HostileDispelTargetCheck check(me, dist, stealable, this);
  3277. + Trinity::UnitLastSearcher <HostileDispelTargetCheck> searcher(me, unit, check);
  3278. +
  3279. + TypeContainerVisitor<Trinity::UnitLastSearcher <HostileDispelTargetCheck>, WorldTypeMapContainer > world_unit_searcher(searcher);
  3280. + TypeContainerVisitor<Trinity::UnitLastSearcher <HostileDispelTargetCheck>, GridTypeMapContainer > grid_unit_searcher(searcher);
  3281. +
  3282. + cell.Visit(p, world_unit_searcher, *me->GetMap(), *me, dist);
  3283. + cell.Visit(p, grid_unit_searcher, *me->GetMap(), *me, dist);
  3284. +
  3285. + return unit;
  3286. +}
  3287. +//Finds single target affected by given spell (and given caster if is)
  3288. +//Can check:
  3289. +// hostile targets (hostile = 0) <default>
  3290. +// our party players (hostile = 1)
  3291. +// our party members (hostile = 2)
  3292. +// any friendly target (hostile = 3)
  3293. +// any target in range (hostile = any other value)
  3294. +Unit* bot_minion_ai::FindAffectedTarget(uint32 spellId, uint64 caster, float dist, uint8 hostile) const
  3295. +{
  3296. + if (master->GetMap()->Instanceable())
  3297. + dist = DEFAULT_VISIBILITY_INSTANCE;
  3298. +
  3299. + CellCoord p(Trinity::ComputeCellCoord(master->GetPositionX(), master->GetPositionY()));
  3300. + Cell cell(p);
  3301. + cell.SetNoCreate();
  3302. +
  3303. + Unit* unit = NULL;
  3304. +
  3305. + AffectedTargetCheck check(caster, dist, spellId, master, hostile);
  3306. + Trinity::UnitLastSearcher <AffectedTargetCheck> searcher(master, unit, check);
  3307. +
  3308. + TypeContainerVisitor<Trinity::UnitLastSearcher <AffectedTargetCheck>, WorldTypeMapContainer > world_unit_searcher(searcher);
  3309. + TypeContainerVisitor<Trinity::UnitLastSearcher <AffectedTargetCheck>, GridTypeMapContainer > grid_unit_searcher(searcher);
  3310. +
  3311. + cell.Visit(p, world_unit_searcher, *master->GetMap(), *master, dist);
  3312. + cell.Visit(p, grid_unit_searcher, *master->GetMap(), *master, dist);
  3313. +
  3314. + return unit;
  3315. +}
  3316. +//Finds target for mage's polymorph (maybe for Hex in future)
  3317. +Unit* bot_minion_ai::FindPolyTarget(float dist, Unit* currTarget) const
  3318. +{
  3319. + if (!currTarget)
  3320. + return NULL;
  3321. +
  3322. + CellCoord p(Trinity::ComputeCellCoord(me->GetPositionX(), me->GetPositionY()));
  3323. + Cell cell(p);
  3324. + cell.SetNoCreate();
  3325. +
  3326. + Unit* unit = NULL;
  3327. +
  3328. + PolyUnitCheck check(me, dist, currTarget);
  3329. + Trinity::UnitLastSearcher <PolyUnitCheck> searcher(me, unit, check);
  3330. +
  3331. + TypeContainerVisitor<Trinity::UnitLastSearcher <PolyUnitCheck>, WorldTypeMapContainer > world_unit_searcher(searcher);
  3332. + TypeContainerVisitor<Trinity::UnitLastSearcher <PolyUnitCheck>, GridTypeMapContainer > grid_unit_searcher(searcher);
  3333. +
  3334. + cell.Visit(p, world_unit_searcher, *me->GetMap(), *me, dist);
  3335. + cell.Visit(p, grid_unit_searcher, *me->GetMap(), *me, dist);
  3336. +
  3337. + return unit;
  3338. +}
  3339. +//Finds target for direct fear (warlock)
  3340. +Unit* bot_minion_ai::FindFearTarget(float dist) const
  3341. +{
  3342. + CellCoord p(Trinity::ComputeCellCoord(me->GetPositionX(), me->GetPositionY()));
  3343. + Cell cell(p);
  3344. + cell.SetNoCreate();
  3345. +
  3346. + Unit* unit = NULL;
  3347. +
  3348. + FearUnitCheck check(me, dist);
  3349. + Trinity::UnitLastSearcher <FearUnitCheck> searcher(me, unit, check);
  3350. +
  3351. + TypeContainerVisitor<Trinity::UnitLastSearcher <FearUnitCheck>, WorldTypeMapContainer > world_unit_searcher(searcher);
  3352. + TypeContainerVisitor<Trinity::UnitLastSearcher <FearUnitCheck>, GridTypeMapContainer > grid_unit_searcher(searcher);
  3353. +
  3354. + cell.Visit(p, world_unit_searcher, *me->GetMap(), *me, dist);
  3355. + cell.Visit(p, grid_unit_searcher, *me->GetMap(), *me, dist);
  3356. +
  3357. + return unit;
  3358. +}
  3359. +//Finds target for paladin's repentance
  3360. +Unit* bot_minion_ai::FindRepentanceTarget(float dist) const
  3361. +{
  3362. + CellCoord p(Trinity::ComputeCellCoord(me->GetPositionX(), me->GetPositionY()));
  3363. + Cell cell(p);
  3364. + cell.SetNoCreate();
  3365. +
  3366. + Unit* unit = NULL;
  3367. +
  3368. + StunUnitCheck check(me, dist);
  3369. + Trinity::UnitLastSearcher <StunUnitCheck> searcher(me, unit, check);
  3370. +
  3371. + TypeContainerVisitor<Trinity::UnitLastSearcher <StunUnitCheck>, WorldTypeMapContainer > world_unit_searcher(searcher);
  3372. + TypeContainerVisitor<Trinity::UnitLastSearcher <StunUnitCheck>, GridTypeMapContainer > grid_unit_searcher(searcher);
  3373. +
  3374. + cell.Visit(p, world_unit_searcher, *me->GetMap(), *me, dist);
  3375. + cell.Visit(p, grid_unit_searcher, *me->GetMap(), *me, dist);
  3376. +
  3377. + return unit;
  3378. +}
  3379. +//Finds target for priest's shackles
  3380. +Unit* bot_minion_ai::FindUndeadCCTarget(float dist, uint32 spellId/* = 0*/) const
  3381. +{
  3382. + CellCoord p(Trinity::ComputeCellCoord(me->GetPositionX(), me->GetPositionY()));
  3383. + Cell cell(p);
  3384. + cell.SetNoCreate();
  3385. +
  3386. + Unit* unit = NULL;
  3387. +
  3388. + UndeadCCUnitCheck check(me, dist, spellId);
  3389. + Trinity::UnitLastSearcher <UndeadCCUnitCheck> searcher(me, unit, check);
  3390. +
  3391. + TypeContainerVisitor<Trinity::UnitLastSearcher <UndeadCCUnitCheck>, WorldTypeMapContainer > world_unit_searcher(searcher);
  3392. + TypeContainerVisitor<Trinity::UnitLastSearcher <UndeadCCUnitCheck>, GridTypeMapContainer > grid_unit_searcher(searcher);
  3393. +
  3394. + cell.Visit(p, world_unit_searcher, *me->GetMap(), *me, dist);
  3395. + cell.Visit(p, grid_unit_searcher, *me->GetMap(), *me, dist);
  3396. +
  3397. + return unit;
  3398. +}
  3399. +//Finds target for druid's Entangling Roots
  3400. +Unit* bot_minion_ai::FindRootTarget(float dist, uint32 spellId) const
  3401. +{
  3402. + CellCoord p(Trinity::ComputeCellCoord(me->GetPositionX(), me->GetPositionY()));
  3403. + Cell cell(p);
  3404. + cell.SetNoCreate();
  3405. +
  3406. + Unit* unit = NULL;
  3407. +
  3408. + RootUnitCheck check(me, me->getVictim(), dist, spellId);
  3409. + Trinity::UnitLastSearcher <RootUnitCheck> searcher(me, unit, check);
  3410. +
  3411. + TypeContainerVisitor<Trinity::UnitLastSearcher <RootUnitCheck>, WorldTypeMapContainer > world_unit_searcher(searcher);
  3412. + TypeContainerVisitor<Trinity::UnitLastSearcher <RootUnitCheck>, GridTypeMapContainer > grid_unit_searcher(searcher);
  3413. +
  3414. + cell.Visit(p, world_unit_searcher, *me->GetMap(), *me, dist);
  3415. + cell.Visit(p, grid_unit_searcher, *me->GetMap(), *me, dist);
  3416. +
  3417. + return unit;
  3418. +}
  3419. +//Finds casting target (friend or enemy)
  3420. +Unit* bot_minion_ai::FindCastingTarget(float dist, bool isFriend, uint32 spellId) const
  3421. +{
  3422. + CellCoord p(Trinity::ComputeCellCoord(me->GetPositionX(), me->GetPositionY()));
  3423. + Cell cell(p);
  3424. + cell.SetNoCreate();
  3425. +
  3426. + Unit* unit = NULL;
  3427. +
  3428. + CastingUnitCheck check(me, dist, isFriend, spellId);
  3429. + Trinity::UnitLastSearcher <CastingUnitCheck> searcher(me, unit, check);
  3430. +
  3431. + TypeContainerVisitor<Trinity::UnitLastSearcher <CastingUnitCheck>, WorldTypeMapContainer > world_unit_searcher(searcher);
  3432. + TypeContainerVisitor<Trinity::UnitLastSearcher <CastingUnitCheck>, GridTypeMapContainer > grid_unit_searcher(searcher);
  3433. +
  3434. + cell.Visit(p, world_unit_searcher, *me->GetMap(), *me, dist);
  3435. + cell.Visit(p, grid_unit_searcher, *me->GetMap(), *me, dist);
  3436. +
  3437. + return unit;
  3438. +}
  3439. +// Returns target for AOE spell (blizzard, hurricane etc.) based on attackers count
  3440. +// Cycles through BotParty, first checks player and, if checked, npcbots
  3441. +// If checked, can return friendly target as target for AOE spell
  3442. +Unit* bot_minion_ai::FindAOETarget(float dist, bool checkbots, bool targetfriend) const
  3443. +{
  3444. + if (me->isMoving() || IsCasting()) return NULL;//prevent aoe casts while running away
  3445. + Unit* unit = NULL;
  3446. + Group* pGroup = master->GetGroup();
  3447. + if (!pGroup)
  3448. + {
  3449. + AttackerSet m_attackers = master->getAttackers();
  3450. + if (m_attackers.size() > 1)
  3451. + {
  3452. + uint32 mCount = 0;
  3453. + for(AttackerSet::iterator iter = m_attackers.begin(); iter != m_attackers.end(); ++iter)
  3454. + {
  3455. + if (!(*iter) || (*iter)->isDead()) continue;
  3456. + if ((*iter)->isMoving()) continue;
  3457. + if ((*iter)->HasBreakableByDamageCrowdControlAura())
  3458. + continue;
  3459. + if (me->GetDistance(*iter) < dist)
  3460. + ++mCount;
  3461. + }
  3462. + if (mCount > 1)
  3463. + {
  3464. + Unit* u = master->getVictim();
  3465. + if (mCount > 3 && targetfriend == true)
  3466. + unit = master;
  3467. + else if (u && FindSplashTarget(dist + 8, u))
  3468. + unit = u;
  3469. + }//end if
  3470. + }//end if
  3471. + if (!checkbots)
  3472. + return unit;
  3473. + for (uint8 i = 0; i != master->GetMaxNpcBots(); ++i)
  3474. + {
  3475. + Creature* bot = master->GetBotMap(i)->_Cre();
  3476. + if (!bot || bot->isDead() || !bot->IsInWorld() || me->GetDistance(bot) > dist) continue;
  3477. +
  3478. + AttackerSet b_attackers = bot->getAttackers();
  3479. + if (b_attackers.size() > 1)
  3480. + {
  3481. + uint32 mCount = 0;
  3482. + for(AttackerSet::iterator iter = b_attackers.begin(); iter != b_attackers.end(); ++iter)
  3483. + {
  3484. + if (!(*iter) || (*iter)->isDead()) continue;
  3485. + if ((*iter)->isMoving()) continue;
  3486. + if ((*iter)->HasBreakableByDamageCrowdControlAura())
  3487. + continue;
  3488. + if (me->GetDistance(*iter) < dist)
  3489. + ++mCount;
  3490. + }
  3491. + if (mCount > 1)
  3492. + {
  3493. + Unit* u = bot->getVictim();
  3494. + if (mCount > 3 && targetfriend == true)
  3495. + unit = bot;
  3496. + else if (u && FindSplashTarget(dist + 8, u))
  3497. + unit = u;
  3498. + }//end if
  3499. + }//end if
  3500. + if (unit) return unit;
  3501. + }//end for
  3502. + return unit;
  3503. + }
  3504. + bool Bots = false;
  3505. + for (GroupReference* itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
  3506. + {
  3507. + Player* tPlayer = itr->getSource();
  3508. + if (!tPlayer) continue;
  3509. + if (checkbots && tPlayer->HaveBot())
  3510. + Bots = true;
  3511. + if (!tPlayer->IsInWorld() || tPlayer->IsBeingTeleported()) continue;
  3512. + if (tPlayer->isDead() || me->GetMap() != tPlayer->FindMap()) continue;
  3513. + if (me->GetDistance(tPlayer) > 40) continue;
  3514. +
  3515. + AttackerSet m_attackers = tPlayer->getAttackers();
  3516. + if (m_attackers.size() > 1)
  3517. + {
  3518. + uint32 mCount = 0;
  3519. + for (AttackerSet::iterator iter = m_attackers.begin(); iter != m_attackers.end(); ++iter)
  3520. + {
  3521. + if (!(*iter) || (*iter)->isDead()) continue;
  3522. + if ((*iter)->isMoving()) continue;
  3523. + if (me->GetDistance(*iter) < dist)
  3524. + ++mCount;
  3525. + }
  3526. + if (mCount > 1)
  3527. + {
  3528. + Unit* u = tPlayer->getVictim();
  3529. + if (mCount > 3 && targetfriend == true)
  3530. + unit = tPlayer;
  3531. + else if (u && FindSplashTarget(dist + 8, u))
  3532. + unit = u;
  3533. + }//end if
  3534. + }//end if
  3535. + if (unit) return unit;
  3536. + }//end for
  3537. + if (!Bots) return NULL;
  3538. + for (GroupReference* itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
  3539. + {
  3540. + Player* tPlayer = itr->getSource();
  3541. + if (tPlayer == NULL || !tPlayer->HaveBot()) continue;
  3542. + if (!tPlayer->IsInWorld() || tPlayer->IsBeingTeleported()) continue;
  3543. + if (me->GetMap() != tPlayer->FindMap()) continue;
  3544. + for (uint8 i = 0; i != tPlayer->GetMaxNpcBots(); ++i)
  3545. + {
  3546. + Creature* bot = tPlayer->GetBotMap(i)->_Cre();
  3547. + if (!bot || bot->isDead() || me->GetMap() != bot->FindMap()) continue;
  3548. + if (!bot->IsInWorld()) continue;
  3549. + if (me->GetDistance(bot) > 40) continue;
  3550. +
  3551. + AttackerSet b_attackers = bot->getAttackers();
  3552. + if (b_attackers.size() > 1)
  3553. + {
  3554. + uint32 mCount = 0;
  3555. + for(AttackerSet::iterator iter = b_attackers.begin(); iter != b_attackers.end(); ++iter)
  3556. + {
  3557. + if (!(*iter) || (*iter)->isDead()) continue;
  3558. + if ((*iter)->isMoving()) continue;
  3559. + if (me->GetDistance(*iter) < dist)
  3560. + ++mCount;
  3561. + }
  3562. + if (mCount > 1)
  3563. + {
  3564. + Unit* u = bot->getVictim();
  3565. + if (mCount > 3 && targetfriend == true)
  3566. + unit = bot;
  3567. + else if (u && FindSplashTarget(dist + 8, u))
  3568. + unit = u;
  3569. + }//end if
  3570. + }//end if
  3571. + }//end for
  3572. + if (unit) return unit;
  3573. + }//end for
  3574. + return unit;
  3575. +}
  3576. +// Finds secondary target for spells like Cleave, Swipe, Mind Sear etc.
  3577. +Unit* bot_minion_ai::FindSplashTarget(float dist, Unit* To) const
  3578. +{
  3579. + if (!To)
  3580. + To = me->getVictim();
  3581. + if (!To)
  3582. + return NULL;
  3583. +
  3584. + if (me->GetDistance(To) > dist)
  3585. + return NULL;
  3586. +
  3587. + CellCoord p(Trinity::ComputeCellCoord(me->GetPositionX(), me->GetPositionY()));
  3588. + Cell cell(p);
  3589. + cell.SetNoCreate();
  3590. +
  3591. + Unit* unit = NULL;
  3592. +
  3593. + SecondEnemyCheck check(me, dist, To, this);
  3594. + Trinity::UnitLastSearcher <SecondEnemyCheck> searcher(me, unit, check);
  3595. +
  3596. + TypeContainerVisitor<Trinity::UnitLastSearcher <SecondEnemyCheck>, WorldTypeMapContainer > world_unit_searcher(searcher);
  3597. + TypeContainerVisitor<Trinity::UnitLastSearcher <SecondEnemyCheck>, GridTypeMapContainer > grid_unit_searcher(searcher);
  3598. +
  3599. + cell.Visit(p, world_unit_searcher, *me->GetMap(), *me, dist);
  3600. + cell.Visit(p, grid_unit_searcher, *me->GetMap(), *me, dist);
  3601. +
  3602. + return unit;
  3603. +}
  3604. +
  3605. +//////////
  3606. +//Internal
  3607. +//////////
  3608. +
  3609. +//Using rist-rank spell as source, returns spell of max rank allowed for given caster
  3610. +uint32 bot_ai::InitSpell(Unit* caster, uint32 spell)
  3611. +{
  3612. + SpellInfo const* info = sSpellMgr->GetSpellInfo(spell);
  3613. + if (!info)
  3614. + return 0;//weird spell with no info, disable it
  3615. +
  3616. + uint8 lvl = caster->getLevel();
  3617. + if (lvl < info->BaseLevel)//only 1st rank spells check
  3618. + return 0;//cannot use this spell
  3619. +
  3620. + if (SpellInfo const* spInfo = info->GetNextRankSpell())
  3621. + {
  3622. + if (lvl < spInfo->BaseLevel)
  3623. + return spell;//cannot use next rank, use this one
  3624. + else
  3625. + return InitSpell(caster, spInfo->Id);//can use next rank, forward check
  3626. + }
  3627. +
  3628. + return spell;//max rank, use this
  3629. +}
  3630. +//Health magement for minions
  3631. +//Including health calcs, set and regeneration
  3632. +void bot_minion_ai::_OnHealthUpdate(uint8 myclass, uint8 mylevel) const
  3633. +{
  3634. + //sLog->outError(LOG_FILTER_PLAYER, "_OnHealthUpdate(): updating bot %s", me->GetName().c_str());
  3635. + float pct = me->GetHealthPct();// needs for regeneration
  3636. + uint32 m_basehp = classinfo->basehealth;
  3637. + //sLog->outError(LOG_FILTER_PLAYER, "class base health: %u", m_basehp);
  3638. + me->SetCreateHealth(m_basehp);
  3639. + float stammod;
  3640. + switch (myclass)
  3641. + {
  3642. + case CLASS_WARRIOR: case CLASS_DEATH_KNIGHT: case BEAR:
  3643. + switch (master->getClass())
  3644. + {
  3645. + case CLASS_PRIEST: case CLASS_MAGE: case CLASS_WARLOCK:
  3646. + stammod = 16.f;
  3647. + break;
  3648. + case CLASS_DRUID: case CLASS_SHAMAN: case CLASS_HUNTER: case CLASS_ROGUE:
  3649. + stammod = 13.f;
  3650. + break;
  3651. + default: stammod = 9.8f; break;
  3652. + }
  3653. + break;
  3654. + case CLASS_PALADIN:
  3655. + switch (master->getClass())
  3656. + {
  3657. + case CLASS_PRIEST: case CLASS_MAGE: case CLASS_WARLOCK:
  3658. + stammod = 15.5f;
  3659. + break;
  3660. + case CLASS_DRUID: case CLASS_SHAMAN: case CLASS_HUNTER: case CLASS_ROGUE:
  3661. + stammod = 12.5f;
  3662. + break;
  3663. + case CLASS_PALADIN:
  3664. + stammod = 9.8f;
  3665. + break;
  3666. + default: stammod = 9.f; break;
  3667. + }
  3668. + break;
  3669. + case CLASS_PRIEST: case CLASS_MAGE: case CLASS_WARLOCK:
  3670. + switch (master->getClass())
  3671. + {
  3672. + case CLASS_PRIEST: case CLASS_MAGE: case CLASS_WARLOCK:
  3673. + stammod = 9.8f;
  3674. + break;
  3675. + case CLASS_DRUID: case CLASS_SHAMAN: case CLASS_HUNTER: case CLASS_ROGUE:
  3676. + stammod = 8.f;
  3677. + break;
  3678. + default: stammod = 5.f; break;
  3679. + }
  3680. + break;
  3681. + case CLASS_DRUID: case CAT: case CLASS_SHAMAN: case CLASS_HUNTER: case CLASS_ROGUE:
  3682. + switch (master->getClass())
  3683. + {
  3684. + case CLASS_PRIEST: case CLASS_MAGE: case CLASS_WARLOCK:
  3685. + stammod = 12.f;
  3686. + break;
  3687. + case CLASS_DRUID: case CLASS_SHAMAN: case CLASS_HUNTER: case CLASS_ROGUE:
  3688. + stammod = 9.8f;
  3689. + break;
  3690. + default: stammod = 8.f; break;
  3691. + }
  3692. + break;
  3693. + default: stammod = 10.f;
  3694. + break;
  3695. + }
  3696. + stammod -= 0.3f;
  3697. + //sLog->outError(LOG_FILTER_PLAYER, "stammod: %f", stammod);
  3698. +
  3699. + //manually pick up stamina from bot's buffs
  3700. + float stamValue = me->GetTotalStatValue(STAT_STAMINA);
  3701. + stamValue = std::max(stamValue - 18.f, 1.f); //remove base stamina (not calculated into health)
  3702. + //sLog->outError(LOG_FILTER_PLAYER, "bot's stats to health add: Stamina (%f), value: %f", stamValue, stamValue * 10.f);
  3703. + int32 hp_add = int32(stamValue * 10.f);
  3704. + //pick up master's stamina from items
  3705. + float total_pct = std::max((master->GetModifierValue(UNIT_MOD_STAT_STAMINA, TOTAL_PCT) - 0.1f), 1.f);
  3706. + float base_stam = master->GetModifierValue(UNIT_MOD_STAT_STAMINA, BASE_VALUE);
  3707. + base_stam = std::max(base_stam - 18.f, 0.f); //remove base stamina (not calculated into health)
  3708. + stamValue = base_stam * master->GetModifierValue(UNIT_MOD_STAT_STAMINA, BASE_PCT) * total_pct;
  3709. + //sLog->outError(LOG_FILTER_PLAYER, "stat to health add: Stamina (%f), value: %f", stamValue, stamValue*stammod);
  3710. + hp_add += int32(stamValue*stammod);
  3711. + //float stamstat = stat * 0.5f;
  3712. + //if (stamValue > stamstat)
  3713. + //{
  3714. + // //sLog->outBasic("selected stat to health add: Stamina (%f), value: %f", stamValue, stamValue*stammod);
  3715. + // hp_add += int32(stamValue * stammod);
  3716. + //}
  3717. + //else
  3718. + //{
  3719. + // //sLog->outBasic("selected stat to health add: stamStat (%f), value: %f", stamstat, stamstat*stammod);
  3720. + // hp_add += int32(stamstat * stammod);
  3721. + //}
  3722. + //sLog->outBasic("health to add after master's stat mod: %i", hp_add);
  3723. + int32 miscVal = me->getGender()*mylevel;
  3724. + //sLog->outError(LOG_FILTER_PLAYER, "health to remove from gender mod: %i", -miscVal);
  3725. + hp_add -= miscVal;//less hp for females lol
  3726. + //sLog->outError(LOG_FILTER_PLAYER, "health to add after gender mod: %i", hp_add);
  3727. + //miscVal = myrace*(mylevel/5);
  3728. + //sLog->outError(LOG_FILTER_PLAYER, "health to add from race mod: %i", miscVal);
  3729. + //hp_add += miscVal;//draenei tanks lol
  3730. + //sLog->outError(LOG_FILTER_PLAYER, "health to add after race mod: %i", hp_add);
  3731. + miscVal = master->GetNpcBotSlot(me->GetGUID()) * (mylevel/5);
  3732. + //sLog->outError(LOG_FILTER_PLAYER, "health to remove from slot mod: %i", -miscVal);
  3733. + hp_add -= miscVal;
  3734. + //sLog->outError(LOG_FILTER_PLAYER, "health to add after slot mod: %i", hp_add);
  3735. + uint32 m_totalhp = m_basehp + hp_add;//m_totalhp = uint32(float(m_basehp + hp_add) * stammod);
  3736. + //sLog->outError(LOG_FILTER_PLAYER, "total base health: %u", m_totalhp);
  3737. + if (master->GetBotTankGuid() == me->GetGUID())
  3738. + {
  3739. + m_totalhp = (m_totalhp * 135) / 100;//35% hp bonus for tanks
  3740. + //sLog->outBasic("total base health (isTank): %u", m_totalhp);
  3741. + }
  3742. + me->SetModifierValue(UNIT_MOD_HEALTH, BASE_VALUE, float(m_totalhp));//replaces base 18900 hp at 80 lvl
  3743. + me->UpdateMaxHealth();//will use our values we just set (update base health and buffs)
  3744. + //sLog->outError(LOG_FILTER_PLAYER, "overall hp: %u", me->GetMaxHealth());
  3745. + me->SetHealth(uint32(0.5f + float(me->GetMaxHealth()) * pct / 100.f));//restore pct
  3746. + if (!me->isInCombat())
  3747. + me->SetHealth(me->GetHealth() + m_basehp / 100);//regenerate
  3748. +}
  3749. +//Mana management for minions
  3750. +//Including calcs and set
  3751. +void bot_minion_ai::_OnManaUpdate(uint8 myclass, uint8 mylevel) const
  3752. +{
  3753. + if (me->getPowerType() != POWER_MANA)
  3754. + return;
  3755. + //sLog->outError(LOG_FILTER_PLAYER, "_OnManaUpdate(): updating bot %s", me->GetName().c_str());
  3756. + float pct = (float(me->GetPower(POWER_MANA)) * 100.f) / float(me->GetMaxPower(POWER_MANA));
  3757. + float m_basemana = classinfo->basemana > 0 ? classinfo->basemana : me->GetCreateMana();
  3758. + //sLog->outError(LOG_FILTER_PLAYER, "classinfo base mana = %f", m_basemana);
  3759. + me->SetCreateMana(m_basemana);//set base mana, critical
  3760. + float manamod = 15.f;//here we set mana multiplier from intellect as we gain mana from MASTER's stats mostly
  3761. + switch (myclass)
  3762. + {
  3763. + case CLASS_PALADIN: case CLASS_HUNTER: manamod = 4.5f; break;
  3764. + case CLASS_SHAMAN: manamod = 11.5f; break;
  3765. + case CLASS_DRUID: manamod = 12.5f; break;
  3766. + case CLASS_PRIEST: manamod = 16.5f; break;
  3767. + case CLASS_MAGE: case CLASS_WARLOCK: manamod = 10.5f; break;
  3768. + default: break;
  3769. + }
  3770. + //manamod += 1.f;//custom
  3771. + //manamod *= 0.70f;//custom
  3772. + //sLog->outError(LOG_FILTER_PLAYER, "Manamod: %f", manamod);
  3773. + float intValue = me->GetTotalStatValue(STAT_INTELLECT);
  3774. + intValue = std::max(intValue - 18.f, 1.f); //remove base int (not calculated into mana)
  3775. + //sLog->outError(LOG_FILTER_PLAYER, "bot's stats to mana add: Int (%f), value: %f", intValue, intValue * manamod);
  3776. + m_basemana += intValue * 15.f;
  3777. + //pick up master's intellect from items if master has mana
  3778. + if (master->getPowerType() == POWER_MANA)
  3779. + {
  3780. + float total_pct = std::max((master->GetModifierValue(UNIT_MOD_STAT_INTELLECT, TOTAL_PCT) - 0.1f), 1.f);
  3781. + intValue = std::max(master->GetModifierValue(UNIT_MOD_STAT_INTELLECT, BASE_VALUE) - 18.f, 1.f); //remove base int (not calculated into mana)
  3782. + intValue = intValue * master->GetModifierValue(UNIT_MOD_STAT_INTELLECT, BASE_PCT) * total_pct;
  3783. + }
  3784. + else// pick up maxstat
  3785. + intValue = stat * 0.5f;
  3786. + //sLog->outError(LOG_FILTER_PLAYER, "mana add from master's stat: %f", intValue * manamod);
  3787. + m_basemana += intValue * manamod;
  3788. + //sLog->outError(LOG_FILTER_PLAYER, "base mana + mana from master's intellect or stat: %f", m_basemana);
  3789. + //intValue = me->GetTotalAuraModValue(UNIT_MOD_STAT_INTELLECT);
  3790. + //sLog->outBasic("Intellect from buffs: %f", intValue);
  3791. + //m_basemana += uint32(intValue) * manamod;
  3792. + //sLog->outBasic("base mana + mana from intellect + mana from buffs: %u", m_basemana);
  3793. + uint8 otherVal = me->getGender()*3*mylevel;
  3794. + //sLog->outError(LOG_FILTER_PLAYER, "mana to add from gender mod: %u", otherVal);
  3795. + m_basemana += float(otherVal);//more mana for females lol
  3796. + //sLog->outError(LOG_FILTER_PLAYER, "base mana after gender mod: %f", m_basemana);
  3797. + otherVal = master->GetNpcBotSlot(me->GetGUID()) * (mylevel/5);// only to make mana unique
  3798. + //sLog->outError(LOG_FILTER_PLAYER, "mana to remove from slot mod: %i", -int8(otherVal));
  3799. + m_basemana -= otherVal;
  3800. + //sLog->outError(LOG_FILTER_PLAYER, "base mana after slot mod: %f", m_basemana);
  3801. + float m_totalmana = m_basemana;
  3802. + //sLog->outError(LOG_FILTER_PLAYER, "total mana to set: %f", m_totalmana);
  3803. + me->SetModifierValue(UNIT_MOD_MANA, BASE_VALUE, m_totalmana);
  3804. + me->UpdateMaxPower(POWER_MANA);
  3805. + //sLog->outError(LOG_FILTER_PLAYER, "Overall mana to set: %u", me->GetMaxPower(POWER_MANA));
  3806. + me->SetPower(POWER_MANA, uint32(0.5f + float(me->GetMaxPower(POWER_MANA)) * pct / 100.f));//restore pct
  3807. + //No Regen
  3808. +}
  3809. +//Melee damage for minions (melee classes only)
  3810. +//Calculation is based on master's attack power if melee/hunter or spellpower
  3811. +void bot_minion_ai::_OnMeleeDamageUpdate(uint8 myclass) const
  3812. +{
  3813. + if (ap_mod == 0.f) return; //do not bother casters
  3814. + //sLog->outBasic("_OnMeleeDamageUpdate: Updating bot %s", me->GetName().c_str());
  3815. + float my_ap_mod = ap_mod;
  3816. + float mod = master->getClass() == CLASS_HUNTER ? (master->GetModifierValue(UNIT_MOD_DAMAGE_RANGED, BASE_PCT) + master->GetModifierValue(UNIT_MOD_DAMAGE_RANGED, TOTAL_PCT))/2.f :
  3817. + (master->GetModifierValue(UNIT_MOD_DAMAGE_MAINHAND, BASE_PCT) + master->GetModifierValue(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_PCT))/2.f;
  3818. + mod = std::max(mod, 1.f); // x1 is Minimum
  3819. + mod = std::min(mod, 2.5f); // x2.5 is Maximum
  3820. + //sLog->outBasic("got base damage modifier: %f", mod);
  3821. + mod -= (mod - 1.f)*0.33f;//reduce bonus by 33%
  3822. + //sLog->outBasic("damage modifier truencated to %f, applying", mod);
  3823. + me->SetModifierValue(UNIT_MOD_DAMAGE_MAINHAND, BASE_PCT, mod);
  3824. + me->SetModifierValue(UNIT_MOD_DAMAGE_OFFHAND, BASE_PCT, mod);
  3825. + me->SetModifierValue(UNIT_MOD_DAMAGE_RANGED, BASE_PCT, mod);
  3826. + me->SetCanDualWield(myclass == CLASS_ROGUE || myclass == CLASS_SHAMAN);
  3827. + //Rogue has mainhand attack speed 1900, other dual-wielders - 2800 or 2600 or 2400
  3828. + if (myclass == CLASS_ROGUE)
  3829. + me->SetAttackTime(BASE_ATTACK, 1900);
  3830. + if (me->CanDualWield())
  3831. + me->SetAttackTime(OFF_ATTACK, myclass == CLASS_ROGUE ? 1400 : 1800);
  3832. + //me->SetModifierValue(UNIT_MOD_DAMAGE_RANGED, BASE_PCT, mod);//NUY
  3833. + mod = (mod - 1.f)*0.5f;
  3834. + //sLog->outBasic("reduced damage modifier to gain bonus: %f", mod);
  3835. + //sLog->outBasic("base ap modifier is %f", my_ap_mod);
  3836. + my_ap_mod *= 0.5f;
  3837. + //sLog->outBasic("ap modifier multiplied to %f", my_ap_mod);
  3838. + my_ap_mod += my_ap_mod > 0.f ? mod : 0.f; //add reduced master's multiplier if can have damage
  3839. + //sLog->outBasic("ap modifier + mod = %f", my_ap_mod);
  3840. + me->SetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_PCT, my_ap_mod);
  3841. + me->SetModifierValue(UNIT_MOD_ATTACK_POWER_RANGED, BASE_PCT, my_ap_mod);
  3842. +
  3843. + int32 sppower = 0;
  3844. + for (uint8 i = SPELL_SCHOOL_HOLY; i != MAX_SPELL_SCHOOL; ++i)
  3845. + {
  3846. + int32 power = master->SpellBaseDamageBonusDone(SpellSchoolMask(1 << i));
  3847. + if (power > sppower)
  3848. + sppower = power;
  3849. + }
  3850. + //sLog->outBasic("master's spellpower is %i, multiplying...", sppower);
  3851. + sppower *= 1.5f;
  3852. + //sLog->outBasic("got spellpower of %i", sppower);
  3853. + //atpower = float(master->GetInt32Value(master->getClass() == CLASS_HUNTER ? UNIT_FIELD_RANGED_ATTACK_POWER : UNIT_FIELD_ATTACK_POWER));
  3854. + float atpower = master->GetTotalAttackPowerValue(master->getClass() == CLASS_HUNTER ? RANGED_ATTACK : BASE_ATTACK);
  3855. + //sLog->outBasic("master's base attack power is %f", atpower);
  3856. + atpower = sppower > atpower ? sppower : atpower;//highest stat is used (either 1.5x spellpower or attack power)
  3857. + //sLog->outBasic("chosen attack power stat value: %f", atpower);
  3858. + //sLog->outBasic("expected attack power: %f", atpower*ap_mod);
  3859. +
  3860. + me->SetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE, atpower);
  3861. + if (myclass == CLASS_HUNTER || myclass == CLASS_ROGUE)
  3862. + {
  3863. + me->SetModifierValue(UNIT_MOD_ATTACK_POWER_RANGED, BASE_VALUE, atpower);
  3864. + me->UpdateAttackPowerAndDamage(true);
  3865. + }
  3866. + me->UpdateAttackPowerAndDamage();
  3867. + //sLog->outBasic("listing stats: ");
  3868. + //sLog->outBasic("attack power main hand: %f", me->GetTotalAttackPowerValue(BASE_ATTACK));
  3869. + //sLog->outBasic("attack power off hand: %f", me->GetTotalAttackPowerValue(OFF_ATTACK));
  3870. + //sLog->outBasic("attack power ranged: %f", me->GetTotalAttackPowerValue(RANGED_ATTACK));
  3871. + //sLog->outBasic("damage multiplier main hand: %f", me->GetModifierValue(UNIT_MOD_DAMAGE_MAINHAND, BASE_PCT) * me->GetModifierValue(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_PCT));
  3872. + //sLog->outBasic("damage multiplier off hand: %f", me->GetModifierValue(UNIT_MOD_DAMAGE_OFFHAND, BASE_PCT) * me->GetModifierValue(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_PCT));
  3873. + //sLog->outBasic("damage multiplier ranged: %f", me->GetModifierValue(UNIT_MOD_DAMAGE_RANGED, BASE_PCT) * me->GetModifierValue(UNIT_MOD_DAMAGE_RANGED, TOTAL_PCT));
  3874. + //sLog->outBasic("Damage range main hand: min: %f, max: %f", me->GetFloatValue(UNIT_FIELD_MINDAMAGE), me->GetFloatValue(UNIT_FIELD_MAXDAMAGE));
  3875. + //sLog->outBasic("Damage range off hand: min: %f, max: %f", me->GetFloatValue(UNIT_FIELD_MINOFFHANDDAMAGE), me->GetFloatValue(UNIT_FIELD_MAXOFFHANDDAMAGE));
  3876. + //sLog->outBasic("Damage range ranged: min: %f, max: %f", me->GetFloatValue(UNIT_FIELD_MINRANGEDDAMAGE), me->GetFloatValue(UNIT_FIELD_MAXRANGEDDAMAGE));
  3877. +}
  3878. +//Health for pets
  3879. +//Same as for minions just simplified (modified to match real pets' values)
  3880. +void bot_pet_ai::_OnHealthUpdate(uint8 /*petType*/, uint8 mylevel) const
  3881. +{
  3882. + float hp_mult = 10.f;
  3883. + switch (GetPetType(me))
  3884. + {
  3885. + case PET_TYPE_VOIDWALKER:
  3886. + hp_mult = 11.f;
  3887. + break;
  3888. + default:
  3889. + break;
  3890. + }
  3891. + float pct = me->GetHealthPct();// needs for regeneration
  3892. + //Use simple checks and calcs
  3893. + //0.3 hp for bots (inaccurate but cheap)
  3894. + uint32 m_basehp = me->GetCreateHealth()/2;
  3895. + //pick up stamina from buffs
  3896. + float stamValue = me->GetTotalStatValue(STAT_STAMINA);
  3897. + stamValue = std::max(stamValue - 18.f, 1.f); //remove base stamina (not calculated into health)
  3898. + uint32 hp_add = uint32(stamValue*hp_mult);
  3899. + hp_add += (m_creatureOwner->GetMaxHealth() - m_creatureOwner->GetCreateHealth())*0.3f;
  3900. + uint8 miscVal = GetPetType(me)*mylevel;
  3901. + hp_add -= miscVal;
  3902. + uint32 m_totalhp = m_basehp + hp_add;
  3903. + if (master->GetBotTankGuid() == me->GetGUID())
  3904. + m_totalhp = (m_totalhp*135) / 100;//35% hp bonus for tanks
  3905. + me->SetModifierValue(UNIT_MOD_HEALTH, BASE_VALUE, float(m_totalhp));
  3906. + me->UpdateMaxHealth();//will use values set (update base health and buffs)
  3907. + me->SetHealth(uint32(0.5f + float(me->GetMaxHealth())*pct / 100.f));//restore pct
  3908. + if (!me->isInCombat())
  3909. + me->SetHealth(me->GetHealth() + m_basehp / 100);//regenerate
  3910. +}
  3911. +//Mana for pets
  3912. +//Same as for minions just simplified (modified to match real pets' values)
  3913. +void bot_pet_ai::_OnManaUpdate(uint8 /*petType*/, uint8 mylevel) const
  3914. +{
  3915. + if (me->getPowerType() != POWER_MANA)
  3916. + return;
  3917. +
  3918. + float mana_mult = 15.f;
  3919. + switch (GetPetType(me))
  3920. + {
  3921. + case PET_TYPE_VOIDWALKER:
  3922. + mana_mult = 11.5f;
  3923. + break;
  3924. + default:
  3925. + break;
  3926. + }
  3927. + float pct = (float(me->GetPower(POWER_MANA)) * 100.f) / float(me->GetMaxPower(POWER_MANA));
  3928. + //Use simple checks and calcs
  3929. + //0.3 mana for bots (inaccurate but cheap)
  3930. + float m_basemana = float(me->GetCreateMana());
  3931. + m_basemana += (std::max<float>(me->GetTotalStatValue(STAT_INTELLECT) - 18.f, 1.f))*mana_mult; //remove base stamina (not calculated into mana)
  3932. + m_basemana += float(m_creatureOwner->GetMaxPower(POWER_MANA) - m_creatureOwner->GetCreateMana())*0.3f;
  3933. + m_basemana -= float(GetPetType(me)*mylevel);
  3934. + me->SetModifierValue(UNIT_MOD_MANA, BASE_VALUE, m_basemana);
  3935. + me->UpdateMaxPower(POWER_MANA);
  3936. + me->SetPower(POWER_MANA, uint32(0.5f + float(me->GetMaxPower(POWER_MANA))*pct / 100.f));//restore pct
  3937. +}
  3938. +//Sends all master's bots a message to not try to evade for a certain period of time
  3939. +void bot_ai::SendPartyEvadeAbort() const
  3940. +{
  3941. + for (uint8 i = 0; i != master->GetMaxNpcBots(); ++i)
  3942. + if (Creature* bot = master->GetBotMap(i)->_Cre())
  3943. + if (bot_minion_ai* ai = bot->GetBotMinionAI())
  3944. + ai->SetEvadeTimer(50);
  3945. +}
  3946. +//Removes buggy bots' threat from party, so no 'stuck in combat' bugs form bot mod
  3947. +//optionally interrupts casted spell if target is dead for bot and it's pet
  3948. +void bot_minion_ai::_OnEvade()
  3949. +{
  3950. + if (me->HasUnitState(UNIT_STATE_CASTING))
  3951. + for (uint8 i = CURRENT_FIRST_NON_MELEE_SPELL; i != CURRENT_AUTOREPEAT_SPELL; ++i)
  3952. + if (Spell* spell = me->GetCurrentSpell(CurrentSpellTypes(i)))
  3953. + if (!spell->GetSpellInfo()->IsChanneled())
  3954. + if (Unit* u = spell->m_targets.GetUnitTarget())
  3955. + if (u->isDead() && !IsInBotParty(u))
  3956. + me->InterruptSpell(CurrentSpellTypes(i), false, false);
  3957. +
  3958. + Creature* m_botsPet = me->GetBotsPet();
  3959. + if (m_botsPet && m_botsPet->HasUnitState(UNIT_STATE_CASTING))
  3960. + for (uint8 i = CURRENT_FIRST_NON_MELEE_SPELL; i != CURRENT_AUTOREPEAT_SPELL; ++i)
  3961. + if (Spell* spell = m_botsPet->GetCurrentSpell(CurrentSpellTypes(i)))
  3962. + if (!spell->GetSpellInfo()->IsChanneled())
  3963. + if (Unit* u = spell->m_targets.GetUnitTarget())
  3964. + if (u->isDead() && !IsInBotParty(u))
  3965. + m_botsPet->InterruptSpell(CurrentSpellTypes(i), false, false);
  3966. +
  3967. + if (Rand() > 10) return;
  3968. + if (!master->isInCombat() && !me->isInCombat() && (!m_botsPet || !m_botsPet->isInCombat())) return;
  3969. + if (CheckAttackTarget(GetBotClassForCreature(me)))
  3970. + return;
  3971. + //ChatHandler ch(master);
  3972. + //ch.PSendSysMessage("_OnEvade() by bot %s", me->GetName().c_str());
  3973. + if (master->isInCombat())
  3974. + {
  3975. + HostileRefManager& mgr = master->getHostileRefManager();
  3976. + if (!mgr.isEmpty())
  3977. + {
  3978. + std::set<Unit*> Set;
  3979. + HostileReference* ref = mgr.getFirst();
  3980. + while (ref)
  3981. + {
  3982. + if (ref->getSource() && ref->getSource()->getOwner())
  3983. + Set.insert(ref->getSource()->getOwner());
  3984. + ref = ref->next();
  3985. + }
  3986. + for (std::set<Unit*>::const_iterator i = Set.begin(); i != Set.end(); ++i)
  3987. + {
  3988. + Unit* unit = (*i);
  3989. + if (/*unit->IsFriendlyTo(master)*/IsInBotParty(unit) || !unit->isInCombat())
  3990. + {
  3991. + //ch.PSendSysMessage("_OnEvade(): %s's hostile reference is removed from %s!", unit->GetName().c_str(), master->GetName().c_str());
  3992. + mgr.deleteReference(unit);
  3993. + }
  3994. + }
  3995. + }
  3996. + }
  3997. + else
  3998. + {
  3999. + SendPartyEvadeAbort();
  4000. + for (uint8 i = 0; i != master->GetMaxNpcBots(); ++i)
  4001. + {
  4002. + Creature* cre = master->GetBotMap(i)->_Cre();
  4003. + if (!cre) continue;
  4004. + if (cre->isInCombat())
  4005. + {
  4006. + cre->DeleteThreatList();
  4007. + HostileRefManager& mgr = cre->getHostileRefManager();
  4008. + if (!mgr.isEmpty())
  4009. + {
  4010. + std::set<Unit*> Set;
  4011. + HostileReference* ref = mgr.getFirst();
  4012. + while (ref)
  4013. + {
  4014. + if (ref->getSource() && ref->getSource()->getOwner())
  4015. + Set.insert(ref->getSource()->getOwner());
  4016. + ref = ref->next();
  4017. + }
  4018. + for (std::set<Unit*>::const_iterator i = Set.begin(); i != Set.end(); ++i)
  4019. + {
  4020. + Unit* unit = (*i);
  4021. + if (!unit->InSamePhase(me)) continue;
  4022. + if (/*unit->IsFriendlyTo(master)*/IsInBotParty(unit) || !unit->isInCombat())
  4023. + {
  4024. + //ch.PSendSysMessage("_OnEvade(): %s's hostile reference is removed from %s!", unit->GetName().c_str(), cre->GetName().c_str());
  4025. + mgr.deleteReference(unit);
  4026. + }
  4027. + }
  4028. + }
  4029. + //if (mgr.isEmpty())// has empty threat list and no hostile refs - we have all rights to stop combat
  4030. + //{
  4031. + // if (cre->isInCombat())
  4032. + // {
  4033. + // //ch.PSendSysMessage("_OnEvade(): %s's HostileRef is empty! Combatstop!", cre->GetName().c_str());
  4034. + // cre->ClearInCombat();
  4035. + // }
  4036. + //}
  4037. + }
  4038. +
  4039. + Creature* m_botsPet = cre->GetBotsPet();
  4040. + if (!m_botsPet || !m_botsPet->isInCombat()) continue;
  4041. + m_botsPet->DeleteThreatList();
  4042. + HostileRefManager& mgr = m_botsPet->getHostileRefManager();
  4043. + if (!mgr.isEmpty())
  4044. + {
  4045. + std::set<Unit*> Set;
  4046. + HostileReference* ref = mgr.getFirst();
  4047. + while (ref)
  4048. + {
  4049. + if (ref->getSource() && ref->getSource()->getOwner())
  4050. + Set.insert(ref->getSource()->getOwner());
  4051. + ref = ref->next();
  4052. + }
  4053. + for (std::set<Unit*>::const_iterator i = Set.begin(); i != Set.end(); ++i)
  4054. + {
  4055. + Unit* unit = (*i);
  4056. + if (!unit->InSamePhase(me)) continue;
  4057. + if (/*unit->IsFriendlyTo(master)*/IsInBotParty(unit) || !unit->isInCombat())
  4058. + {
  4059. + //ch.PSendSysMessage("_OnEvade(): %s's hostile reference is removed from %s!", unit->GetName().c_str(), m_botsPet->GetName().c_str());
  4060. + mgr.deleteReference(unit);
  4061. + }
  4062. + }
  4063. + }
  4064. + //if (mgr.isEmpty())// has empty threat list and no hostile refs - we have all rights to stop combat
  4065. + //{
  4066. + // if (m_botsPet->isInCombat())
  4067. + // {
  4068. + // //ch.PSendSysMessage("_OnEvade(): %s's HostileRef is empty! Combatstop!", pet->GetName().c_str());
  4069. + // m_botsPet->ClearInCombat();
  4070. + // }
  4071. + //}
  4072. + }
  4073. + }
  4074. +}
  4075. +//SpellHit()... OnSpellHit()
  4076. +void bot_ai::OnSpellHit(Unit* /*caster*/, SpellInfo const* spell)
  4077. +{
  4078. + for (uint8 i = 0; i != MAX_SPELL_EFFECTS; ++i)
  4079. + {
  4080. + uint32 auraname = spell->Effects[i].ApplyAuraName;
  4081. + //remove pet on mount
  4082. + if (auraname == SPELL_AURA_MOUNTED)
  4083. + me->SetBotsPetDied();
  4084. + //update stats
  4085. + if (auraname == SPELL_AURA_MOD_STAT)
  4086. + {
  4087. + doHealth = true;
  4088. + doMana = true;
  4089. + }
  4090. + else
  4091. + {
  4092. + if (auraname == SPELL_AURA_MOD_INCREASE_HEALTH ||
  4093. + auraname == SPELL_AURA_MOD_INCREASE_HEALTH_2 ||
  4094. + auraname == SPELL_AURA_230 || // SPELL_AURA_MOD_INCREASE_HEALTH_2
  4095. + auraname == SPELL_AURA_MOD_INCREASE_HEALTH_PERCENT)
  4096. + doHealth = true;
  4097. + else if (auraname == SPELL_AURA_MOD_INCREASE_ENERGY ||
  4098. + auraname == SPELL_AURA_MOD_INCREASE_ENERGY_PERCENT)
  4099. + doMana = true;
  4100. + }
  4101. + }
  4102. +}
  4103. +//Messed up
  4104. +//Hp + Mana update
  4105. +//target update
  4106. +//returns fake wait time between overall AI updates (if it is even understandable)
  4107. +uint8 bot_ai::GetWait()
  4108. +{
  4109. + if (doHealth)
  4110. + {
  4111. + doHealth = false;
  4112. + _OnHealthUpdate(me->GetBotClass(), master->getLevel());
  4113. + }
  4114. + if (doMana)
  4115. + {
  4116. + doMana = false;
  4117. + _OnManaUpdate(me->GetBotClass(), master->getLevel());
  4118. + }
  4119. + CheckAuras(true);
  4120. + //0 to 2 plus 1 for every 3 bots except first one
  4121. + return (1 + (master->GetNpcBotsCount() - 1)/3 + (irand(0,100) <= 50)*int8(RAND(-1,1)));
  4122. +}
  4123. +//Damage Mods
  4124. +//1) Apply class-specified damage/crit chance/crit damage bonuses
  4125. +//2) Apply bot damage multiplier
  4126. +//3) Remove Creature damage multiplier (make independent from original config)
  4127. +//Bug with config reload (creatures do not update their damage on reload) is not bot-related but still annoying
  4128. +void bot_ai::ApplyBotDamageMultiplierMelee(uint32& damage, CalcDamageInfo& damageinfo) const
  4129. +{
  4130. + ApplyClassDamageMultiplierMelee(damage, damageinfo);
  4131. + damage = int32(float(damage)*dmgmult_melee/dmgmod_melee);
  4132. +}
  4133. +void bot_ai::ApplyBotDamageMultiplierMelee(int32& damage, SpellNonMeleeDamage& damageinfo, SpellInfo const* spellInfo, WeaponAttackType attackType, bool& crit) const
  4134. +{
  4135. + ApplyClassDamageMultiplierMelee(damage, damageinfo, spellInfo, attackType, crit);
  4136. + damage = int32(float(damage)*dmgmult_melee/dmgmod_melee);
  4137. +}
  4138. +void bot_ai::ApplyBotDamageMultiplierSpell(int32& damage, SpellNonMeleeDamage& damageinfo, SpellInfo const* spellInfo, WeaponAttackType attackType, bool& crit) const
  4139. +{
  4140. + ApplyClassDamageMultiplierSpell(damage, damageinfo, spellInfo, attackType, crit);
  4141. + damage = int32(float(damage)*dmgmult_spell/dmgmod_spell);
  4142. +}
  4143. +//////////
  4144. +//GOSSIP//
  4145. +//////////
  4146. +//Implemented: Mage,.. and nothing more...
  4147. +bool bot_minion_ai::OnGossipHello(Player* player, Creature* creature)
  4148. +{
  4149. + switch (creature->GetBotClass())
  4150. + {
  4151. + case CLASS_MAGE:
  4152. + if (creature->isInCombat())
  4153. + {
  4154. + player->CLOSE_GOSSIP_MENU();
  4155. + break;
  4156. + }
  4157. + player->ADD_GOSSIP_ITEM(0, "I need food", 6001, GOSSIP_ACTION_INFO_DEF + 1);
  4158. + player->ADD_GOSSIP_ITEM(0, "I need drink", 6001, GOSSIP_ACTION_INFO_DEF + 2);
  4159. + player->PlayerTalkClass->SendGossipMenu(GOSSIP_SERVE_MASTER, creature->GetGUID());
  4160. + break;
  4161. + default:
  4162. + player->CLOSE_GOSSIP_MENU();
  4163. + break;
  4164. + }
  4165. + return true;
  4166. +}
  4167. +//GossipSelect
  4168. +//Mage: rations implemented
  4169. +bool bot_minion_ai::OnGossipSelect(Player* player, Creature* creature, uint32 sender, uint32 action)
  4170. +{
  4171. + if (!IsInBotParty(player))
  4172. + {
  4173. + player->CLOSE_GOSSIP_MENU();
  4174. + creature->MonsterWhisper("Get away from me!", player->GetGUID());
  4175. + return false;
  4176. + }
  4177. + switch (creature->GetBotClass())
  4178. + {
  4179. + case CLASS_MAGE:
  4180. + switch (sender)
  4181. + {
  4182. + case 6001:// food/drink
  4183. + {
  4184. + //Prevent high-leveled consumables for low-level characters
  4185. + Unit* checker;
  4186. + if (player->getLevel() < creature->getLevel())
  4187. + checker = player;
  4188. + else
  4189. + checker = creature;
  4190. +
  4191. + // Conjure Refreshment rank 1
  4192. + uint32 food = InitSpell(checker, 42955);
  4193. + bool iswater = (action == GOSSIP_ACTION_INFO_DEF + 2);
  4194. + if (!food)
  4195. + {
  4196. + if (!iswater)// Conjure Food rank 1
  4197. + food = InitSpell(checker, 587);
  4198. + else// Conjure Water rank 1
  4199. + food = InitSpell(checker, 5504);
  4200. + }
  4201. + if (!food)
  4202. + {
  4203. + std::string errorstr = "I can't conjure ";
  4204. + errorstr += iswater ? "water" : "food";
  4205. + errorstr += " yet";
  4206. + creature->MonsterWhisper(errorstr.c_str(), player->GetGUID());
  4207. + //player->PlayerTalkClass->ClearMenus();
  4208. + //return OnGossipHello(player, creature);
  4209. + player->CLOSE_GOSSIP_MENU();
  4210. + return false;
  4211. + }
  4212. + player->CLOSE_GOSSIP_MENU();
  4213. + SpellInfo const* Info = sSpellMgr->GetSpellInfo(food);
  4214. + Spell* foodspell = new Spell(creature, Info, TRIGGERED_NONE, player->GetGUID());
  4215. + SpellCastTargets targets;
  4216. + targets.SetUnitTarget(player);
  4217. + //TODO implement checkcast for bots
  4218. + SpellCastResult result = creature->IsMounted() || CCed(creature) ? SPELL_FAILED_CUSTOM_ERROR : foodspell->CheckPetCast(player);
  4219. + if (result != SPELL_CAST_OK)
  4220. + {
  4221. + foodspell->finish(false);
  4222. + delete foodspell;
  4223. + creature->MonsterWhisper("I can't do it right now", player->GetGUID());
  4224. + creature->SendPetCastFail(food, result);
  4225. + }
  4226. + else
  4227. + {
  4228. + aftercastTargetGuid = player->GetGUID();
  4229. + foodspell->prepare(&targets);
  4230. + creature->MonsterWhisper("Here you go...", player->GetGUID());
  4231. + }
  4232. + break;
  4233. + }
  4234. + }
  4235. + break;
  4236. + default:
  4237. + break;
  4238. + }
  4239. + return true;
  4240. +}
  4241. +//Summons pet for bot
  4242. +void bot_minion_ai::SummonBotsPet(uint32 entry)
  4243. +{
  4244. + Creature* m_botsPet = me->GetBotsPet();
  4245. + if (m_botsPet)
  4246. + me->SetBotsPetDied();
  4247. +
  4248. + uint8 mylevel = std::min<uint8>(master->getLevel(), 80);
  4249. + uint32 originalentry = bot_pet_ai::GetPetOriginalEntry(entry);
  4250. + if (!originalentry)
  4251. + {
  4252. + //annoy master
  4253. + me->MonsterWhisper("Why am I trying to summon unknown pet!?", master->GetGUID());
  4254. + return;
  4255. + }
  4256. + uint32 armor = 0;
  4257. + float x(0),y(0),z(0);
  4258. + me->GetClosePoint(x, y, z, me->GetObjectSize());
  4259. + m_botsPet = me->SummonCreature(entry, x, y, z, 0, TEMPSUMMON_DEAD_DESPAWN);
  4260. +
  4261. + if (!m_botsPet)
  4262. + {
  4263. + me->MonsterWhisper("Failed to summon pet!", master->GetGUID());
  4264. + return;
  4265. + }
  4266. +
  4267. + //std::string name = sObjectMgr->GeneratePetName(originalentry);//voidwalker
  4268. + //if (!name.empty())
  4269. + // m_botsPet->SetName(name);
  4270. +
  4271. + PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_NPCBOT_PET_LEVELSTATS);
  4272. + stmt->setUInt32(0, originalentry);
  4273. + stmt->setUInt8(1, mylevel);
  4274. + PreparedQueryResult result = WorldDatabase.Query(stmt);
  4275. + //QueryResult result = WorldDatabase.PQuery("SELECT hp, mana, armor, str, agi, sta, inte, spi FROM `pet_levelstats` WHERE `creature_entry` = '%u' AND `level` = '%u'", originalentry, mylevel);
  4276. +
  4277. + if (result)
  4278. + {
  4279. + Field* fields = result->Fetch();
  4280. + uint32 hp = fields[0].GetUInt16();
  4281. + uint32 mana = fields[1].GetUInt16();
  4282. + armor = fields[2].GetUInt32();
  4283. + uint32 str = fields[3].GetUInt16();
  4284. + uint32 agi = fields[4].GetUInt16();
  4285. + uint32 sta = fields[5].GetUInt16();
  4286. + uint32 inte = fields[6].GetUInt16();
  4287. + uint32 spi = fields[7].GetUInt16();
  4288. +
  4289. + m_botsPet->SetCreateHealth(hp);
  4290. + m_botsPet->SetMaxHealth(hp);
  4291. + m_botsPet->SetCreateMana(mana);
  4292. + m_botsPet->SetMaxPower(POWER_MANA, mana);
  4293. +
  4294. + m_botsPet->SetCreateStat(STAT_STRENGTH, str);
  4295. + m_botsPet->SetCreateStat(STAT_AGILITY, agi);
  4296. + m_botsPet->SetCreateStat(STAT_STAMINA, sta);
  4297. + m_botsPet->SetCreateStat(STAT_INTELLECT, inte);
  4298. + m_botsPet->SetCreateStat(STAT_SPIRIT, spi);
  4299. + }
  4300. +
  4301. + m_botsPet->SetBotOwner(master);
  4302. + m_botsPet->SetCreatureOwner(me);
  4303. + m_botsPet->SetBotClass(bot_pet_ai::GetPetClass(m_botsPet));
  4304. + master->SetMinion((Minion*)m_botsPet, true);
  4305. + m_botsPet->SetUInt64Value(UNIT_FIELD_CREATEDBY, me->GetGUID());
  4306. + m_botsPet->DeleteThreatList();
  4307. + m_botsPet->AddUnitTypeMask(UNIT_MASK_MINION);
  4308. + //m_botsPet->SetLevel(master->getLevel());
  4309. + m_botsPet->AIM_Initialize();
  4310. + m_botsPet->InitBotAI(true);
  4311. + m_botsPet->setFaction(master->getFaction());
  4312. + //bot_pet_ai* petai = m_botsPet->GetBotPetAI();
  4313. + //petai->SetCreatureOwner(me);
  4314. + //petai->SetBaseArmor(armor);
  4315. + //petai->setStats(mylevel, bot_pet_ai::GetPetType(m_botsPet), true);
  4316. + m_botsPet->SetBotCommandState(COMMAND_FOLLOW, true);
  4317. +
  4318. + me->SetBotsPet(m_botsPet);
  4319. +
  4320. + m_botsPet->SendUpdateToPlayer(master);
  4321. +}
  4322. +
  4323. +uint16 bot_ai::Rand() const
  4324. +{
  4325. + return urand(0, 100 + (master->GetNpcBotsCount() - 1) * 10);
  4326. +}
  4327. +//Used for outside checks for druid
  4328. +uint8 bot_minion_ai::GetBotClassForCreature(Creature* bot)
  4329. +{
  4330. + uint8 botClass = bot->GetBotClass();
  4331. + switch (botClass)
  4332. + {
  4333. + case CAT: case BEAR:
  4334. + return CLASS_DRUID;
  4335. + default:
  4336. + return botClass;
  4337. + }
  4338. +}
  4339. +//Returns pet type (maybe unneeded)
  4340. +uint8 bot_pet_ai::GetPetType(Creature* pet)
  4341. +{
  4342. + switch (pet->GetEntry())
  4343. + {
  4344. + case PET_VOIDWALKER:
  4345. + return PET_TYPE_VOIDWALKER;
  4346. + }
  4347. + return PET_TYPE_NONE;
  4348. +}
  4349. +//Returns pet's class
  4350. +uint8 bot_pet_ai::GetPetClass(Creature* pet)
  4351. +{
  4352. + switch (GetPetType(pet))
  4353. + {
  4354. + case PET_TYPE_IMP:
  4355. + return CLASS_MAGE;
  4356. + default:
  4357. + return CLASS_PALADIN;
  4358. + }
  4359. +}
  4360. +//Return entry used to summon real pets
  4361. +uint32 bot_pet_ai::GetPetOriginalEntry(uint32 entry)
  4362. +{
  4363. + switch (entry)
  4364. + {
  4365. + case PET_VOIDWALKER:
  4366. + return ORIGINAL_ENTRY_VOIDWALKER;
  4367. + default:
  4368. + return 0;
  4369. + }
  4370. +}
  4371. +//PvP trinket for minions
  4372. +void bot_minion_ai::BreakCC(uint32 diff)
  4373. +{
  4374. + if (pvpTrinket_cd <= diff && CCed(me, true) && (me->getVictim() || !me->getAttackers().empty()))
  4375. + {
  4376. + temptimer = GC_Timer;
  4377. + if (doCast(me, PVPTRINKET))
  4378. + {
  4379. + pvpTrinket_cd = PVPTRINKET_CD;
  4380. + GC_Timer = temptimer;
  4381. + return;
  4382. + }
  4383. + }
  4384. +}
  4385. +//Returns attack range based on given range
  4386. +//If mounted: 20%
  4387. +//If ranged: 125%
  4388. +//If master is dead: max range
  4389. +float bot_ai::InitAttackRange(float origRange, bool ranged) const
  4390. +{
  4391. + if (me->IsMounted())
  4392. + origRange *= 0.2f;
  4393. + else
  4394. + {
  4395. + if (ranged)
  4396. + origRange *= 1.25f;
  4397. + if (master->isDead())
  4398. + origRange += sWorld->GetMaxVisibleDistanceOnContinents();
  4399. + }
  4400. + return origRange;
  4401. +}
  4402. +//Force bots to start attack anyone who tries to DAMAGE me or master
  4403. +//This means that anyone who attacks party will be attacked by whole bot party (see GetTarget())
  4404. +void bot_minion_ai::OnOwnerDamagedBy(Unit* attacker)
  4405. +{
  4406. + if (me->getVictim())
  4407. + return;
  4408. + if (InDuel(attacker))
  4409. + return;
  4410. + bool byspell = false, ranged = false;
  4411. + switch (GetBotClassForCreature(me))
  4412. + {
  4413. + case CLASS_DRUID:
  4414. + byspell = me->GetShapeshiftForm() == FORM_NONE ||
  4415. + me->GetShapeshiftForm() == FORM_TREE ||
  4416. + me->GetShapeshiftForm() == FORM_MOONKIN;
  4417. + ranged = byspell;
  4418. + break;
  4419. + case CLASS_PRIEST:
  4420. + case CLASS_MAGE:
  4421. + case CLASS_WARLOCK:
  4422. + case CLASS_SHAMAN:
  4423. + byspell = true;
  4424. + ranged = true;
  4425. + break;
  4426. + case CLASS_HUNTER:
  4427. + ranged = true;
  4428. + break;
  4429. + default:
  4430. + break;
  4431. + }
  4432. + float maxdist = InitAttackRange(float(master->GetBotFollowDist()), ranged);//use increased range
  4433. + if (!attacker->IsWithinDist(me, maxdist))
  4434. + return;
  4435. + if (!CanBotAttack(attacker, byspell))
  4436. + return;
  4437. +
  4438. + m_botCommandState = COMMAND_ABANDON;//reset AttackStart()
  4439. + me->Attack(attacker, !ranged);
  4440. +}
  4441. diff --git a/src/server/game/AI/NpcBots/bot_ai.h b/src/server/game/AI/NpcBots/bot_ai.h
  4442. new file mode 100644
  4443. index 0000000..fc2cfe9
  4444. --- /dev/null
  4445. +++ b/src/server/game/AI/NpcBots/bot_ai.h
  4446. @@ -0,0 +1,355 @@
  4447. +#ifndef _BOT_AI_H
  4448. +#define _BOT_AI_H
  4449. +
  4450. +#include "ScriptedCreature.h"
  4451. +
  4452. +enum CommonValues
  4453. +{
  4454. +//COMMON SPELLS
  4455. + MANAPOTION = 32453,//"???" forgot 0_o
  4456. + HEALINGPOTION = 15504,//"Drinks Holy Elixir to heal the caster"
  4457. + DRINK = 66041,//"Restores 4% mana per sec for 30 sec"
  4458. + EAT = 66478,//"Restores Health"
  4459. + PVPTRINKET = 42292,//PvP Trinket no CD
  4460. +//COMMON CDs
  4461. + POTION_CD = 60000,//default 60sec potion cd
  4462. + PVPTRINKET_CD = 120000,//default 2 min pvp trinket cd
  4463. +//COMMON PASSIVES
  4464. + //1) "Increase(d) @whatever"
  4465. + //SPELL_BONUS_10 = 33021,//10spp
  4466. + SPELL_BONUS_50 = 45011,//50spp
  4467. + SPELL_BONUS_150 = 28141,//150spp
  4468. + SPELL_BONUS_250 = 69709,//250spp
  4469. + FIREDAM_86 = 33816,//86 fire spp
  4470. + MANAREGEN45 = 35867,//45 mp5
  4471. + MANAREGEN100 = 45216,//100 mp5
  4472. + //2) Talents
  4473. + HASTE /*Gift of the EarthMother*/= 51183,//rank 5 10% spell haste
  4474. + HASTE2 /*Blood Frenzy - warrior*/ = 29859,//rank2 10% haste, bonus for rend (warriors only)//13789//rank 3 10% haste 6% dodge
  4475. + CRITS /*Thundering Strikes-sham*/= 16305,//rank 5 5% crit
  4476. + HOLYCRIT /*Holy Spec - priest*/ = 15011,//rank 5 5% holy crit
  4477. + DODGE /*Anticipation - paladin*/ = 20100,//rank 5 5% dodge
  4478. + PARRY /*Deflection - warrior*/ = 16466,//rank 5 5% parry
  4479. + PRECISION /*Precision - warrior*/ = 29592,//rank 3 3% melee hit
  4480. + PRECISION2/*Precision - mage*/ = 29440,//rank 3 3% spell hit
  4481. + DMG_TAKEN/*Deadened Nerves - rogue*/= 31383,//rank 3 (-6%)
  4482. + //3) Pet/Special
  4483. + THREAT /*Tank Class Passive*/ = 57339,//+43% threat
  4484. + BOR /*Blood of Rhino - pet*/ = 53482,//rank 2 +40% healing taken
  4485. + RCP /*Rogue Class Passive*/ = 21184,//-27% threat caused
  4486. + DEFENSIVE_STANCE_PASSIVE = 7376, //+threat/damage reduction
  4487. +//COMMON GOSSIPS
  4488. + GOSSIP_SERVE_MASTER = 2279 //"I live only to serve the master."
  4489. +};
  4490. +
  4491. +//TODO: slow fall / water walking for master
  4492. +//enum HoverSpells
  4493. +//{
  4494. +// LEVITATE = 1706,
  4495. +// SLOW_FALL = 130,
  4496. +// //WATER_WALKING = 546,
  4497. +//};
  4498. +
  4499. +enum DruidStances//bot's temp set class
  4500. +{
  4501. + BEAR = 15,
  4502. + CAT = 25,
  4503. + //TRAVEL = 35, //NUY
  4504. + //FLY = 45, //NUY
  4505. +};
  4506. +
  4507. +enum BotPetTypes
  4508. +{
  4509. + PET_TYPE_NONE,
  4510. +//Warlock
  4511. + PET_TYPE_IMP,
  4512. + PET_TYPE_VOIDWALKER,
  4513. + PET_TYPE_SUCCUBUS,
  4514. + PET_TYPE_FELHUNTER,
  4515. + PET_TYPE_FELGUARD,
  4516. +//Mage
  4517. + PET_TYPE_WATER_ELEMENTAL,
  4518. +//Shaman
  4519. + //PET_TYPE_GHOSTLY_WOLF,
  4520. + PET_TYPE_FIRE_ELEMENTAL,
  4521. + PET_TYPE_EARTH_ELEMENTAL,
  4522. +//Hunter
  4523. + PET_TYPE_VULTURE,
  4524. +
  4525. + MAX_PET_TYPES
  4526. +};
  4527. +
  4528. +enum WarlockBotPets
  4529. +{
  4530. + //PET_IMP = ,
  4531. + PET_VOIDWALKER = 60237,
  4532. + //PET_SUCCUBUS =
  4533. +};
  4534. +
  4535. +enum HunterBotPets
  4536. +{
  4537. + PET_VULTURE = 60238
  4538. +};
  4539. +
  4540. +enum BotPetsOriginalEntries
  4541. +{
  4542. + ORIGINAL_ENTRY_VOIDWALKER = 1860
  4543. +};
  4544. +
  4545. +class bot_ai : public ScriptedAI
  4546. +{
  4547. + public:
  4548. + virtual ~bot_ai();
  4549. + bot_ai(Creature* creature);
  4550. + //Player* GetMaster() const { return master; }
  4551. + virtual bool IsMinionAI() const = 0;
  4552. + virtual bool IsPetAI() const = 0;
  4553. + virtual void SetBotCommandState(CommandStates /*st*/, bool /*force*/ = false, Position* /*newpos*/ = NULL) = 0;
  4554. + virtual const bot_minion_ai* GetMinionAI() const { return NULL; }
  4555. + virtual const bot_pet_ai* GetPetAI() const { return NULL; }
  4556. + bool IsInBotParty(Unit* unit) const;
  4557. + bool CanBotAttack(Unit* target, int8 byspell = 0) const;
  4558. + bool InDuel(Unit* target) const;
  4559. + void ApplyBotDamageMultiplierMelee(uint32& damage, CalcDamageInfo& damageinfo) const;
  4560. + void ApplyBotDamageMultiplierMelee(int32& damage, SpellNonMeleeDamage& damageinfo, SpellInfo const* spellInfo, WeaponAttackType attackType, bool& crit) const;
  4561. + void ApplyBotDamageMultiplierSpell(int32& damage, SpellNonMeleeDamage& damageinfo, SpellInfo const* spellInfo, WeaponAttackType attackType, bool& crit) const;
  4562. + inline void SendPartyEvadeAbort() const;
  4563. + inline void SetShouldUpdateStats() { shouldUpdateStats = true; }
  4564. + inline void UpdateHealth() { doHealth = true; }
  4565. + inline void UpdateMana() { doMana = true; }
  4566. + inline void SetBotTank(Unit* newtank) { tank = newtank; m_TankGuid = newtank ? newtank->GetGUID() : 0; }
  4567. + static Unit* GetBotGroupMainTank(Group* group) { return _GetBotGroupMainTank(group); }
  4568. + inline float GetManaRegen() const { return regen_mp5; }
  4569. + inline float GetHitRating() const { return hit; }
  4570. + inline uint64 GetBotTankGuid() const { return m_TankGuid; }
  4571. + inline int32 GetSpellPower() const { return m_spellpower; }
  4572. + inline uint8 GetHaste() const { return haste; }
  4573. +
  4574. + void ReceiveEmote(Player* player, uint32 emote);
  4575. + void ApplyPassives(uint8 botOrPetType) const;
  4576. +
  4577. + protected:
  4578. + static inline bool CCed(Unit* target, bool root = false)
  4579. + {
  4580. + return target ? target->HasUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_STUNNED | UNIT_STATE_FLEEING | UNIT_STATE_DISTRACTED | UNIT_STATE_CONFUSED_MOVE | UNIT_STATE_FLEEING_MOVE) || (root && target->HasUnitState(UNIT_STATE_ROOT)) : true;
  4581. + }
  4582. + static uint32 InitSpell(Unit* caster, uint32 spell);
  4583. +
  4584. + bool HasAuraName(Unit* unit, const std::string spell, uint64 casterGuid = 0, bool exclude = false) const;
  4585. + bool HasAuraName(Unit* unit, uint32 spellId, uint64 casterGuid = 0, bool exclude = false) const;
  4586. + bool RefreshAura(uint32 spell, int8 count = 1, Unit* target = NULL) const;
  4587. + bool CheckAttackTarget(uint8 botOrPetType);
  4588. + bool MoveBehind(Unit &target) const;
  4589. + bool CheckImmunities(uint32 spell, Unit* target = NULL) const { return (spell && target && !target->ToCorpse() && target->IsHostileTo(me) ? !target->IsImmunedToDamage(sSpellMgr->GetSpellInfo(spell)) : true); }
  4590. +
  4591. + //everything cast-related
  4592. + bool doCast(Unit* victim, uint32 spellId, bool triggered = false, uint64 originalCaster = 0);
  4593. + SpellCastResult checkBotCast(Unit* victim, uint32 spellId, uint8 botclass) const;
  4594. + virtual void removeFeralForm(bool /*force*/ = false, bool /*init*/ = true, uint32 /*diff*/ = 0) {}
  4595. +
  4596. + inline bool Feasting() const { return (me->HasAura(EAT) || me->HasAura(DRINK)); }
  4597. + inline bool isMeleeClass(uint8 m_class) const { return (m_class == CLASS_WARRIOR || m_class == CLASS_ROGUE || m_class == CLASS_PALADIN || m_class == CLASS_DEATH_KNIGHT || m_class == BEAR); }
  4598. + inline bool IsChanneling(Unit* u = NULL) const { if (!u) u = me; return u->GetCurrentSpell(CURRENT_CHANNELED_SPELL); }
  4599. + inline bool IsCasting(Unit* u = NULL) const { if (!u) u = me; return (u->HasUnitState(UNIT_STATE_CASTING) || IsChanneling(u) || u->IsNonMeleeSpellCasted(false)); }
  4600. +
  4601. + void GetInPosition(bool force = false, bool ranged = true, Unit* newtarget = NULL, Position* pos = NULL);
  4602. + void OnSpellHit(Unit* caster, SpellInfo const* spell);
  4603. + void FindTank();
  4604. + void listAuras(Player* player, Unit* unit) const;
  4605. + void CalculateAttackPos(Unit* target, Position &pos) const;
  4606. +
  4607. + virtual void ApplyClassDamageMultiplierMelee(uint32& /*damage*/, CalcDamageInfo& /*damageinfo*/) const {}
  4608. + virtual void ApplyClassDamageMultiplierMelee(int32& /*damage*/, SpellNonMeleeDamage& /*damageinfo*/, SpellInfo const* /*spellInfo*/, WeaponAttackType /*attackType*/, bool& /*crit*/) const {}
  4609. + virtual void ApplyClassDamageMultiplierSpell(int32& /*damage*/, SpellNonMeleeDamage& /*damageinfo*/, SpellInfo const* /*spellInfo*/, WeaponAttackType /*attackType*/, bool& /*crit*/) const {}
  4610. + virtual void CureGroup(Player* /*pTarget*/, uint32 /*cureSpell*/, uint32 /*diff*/) {}
  4611. + virtual void CheckAuras(bool /*force*/ = false) {}
  4612. + virtual void BuffAndHealGroup(Player* /*gPlayer*/, uint32 /*diff*/) {}
  4613. + virtual void RezGroup(uint32 /*REZZ*/, Player* /*gPlayer*/) {}
  4614. + //virtual void DoNonCombatActions(uint32 const /*diff*/) {}
  4615. + //virtual void StartAttack(Unit* /*u*/, bool /*force*/ = false) {}
  4616. + virtual void InitSpells() {}
  4617. + virtual void _OnHealthUpdate(uint8 /*myclass*/, uint8 /*mylevel*/) const = 0;
  4618. + virtual void _OnManaUpdate(uint8 /*myclass*/, uint8 /*mylevel*/) const = 0;
  4619. + //virtual void _OnMeleeDamageUpdate(uint8 /*myclass*/) const = 0;
  4620. +
  4621. + //virtual void ReceiveEmote(Player* /*player*/, uint32 /*emote*/) {}
  4622. + //virtual void CommonTimers(uint32 diff) = 0;
  4623. +
  4624. + virtual bool HealTarget(Unit* /*target*/, uint8 /*pct*/, uint32 /*diff*/) { return false; }
  4625. + virtual bool BuffTarget(Unit* /*target*/, uint32 /*diff*/) { return false; }
  4626. + virtual bool CureTarget(Unit* /*target*/, uint32 /*cureSpell*/, uint32 /*diff*/) { return false; }
  4627. +
  4628. + uint8 GetWait();
  4629. + inline float InitAttackRange(float origRange, bool ranged) const;
  4630. + uint16 Rand() const;
  4631. + static inline uint32 GetLostHP(Unit* unit) { return unit->GetMaxHealth() - unit->GetHealth(); }
  4632. + static inline uint8 GetHealthPCT(Unit* hTarget) { if (!hTarget || hTarget->isDead()) return 100; return (hTarget->GetHealth()*100/hTarget->GetMaxHealth()); }
  4633. + static inline uint8 GetManaPCT(Unit* hTarget) { if (!hTarget || hTarget->isDead() || hTarget->getPowerType() != POWER_MANA) return 100; return (hTarget->GetPower(POWER_MANA)*100/hTarget->GetMaxPower(POWER_MANA)); }
  4634. +
  4635. + Unit* getTarget(bool byspell, bool ranged, bool &reset) const;
  4636. +
  4637. + CommandStates GetBotCommandState() const { return m_botCommandState; }
  4638. +
  4639. + typedef std::set<Unit*> AttackerSet;
  4640. +
  4641. + Player* master;
  4642. + Unit* opponent;
  4643. + Unit* tank;
  4644. + CommandStates m_botCommandState;
  4645. + SpellInfo const* info;
  4646. + Position pos, attackpos;
  4647. + float stat, atpower, maxdist, regen_mp5, hit,
  4648. + ap_mod, spp_mod, crit_mod;
  4649. + uint64 aftercastTargetGuid;
  4650. + int32 cost, value, sppower, m_spellpower;
  4651. + uint32 GC_Timer, temptimer, checkAurasTimer, wait, currentSpell;
  4652. + uint8 clear_cd, haste, healTargetIconFlags;
  4653. + bool doHealth, doMana, shouldUpdateStats;
  4654. +
  4655. + private:
  4656. + static Unit* _GetBotGroupMainTank(Group* group);
  4657. + static inline float _getAttackDistance(float distance) { return distance > 0.f ? distance*0.72 : 0.f; }
  4658. + Unit* extank;
  4659. + float dmgmult_melee, dmgmult_spell;
  4660. + float dmgmod_melee, dmgmod_spell;
  4661. + uint64 m_TankGuid;
  4662. +};
  4663. +
  4664. +class bot_minion_ai : public bot_ai
  4665. +{
  4666. + public:
  4667. + virtual ~bot_minion_ai();
  4668. + bot_minion_ai(Creature* creature);
  4669. + const bot_minion_ai* GetMinionAI() const { return this; }
  4670. + bool IsMinionAI() const { return true; }
  4671. + bool IsPetAI() const { return false; }
  4672. + void SummonBotsPet(uint32 entry);
  4673. + inline bool IAmDead() const { return (!master || me->isDead()); }
  4674. + void SetBotCommandState(CommandStates st, bool force = false, Position* newpos = NULL);
  4675. + //virtual bool HealTarget(Unit* /*target*/, uint8 /*pct*/, uint32 const /*diff*/) { return false; }
  4676. + //virtual bool BuffTarget(Unit* /*target*/, uint32 const /*diff*/) { return false; }
  4677. + //virtual bool doCast(Unit* /*victim*/, uint32 /*spellId*/, bool /*triggered*/ = false) { return false; }
  4678. + void CureGroup(Player* pTarget, uint32 cureSpell, uint32 diff);
  4679. + bool CureTarget(Unit* target, uint32 cureSpell, uint32 diff);
  4680. + void CheckAuras(bool force = false);
  4681. + //virtual void DoNonCombatActions(uint32 const /*diff*/) {}
  4682. + //virtual void StartAttack(Unit* /*u*/, bool /*force*/ = false) {}
  4683. + void setStats(uint8 myclass, uint8 myrace, uint8 mylevel, bool force = false);
  4684. +
  4685. + static bool OnGossipHello(Player* player, Creature* creature);
  4686. + bool OnGossipSelect(Player* player, Creature* creature, uint32 sender, uint32 action);
  4687. +
  4688. + void InitSpells() {}
  4689. + void _OnHealthUpdate(uint8 myclass, uint8 mylevel) const;
  4690. + void _OnManaUpdate(uint8 myclass, uint8 mylevel) const;
  4691. + void _OnMeleeDamageUpdate(uint8 myclass) const;
  4692. +
  4693. + void OnOwnerDamagedBy(Unit* attacker);
  4694. +
  4695. + inline void SetEvadeTimer(uint8 time) { evade_cd = time; }
  4696. +
  4697. + static inline uint8 GetBotClassForCreature(Creature* bot);
  4698. +
  4699. + protected:
  4700. + void BuffAndHealGroup(Player* gPlayer, uint32 diff);
  4701. + void RezGroup(uint32 REZZ, Player* gPlayer);
  4702. +
  4703. + void Follow(bool force = false, Position* newpos = NULL)
  4704. + {
  4705. + if (force ||
  4706. + (me->isAlive() && (!me->isInCombat() || !opponent) && m_botCommandState != COMMAND_STAY))
  4707. + SetBotCommandState(COMMAND_FOLLOW, force, newpos);
  4708. + }
  4709. +
  4710. + inline void Evade() { _OnEvade(); }
  4711. +
  4712. + virtual void BreakCC(uint32 diff);
  4713. +
  4714. + void CommonTimers(uint32 diff)
  4715. + {
  4716. + if (pvpTrinket_cd > diff) pvpTrinket_cd -= diff;
  4717. + if (Potion_cd > diff) Potion_cd -= diff;
  4718. + if (GC_Timer > diff) GC_Timer -= diff;
  4719. + if (temptimer > diff) temptimer -= diff;
  4720. + if (checkAurasTimer != 0) --checkAurasTimer;
  4721. + if (wait != 0) --wait;
  4722. + if (evade_cd != 0) --evade_cd;
  4723. + }
  4724. +
  4725. + Unit* FindHostileDispelTarget(float dist = 30, bool stealable = false) const;
  4726. + Unit* FindAffectedTarget(uint32 spellId, uint64 caster = 0, float dist = DEFAULT_VISIBILITY_DISTANCE, uint8 hostile = 0) const;
  4727. + Unit* FindPolyTarget(float dist = 30, Unit* currTarget = NULL) const;
  4728. + Unit* FindFearTarget(float dist = 30) const;
  4729. + Unit* FindRepentanceTarget(float dist = 20) const;
  4730. + Unit* FindUndeadCCTarget(float dist = 30, uint32 spellId = 0) const;
  4731. + Unit* FindRootTarget(float dist = 30, uint32 spellId = 0) const;
  4732. + Unit* FindCastingTarget(float dist = 10, bool isFriend = false, uint32 spellId = 0) const;
  4733. + Unit* FindAOETarget(float dist = 30, bool checkbots = false, bool targetfriend = true) const;
  4734. + Unit* FindSplashTarget(float dist = 5, Unit* To = NULL) const;
  4735. +
  4736. + uint32 Potion_cd, pvpTrinket_cd;
  4737. +
  4738. + private:
  4739. + bool CanCureTarget(Unit* target, uint32 cureSpell, uint32 diff) const;
  4740. + void CalculatePos(Position& pos);
  4741. + void UpdateMountedState();
  4742. + void UpdateStandState() const;
  4743. + void UpdateRations() const;
  4744. + void _OnEvade();
  4745. + PlayerClassLevelInfo* classinfo;
  4746. + float myangle, armor_mod, haste_mod, dodge_mod, parry_mod;
  4747. + uint8 rezz_cd, evade_cd;
  4748. +};
  4749. +
  4750. +class bot_pet_ai : public bot_ai
  4751. +{
  4752. + public:
  4753. + virtual ~bot_pet_ai();
  4754. + bot_pet_ai(Creature* creature);
  4755. + const bot_pet_ai* GetPetAI() const { return this; }
  4756. + Creature* GetCreatureOwner() const { return m_creatureOwner; }
  4757. + bool IsMinionAI() const { return false; }
  4758. + bool IsPetAI() const { return true; }
  4759. + inline bool IAmDead() const { return (!master || !m_creatureOwner || me->isDead()); }
  4760. + //void SetCreatureOwner(Creature* newowner) { m_creatureOwner = newowner; }
  4761. + void SetBotCommandState(CommandStates st, bool force = false, Position* newpos = NULL);
  4762. + //virtual bool HealTarget(Unit* /*target*/, uint8 /*pct*/, uint32 const /*diff*/) { return false; }
  4763. + //virtual bool BuffTarget(Unit* /*target*/, uint32 const /*diff*/) { return false; }
  4764. + //void BuffAndHealGroup(Player* /*gPlayer*/, uint32 const /*diff*/) {}
  4765. + //void RezGroup(uint32 /*REZZ*/, Player* /*gPlayer*/) {}
  4766. + //virtual bool doCast(Unit* /*victim*/, uint32 /*spellId*/, bool /*triggered*/ = false) { return false; }
  4767. + //void CureGroup(Player* /*pTarget*/, uint32 /*cureSpell*/, uint32 const /*diff*/) {}
  4768. + //bool CureTarget(Unit* /*target*/, uint32 /*cureSpell*/, uint32 const /*diff*/) { return false; }
  4769. + void CheckAuras(bool force = false);
  4770. + //virtual void DoNonCombatActions(uint32 const /*diff*/) {}
  4771. + //virtual void StartAttack(Unit* /*u*/, bool /*force*/ = false) {}
  4772. + void setStats(uint8 mylevel, uint8 petType, bool force = false);
  4773. +
  4774. + static uint8 GetPetType(Creature* pet);
  4775. + static uint8 GetPetClass(Creature* pet);
  4776. + static uint32 GetPetOriginalEntry(uint32 entry);
  4777. +
  4778. + //debug
  4779. + //virtual void ListSpells(ChatHandler* /*handler*/) const {}
  4780. +
  4781. + void InitSpells() {}
  4782. + void _OnHealthUpdate(uint8 petType, uint8 mylevel) const;
  4783. + void _OnManaUpdate(uint8 petType, uint8 mylevel) const;
  4784. + //void _OnMeleeDamageUpdate(uint8 /*myclass*/) const {}
  4785. + void SetBaseArmor(uint32 armor) { basearmor = armor; }
  4786. +
  4787. + protected:
  4788. + void CommonTimers(uint32 diff)
  4789. + {
  4790. + if (GC_Timer > diff) GC_Timer -= diff;
  4791. + if (temptimer > diff) temptimer -= diff;
  4792. + if (checkAurasTimer != 0) --checkAurasTimer;
  4793. + if (wait != 0) --wait;
  4794. + }
  4795. +
  4796. + Creature* m_creatureOwner;
  4797. + private:
  4798. + uint32 basearmor;
  4799. +};
  4800. +
  4801. +#endif
  4802. diff --git a/src/server/game/AI/NpcBots/bot_druid_ai.cpp b/src/server/game/AI/NpcBots/bot_druid_ai.cpp
  4803. new file mode 100644
  4804. index 0000000..71e8328
  4805. --- /dev/null
  4806. +++ b/src/server/game/AI/NpcBots/bot_druid_ai.cpp
  4807. @@ -0,0 +1,1092 @@
  4808. +#include "bot_ai.h"
  4809. +#include "Group.h"
  4810. +#include "Player.h"
  4811. +#include "ScriptMgr.h"
  4812. +#include "SpellAuras.h"
  4813. +#include "WorldSession.h"
  4814. +/*
  4815. +Druid NpcBot (reworked by Graff [email protected])
  4816. +Complete - Maybe 30%
  4817. +TODO: Feral Spells (from scratch), More Forms, Balance Spells + treants...
  4818. +*/
  4819. +class druid_bot : public CreatureScript
  4820. +{
  4821. +public:
  4822. + druid_bot() : CreatureScript("druid_bot") { }
  4823. +
  4824. + CreatureAI* GetAI(Creature* creature) const
  4825. + {
  4826. + return new bot_druid_ai(creature);
  4827. + }
  4828. +
  4829. + bool OnGossipHello(Player* player, Creature* creature)
  4830. + {
  4831. + return bot_minion_ai::OnGossipHello(player, creature);
  4832. + }
  4833. +
  4834. + bool OnGossipSelect(Player* player, Creature* creature, uint32 sender, uint32 action)
  4835. + {
  4836. + if (bot_minion_ai* ai = creature->GetBotMinionAI())
  4837. + return ai->OnGossipSelect(player, creature, sender, action);
  4838. + return true;
  4839. + }
  4840. +
  4841. + struct bot_druid_ai : public bot_minion_ai
  4842. + {
  4843. + bot_druid_ai(Creature* creature) : bot_minion_ai(creature) { }
  4844. +
  4845. + bool doCast(Unit* victim, uint32 spellId, bool triggered = false)
  4846. + {
  4847. + if (checkBotCast(victim, spellId, CLASS_DRUID) != SPELL_CAST_OK)
  4848. + return false;
  4849. +
  4850. + info = sSpellMgr->GetSpellInfo(spellId);
  4851. + if (swiftness && info->CalcCastTime() > 0)
  4852. + {
  4853. + DoCast(victim, spellId, true);
  4854. + me->RemoveAurasDueToSpell(NATURES_SWIFTNESS, me->GetGUID(), 0, AURA_REMOVE_BY_EXPIRE);
  4855. + me->RemoveAurasDueToSpell(CRIT_50, me->GetGUID(), 0, AURA_REMOVE_BY_EXPIRE);
  4856. + swiftness = false;
  4857. + return true;
  4858. + }
  4859. + if (spellId == BEAR_FORM || spellId == CAT_FORM)
  4860. + {
  4861. + me->ModifyPower(POWER_MANA, - int32(info->CalcPowerCost(me, info->GetSchoolMask())));
  4862. + mana = me->GetPower(POWER_MANA);
  4863. + if (Unit* u = me->getVictim())
  4864. + GetInPosition(true, false, u);
  4865. + }
  4866. +
  4867. + bool result = bot_ai::doCast(victim, spellId, triggered);
  4868. +
  4869. + if (result &&
  4870. + //spellId != BEAR_FORM && spellId != CAT_FORM &&
  4871. + spellId != MANAPOTION && spellId != WARSTOMP &&
  4872. + me->HasAura(OMEN_OF_CLARITY_BUFF))
  4873. + {
  4874. + cost = info->CalcPowerCost(me, info->GetSchoolMask());
  4875. + clearcast = true;
  4876. + power = me->getPowerType();
  4877. + }
  4878. + return result;
  4879. + }
  4880. +
  4881. + void EnterCombat(Unit*) { }
  4882. + void Aggro(Unit*) { }
  4883. + void AttackStart(Unit*) { }
  4884. + void KilledUnit(Unit*) { }
  4885. + void EnterEvadeMode() { }
  4886. + void MoveInLineOfSight(Unit*) { }
  4887. + void JustDied(Unit*) { removeFeralForm(true, false); master->SetNpcBotDied(me->GetGUID()); }
  4888. +
  4889. + void warstomp(uint32 diff)
  4890. + {
  4891. + if (me->getRace() != RACE_TAUREN) return;
  4892. + if (Warstomp_Timer > diff) return;
  4893. + if (me->GetShapeshiftForm() != FORM_NONE)
  4894. + return;
  4895. +
  4896. + AttackerSet b_attackers = me->getAttackers();
  4897. +
  4898. + if (b_attackers.empty())
  4899. + {
  4900. + Unit* u = me->SelectNearestTarget(5);
  4901. + if (u && u->isInCombat() && u->isTargetableForAttack())
  4902. + {
  4903. + if (doCast(me, WARSTOMP))
  4904. + {
  4905. + Warstomp_Timer = 30000; //30sec
  4906. + return;
  4907. + }
  4908. + }
  4909. + }
  4910. + for(AttackerSet::iterator iter = b_attackers.begin(); iter != b_attackers.end(); ++iter)
  4911. + {
  4912. + if (!(*iter) || (*iter)->isDead()) continue;
  4913. + if (!(*iter)->isTargetableForAttack()) continue;
  4914. + if (me->GetDistance((*iter)) <= 5)
  4915. + {
  4916. + if (doCast(me, WARSTOMP))
  4917. + Warstomp_Timer = 30000; //30sec
  4918. + }
  4919. + }
  4920. + }
  4921. +
  4922. + bool DamagePossible()
  4923. + {
  4924. + return true;
  4925. + //return (GetManaPCT(me) < 30 || GetHealthPCT(master) < 50);
  4926. + /*if (GetHealthPCT(master) < 75 || GetHealthPCT(me) < 75) return false;
  4927. +
  4928. + if (Group* pGroup = master->GetGroup())
  4929. + {
  4930. + uint8 LHPcount = 0;
  4931. + uint8 DIScount = 0;
  4932. + for (GroupReference* itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
  4933. + {
  4934. + Player* tPlayer = itr->getSource();
  4935. + if (!tPlayer || tPlayer->isDead()) continue;
  4936. + if (me->GetExactDist(tPlayer) > 30) continue;
  4937. + if (tPlayer->GetHealth()*100 / tPlayer->GetMaxHealth() < 75)
  4938. + ++LHPcount;
  4939. + Unit::AuraApplicationMap const& auras = tPlayer->GetAppliedAuras();
  4940. + for (Unit::AuraApplicationMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr)
  4941. + if (itr->second->GetBase()->GetSpellInfo()->Dispel == DISPEL_POISON)
  4942. + ++DIScount;
  4943. + }
  4944. + uint8 members = master->GetGroup()->GetMembersCount();
  4945. +
  4946. + if (members > 10)
  4947. + {
  4948. + if (LHPcount > 1 || DIScount > 2) return false;
  4949. + }
  4950. + if (members > 4)
  4951. + {
  4952. + if (LHPcount > 0 || DIScount > 1) return false;
  4953. + }
  4954. + if (members < 5)
  4955. + {
  4956. + if (LHPcount > 0 || DIScount > 0) return false;
  4957. + }
  4958. + }//endif unitlist
  4959. +
  4960. + Unit* u = master->getVictim();
  4961. + if (master->getAttackers().size() > 4 ||
  4962. + (!master->getAttackers().empty() &&
  4963. + u != NULL && u->GetHealth() > me->GetMaxHealth()*17))
  4964. + return false;
  4965. +
  4966. + return true;*/
  4967. + }
  4968. +
  4969. + void removeFeralForm(bool force = false, bool init = true, uint32 diff = 0)
  4970. + {
  4971. + if (!force && formtimer > diff) return;
  4972. + ShapeshiftForm form = me->GetShapeshiftForm();
  4973. + if (form != FORM_NONE)
  4974. + {
  4975. + switch (form)
  4976. + {
  4977. + case FORM_DIREBEAR:
  4978. + case FORM_BEAR:
  4979. + me->RemoveAurasDueToSpell(BEAR_FORM);
  4980. + break;
  4981. + case FORM_CAT:
  4982. + me->RemoveAurasDueToSpell(CAT_FORM);
  4983. + me->RemoveAurasDueToSpell(ENERGIZE);
  4984. + break;
  4985. + default:
  4986. + break;
  4987. + }
  4988. + SetStats(CLASS_DRUID, init);
  4989. + }
  4990. + }
  4991. +
  4992. + void StartAttack(Unit* u, bool force = false)
  4993. + {
  4994. + if (GetBotCommandState() == COMMAND_ATTACK && !force) return;
  4995. + Aggro(u);
  4996. + SetBotCommandState(COMMAND_ATTACK);
  4997. + GetInPosition(force, me->GetShapeshiftForm() == FORM_NONE);
  4998. + }
  4999. +
  5000. + void doBearActions(uint32 diff)
  5001. + {
  5002. + if (me->getPowerType() != POWER_RAGE) return;
  5003. +
  5004. + if (GetHealthPCT(me) < 75)
  5005. + HealTarget(me, GetHealthPCT(me), diff);
  5006. + opponent = me->getVictim();
  5007. + if (opponent)
  5008. + StartAttack(opponent, true);
  5009. + else
  5010. + return;
  5011. +
  5012. + //range check (melee) to prevent fake casts
  5013. + if (me->GetDistance(opponent) > 5) return;
  5014. +
  5015. + if (MangleB_Timer <= diff && rage >= 200 && doCast(opponent, MANGLE_BEAR))
  5016. + {
  5017. + MangleB_Timer = 6000 - me->getLevel()/4 * 100;
  5018. + return;
  5019. + }
  5020. +
  5021. + if (GC_Timer <= diff && rage >= 200 && doCast(opponent, SWIPE))
  5022. + return;
  5023. +
  5024. + }//end doBearActions
  5025. +
  5026. + void doCatActions(uint32 diff)
  5027. + {
  5028. + if (GetHealthPCT(me) < 75)
  5029. + HealTarget(me, GetHealthPCT(me), diff);
  5030. + opponent = me->getVictim();
  5031. + if (opponent)
  5032. + StartAttack(opponent, true);
  5033. + else
  5034. + return;
  5035. + uint32 energy = me->GetPower(POWER_ENERGY);
  5036. +
  5037. + if (MoveBehind(*opponent))
  5038. + wait = 5;
  5039. + //{ wait = 5; return; }
  5040. +
  5041. + //range check (melee) to prevent fake casts
  5042. + if (me->GetDistance(opponent) > 5) return;
  5043. +
  5044. + if (Mangle_Cat_Timer <= diff && energy > 45 && doCast(opponent, MANGLE_CAT))
  5045. + Mangle_Cat_Timer = 6000;
  5046. + if (Rake_Timer <= diff && energy > 40 && doCast(opponent, RAKE))
  5047. + Rake_Timer = 10000;
  5048. + if (Shred_Timer <= diff && energy > 60 && !opponent->HasInArc(M_PI, me) && doCast(opponent, SHRED))
  5049. + Shred_Timer = 12000;
  5050. + if (Rip_Timer <= diff && energy > 30 && doCast(opponent, RIP))
  5051. + Rip_Timer = 15000;
  5052. + if (Claw_Timer <= diff && energy > 45 && doCast(opponent, CLAW))
  5053. + Claw_Timer = GC_Timer;
  5054. + }//end doCatActions
  5055. +
  5056. + void doBalanceActions(uint32 diff)
  5057. + {
  5058. + removeFeralForm(true, true);
  5059. + opponent = me->getVictim();
  5060. + if (opponent)
  5061. + {
  5062. + if (!IsCasting())
  5063. + StartAttack(opponent);
  5064. + }
  5065. + else
  5066. + return;
  5067. + AttackerSet m_attackers = master->getAttackers();
  5068. + AttackerSet b_attackers = me->getAttackers();
  5069. +
  5070. + //range check (melee) to prevent fake casts
  5071. + if (me->GetExactDist(opponent) > 30 || !DamagePossible()) return;
  5072. +
  5073. + if (HURRICANE && Hurricane_Timer <= diff && GC_Timer <= diff && Rand() > 35 && !IsCasting())
  5074. + {
  5075. + Unit* target = FindAOETarget(30, true);
  5076. + if (target && doCast(target, HURRICANE))
  5077. + {
  5078. + Hurricane_Timer = 5000;
  5079. + return;
  5080. + }
  5081. + Hurricane_Timer = 2000;//fail
  5082. + }
  5083. + if (GC_Timer <= diff && !opponent->HasAura(FAIRIE_FIRE))
  5084. + if (doCast(opponent, FAIRIE_FIRE))
  5085. + return;
  5086. + if (Rand() > 30 && Moonfire_Timer <= diff && GC_Timer <= diff &&
  5087. + !opponent->HasAura(MOONFIRE, me->GetGUID()))
  5088. + if (doCast(opponent, MOONFIRE))
  5089. + {
  5090. + Moonfire_Timer = 5000;
  5091. + return;
  5092. + }
  5093. + if (Rand() > 30 && Starfire_Timer <= diff && GC_Timer <= diff &&
  5094. + doCast(opponent, STARFIRE))
  5095. + {
  5096. + Starfire_Timer = 11000;
  5097. + return;
  5098. + }
  5099. + if (Rand() > 50 && Wrath_Timer <= diff && GC_Timer <= diff &&
  5100. + doCast(opponent, WRATH))
  5101. + {
  5102. + Wrath_Timer = uint32(sSpellMgr->GetSpellInfo(WRATH)->CalcCastTime()/100 * me->GetFloatValue(UNIT_MOD_CAST_SPEED) + 1);
  5103. + return;
  5104. + }
  5105. + }
  5106. +
  5107. + bool MassGroupHeal(Player* gPlayer, uint32 diff)
  5108. + {
  5109. + if (!gPlayer || GC_Timer > diff) return false;
  5110. + if (!TRANQUILITY && !WILD_GROWTH) return false;
  5111. + if (Tranquility_Timer > diff && Wild_Growth_Timer > diff) return false;
  5112. + if (Rand() > 30) return false;
  5113. + if (IsCasting()) return false; // if I'm already casting
  5114. + Group* pGroup = gPlayer->GetGroup();
  5115. + if (!pGroup) return false;
  5116. + uint8 LHPcount = 0;
  5117. + uint8 pct = 100;
  5118. + Unit* healTarget = NULL;
  5119. + for (GroupReference* itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
  5120. + {
  5121. + Player* tPlayer = itr->getSource();
  5122. + if (!tPlayer || (tPlayer->isDead() && !tPlayer->HaveBot())) continue;
  5123. + if (me->GetExactDist(tPlayer) > 39) continue;
  5124. + if (GetHealthPCT(tPlayer) < 80)
  5125. + {
  5126. + if (GetHealthPCT(tPlayer) < pct)
  5127. + {
  5128. + pct = GetHealthPCT(tPlayer);
  5129. + healTarget = tPlayer;
  5130. + }
  5131. + ++LHPcount;
  5132. + if (LHPcount > 2) break;
  5133. + }
  5134. + if (tPlayer->HaveBot())
  5135. + {
  5136. + for (uint8 i = 0; i != tPlayer->GetMaxNpcBots(); ++i)
  5137. + {
  5138. + Creature* bot = tPlayer->GetBotMap(i)->_Cre();
  5139. + if (bot && bot->IsInWorld() && bot->GetExactDist(me) < 40 && GetHealthPCT(bot) < 80)
  5140. + {
  5141. + if (GetHealthPCT(bot) < pct)
  5142. + {
  5143. + pct = GetHealthPCT(bot);
  5144. + healTarget = bot;
  5145. + }
  5146. + ++LHPcount;
  5147. + if (LHPcount > 2) break;
  5148. + }
  5149. + }
  5150. + }
  5151. + }
  5152. + if (LHPcount > 2 && TRANQUILITY && Tranquility_Timer <= diff &&
  5153. + doCast(me, TRANQUILITY))
  5154. + { Tranquility_Timer = 45000; return true; }
  5155. + if (LHPcount > 0 && WILD_GROWTH && Wild_Growth_Timer <= diff &&
  5156. + doCast(healTarget, WILD_GROWTH))
  5157. + { Wild_Growth_Timer = 6000; return true; }
  5158. + return false;
  5159. + }//end MassGroupHeal
  5160. +
  5161. + void UpdateAI(uint32 diff)
  5162. + {
  5163. + ReduceCD(diff);
  5164. + if ((me->GetShapeshiftForm() == FORM_DIREBEAR || me->GetShapeshiftForm() == FORM_BEAR) &&
  5165. + me->getPowerType() != POWER_RAGE)
  5166. + me->setPowerType(POWER_RAGE);
  5167. + if (me->GetShapeshiftForm() == FORM_CAT && me->getPowerType() != POWER_ENERGY)
  5168. + me->setPowerType(POWER_ENERGY);
  5169. + if (me->GetShapeshiftForm() == FORM_NONE && me->getPowerType() != POWER_MANA)
  5170. + me->setPowerType(POWER_MANA);
  5171. + if (IAmDead()) return;
  5172. + if (!me->getVictim())
  5173. + Evade();
  5174. + if (me->GetShapeshiftForm() == FORM_DIREBEAR || me->GetShapeshiftForm() == FORM_BEAR)
  5175. + {
  5176. + rage = me->GetPower(POWER_RAGE);
  5177. + if (ragetimer2 <= diff)
  5178. + {
  5179. + if (me->isInCombat() && me->getLevel() >= 30)
  5180. + {
  5181. + if (rage < 990 && rage >= 0)
  5182. + me->SetPower(POWER_RAGE, rage + uint32(10.f*rageIncomeMult));//1 rage per 2 sec
  5183. + else
  5184. + me->SetPower(POWER_RAGE, 1000);//max
  5185. + }
  5186. + ragetimer2 = 2000;
  5187. + }
  5188. + if (ragetimer <= diff)
  5189. + {
  5190. + if (!me->isInCombat())
  5191. + {
  5192. + if (rage > 10.f*rageLossMult)
  5193. + me->SetPower(POWER_RAGE, rage - uint32(10.f*rageLossMult));//-1 rage per 1.5 sec
  5194. + else
  5195. + me->SetPower(POWER_RAGE, 0);//min
  5196. + }
  5197. + ragetimer = 1500;
  5198. + if (rage > 1000) me->SetPower(POWER_RAGE, 1000);
  5199. + if (rage < 10) me->SetPower(POWER_RAGE, 0);
  5200. + }
  5201. + }
  5202. + if (clearcast && me->HasAura(OMEN_OF_CLARITY_BUFF) && !me->IsNonMeleeSpellCasted(false))
  5203. + {
  5204. + me->ModifyPower(POWER_MANA, cost);
  5205. + me->RemoveAurasDueToSpell(OMEN_OF_CLARITY_BUFF,me->GetGUID(),0,AURA_REMOVE_BY_EXPIRE);
  5206. + clearcast = false;
  5207. + }
  5208. + if (wait == 0)
  5209. + wait = GetWait();
  5210. + else
  5211. + return;
  5212. + CheckAuras();
  5213. + BreakCC(diff);
  5214. + if (CCed(me)) return;
  5215. + warstomp(diff);
  5216. +
  5217. + if (me->getPowerType() == POWER_MANA && GetManaPCT(me) < 20 && Potion_cd <= diff)
  5218. + {
  5219. + temptimer = GC_Timer;
  5220. + if (doCast(me, MANAPOTION))
  5221. + Potion_cd = POTION_CD;
  5222. + GC_Timer = temptimer;
  5223. + }
  5224. +
  5225. + //Heal master
  5226. + if (GetHealthPCT(master) < 85)
  5227. + HealTarget(master, GetHealthPCT(master), diff);
  5228. + //Innervate
  5229. + if (INNERVATE && Innervate_Timer <= diff && GC_Timer <= diff)
  5230. + {
  5231. + doInnervate();
  5232. + if (Innervate_Timer <= diff)//if failed or not found target
  5233. + Innervate_Timer = 3000;//set delay
  5234. + }
  5235. +
  5236. + MassGroupHeal(master, diff);
  5237. + if (!me->isInCombat())
  5238. + DoNonCombatActions(diff);
  5239. + else
  5240. + CheckBattleRez(diff);
  5241. + BuffAndHealGroup(master, diff);
  5242. + CureTarget(master, CURE_POISON, diff);
  5243. + CureGroup(master, CURE_POISON, diff);
  5244. +
  5245. + if (!CheckAttackTarget(CLASS_DRUID))
  5246. + return;
  5247. +
  5248. + if (GetHealthPCT(me) < 75)
  5249. + {
  5250. + HealTarget(me, GetHealthPCT(me), diff);
  5251. + return;
  5252. + }
  5253. +
  5254. + if (IsCasting()) return;//Casting heal or something
  5255. + CheckRoots(diff);
  5256. +
  5257. + if (DamagePossible())
  5258. + {
  5259. + Unit* u = opponent->getVictim();
  5260. + //if the target is attacking us, we want to go bear
  5261. + if (BEAR_FORM && !CCed(opponent) &&
  5262. + (u == me || (tank == me && IsInBotParty(u))) ||
  5263. + (!me->getAttackers().empty() && (*me->getAttackers().begin()) == opponent && opponent->GetMaxHealth() > me->GetMaxHealth()*2))
  5264. + {
  5265. + //if we don't have bear yet
  5266. + if (me->GetShapeshiftForm() != FORM_DIREBEAR &&
  5267. + me->GetShapeshiftForm() != FORM_BEAR &&
  5268. + formtimer <= diff &&
  5269. + doCast(me, BEAR_FORM))
  5270. + {
  5271. + SetStats(BEAR);
  5272. + formtimer = 1500;
  5273. + }
  5274. + if (me->GetShapeshiftForm() == FORM_DIREBEAR ||
  5275. + me->GetShapeshiftForm() == FORM_BEAR)
  5276. + {
  5277. + doBearActions(diff);
  5278. + ScriptedAI::UpdateAI(diff);
  5279. + }
  5280. + }
  5281. + else
  5282. + if (CAT_FORM && master->getVictim() != opponent && tank &&
  5283. + u == tank && u != me &&
  5284. + opponent->GetMaxHealth() < tank->GetMaxHealth()*3)
  5285. + {
  5286. + //if we don't have cat yet
  5287. + if (me->GetShapeshiftForm() != FORM_CAT && formtimer <= diff)
  5288. + {
  5289. + if (doCast(me, CAT_FORM))
  5290. + {
  5291. + SetStats(CAT);
  5292. + formtimer = 1500;
  5293. + }
  5294. + }
  5295. + if (me->GetShapeshiftForm() == FORM_CAT)
  5296. + {
  5297. + doCatActions(diff);
  5298. + ScriptedAI::UpdateAI(diff);
  5299. + }
  5300. + }
  5301. + else if (tank != me)
  5302. + {
  5303. + doBalanceActions(diff);
  5304. + }
  5305. + }
  5306. + else if (tank != me)
  5307. + {
  5308. + doBalanceActions(diff);
  5309. + }
  5310. + }
  5311. +
  5312. + bool HealTarget(Unit* target, uint8 hp, uint32 diff)
  5313. + {
  5314. + if (hp > 95) return false;
  5315. + if (!target || target->isDead()) return false;
  5316. + if (tank == me && hp > 35) return false;
  5317. + if (hp > 50 && me->GetShapeshiftForm() != FORM_NONE) return false;//do not waste heal if in feral or so
  5318. + if (Rand() > 50 + 20*target->isInCombat() + 50*master->GetMap()->IsRaid() - 50*me->GetShapeshiftForm()) return false;
  5319. + if (me->GetExactDist(target) > 40) return false;
  5320. +
  5321. + if ((hp < 15 || (hp < 35 && target->getAttackers().size() > 2)) &&
  5322. + Nature_Swiftness_Timer <= diff && (target->isInCombat() || !target->getAttackers().empty()))
  5323. + {
  5324. + if (me->IsNonMeleeSpellCasted(false))
  5325. + me->InterruptNonMeleeSpells(false);
  5326. + if (NATURES_SWIFTNESS && doCast(me, NATURES_SWIFTNESS) && RefreshAura(CRIT_50, 2))//need to be critical
  5327. + {
  5328. + swiftness = true;
  5329. + if (doCast(target, HEALING_TOUCH, true))
  5330. + {
  5331. + Nature_Swiftness_Timer = 120000;//2 min
  5332. + Heal_Timer = 3000;
  5333. + return true;
  5334. + }
  5335. + }
  5336. + }
  5337. + if (SWIFTMEND && (hp < 25 || GetLostHP(target) > 5000) && Swiftmend_Timer <= 3000 &&
  5338. + (HasAuraName(target, REGROWTH) || HasAuraName(target, REJUVENATION)))
  5339. + {
  5340. + if (doCast(target, SWIFTMEND))
  5341. + {
  5342. + Swiftmend_Timer = 10000;
  5343. + if (GetHealthPCT(target) > 75)
  5344. + return true;
  5345. + else if (!target->getAttackers().empty())
  5346. + {
  5347. + if (doCast(target, REGROWTH))
  5348. + {
  5349. + GC_Timer = 300;
  5350. + return true;
  5351. + }
  5352. + }
  5353. + }
  5354. + }
  5355. + if (hp > 35 && (hp < 75 || GetLostHP(target) > 3000) && Heal_Timer <= diff && NOURISH)
  5356. + {
  5357. + switch (urand(1,3))
  5358. + {
  5359. + case 1:
  5360. + case 2:
  5361. + if (doCast(target, NOURISH))
  5362. + {
  5363. + Heal_Timer = 3000;
  5364. + return true;
  5365. + }
  5366. + break;
  5367. + case 3:
  5368. + if (doCast(target, HEALING_TOUCH))
  5369. + {
  5370. + Heal_Timer = 3000;
  5371. + return true;
  5372. + }
  5373. + break;
  5374. + }
  5375. + }
  5376. + //maintain HoTs
  5377. + Unit* u = target->getVictim();
  5378. + Creature* boss = u && u->ToCreature() && u->ToCreature()->isWorldBoss() ? u->ToCreature() : NULL;
  5379. + bool tanking = tank == target && boss;
  5380. + if (( (hp < 80 || GetLostHP(target) > 3500 || tanking) &&
  5381. + Regrowth_Timer <= diff && GC_Timer <= diff && !target->HasAura(REGROWTH, me->GetGUID()) )
  5382. + ||
  5383. + (target->HasAura(REGROWTH, me->GetGUID()) && target->HasAura(REJUVENATION, me->GetGUID()) &&
  5384. + (hp < 70 || GetLostHP(target) > 3000) && Regrowth_Timer <= diff && GC_Timer <= diff))
  5385. + {
  5386. + if (doCast(target, REGROWTH))
  5387. + { Regrowth_Timer = 2000; return true; }
  5388. + }
  5389. + if (hp > 25 && (hp < 90 || GetLostHP(target) > 2000 || tanking) && GC_Timer <= diff &&
  5390. + !HasAuraName(target, REJUVENATION, me->GetGUID()))
  5391. + {
  5392. + if (doCast(target, REJUVENATION))
  5393. + {
  5394. + if (!target->getAttackers().empty() && (hp < 75 || GetLostHP(target) > 4000))
  5395. + if (SWIFTMEND && Swiftmend_Timer <= diff && doCast(target, SWIFTMEND))
  5396. + Swiftmend_Timer = 10000;
  5397. + GC_Timer = 500;
  5398. + return true;
  5399. + }
  5400. + }
  5401. + if (LIFEBLOOM != 0 && GC_Timer <= diff &&
  5402. + ((hp < 85 && hp > 40) || (hp > 70 && tanking) ||
  5403. + (hp < 70 && hp > 25 && HasAuraName(target, REGROWTH) && HasAuraName(target, REJUVENATION)) ||
  5404. + (GetLostHP(target) > 1500 && hp > 35)))
  5405. + {
  5406. + Aura* bloom = target->GetAura(LIFEBLOOM, me->GetGUID());
  5407. + if ((!bloom || bloom->GetStackAmount() < 3) && doCast(target, LIFEBLOOM))
  5408. + return true;
  5409. + }
  5410. + if (hp > 30 && (hp < 70 || GetLostHP(target) > 3000) && Heal_Timer <= diff &&
  5411. + doCast(target, HEALING_TOUCH))
  5412. + {
  5413. + Heal_Timer = 3000;
  5414. + return true;
  5415. + }
  5416. + return false;
  5417. + }
  5418. +
  5419. + bool BuffTarget(Unit* target, uint32 diff)
  5420. + {
  5421. + if (GC_Timer > diff || Rand() > 40) return false;
  5422. + if (me->isInCombat() && !master->GetMap()->IsRaid()) return false;
  5423. + if (target && target->isAlive() && me->GetExactDist(target) < 30)
  5424. + {
  5425. + if (!HasAuraName(target, MARK_OF_THE_WILD))
  5426. + if (doCast(target, MARK_OF_THE_WILD))
  5427. + return true;
  5428. + if (!HasAuraName(target, THORNS))
  5429. + if (doCast(target, THORNS))
  5430. + return true;
  5431. + }
  5432. + return false;
  5433. + }
  5434. +
  5435. + void DoNonCombatActions(uint32 diff)
  5436. + {
  5437. + //if eating or drinking don't do anything
  5438. + if (GC_Timer > diff || me->IsMounted()) return;
  5439. +
  5440. + RezGroup(REVIVE, master);
  5441. +
  5442. + if (Feasting()) return;
  5443. +
  5444. + if (BuffTarget(master, diff))
  5445. + {
  5446. + /*GC_Timer = 800;*/
  5447. + return;
  5448. + }
  5449. + if (BuffTarget(me, diff))
  5450. + {
  5451. + /*GC_Timer = 800;*/
  5452. + return;
  5453. + }
  5454. + }
  5455. +
  5456. + void doInnervate(uint8 minmanaval = 30)
  5457. + {
  5458. + Unit* iTarget = NULL;
  5459. +
  5460. + if (master->isInCombat() && GetManaPCT(master) < 20)
  5461. + iTarget = master;
  5462. + else if (me->isInCombat() && GetManaPCT(me) < 20)
  5463. + iTarget = me;
  5464. +
  5465. + Group* group = master->GetGroup();
  5466. + if (!iTarget && !group)//first check master's bots
  5467. + {
  5468. + for (uint8 i = 0; i != master->GetMaxNpcBots(); ++i)
  5469. + {
  5470. + Creature* bot = master->GetBotMap(i)->_Cre();
  5471. + if (!bot || !bot->isInCombat() || bot->isDead()) continue;
  5472. + if (me->GetExactDist(bot) > 30) continue;
  5473. + if (GetManaPCT(bot) < minmanaval)
  5474. + {
  5475. + iTarget = bot;
  5476. + break;
  5477. + }
  5478. + }
  5479. + }
  5480. + if (!iTarget)//cycle through player members...
  5481. + {
  5482. + for (GroupReference* itr = group->GetFirstMember(); itr != NULL; itr = itr->next())
  5483. + {
  5484. + Player* tPlayer = itr->getSource();
  5485. + if (tPlayer == NULL || !tPlayer->isInCombat() || tPlayer->isDead()) continue;
  5486. + if (me->GetExactDist(tPlayer) > 30) continue;
  5487. + if (GetManaPCT(tPlayer) < minmanaval)
  5488. + {
  5489. + iTarget = tPlayer;
  5490. + break;
  5491. + }
  5492. + }
  5493. + }
  5494. + if (!iTarget)//... and their bots.
  5495. + {
  5496. + for (GroupReference* itr = group->GetFirstMember(); itr != NULL; itr = itr->next())
  5497. + {
  5498. + Player* tPlayer = itr->getSource();
  5499. + if (tPlayer == NULL || !tPlayer->HaveBot()) continue;
  5500. + for (uint8 i = 0; i != tPlayer->GetMaxNpcBots(); ++i)
  5501. + {
  5502. + Creature* bot = tPlayer->GetBotMap(i)->_Cre();
  5503. + if (!bot || bot->isDead()) continue;
  5504. + if (me->GetExactDist(bot) > 30) continue;
  5505. + if (GetManaPCT(bot) < minmanaval)
  5506. + {
  5507. + iTarget = bot;
  5508. + break;
  5509. + }
  5510. + }
  5511. + }
  5512. + }
  5513. +
  5514. + if (iTarget && !iTarget->HasAura(INNERVATE) && doCast(iTarget, INNERVATE))
  5515. + {
  5516. + if (iTarget->GetTypeId() == TYPEID_PLAYER)
  5517. + me->MonsterWhisper("Innervate on You!", iTarget->GetGUID());
  5518. + Innervate_Timer = iTarget->GetTypeId() == TYPEID_PLAYER ? 60000 : 30000;//1 min if player and 30 sec if bot
  5519. + }
  5520. + }
  5521. +
  5522. + void CheckRoots(uint32 diff)
  5523. + {
  5524. + if (!ENTANGLING_ROOTS || GC_Timer > diff) return;
  5525. + if (me->GetShapeshiftForm() != FORM_NONE) return;
  5526. + if (FindAffectedTarget(ENTANGLING_ROOTS, me->GetGUID(), 60)) return;
  5527. + if (Unit* target = FindRootTarget(30, ENTANGLING_ROOTS))
  5528. + if (doCast(target, ENTANGLING_ROOTS))
  5529. + return;
  5530. + }
  5531. +
  5532. + void CheckBattleRez(uint32 diff)
  5533. + {
  5534. + if (!REBIRTH || Rebirth_Timer > diff || Rand() > 10 || IsCasting() || me->IsMounted()) return;
  5535. + Group* gr = master->GetGroup();
  5536. + if (!gr)
  5537. + {
  5538. + Unit* target = master;
  5539. + if (master->isAlive()) return;
  5540. + if (master->isRessurectRequested()) return; //ressurected
  5541. + if (master->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST))
  5542. + target = (Unit*)master->GetCorpse();
  5543. + if (me->GetExactDist(target) > 30)
  5544. + {
  5545. + me->GetMotionMaster()->MovePoint(master->GetMapId(), *target);
  5546. + Rebirth_Timer = 1500;
  5547. + return;
  5548. + }
  5549. + else if (!target->IsWithinLOSInMap(me))
  5550. + me->Relocate(*target);
  5551. +
  5552. + if (doCast(target, REBIRTH))//rezzing
  5553. + {
  5554. + me->MonsterWhisper("Rezzing You", master->GetGUID());
  5555. + Rebirth_Timer = me->getLevel() >= 60 ? 300000 : 600000; //5-10 min (improved possible)
  5556. + }
  5557. + return;
  5558. + }
  5559. + for (GroupReference* itr = gr->GetFirstMember(); itr != NULL; itr = itr->next())
  5560. + {
  5561. + Player* tPlayer = itr->getSource();
  5562. + Unit* target = tPlayer;
  5563. + if (!tPlayer || tPlayer->isAlive()) continue;
  5564. + if (tPlayer->isRessurectRequested()) continue; //ressurected
  5565. + if (tPlayer->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST))
  5566. + target = (Unit*)tPlayer->GetCorpse();
  5567. + if (master->GetMap() != target->FindMap()) continue;
  5568. + if (!target->IsInWorld()) continue;
  5569. + if (me->GetExactDist(target) > 30)
  5570. + {
  5571. + me->GetMotionMaster()->MovePoint(target->GetMapId(), *target);
  5572. + Rebirth_Timer = 1500;
  5573. + return;
  5574. + }
  5575. + else if (!target->IsWithinLOSInMap(me))
  5576. + me->Relocate(*target);
  5577. +
  5578. + if (doCast(target, REBIRTH))//rezzing
  5579. + {
  5580. + me->MonsterWhisper("Rezzing You", tPlayer->GetGUID());
  5581. + Rebirth_Timer = me->getLevel() >= 60 ? 300000 : 600000; //5-10 min (improved possible)
  5582. + return;
  5583. + }
  5584. + }
  5585. + }
  5586. +
  5587. + void SetStats(uint8 form, bool init = true)
  5588. + {
  5589. + switch (form)
  5590. + {
  5591. + case BEAR:
  5592. + me->SetBotClass(BEAR);
  5593. + if (me->getPowerType() != POWER_RAGE)
  5594. + {
  5595. + me->setPowerType(POWER_RAGE);
  5596. + me->SetMaxPower(POWER_RAGE, 1000);
  5597. + }
  5598. + if (me->getLevel() >= 15)
  5599. + me->SetPower(POWER_RAGE, 200);
  5600. + else
  5601. + me->SetPower(POWER_RAGE, 0);
  5602. + if (me->getLevel() >= 40 && !me->HasAura(LEADER_OF_THE_PACK))
  5603. + RefreshAura(LEADER_OF_THE_PACK);
  5604. + setStats(BEAR, me->getRace(), master->getLevel());
  5605. + break;
  5606. + case CAT:
  5607. + me->SetBotClass(CAT);
  5608. + if (me->getPowerType() != POWER_ENERGY)
  5609. + {
  5610. + me->setPowerType(POWER_ENERGY);
  5611. + me->SetMaxPower(POWER_ENERGY, 100);
  5612. + me->SetPower(POWER_ENERGY, 0);
  5613. + }
  5614. + if (me->getLevel() >= 15)
  5615. + me->SetPower(POWER_ENERGY, 60);
  5616. + else
  5617. + me->SetPower(POWER_ENERGY, 0);
  5618. + if (me->getLevel() >= 40 && !me->HasAura(LEADER_OF_THE_PACK))
  5619. + RefreshAura(LEADER_OF_THE_PACK);
  5620. + RefreshAura(ENERGIZE, me->getLevel()/40 + master->Has310Flyer(false));
  5621. + setStats(CAT, me->getRace(), master->getLevel());
  5622. + break;
  5623. + case CLASS_DRUID:
  5624. + me->SetBotClass(CLASS_DRUID);
  5625. + if (me->getPowerType() != POWER_MANA)
  5626. + me->setPowerType(POWER_MANA);
  5627. + if (init)
  5628. + me->SetPower(POWER_MANA, mana);
  5629. + setStats(CLASS_DRUID, me->getRace(), master->getLevel());
  5630. + break;
  5631. + }
  5632. + }
  5633. +
  5634. + void SpellHit(Unit* caster, SpellInfo const* spell)
  5635. + {
  5636. + OnSpellHit(caster, spell);
  5637. + }
  5638. +
  5639. + void DamageTaken(Unit* u, uint32& /*damage*/)
  5640. + {
  5641. + OnOwnerDamagedBy(u);
  5642. + }
  5643. +
  5644. + void OwnerAttackedBy(Unit* u)
  5645. + {
  5646. + OnOwnerDamagedBy(u);
  5647. + }
  5648. +
  5649. + void Reset()
  5650. + {
  5651. + Heal_Timer = 0;
  5652. + Regrowth_Timer = 0;
  5653. + Swiftmend_Timer = 0;
  5654. + Wild_Growth_Timer = 0;
  5655. + Tranquility_Timer = 0;
  5656. + Nature_Swiftness_Timer = 0;
  5657. + Rebirth_Timer = 0;
  5658. + Warstomp_Timer = 0;
  5659. + MangleB_Timer = 0;
  5660. + Claw_Timer = 0;
  5661. + Rake_Timer = 0;
  5662. + Shred_Timer = 0;
  5663. + Rip_Timer = 0;
  5664. + Mangle_Cat_Timer = 0;
  5665. + Moonfire_Timer = 0;
  5666. + Starfire_Timer = 0;
  5667. + Wrath_Timer = 0;
  5668. + Hurricane_Timer = 0;
  5669. + Innervate_Timer = 0;
  5670. + formtimer = 0;
  5671. + clearcast = false;
  5672. + swiftness = false;
  5673. + power = POWER_MANA;
  5674. + mana = 0;
  5675. + rage = 0;
  5676. + rageIncomeMult = sWorld->getRate(RATE_POWER_RAGE_INCOME);
  5677. + rageLossMult = sWorld->getRate(RATE_POWER_RAGE_LOSS);
  5678. + ragetimer = 0;
  5679. + ragetimer2 = 0;
  5680. +
  5681. + if (master)
  5682. + {
  5683. + setStats(CLASS_DRUID, me->getRace(), master->getLevel(), true);
  5684. + ApplyClassPassives();
  5685. + ApplyPassives(CLASS_DRUID);
  5686. + }
  5687. + }
  5688. +
  5689. + void ReduceCD(uint32 diff)
  5690. + {
  5691. + CommonTimers(diff);
  5692. + if (MangleB_Timer > diff) MangleB_Timer -= diff;
  5693. + if (Claw_Timer > diff) Claw_Timer -= diff;
  5694. + if (Rake_Timer > diff) Rake_Timer -= diff;
  5695. + if (Shred_Timer > diff) Shred_Timer -= diff;
  5696. + if (Mangle_Cat_Timer > diff) Mangle_Cat_Timer -= diff;
  5697. + if (Moonfire_Timer > diff) Moonfire_Timer -= diff;
  5698. + if (Starfire_Timer > diff) Starfire_Timer -= diff;
  5699. + if (Wrath_Timer > diff) Wrath_Timer -= diff;
  5700. + if (Hurricane_Timer > diff) Hurricane_Timer -= diff;
  5701. + if (Innervate_Timer > diff) Innervate_Timer -= diff;
  5702. + if (Rip_Timer > diff) Rip_Timer -= diff;
  5703. + if (Regrowth_Timer > diff) Regrowth_Timer -= diff;
  5704. + if (Heal_Timer > diff) Heal_Timer -= diff;
  5705. + if (Swiftmend_Timer > diff) Swiftmend_Timer -= diff;
  5706. + if (Wild_Growth_Timer > diff) Wild_Growth_Timer -= diff;
  5707. + if (Nature_Swiftness_Timer > diff) Nature_Swiftness_Timer -= diff;
  5708. + if (Tranquility_Timer > diff) Tranquility_Timer -= diff;
  5709. + if (Rebirth_Timer > diff) Rebirth_Timer -= diff;
  5710. + if (Warstomp_Timer > diff) Warstomp_Timer -= diff;
  5711. + if (formtimer > diff) formtimer -= diff;
  5712. + if (ragetimer > diff) ragetimer -= diff;
  5713. + if (ragetimer2 > diff) ragetimer2 -= diff;
  5714. + }
  5715. +
  5716. + bool CanRespawn()
  5717. + {return false;}
  5718. +
  5719. + void InitSpells()
  5720. + {
  5721. + uint8 lvl = me->getLevel();
  5722. + MARK_OF_THE_WILD = InitSpell(me, MARK_OF_THE_WILD_1);
  5723. + THORNS = InitSpell(me, THORNS_1);
  5724. + HEALING_TOUCH = InitSpell(me, HEALING_TOUCH_1);
  5725. + REGROWTH = InitSpell(me, REGROWTH_1);
  5726. + REJUVENATION = InitSpell(me, REJUVENATION_1);
  5727. + LIFEBLOOM = InitSpell(me, LIFEBLOOM_1);
  5728. + NOURISH = InitSpell(me, NOURISH_1);
  5729. + /*tal*/WILD_GROWTH = lvl >= 60 ? InitSpell(me, WILD_GROWTH_1) : 0;
  5730. + /*tal*/SWIFTMEND = lvl >= 40 ? InitSpell(me, SWIFTMEND_1) : 0;
  5731. + TRANQUILITY = InitSpell(me, TRANQUILITY_1);
  5732. + REVIVE = InitSpell(me, REVIVE_1);
  5733. + REBIRTH = InitSpell(me, REBIRTH_1);
  5734. + BEAR_FORM = InitSpell(me, BEAR_FORM_1);
  5735. + SWIPE = InitSpell(me, SWIPE_1);
  5736. + /*tal*/MANGLE_BEAR = lvl >= 50 ? InitSpell(me, MANGLE_BEAR_1) : 0;
  5737. + BASH = InitSpell(me, BASH_1);
  5738. + CAT_FORM = InitSpell(me, CAT_FORM_1);
  5739. + CLAW = InitSpell(me, CLAW_1);
  5740. + RAKE = InitSpell(me, RAKE_1);
  5741. + SHRED = InitSpell(me, SHRED_1);
  5742. + RIP = InitSpell(me, RIP_1);
  5743. + /*tal*/MANGLE_CAT = lvl >= 50 ? InitSpell(me, MANGLE_CAT_1) : 0;
  5744. + MOONFIRE = InitSpell(me, MOONFIRE_1);
  5745. + STARFIRE = InitSpell(me, STARFIRE_1);
  5746. + WRATH = InitSpell(me, WRATH_1);
  5747. + HURRICANE = InitSpell(me, HURRICANE_1);
  5748. + FAIRIE_FIRE = InitSpell(me, FAIRIE_FIRE_1);
  5749. + CURE_POISON = InitSpell(me, CURE_POISON_1);
  5750. + INNERVATE = InitSpell(me, INNERVATE_1);
  5751. + ENTANGLING_ROOTS = InitSpell(me, ENTANGLING_ROOTS_1);
  5752. + /*tal*/NATURES_SWIFTNESS = lvl >= 30 ? InitSpell(me, NATURES_SWIFTNESS_1) : 0;
  5753. + WARSTOMP = WARSTOMP_1;
  5754. + }
  5755. +
  5756. + void ApplyClassPassives()
  5757. + {
  5758. + uint8 level = master->getLevel();
  5759. + if (level >= 78)
  5760. + RefreshAura(SPELLDMG2, 3); //+18%
  5761. + else if (level >= 65)
  5762. + RefreshAura(SPELLDMG2, 2); //+12%
  5763. + else if (level >= 50)
  5764. + RefreshAura(SPELLDMG2); //+6%
  5765. + if (level >= 45)
  5766. + RefreshAura(NATURAL_PERFECTION3); //4%
  5767. + else if (level >= 43)
  5768. + RefreshAura(NATURAL_PERFECTION2); //3%
  5769. + else if (level >= 41)
  5770. + RefreshAura(NATURAL_PERFECTION1); //2%
  5771. + if (level >= 50)
  5772. + RefreshAura(LIVING_SEED3); //100%
  5773. + else if (level >= 48)
  5774. + RefreshAura(LIVING_SEED2); //66%
  5775. + else if (level >= 46)
  5776. + RefreshAura(LIVING_SEED1); //33%
  5777. + if (level >= 55)
  5778. + RefreshAura(REVITALIZE3, 5); //75% (15%)x5
  5779. + else if (level >= 53)
  5780. + RefreshAura(REVITALIZE2, 3); //30% (10%)x3
  5781. + else if (level >= 51)
  5782. + RefreshAura(REVITALIZE1, 3); //15% (5%)x3
  5783. + if (level >= 70)
  5784. + RefreshAura(OMEN_OF_CLARITY, 3); //x3
  5785. + else if (level >= 40)
  5786. + RefreshAura(OMEN_OF_CLARITY, 2); //x2
  5787. + else if (level >= 20)
  5788. + RefreshAura(OMEN_OF_CLARITY); //x1
  5789. + if (level >= 45)
  5790. + RefreshAura(GLYPH_SWIFTMEND); //no comsumption
  5791. + if (level >= 40)
  5792. + RefreshAura(GLYPH_INNERVATE); //no comsumption
  5793. + if (level >= 20)
  5794. + RefreshAura(NATURESGRACE);
  5795. + if (level >= 78)
  5796. + {
  5797. + RefreshAura(T9_RESTO_P4_BONUS);
  5798. + RefreshAura(T8_RESTO_P4_BONUS);
  5799. + RefreshAura(T9_BALANCE_P2_BONUS);
  5800. + RefreshAura(T10_BALANCE_P2_BONUS);
  5801. + RefreshAura(T10_BALANCE_P4_BONUS);
  5802. + }
  5803. + }
  5804. +
  5805. + private:
  5806. + uint32
  5807. + /*Buffs*/MARK_OF_THE_WILD, THORNS,
  5808. +/*Heal/Rez*/HEALING_TOUCH, REGROWTH, REJUVENATION, LIFEBLOOM, NOURISH, WILD_GROWTH, SWIFTMEND, TRANQUILITY, REVIVE, REBIRTH,
  5809. + /*Bear*/BEAR_FORM, SWIPE, MANGLE_BEAR, BASH,
  5810. + /*Cat*/CAT_FORM, CLAW, RAKE, SHRED, RIP, MANGLE_CAT,
  5811. + /*Balance*/MOONFIRE, STARFIRE, WRATH, HURRICANE, FAIRIE_FIRE,
  5812. + /*Misc*/CURE_POISON, INNERVATE, ENTANGLING_ROOTS, NATURES_SWIFTNESS, WARSTOMP;
  5813. + //Timers/other
  5814. +/*Heal*/uint32 Heal_Timer, Regrowth_Timer, Swiftmend_Timer, Wild_Growth_Timer,
  5815. +/*Heal*/ Tranquility_Timer, Nature_Swiftness_Timer, Rebirth_Timer;
  5816. +/*Bear*/uint32 MangleB_Timer;
  5817. +/*Cat*/ uint32 Claw_Timer, Rake_Timer, Shred_Timer, Rip_Timer, Mangle_Cat_Timer;
  5818. +/*Bal*/ uint32 Moonfire_Timer, Starfire_Timer, Wrath_Timer, Hurricane_Timer, Innervate_Timer;
  5819. +/*Misc*/uint32 formtimer, ragetimer, ragetimer2, Warstomp_Timer;
  5820. +/*Chck*/bool clearcast, swiftness;
  5821. +/*Misc*/Powers power; uint32 mana, rage;
  5822. +/*Misc*/float rageIncomeMult, rageLossMult;
  5823. +
  5824. + enum DruidBaseSpells
  5825. + {
  5826. + MARK_OF_THE_WILD_1 = 1126,
  5827. + THORNS_1 = 467,
  5828. + HEALING_TOUCH_1 = 5185,
  5829. + REGROWTH_1 = 8936,
  5830. + REJUVENATION_1 = 774,
  5831. + LIFEBLOOM_1 = 33763,
  5832. + NOURISH_1 = 50464,
  5833. + /*tal*/WILD_GROWTH_1 = 48438,
  5834. + /*tal*/SWIFTMEND_1 = 18562,
  5835. + TRANQUILITY_1 = 740,
  5836. + REVIVE_1 = 50769,
  5837. + REBIRTH_1 = 20484,
  5838. + BEAR_FORM_1 = 5487,
  5839. + SWIPE_1 = 779,
  5840. + /*tal*/MANGLE_BEAR_1 = 33878,
  5841. + BASH_1 = 5211,
  5842. + CAT_FORM_1 = 768,
  5843. + CLAW_1 = 1082,
  5844. + RAKE_1 = 1822,
  5845. + SHRED_1 = 5221,
  5846. + RIP_1 = 1079,
  5847. + /*tal*/MANGLE_CAT_1 = 33876,
  5848. + MOONFIRE_1 = 8921,
  5849. + STARFIRE_1 = 2912,
  5850. + WRATH_1 = 5176,
  5851. + HURRICANE_1 = 16914,
  5852. + FAIRIE_FIRE_1 = 770,
  5853. + CURE_POISON_1 = 8946,
  5854. + INNERVATE_1 = 29166,
  5855. + ENTANGLING_ROOTS_1 = 339,
  5856. + /*tal*/NATURES_SWIFTNESS_1 = 17116,
  5857. + WARSTOMP_1 = 20549,
  5858. + };
  5859. + enum DruidPassives
  5860. + {
  5861. + //Talents
  5862. + OMEN_OF_CLARITY = 16864,//clearcast
  5863. + NATURESGRACE = 61346,//haste 20% for 3 sec
  5864. + NATURAL_PERFECTION1 = 33881,
  5865. + NATURAL_PERFECTION2 = 33882,
  5866. + NATURAL_PERFECTION3 = 33883,
  5867. + LIVING_SEED1 = 48496,//rank 1
  5868. + LIVING_SEED2 = 48499,//rank 2
  5869. + LIVING_SEED3 = 48500,//rank 3
  5870. + REVITALIZE1 = 48539,//rank 1
  5871. + REVITALIZE2 = 48544,//rank 2
  5872. + REVITALIZE3 = 48545,//rank 3
  5873. + /*Talent*/LEADER_OF_THE_PACK = 24932,
  5874. + //Glyphs
  5875. + GLYPH_SWIFTMEND = 54824,//no consumption
  5876. + GLYPH_INNERVATE = 54832,//self regen
  5877. + //other
  5878. + T9_RESTO_P4_BONUS = 67128,//rejuve crits
  5879. + T8_RESTO_P4_BONUS = 64760,//rejuve init heal
  5880. + T9_BALANCE_P2_BONUS = 67125,//moonfire crits
  5881. + T10_BALANCE_P2_BONUS = 70718,//omen of doom (15%)
  5882. + T10_BALANCE_P4_BONUS = 70723,//Languish(DOT)
  5883. + SPELLDMG/*Arcane Instability-mage*/ = 15060,//rank3 3% dam/crit
  5884. + SPELLDMG2/*Earth and Moon - druid*/ = 48511,//rank3 6% dam
  5885. + ENERGIZE = 27787,//Rogue Armor Energize (chance: +35 energy on hit)
  5886. + CRIT_50 = 23434,//50% spell crit
  5887. + };
  5888. + enum DruidSpecial
  5889. + {
  5890. + //NATURESGRACEBUFF = 16886,
  5891. + OMEN_OF_CLARITY_BUFF = 16870,
  5892. + };
  5893. + };
  5894. +};
  5895. +
  5896. +void AddSC_druid_bot()
  5897. +{
  5898. + new druid_bot();
  5899. +}
  5900. diff --git a/src/server/game/AI/NpcBots/bot_hunter_ai.cpp b/src/server/game/AI/NpcBots/bot_hunter_ai.cpp
  5901. new file mode 100644
  5902. index 0000000..ef321bb
  5903. --- /dev/null
  5904. +++ b/src/server/game/AI/NpcBots/bot_hunter_ai.cpp
  5905. @@ -0,0 +1,340 @@
  5906. +#include "bot_ai.h"
  5907. +//#include "Group.h"
  5908. +#include "Player.h"
  5909. +#include "ScriptMgr.h"
  5910. +//#include "SpellAuras.h"
  5911. +/*
  5912. +Hunter NpcBot (reworked by Graff [email protected])
  5913. +Complete - 1%
  5914. +TODO:
  5915. +*/
  5916. +class hunter_bot : public CreatureScript
  5917. +{
  5918. +public:
  5919. + hunter_bot() : CreatureScript("hunter_bot") { }
  5920. +
  5921. + CreatureAI* GetAI(Creature* creature) const
  5922. + {
  5923. + return new hunter_botAI(creature);
  5924. + }
  5925. +
  5926. + bool OnGossipHello(Player* player, Creature* creature)
  5927. + {
  5928. + return bot_minion_ai::OnGossipHello(player, creature);
  5929. + }
  5930. +
  5931. + bool OnGossipSelect(Player* player, Creature* creature, uint32 sender, uint32 action)
  5932. + {
  5933. + if (bot_minion_ai* ai = creature->GetBotMinionAI())
  5934. + return ai->OnGossipSelect(player, creature, sender, action);
  5935. + return true;
  5936. + }
  5937. +
  5938. + struct hunter_botAI : public bot_minion_ai
  5939. + {
  5940. + hunter_botAI(Creature* creature) : bot_minion_ai(creature)
  5941. + {
  5942. + Reset();
  5943. + }
  5944. +
  5945. + //void CreatePet()
  5946. + //{
  5947. +
  5948. + // pet = me->GetBotsPet(60238);
  5949. +
  5950. + // if (pet == NULL)
  5951. + // return;
  5952. +
  5953. + // pet->UpdateCharmAI();
  5954. + // pet->setFaction(me->getFaction());
  5955. + // pet->SetReactState(REACT_DEFENSIVE);
  5956. + // pet->GetMotionMaster()->MoveFollow(me, PET_FOLLOW_DIST*urand(1, 2),PET_FOLLOW_ANGLE);
  5957. + // CharmInfo* charmInfonewbot = pet->InitCharmInfo();
  5958. + // pet->GetCharmInfo()->SetCommandState(COMMAND_FOLLOW);
  5959. + // pet->UpdateStats(STAT_STRENGTH);
  5960. + // pet->UpdateStats(STAT_AGILITY);
  5961. + // pet->SetLevel(master->getLevel());
  5962. +
  5963. + // /*float val2 = master->getLevel()*4.f + pet->GetStat(STAT_STRENGTH)*5.f;
  5964. +
  5965. + // val2=100.0;
  5966. + // uint32 attPowerMultiplier=1;
  5967. + // pet->SetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE, uint32(val2));
  5968. + // pet->UpdateAttackPowerAndDamage();
  5969. + // pet->SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, uint32(val2 * attPowerMultiplier));
  5970. + // pet->SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, uint32(val2 * attPowerMultiplier)*3+master->getLevel());
  5971. + // pet->UpdateDamagePhysical(BASE_ATTACK);*/
  5972. +
  5973. + //}
  5974. +
  5975. + bool doCast(Unit* victim, uint32 spellId, bool triggered = false)
  5976. + {
  5977. + if (checkBotCast(victim, spellId, CLASS_HUNTER) != SPELL_CAST_OK)
  5978. + return false;
  5979. + return bot_ai::doCast(victim, spellId, triggered);
  5980. + }
  5981. +
  5982. + void EnterCombat(Unit*) { }
  5983. + void Aggro(Unit*) { }
  5984. + void AttackStart(Unit*) { }
  5985. + void KilledUnit(Unit*) { }
  5986. + void EnterEvadeMode() { }
  5987. + void MoveInLineOfSight(Unit*) { }
  5988. + void JustDied(Unit*) { master->SetNpcBotDied(me->GetGUID()); }
  5989. + void DoNonCombatActions(uint32 const /*diff*/)
  5990. + {}
  5991. +
  5992. + void UpdateAI(uint32 diff)
  5993. + {
  5994. + ReduceCD(diff);
  5995. +
  5996. + if (IAmDead()) return;
  5997. +
  5998. + if (!me->isInCombat())
  5999. + DoNonCombatActions(diff);
  6000. +
  6001. + //if (pet && pet != NULL && pet->isDead())
  6002. + //{
  6003. + // me->SetBotsPetDied();
  6004. + // pet = NULL;
  6005. + //}
  6006. +
  6007. + //if we think we have a pet, but master doesn't, it means we teleported
  6008. + //if (pet && !me->getBotsPet())
  6009. + //{
  6010. + // me->SetBotsPetDied();
  6011. + // pet = NULL;
  6012. + //}
  6013. +
  6014. + DoNormalAttack(diff);
  6015. + ScriptedAI::UpdateAI(diff);
  6016. +
  6017. + //if low on health, drink a potion
  6018. + if (GetHealthPCT(me) < 65)
  6019. + {
  6020. + if (doCast(me, HEALINGPOTION))
  6021. + Potion_cd = POTION_CD;
  6022. + }
  6023. +
  6024. + //if low on mana, drink a potion
  6025. + if (GetManaPCT(me) < 65 && Potion_cd <= diff)
  6026. + {
  6027. + if (doCast(me, MANAPOTION))
  6028. + Potion_cd = POTION_CD;
  6029. + }
  6030. +
  6031. + opponent = SelectTarget(SELECT_TARGET_TOPAGGRO, 0);
  6032. + if (!opponent && !me->getVictim())
  6033. + {
  6034. + me->CombatStop();
  6035. + //ResetOrGetNextTarget();
  6036. +
  6037. + //to reduce the number of crashes, remove pet whenever we are not in combat
  6038. + //if (pet != NULL && pet->isAlive())
  6039. + //{
  6040. + // me->SetBotsPetDied();
  6041. + // pet = NULL;
  6042. + //}
  6043. + return;
  6044. + }
  6045. +
  6046. + //if (pet == NULL)
  6047. + // CreatePet();
  6048. +
  6049. + //if (pet && pet->isAlive() &&
  6050. + // !pet->isInCombat() &&
  6051. + // me->getVictim())
  6052. + //{
  6053. + // pet->Attack (me->getVictim(), true);
  6054. + // pet->GetMotionMaster()->MoveChase(me->getVictim(), 1, 0);
  6055. + //}
  6056. + }
  6057. +
  6058. + void DoNormalAttack(uint32 diff)
  6059. + {
  6060. + AttackerSet m_attackers = master->getAttackers();
  6061. + if (!opponent || opponent->isDead()) return;
  6062. +
  6063. + // try to get rid of enrage effect
  6064. + if (TRANQ_SHOT && (HasAuraName(opponent, "Enrage") || (HasAuraName(opponent, "Frenzy"))))
  6065. + {
  6066. + me->InterruptNonMeleeSpells(true, AUTO_SHOT);
  6067. + me->MonsterSay("Tranquil shot!", LANG_UNIVERSAL, opponent->GetGUID());
  6068. + doCast(opponent, TRANQ_SHOT, true);
  6069. + // doCast(opponent, AUTO_SHOT);
  6070. + // return;
  6071. + }
  6072. +
  6073. + // silence it
  6074. + if (SILENCING_SHOT && opponent->HasUnitState(UNIT_STATE_CASTING) && SilencingShot_Timer <= diff)
  6075. + {
  6076. + doCast(opponent, SILENCING_SHOT);
  6077. + SilencingShot_Timer = 25000;
  6078. + // doCast(opponent, AUTO_SHOT);
  6079. + // return;
  6080. + }
  6081. +
  6082. + // mark it
  6083. + if (!HasAuraName(opponent, "Hunter's Mark"))
  6084. + {
  6085. + doCast(opponent, HUNTERS_MARK);
  6086. + // doCast(opponent, AUTO_SHOT);
  6087. + // return;
  6088. + }
  6089. +
  6090. + // sting it
  6091. + if (SCORPID_STING && !opponent->HasAura(SCORPID_STING, me->GetGUID()))
  6092. + {
  6093. + me->InterruptNonMeleeSpells(true, AUTO_SHOT);
  6094. + doCast(opponent, SCORPID_STING);
  6095. + // me->MonsterSay("Scorpid Sting!", LANG_UNIVERSAL, NULL);
  6096. + // doCast(opponent, AUTO_SHOT);
  6097. + // return;
  6098. + }
  6099. +
  6100. + if (CHIMERA_SHOT && ChimeraShot_Timer <= diff && GC_Timer <= diff)
  6101. + {
  6102. + me->InterruptNonMeleeSpells(true, AUTO_SHOT);
  6103. + doCast(opponent, CHIMERA_SHOT);
  6104. + ChimeraShot_Timer = 10000;
  6105. + // me->MonsterSay("Chimera Sting!", LANG_UNIVERSAL, NULL);
  6106. + // doCast(opponent, AUTO_SHOT);
  6107. + // return;
  6108. + }
  6109. +
  6110. + if (ARCANE_SHOT && ArcaneShot_cd <= diff && GC_Timer <= diff)
  6111. + {
  6112. + me->InterruptNonMeleeSpells(true, AUTO_SHOT);
  6113. + doCast(opponent, ARCANE_SHOT);
  6114. + // me->MonsterSay("Arcane shot!", LANG_UNIVERSAL, NULL);
  6115. + ArcaneShot_cd = 60;
  6116. + // doCast(opponent, AUTO_SHOT);
  6117. + // return;
  6118. + }
  6119. +
  6120. + if (AIMED_SHOT && AimedShot_Timer <= diff && GC_Timer <= diff)
  6121. + {
  6122. + me->InterruptNonMeleeSpells( true, AUTO_SHOT );
  6123. + doCast(opponent, AIMED_SHOT);
  6124. + // me->MonsterSay("Aimed shot!", LANG_UNIVERSAL, NULL);
  6125. + AimedShot_Timer = 120;
  6126. + // doCast(opponent, AUTO_SHOT);
  6127. + // return;
  6128. + }
  6129. + //Temp Feign death For Debug
  6130. + AttackerSet b_attackers = me->getAttackers();
  6131. + if (!b_attackers.empty())
  6132. + {
  6133. + for(AttackerSet::iterator iter = b_attackers.begin(); iter != b_attackers.end(); ++iter)
  6134. + if (*iter && (*iter)->getVictim()->GetGUID() == me->GetGUID() &&
  6135. + me->GetDistance(*iter) < 10 &&
  6136. + Feign_Death_Timer <= diff && GC_Timer <= diff)
  6137. + {
  6138. + doCast(me, FEIGN_DEATH, true);
  6139. + opponent->AddThreat(me, -100000);
  6140. + me->CombatStop();
  6141. + Feign_Death_Timer = 25000;
  6142. + me->CombatStart(opponent);
  6143. + }
  6144. + }
  6145. +
  6146. + doCast(opponent, AUTO_SHOT);
  6147. + }
  6148. +
  6149. + void SpellHit(Unit* caster, SpellInfo const* spell)
  6150. + {
  6151. + OnSpellHit(caster, spell);
  6152. + }
  6153. +
  6154. + void DamageTaken(Unit* u, uint32& /*damage*/)
  6155. + {
  6156. + OnOwnerDamagedBy(u);
  6157. + }
  6158. +
  6159. + void OwnerAttackedBy(Unit* u)
  6160. + {
  6161. + OnOwnerDamagedBy(u);
  6162. + }
  6163. +
  6164. + void Reset()
  6165. + {
  6166. + ArcaneShot_cd = 0;
  6167. + ChimeraShot_Timer = 0;
  6168. + SilencingShot_Timer = 0;
  6169. + AimedShot_Timer = 0;
  6170. + Feign_Death_Timer = 0;
  6171. +
  6172. + if (master)
  6173. + {
  6174. + setStats(CLASS_HUNTER, me->getRace(), master->getLevel(), true);
  6175. + ApplyClassPassives();
  6176. + ApplyPassives(CLASS_HUNTER);
  6177. + }
  6178. + }
  6179. +
  6180. + void ReduceCD(uint32 diff)
  6181. + {
  6182. + CommonTimers(diff);
  6183. + if (ArcaneShot_cd > diff) ArcaneShot_cd -= diff;
  6184. + if (ChimeraShot_Timer > diff) ChimeraShot_Timer -= diff;
  6185. + if (SilencingShot_Timer > diff) SilencingShot_Timer -= diff;
  6186. + if (AimedShot_Timer > diff) AimedShot_Timer -= diff;
  6187. + if (Feign_Death_Timer > diff) Feign_Death_Timer -= diff;
  6188. + }
  6189. +
  6190. + bool CanRespawn()
  6191. + {return false;}
  6192. +
  6193. + void InitSpells()
  6194. + {
  6195. + uint8 lvl = me->getLevel();
  6196. + AUTO_SHOT = AUTO_SHOT_1;
  6197. + TRANQ_SHOT = InitSpell(me, TRANQ_SHOT_1);
  6198. + SCORPID_STING = InitSpell(me, SCORPID_STING_1);
  6199. + HUNTERS_MARK = InitSpell(me, HUNTERS_MARK_1);
  6200. + ARCANE_SHOT = InitSpell(me, ARCANE_SHOT_1);
  6201. + CHIMERA_SHOT = lvl >= 60 ? CHIMERA_SHOT_1 : 0;
  6202. + AIMED_SHOT = lvl >= 20 ? InitSpell(me, AIMED_SHOT_1) : 0;
  6203. + SILENCING_SHOT = lvl >= 50 ? SILENCING_SHOT_1 : 0;
  6204. + ASPECT_OF_THE_DRAGONHAWK = InitSpell(me, ASPECT_OF_THE_DRAGONHAWK_1);
  6205. + FEIGN_DEATH = InitSpell(me, FEIGN_DEATH_1);
  6206. + }
  6207. +
  6208. + void ApplyClassPassives()
  6209. + { }
  6210. +
  6211. + private:
  6212. + uint32
  6213. + AUTO_SHOT, TRANQ_SHOT, SCORPID_STING, HUNTERS_MARK, ARCANE_SHOT, CHIMERA_SHOT, AIMED_SHOT,
  6214. + SILENCING_SHOT, ASPECT_OF_THE_DRAGONHAWK, FEIGN_DEATH;
  6215. + //Timers
  6216. + uint32 ArcaneShot_cd, ChimeraShot_Timer, SilencingShot_Timer, AimedShot_Timer, Feign_Death_Timer;
  6217. +
  6218. + enum HunterBaseSpells
  6219. + {
  6220. + AUTO_SHOT_1 = 75,
  6221. + TRANQ_SHOT_1 = 19801,
  6222. + SCORPID_STING_1 = 3043,
  6223. + HUNTERS_MARK_1 = 14325,
  6224. + ARCANE_SHOT_1 = 3044,
  6225. + CHIMERA_SHOT_1 = 53209,
  6226. + AIMED_SHOT_1 = 19434,
  6227. + SILENCING_SHOT_1 = 34490,
  6228. + ASPECT_OF_THE_DRAGONHAWK_1 = 61846,
  6229. + FEIGN_DEATH_1 = 5384,
  6230. + };
  6231. +
  6232. + enum HunterPassives
  6233. + {
  6234. + };
  6235. +
  6236. + enum HunterSpecial
  6237. + {
  6238. + };
  6239. + };
  6240. +};
  6241. +
  6242. +void AddSC_hunter_bot()
  6243. +{
  6244. + new hunter_bot();
  6245. +}
  6246. diff --git a/src/server/game/AI/NpcBots/bot_mage_ai.cpp b/src/server/game/AI/NpcBots/bot_mage_ai.cpp
  6247. new file mode 100644
  6248. index 0000000..1288f4d
  6249. --- /dev/null
  6250. +++ b/src/server/game/AI/NpcBots/bot_mage_ai.cpp
  6251. @@ -0,0 +1,935 @@
  6252. +#include "bot_ai.h"
  6253. +#include "Group.h"
  6254. +#include "Player.h"
  6255. +#include "ScriptMgr.h"
  6256. +#include "SpellAuras.h"
  6257. +#include "WorldSession.h"
  6258. +/*
  6259. +Mage NpcBot (reworked by Graff [email protected])
  6260. +Complete - Around 45%
  6261. +TODO: Ice Lance, Deep Freeze, Mana Gems, Pet etc...
  6262. +*/
  6263. +class mage_bot : public CreatureScript
  6264. +{
  6265. +public:
  6266. + mage_bot() : CreatureScript("mage_bot") { }
  6267. +
  6268. + CreatureAI* GetAI(Creature* creature) const
  6269. + {
  6270. + return new mage_botAI(creature);
  6271. + }
  6272. +
  6273. + bool OnGossipHello(Player* player, Creature* creature)
  6274. + {
  6275. + return bot_minion_ai::OnGossipHello(player, creature);
  6276. + }
  6277. +
  6278. + bool OnGossipSelect(Player* player, Creature* creature, uint32 sender, uint32 action)
  6279. + {
  6280. + if (bot_minion_ai* ai = creature->GetBotMinionAI())
  6281. + return ai->OnGossipSelect(player, creature, sender, action);
  6282. + return true;
  6283. + }
  6284. +
  6285. + struct mage_botAI : public bot_minion_ai
  6286. + {
  6287. + mage_botAI(Creature* creature) : bot_minion_ai(creature) { }
  6288. +
  6289. + bool doCast(Unit* victim, uint32 spellId, bool triggered = false)
  6290. + {
  6291. + if (checkBotCast(victim, spellId, CLASS_MAGE) != SPELL_CAST_OK)
  6292. + return false;
  6293. + bool result = bot_ai::doCast(victim, spellId, triggered);
  6294. +
  6295. + if (result && spellId != MANAPOTION && me->HasAura(CLEARCASTBUFF))
  6296. + {
  6297. + cost = info->CalcPowerCost(me, info->GetSchoolMask());
  6298. + clearcast = true;
  6299. + }
  6300. +
  6301. + return result;
  6302. + }
  6303. +
  6304. + void EnterCombat(Unit*) { }
  6305. + void Aggro(Unit*) { }
  6306. + void AttackStart(Unit*) { }
  6307. + void KilledUnit(Unit*) { }
  6308. + void EnterEvadeMode() { }
  6309. + void MoveInLineOfSight(Unit*) { }
  6310. + void JustDied(Unit*) { master->SetNpcBotDied(me->GetGUID()); }
  6311. +
  6312. + void StartAttack(Unit* u, bool force = false)
  6313. + {
  6314. + if (GetBotCommandState() == (COMMAND_ATTACK) && !force) return;
  6315. + Aggro(u);
  6316. + SetBotCommandState(COMMAND_ATTACK);
  6317. + GetInPosition(force, true);
  6318. + }
  6319. +
  6320. + void Counter()
  6321. + {
  6322. + Unit* u = me->getVictim();
  6323. + bool cSpell = CounterSpell_cd <= 5000;
  6324. + bool blast = FireBlast_cd <= 3000 && !(u && u->ToCreature() && (u->ToCreature()->isWorldBoss() || u->ToCreature()->IsDungeonBoss())) && me->HasAura(IMPACT_BUFF);
  6325. + if (!cSpell && !blast) return;
  6326. + if (u && u->IsNonMeleeSpellCasted(false))
  6327. + {
  6328. + temptimer = GC_Timer;
  6329. + if (me->IsNonMeleeSpellCasted(false))
  6330. + me->InterruptNonMeleeSpells(false);
  6331. + if (cSpell && doCast(u, COUNTERSPELL))
  6332. + CounterSpell_cd = 15000;
  6333. + else if (blast && doCast(u, FIREBLAST))
  6334. + FireBlast_cd = 6000;
  6335. + GC_Timer = temptimer;
  6336. + }
  6337. + else if (cSpell)
  6338. + {
  6339. + if (Unit* target = FindCastingTarget(30))
  6340. + {
  6341. + temptimer = GC_Timer;
  6342. + if (me->IsNonMeleeSpellCasted(false))
  6343. + me->InterruptNonMeleeSpells(false);
  6344. + if (doCast(target, COUNTERSPELL))
  6345. + {
  6346. + CounterSpell_cd = 15000;
  6347. + GC_Timer = temptimer;
  6348. + }
  6349. + }
  6350. + }
  6351. + }
  6352. +
  6353. + void CheckSpellSteal(uint32 diff)
  6354. + {
  6355. + if (!SPELLSTEAL || Rand() > 25 || GC_Timer > diff || IsCasting()) return;
  6356. + Unit* target = FindHostileDispelTarget(30, true);
  6357. + if (target && doCast(target, SPELLSTEAL))
  6358. + GC_Timer = 800;
  6359. + }
  6360. +
  6361. + void DoNonCombatActions(uint32 diff)
  6362. + {
  6363. + if (GC_Timer > diff || me->IsMounted()) return;
  6364. + if (Feasting()) return;
  6365. +
  6366. + if (!HasAuraName(me, DAMPENMAGIC) &&
  6367. + doCast(me, DAMPENMAGIC))
  6368. + { /*GC_Timer = 800;*/ return; }
  6369. +
  6370. + if (!HasAuraName(me, ICEARMOR) &&
  6371. + doCast(me, ICEARMOR))
  6372. + { /*GC_Timer = 800;*/ return; }
  6373. + }
  6374. +
  6375. + bool BuffTarget(Unit* target, uint32 diff)
  6376. + {
  6377. + if (GC_Timer > diff || !target || target->isDead() || Rand() > 50) return false;
  6378. + if (me->isInCombat() && !master->GetMap()->IsRaid()) return false;
  6379. + if (me->GetExactDist(target) > 30) return false;
  6380. + if (target->getPowerType() == POWER_MANA &&
  6381. + !HasAuraName(target, ARCANEINTELLECT) &&
  6382. + doCast(target, ARCANEINTELLECT))
  6383. + {
  6384. + /*GC_Timer = 800;*/
  6385. + return true;
  6386. + }
  6387. + return false;
  6388. + }
  6389. +
  6390. + void UpdateAI(uint32 diff)
  6391. + {
  6392. + ReduceCD(diff);
  6393. + if (IAmDead()) return;
  6394. + if (!me->getVictim())
  6395. + Evade();
  6396. + if (clearcast && me->HasAura(CLEARCASTBUFF) && !me->IsNonMeleeSpellCasted(false))
  6397. + {
  6398. + me->ModifyPower(POWER_MANA, cost);
  6399. + me->RemoveAurasDueToSpell(CLEARCASTBUFF,me->GetGUID(),0,AURA_REMOVE_BY_EXPIRE);
  6400. + if (me->HasAura(ARCANE_POTENCY_BUFF1))
  6401. + me->RemoveAurasDueToSpell(ARCANE_POTENCY_BUFF1,me->GetGUID(),0,AURA_REMOVE_BY_EXPIRE);
  6402. + if (me->HasAura(ARCANE_POTENCY_BUFF2))
  6403. + me->RemoveAurasDueToSpell(ARCANE_POTENCY_BUFF2,me->GetGUID(),0,AURA_REMOVE_BY_EXPIRE);
  6404. + clearcast = false;
  6405. + }
  6406. + if (wait == 0)
  6407. + wait = GetWait();
  6408. + else
  6409. + return;
  6410. + CheckAuras();
  6411. + BreakCC(diff);
  6412. + if (CCed(me) && (!ICEBLOCK || !me->HasAura(ICEBLOCK))) return;//TODO
  6413. +
  6414. + CheckBlink(diff);
  6415. + CheckPoly(diff);
  6416. + CheckPots(diff);
  6417. + CureTarget(master, REMOVE_CURSE, diff);
  6418. + CureTarget(me, REMOVE_CURSE, diff);
  6419. + CureGroup(master, REMOVE_CURSE, diff);
  6420. +
  6421. + FocusMagic(diff);
  6422. + BuffAndHealGroup(master, diff);
  6423. +
  6424. + if (!me->isInCombat())
  6425. + DoNonCombatActions(diff);
  6426. +
  6427. + if (!CheckAttackTarget(CLASS_MAGE))
  6428. + return;
  6429. +
  6430. + CheckPoly2();//this should go AFTER getting opponent
  6431. +
  6432. + Counter();
  6433. + CheckSpellSteal(diff);
  6434. + DoNormalAttack(diff);
  6435. + }
  6436. +
  6437. + void DoNormalAttack(uint32 diff)
  6438. + {
  6439. + opponent = me->getVictim();
  6440. + if (opponent)
  6441. + {
  6442. + if (!IsCasting())
  6443. + StartAttack(opponent);
  6444. + }
  6445. + else
  6446. + return;
  6447. + AttackerSet m_attackers = master->getAttackers();
  6448. + AttackerSet b_attackers = me->getAttackers();
  6449. +
  6450. + Unit* u = me->SelectNearestTarget(20);
  6451. + //ICE_BARRIER
  6452. + if (ICE_BARRIER && Ice_Barrier_cd <= diff && u && u->getVictim() == me &&
  6453. + u->GetDistance(me) < 8 && !me->HasAura(ICE_BARRIER))
  6454. + {
  6455. + if (me->IsNonMeleeSpellCasted(true))
  6456. + me->InterruptNonMeleeSpells(true);
  6457. + if (doCast(me, ICE_BARRIER))
  6458. + {
  6459. + Ice_Barrier_cd = 41000 - me->getLevel()*200;//down to 25 sec on 80
  6460. + GC_Timer = 800;
  6461. + return;
  6462. + }
  6463. + }
  6464. + if ((!ICE_BARRIER || Ice_Barrier_cd > diff) &&
  6465. + BLINK && Blink_cd < 3000 && u && u->getVictim() == me &&
  6466. + !me->HasAura(ICE_BARRIER) && u->GetDistance(me) < 6)
  6467. + {
  6468. + if (me->IsNonMeleeSpellCasted(true))
  6469. + me->InterruptNonMeleeSpells(true);
  6470. + if (doCast(me, BLINK))
  6471. + {
  6472. + Blink_cd = 15000 - me->getLevel()/4 * 100;
  6473. + GC_Timer = 800;
  6474. + return;
  6475. + }
  6476. + }
  6477. +
  6478. + if (me->HasAura(ICEBLOCK))
  6479. + if (((GetManaPCT(me) > 45 && GetHealthPCT(me) > 80) || b_attackers.empty()) && Iceblock_cd <= 57000 && tank)
  6480. + me->RemoveAurasDueToSpell(ICEBLOCK);
  6481. + //ICEBLOCK
  6482. + if (ICEBLOCK && Rand() < 50 && !b_attackers.empty() && tank && Iceblock_cd <= diff &&
  6483. + (GetManaPCT(me) < 15 || GetHealthPCT(me) < 45 || b_attackers.size() > 4) &&
  6484. + !me->HasAura(ICEBLOCK))
  6485. + {
  6486. + if (me->IsNonMeleeSpellCasted(true))
  6487. + me->InterruptNonMeleeSpells(true);
  6488. + if (doCast(me, ICEBLOCK))
  6489. + {
  6490. + Iceblock_cd = 60000;
  6491. + return;
  6492. + }
  6493. + }
  6494. +
  6495. + if (IsCasting()) return;
  6496. +
  6497. + BOLT = (CCed(opponent, true) || (opponent->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISARMED) && me->HasAura(COMBUSTION))) ? FIREBALL : FROSTBOLT;
  6498. + NOVA = BOLT == FIREBALL && BLASTWAVE ? BLASTWAVE : FROSTNOVA ? FROSTNOVA : 0;
  6499. +
  6500. + float dist = me->GetExactDist(opponent);
  6501. + if (dist > 30)
  6502. + return;
  6503. +
  6504. + if (COMBUSTION && Rand() < 15 &&
  6505. + (opponent->GetMaxHealth() > master->GetMaxHealth()*10 ||
  6506. + m_attackers.size() > 1 || b_attackers.size() > 2))
  6507. + {
  6508. + if (!HasAuraName(me, "Combustion") && Combustion_cd <= diff)
  6509. + {
  6510. + temptimer = GC_Timer;
  6511. + if (doCast(me, COMBUSTION))
  6512. + {
  6513. + Combustion_cd = 60000;
  6514. + //Reset timers for fun
  6515. + Nova_cd = 0; FireBlast_cd = 0; DragonBreath_cd = 0;
  6516. + }
  6517. + GC_Timer = temptimer;
  6518. + }
  6519. + }
  6520. + //DAMAGE
  6521. + //PYROBLAST
  6522. + if (PYROBLAST && Rand() < 75 && Pyroblast_cd <= diff && GC_Timer <= diff &&
  6523. + b_attackers.size() < 2 && dist < 30 && opponent->IsPolymorphed() &&
  6524. + doCast(opponent, PYROBLAST))
  6525. + Pyroblast_cd = 50;
  6526. + //nova //TODO: SEPARATE
  6527. + u = me->SelectNearestTarget(7);
  6528. + if (u && NOVA && Nova_cd <= diff && !CCed(u, true) && IsInBotParty(u->getVictim()))
  6529. + {
  6530. + Unit* tar = u->getVictim();
  6531. + if (tar && IsInBotParty(tar) && doCast(me, NOVA))
  6532. + {
  6533. + Nova_cd = 15000;
  6534. + return;
  6535. + }
  6536. + }
  6537. + //living bomb
  6538. + if (LIVINGBOMB && Rand() < 25 && Living_Bomb_cd <= diff && GC_Timer <= diff &&
  6539. + dist < 30 && opponent->GetHealth() > me->GetHealth()/2 &&
  6540. + !opponent->HasAura(LIVINGBOMB, me->GetGUID()) &&
  6541. + doCast(opponent, LIVINGBOMB))
  6542. + {
  6543. + Living_Bomb_cd = 6000;
  6544. + GC_Timer = 500;
  6545. + return;
  6546. + }
  6547. + //cone of cold
  6548. + if (CONEOFCOLD && ConeofCold_cd <= diff && GC_Timer <= diff && dist < 7 &&
  6549. + me->HasInArc(M_PI, opponent) &&
  6550. + doCast(opponent, CONEOFCOLD))
  6551. + {
  6552. + ConeofCold_cd = 14000;
  6553. + GC_Timer = 500;
  6554. + return;
  6555. + }
  6556. + //dragon's breath
  6557. + u = me->SelectNearestTarget(7);
  6558. + if (DRAGONBREATH && u && DragonBreath_cd <= diff && GC_Timer <= diff &&
  6559. + me->HasInArc(M_PI, opponent) && !HasAuraName(u, FROSTNOVA) &&
  6560. + doCast(opponent, DRAGONBREATH))
  6561. + {
  6562. + DragonBreath_cd = 25000;
  6563. + GC_Timer = 800;
  6564. + return;
  6565. + }
  6566. + /*//blast wave //TODO Separate again
  6567. + u = me->SelectNearestTarget(8);
  6568. + if (BLASTWAVE != 0 && u && isTimerReady(BlastWave_cd) &&
  6569. + !HasAuraName(u, FROSTNOVA) && !HasAuraName(u, DRAGONBREATH) &&
  6570. + doCast(me, BLASTWAVE))
  6571. + {
  6572. + BlastWave_cd = BLASTWAVE_CD;
  6573. + GC_Timer = 800;
  6574. + }*/
  6575. + //fire blast
  6576. + if (FireBlast_cd <= diff && GC_Timer <= diff && dist < 20 &&
  6577. + Rand() < 20 + 80*(!opponent->HasAuraType(SPELL_AURA_MOD_STUN) && me->HasAura(IMPACT_BUFF)) &&
  6578. + doCast(opponent, FIREBLAST))
  6579. + {
  6580. + FireBlast_cd = 6000;
  6581. + GC_Timer = 500;
  6582. + return;
  6583. + }
  6584. + //flamestrike
  6585. + if (GC_Timer <= diff && Rand() < 60 && me->HasAura(FIRESTARTERBUFF))
  6586. + {
  6587. + Unit* FStarget = FindAOETarget(30, true, false);
  6588. + if (FStarget && doCast(FStarget, FLAMESTRIKE, true))
  6589. + {
  6590. + me->RemoveAurasDueToSpell(FIRESTARTERBUFF);
  6591. + GC_Timer = 0;
  6592. + return;
  6593. + }
  6594. + }
  6595. + //blizzard
  6596. + if (BLIZZARD && Rand() < 80 && Blizzard_cd <= diff)
  6597. + {
  6598. + Unit* blizztarget = FindAOETarget(30, true);
  6599. + if (blizztarget && doCast(blizztarget, BLIZZARD))
  6600. + {
  6601. + Blizzard_cd = 5000;
  6602. + return;
  6603. + }
  6604. + Blizzard_cd = 2000;//fail
  6605. + }
  6606. + //Frost of Fire Bolt
  6607. + if (Rand() < 75 && Bolt_cd <= diff && dist < 30 &&
  6608. + doCast(opponent, BOLT))
  6609. + {
  6610. + Bolt_cd = uint32(float(sSpellMgr->GetSpellInfo(BOLT)->CalcCastTime()/100) * me->GetFloatValue(UNIT_MOD_CAST_SPEED) + 200);
  6611. + return;
  6612. + }
  6613. + //Arcane Missiles
  6614. + if (Rand() < 10 && GC_Timer <= diff && !me->isMoving() && dist < 20 &&
  6615. + doCast(opponent, ARCANEMISSILES))
  6616. + return;
  6617. + }
  6618. +
  6619. + void CheckPoly(uint32 diff)
  6620. + {
  6621. + if (polyCheckTimer <= diff)
  6622. + {
  6623. + Polymorph = FindAffectedTarget(POLYMORPH, me->GetGUID());
  6624. + polyCheckTimer = 2000;
  6625. + }
  6626. + }
  6627. +
  6628. + void CheckPoly2()
  6629. + {
  6630. + if (Polymorph == false && GC_Timer < 500)
  6631. + {
  6632. + if (Unit* target = FindPolyTarget(30, me->getVictim()))
  6633. + {
  6634. + if (doCast(target, POLYMORPH))
  6635. + {
  6636. + Polymorph = true;
  6637. + polyCheckTimer = 2000;
  6638. + }
  6639. + }
  6640. + }
  6641. + }
  6642. +
  6643. + void CheckPots(uint32 diff)
  6644. + {
  6645. + if (GetHealthPCT(me) < 70 && Potion_cd <= diff)
  6646. + {
  6647. + temptimer = GC_Timer;
  6648. + if (doCast(me, HEALINGPOTION))
  6649. + Potion_cd = POTION_CD;
  6650. + GC_Timer = temptimer;
  6651. + }
  6652. + if (GetManaPCT(me) < 35)
  6653. + {
  6654. + if (Evocation_cd <= diff && !me->isMoving() && me->getAttackers().empty() && doCast(me, EVOCATION))
  6655. + Evocation_cd = 60000;
  6656. + else if (Potion_cd <= diff)
  6657. + {
  6658. + temptimer = GC_Timer;
  6659. + if (doCast(me, MANAPOTION))
  6660. + Potion_cd = POTION_CD;
  6661. + GC_Timer = temptimer;
  6662. + }
  6663. + }
  6664. + }
  6665. +
  6666. + void CheckBlink(uint32 diff)
  6667. + {
  6668. + if (GetBotCommandState() == COMMAND_STAY || me->IsMounted()) return;
  6669. + if (Blink_cd > diff || me->getLevel() < 20 || IsCasting()) return;
  6670. + if (me->GetExactDist(master) > std::max(float(master->GetBotFollowDist()), 25.f)/* && me->IsWithinLOSInMap(master)*/)
  6671. + {
  6672. + me->SetFacingTo(me->GetAngle(master));
  6673. + if (doCast(me, BLINK))
  6674. + {
  6675. + Blink_cd = 15000 - me->getLevel()/4 * 100; //13 sec with improved
  6676. + GC_Timer = 500;
  6677. + }
  6678. + return;
  6679. + }
  6680. + if (!me->getAttackers().empty() && me->GetExactDist(master) > 15)
  6681. + {
  6682. + if (Unit* op = me->SelectNearestTarget(10))
  6683. + {
  6684. + if (op->getVictim() == me)
  6685. + {
  6686. + me->SetFacingTo(me->GetAngle(master));
  6687. + if (doCast(me, BLINK))
  6688. + {
  6689. + Blink_cd = 15000 - me->getLevel()/4 * 100; //13 sec with improved
  6690. + GC_Timer = 500;
  6691. + }
  6692. + }
  6693. + }
  6694. + }
  6695. + }
  6696. +
  6697. + void FocusMagic(uint32 diff)
  6698. + {
  6699. + if (!FOCUSMAGIC || me->getLevel() < 20 || fmCheckTimer > diff || GC_Timer > diff || Rand() < 50 || IsCasting())
  6700. + return;
  6701. + if (Unit* target = FindAffectedTarget(FOCUSMAGIC, me->GetGUID(), 70, 2))
  6702. + {
  6703. + fmCheckTimer = 30000;
  6704. + return;
  6705. + }
  6706. + else
  6707. + {
  6708. + Group* pGroup = master->GetGroup();
  6709. + if (!pGroup)
  6710. + {
  6711. + if (master->getPowerType() == POWER_MANA && !master->HasAura(FOCUSMAGIC) && me->GetExactDist(master) < 30)
  6712. + target = master;
  6713. + }
  6714. + else
  6715. + {
  6716. + for (GroupReference* itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
  6717. + {
  6718. + Player* pPlayer = itr->getSource();
  6719. + if (!pPlayer || pPlayer->isDead()) continue;
  6720. + if (me->GetMapId() != pPlayer->GetMapId()) continue;
  6721. + if ((pPlayer->getClass() == CLASS_MAGE ||
  6722. + pPlayer->getClass() == CLASS_PRIEST ||
  6723. + pPlayer->getClass() == CLASS_SHAMAN ||
  6724. + pPlayer->getClass() == CLASS_DRUID ||
  6725. + pPlayer->getClass() == CLASS_PALADIN ||
  6726. + pPlayer->getClass() == CLASS_WARLOCK) &&
  6727. + !pPlayer->HasAura(FOCUSMAGIC) && me->GetExactDist(pPlayer) < 30)
  6728. + {
  6729. + target = pPlayer;
  6730. + break;
  6731. + }
  6732. + }
  6733. + if (!target)
  6734. + {
  6735. + for (GroupReference* itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
  6736. + {
  6737. + Player* pPlayer = itr->getSource();
  6738. + if (!pPlayer || !pPlayer->HaveBot()) continue;
  6739. + if (me->GetMapId() != pPlayer->GetMapId()) continue;
  6740. + for (uint8 i = 0; i != pPlayer->GetMaxNpcBots(); ++i)
  6741. + {
  6742. + Creature* cre = pPlayer->GetBotMap(i)->_Cre();
  6743. + if (!cre || cre == me || cre->isDead() || cre->getPowerType() != POWER_MANA) continue;
  6744. + if ((cre->GetBotClass() == CLASS_MAGE ||
  6745. + cre->GetBotClass() == CLASS_PRIEST ||
  6746. + cre->GetBotClass() == CLASS_SHAMAN ||
  6747. + cre->GetBotClass() == CLASS_DRUID ||
  6748. + cre->GetBotClass() == CLASS_WARLOCK) &&
  6749. + !cre->HasAura(FOCUSMAGIC) && me->GetExactDist(cre) < 30)
  6750. + {
  6751. + target = cre;
  6752. + break;
  6753. + }
  6754. + }
  6755. + }
  6756. + }
  6757. + }
  6758. + if (target && doCast(target, FOCUSMAGIC))
  6759. + {
  6760. + GC_Timer = 500;
  6761. + fmCheckTimer = 30000;
  6762. + return;
  6763. + }
  6764. + }
  6765. + fmCheckTimer = 5000;
  6766. + }
  6767. +
  6768. + void ApplyClassDamageMultiplierSpell(int32& damage, SpellNonMeleeDamage& /*damageinfo*/, SpellInfo const* spellInfo, WeaponAttackType /*attackType*/, bool& crit) const
  6769. + {
  6770. + uint32 spellId = spellInfo->Id;
  6771. + uint8 lvl = me->getLevel();
  6772. + float fdamage = float(damage);
  6773. + //1) apply additional crit chance. This additional chance roll will replace original (balance safe)
  6774. + if (!crit)
  6775. + {
  6776. + float aftercrit = 0.f;
  6777. + //Combustion: 10% per stack
  6778. + if (SPELL_SCHOOL_MASK_FIRE & spellInfo->GetSchoolMask())
  6779. + if (Aura* combustion = me->GetAura(COMBUSTION_BUFF))
  6780. + aftercrit += float(combustion->GetStackAmount()*10);
  6781. + //Incineration: 6% additional critical chance for Fire Blast, Scorch, Arcane Blast and Cone of Cold
  6782. + if (lvl >= 10 &&
  6783. + (spellId == FIREBLAST ||
  6784. + spellId == CONEOFCOLD/* ||
  6785. + spellId == ARCANEBLAST ||
  6786. + spellId == SCORCH*/))
  6787. + aftercrit += 6.f;
  6788. + //World In Flames: 6% additional critical chance for
  6789. + //Flamestrike, Pyroblast, Blast Wave, Dragon's Breath, Living Bomb, Blizzard and Arcane Explosion
  6790. + if (lvl >= 15 &&
  6791. + (spellId == FLAMESTRIKE ||
  6792. + spellId == PYROBLAST ||
  6793. + spellId == BLASTWAVE ||
  6794. + spellId == DRAGONBREATH/* ||
  6795. + spellId == ARCANEXPLOSION ||
  6796. + spellId == LIVINGBOMB || //cannot be handled here
  6797. + spellId == BLIZZARD*/)) //cannot be handled here
  6798. + aftercrit += 6.f;
  6799. +
  6800. + if (aftercrit > 0.f)
  6801. + crit = roll_chance_f(aftercrit);
  6802. + }
  6803. +
  6804. + //2) apply bonus damage mods
  6805. + float pctbonus = 0.0f;
  6806. + if (crit)
  6807. + {
  6808. + //!!!spell damage is not yet critical and will be multiplied by 1.5
  6809. + //so we should put here bonus damage mult /1.5
  6810. + //Spell Power: 50% additional crit damage bonus for All spells
  6811. + if (lvl >= 55)
  6812. + pctbonus += 0.333f;
  6813. + //Ice Shards: 50% additional crit damage bonus for Frost spells
  6814. + else if (lvl >= 15 && (SPELL_SCHOOL_MASK_FROST & spellInfo->GetSchoolMask()))
  6815. + pctbonus += 0.333f;
  6816. + }
  6817. + //Improved Cone of Cold: 35% bonus damage for Cone of Cold
  6818. + if (lvl >= 30 && spellId == CONEOFCOLD)
  6819. + pctbonus += 0.35f;
  6820. + //Fire Power: 10% bonus damage for Fire spells
  6821. + if (lvl >= 35 && (SPELL_SCHOOL_MASK_FIRE & spellInfo->GetSchoolMask()))
  6822. + pctbonus += 0.1f;
  6823. +
  6824. + damage = int32(fdamage * (1.0f + pctbonus));
  6825. + }
  6826. +
  6827. + void SpellHit(Unit* caster, SpellInfo const* spell)
  6828. + {
  6829. + OnSpellHit(caster, spell);
  6830. + }
  6831. +
  6832. + void SpellHitTarget(Unit* /*target*/, SpellInfo const* spell)
  6833. + {
  6834. + if (aftercastTargetGuid != 0)
  6835. + {
  6836. + //only players for now
  6837. + if (!IS_PLAYER_GUID(aftercastTargetGuid))
  6838. + {
  6839. + aftercastTargetGuid = 0;
  6840. + return;
  6841. + }
  6842. + Player* pTarget = sObjectAccessor->FindPlayer(aftercastTargetGuid);
  6843. + aftercastTargetGuid = 0;
  6844. + if (!pTarget/* || me->GetDistance(pTarget) > 15*/)
  6845. + return;
  6846. +
  6847. + //handle effects
  6848. + for (uint8 i = 0; i != MAX_SPELL_EFFECTS; ++i)
  6849. + {
  6850. + switch (spell->Effects[i].Effect)
  6851. + {
  6852. + case SPELL_EFFECT_CREATE_ITEM:
  6853. + case SPELL_EFFECT_CREATE_ITEM_2:
  6854. + {
  6855. + uint32 newitemid = spell->Effects[i].ItemType;
  6856. + if (newitemid)
  6857. + {
  6858. + ItemPosCountVec dest;
  6859. + ItemTemplate const* pProto = sObjectMgr->GetItemTemplate(newitemid);
  6860. + if (!pProto)
  6861. + return;
  6862. + uint32 count = pProto->GetMaxStackSize();
  6863. + uint32 no_space = 0;
  6864. + InventoryResult msg = pTarget->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, newitemid, count, &no_space);
  6865. + if (msg != EQUIP_ERR_OK)
  6866. + {
  6867. + if (msg == EQUIP_ERR_INVENTORY_FULL || msg == EQUIP_ERR_CANT_CARRY_MORE_OF_THIS)
  6868. + count -= no_space;
  6869. + else
  6870. + {
  6871. + // if not created by another reason from full inventory or unique items amount limitation
  6872. + pTarget->SendEquipError(msg, NULL, NULL, newitemid);
  6873. + continue;
  6874. + }
  6875. + }
  6876. + if (count)
  6877. + {
  6878. + Item* pItem = pTarget->StoreNewItem(dest, newitemid, true, Item::GenerateItemRandomPropertyId(newitemid));
  6879. + if (!pItem)
  6880. + {
  6881. + pTarget->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL);
  6882. + continue;
  6883. + }
  6884. + //unsafe possible
  6885. + pItem->SetUInt32Value(ITEM_FIELD_CREATOR, me->GetGUIDLow());
  6886. +
  6887. + pTarget->SendNewItem(pItem, count, true, false, true);
  6888. + }
  6889. + }
  6890. + break;
  6891. + }
  6892. + default:
  6893. + break;
  6894. + }
  6895. + }
  6896. + }
  6897. + }
  6898. +
  6899. + void DamageTaken(Unit* u, uint32& /*damage*/)
  6900. + {
  6901. + OnOwnerDamagedBy(u);
  6902. + }
  6903. +
  6904. + void OwnerAttackedBy(Unit* u)
  6905. + {
  6906. + OnOwnerDamagedBy(u);
  6907. + }
  6908. +
  6909. + void Reset()
  6910. + {
  6911. + Pyroblast_cd = 0;
  6912. + FireBlast_cd = 0;
  6913. + DragonBreath_cd = 0;
  6914. + Combustion_cd = 30000;//30 sec for reset
  6915. + Ice_Barrier_cd = 0;
  6916. + Iceblock_cd = 0;
  6917. + ConeofCold_cd = 0;
  6918. + Blizzard_cd = 10000;
  6919. + CounterSpell_cd = 0;
  6920. + Evocation_cd = 0;
  6921. + Blink_cd = 0;
  6922. + Bolt_cd = 0;
  6923. + Nova_cd = 0;
  6924. + polyCheckTimer = 0;
  6925. + fmCheckTimer = 0;
  6926. + Polymorph = false;
  6927. + clearcast = false;
  6928. + BOLT = FROSTBOLT;//default frost
  6929. + NOVA = BLASTWAVE != 0 ? BLASTWAVE : FROSTNOVA;
  6930. +
  6931. + if (master)
  6932. + {
  6933. + setStats(CLASS_MAGE, me->getRace(), master->getLevel(), true);
  6934. + ApplyClassPassives();
  6935. + ApplyPassives(CLASS_MAGE);
  6936. + }
  6937. + }
  6938. +
  6939. + void ReduceCD(uint32 diff)
  6940. + {
  6941. + CommonTimers(diff);
  6942. + if (Pyroblast_cd > diff) Pyroblast_cd -= diff;
  6943. + if (Ice_Barrier_cd > diff) Ice_Barrier_cd -= diff;
  6944. + if (Iceblock_cd > diff) Iceblock_cd -= diff;
  6945. + if (ConeofCold_cd > diff) ConeofCold_cd -= diff;
  6946. + if (Living_Bomb_cd > diff) Living_Bomb_cd -= diff;
  6947. + if (FireBlast_cd > diff) FireBlast_cd -= diff;
  6948. + if (Bolt_cd > diff) Bolt_cd -= diff;
  6949. + if (Blizzard_cd > diff) Blizzard_cd -= diff;
  6950. + if (CounterSpell_cd > diff) CounterSpell_cd -= diff;
  6951. + if (Nova_cd > diff) Nova_cd -= diff;
  6952. + if (DragonBreath_cd > diff) DragonBreath_cd -= diff;
  6953. + if (Blink_cd > diff) Blink_cd -= diff;
  6954. + if (Combustion_cd > diff) Combustion_cd -= diff;
  6955. + if (Evocation_cd > diff) Evocation_cd -= diff;
  6956. + if (polyCheckTimer > diff) polyCheckTimer -= diff;
  6957. + if (fmCheckTimer > diff) fmCheckTimer -= diff;
  6958. + }
  6959. +
  6960. + bool CanRespawn()
  6961. + {return false;}
  6962. +
  6963. + void InitSpells()
  6964. + {
  6965. + uint8 lvl = me->getLevel();
  6966. + DAMPENMAGIC = InitSpell(me, DAMPENMAGIC_1);
  6967. + ARCANEINTELLECT = InitSpell(me, ARCANEINTELLECT_1);
  6968. + ARCANEMISSILES = InitSpell(me, ARCANEMISSILES_1);
  6969. + POLYMORPH = InitSpell(me, POLYMORPH_1);
  6970. + COUNTERSPELL = InitSpell(me, COUNTERSPELL_1);
  6971. + SPELLSTEAL = InitSpell(me, SPELLSTEAL_1);
  6972. + EVOCATION = InitSpell(me, EVOCATION_1);
  6973. + BLINK = InitSpell(me, BLINK_1);
  6974. + REMOVE_CURSE = InitSpell(me, REMOVE_CURSE_1);
  6975. + //INVISIBILITY = InitSpell(me, INVISIBILITY_1);
  6976. + FIREBALL = InitSpell(me, FIREBALL_1);
  6977. + BLASTWAVE = lvl >= 30 ? InitSpell(me, BLASTWAVE_1) : 0;
  6978. + DRAGONBREATH = lvl >= 40 ? InitSpell(me, DRAGONBREATH_1) : 0;
  6979. + FIREBLAST = InitSpell(me, FIREBLAST_1);
  6980. + PYROBLAST = lvl >= 20 ? InitSpell(me, PYROBLAST_1) : 0;
  6981. + LIVINGBOMB = lvl >= 60 ? InitSpell(me, LIVINGBOMB_1) : 0;
  6982. + FLAMESTRIKE = InitSpell(me, DAMPENMAGIC_1);
  6983. + COMBUSTION = lvl >= 50 ? COMBUSTION_1 : 0;
  6984. + FROSTBOLT = InitSpell(me, FROSTBOLT_1);
  6985. + FROSTNOVA = InitSpell(me, FROSTNOVA_1);
  6986. + CONEOFCOLD = InitSpell(me, CONEOFCOLD_1);
  6987. + BLIZZARD = InitSpell(me, BLIZZARD_1);
  6988. + ICEARMOR = lvl >= 20 ? InitSpell(me, ICEARMOR_1) : InitSpell(me, FROSTARMOR_1);
  6989. + ICE_BARRIER = lvl >= 40 ? InitSpell(me, ICE_BARRIER_1) : 0;
  6990. + ICEBLOCK = InitSpell(me, ICEBLOCK_1);
  6991. + FOCUSMAGIC = lvl >= 20 ? FOCUSMAGIC_1 : 0;
  6992. + }
  6993. +
  6994. + void ApplyClassPassives()
  6995. + {
  6996. + uint8 level = master->getLevel();
  6997. + //Dam+(-Hit)
  6998. + if (level >= 50)
  6999. + RefreshAura(ARCTIC_WINDS,3); //+15%/-15%
  7000. + else if (level >= 25)
  7001. + RefreshAura(ARCTIC_WINDS,2); //+10%/-10%
  7002. + else if (level >= 10)
  7003. + RefreshAura(ARCTIC_WINDS); //+5%/-5%
  7004. + //CHILL
  7005. + if (level >= 30)
  7006. + RefreshAura(WINTERS_CHILL3); //100%
  7007. + else if (level >= 25)
  7008. + RefreshAura(WINTERS_CHILL2); //66%
  7009. + else if (level >= 20)
  7010. + RefreshAura(WINTERS_CHILL1); //33%
  7011. + //Imp Blizzard
  7012. + if (level >= 20)
  7013. + RefreshAura(IMPROVED_BLIZZARD); //50% slow
  7014. + //Frostbite
  7015. + if (level >= 80)
  7016. + FROSTBITE = FROSTBITE3;
  7017. + else if (level >= 50)
  7018. + FROSTBITE = FROSTBITE2;
  7019. + else if (level >= 10)
  7020. + FROSTBITE = FROSTBITE1;
  7021. + if (level >= 60)
  7022. + RefreshAura(FROSTBITE,3);//3x
  7023. + else if (level >= 30)
  7024. + RefreshAura(FROSTBITE,2);//2x
  7025. + else if (level >= 10)
  7026. + RefreshAura(FROSTBITE);//1x
  7027. + //Shattered Barrier
  7028. + if (level >= 45)
  7029. + RefreshAura(SHATTERED_BARRIER);
  7030. + //Bonus
  7031. + if (level >= 65)
  7032. + RefreshAura(ARCANE_INSTABILITY,4); //+12%dmg crit
  7033. + else if (level >= 55)
  7034. + RefreshAura(ARCANE_INSTABILITY,3); //+9%dmg crit
  7035. + else if (level >= 45)
  7036. + RefreshAura(ARCANE_INSTABILITY,2); //+6%dmg crit
  7037. + else if (level >= 35)
  7038. + RefreshAura(ARCANE_INSTABILITY); //+3%dmg crit
  7039. + //Absorb
  7040. + if (level >= 50)
  7041. + RefreshAura(INCANTERS_ABSORPTION3);
  7042. + else if (level >= 45)
  7043. + RefreshAura(INCANTERS_ABSORPTION2);
  7044. + else if (level >= 40)
  7045. + RefreshAura(INCANTERS_ABSORPTION1);
  7046. + //Shatter
  7047. + if (level >= 35)
  7048. + RefreshAura(SHATTER3);
  7049. + else if (level >= 30)
  7050. + RefreshAura(SHATTER2);
  7051. + else if (level >= 25)
  7052. + RefreshAura(SHATTER1);
  7053. + //ClearCasting
  7054. + if (level >= 75)
  7055. + RefreshAura(CLEARCAST,3);//30%
  7056. + else if (level >= 40)
  7057. + RefreshAura(CLEARCAST,2);//20%
  7058. + else if (level >= 15)
  7059. + RefreshAura(CLEARCAST);//10%
  7060. + //Fingers
  7061. + if (level >= 45)
  7062. + RefreshAura(FINGERS_OF_FROST);//15%
  7063. + //Potency
  7064. + if (level >= 40)
  7065. + RefreshAura(ARCANE_POTENCY2);//30% bonus
  7066. + else if (level >= 35)
  7067. + RefreshAura(ARCANE_POTENCY1);//15% bonus
  7068. + //Ignite
  7069. + if (level >= 15)
  7070. + RefreshAura(IGNITE);
  7071. + //Impact
  7072. + if (level >= 60)
  7073. + RefreshAura(IMPACT,2);
  7074. + else if (level >= 20)
  7075. + RefreshAura(IMPACT);
  7076. + //Imp. Counterspell
  7077. + if (level >= 35)
  7078. + RefreshAura(IMPROVED_COUNTERSPELL2);//4 sec
  7079. + else if (level >= 25)
  7080. + RefreshAura(IMPROVED_COUNTERSPELL1);//2 sec
  7081. + //Firestarter
  7082. + if (level >= 55)
  7083. + RefreshAura(FIRESTARTER2);//100% chance
  7084. + else if (level >= 45)
  7085. + RefreshAura(FIRESTARTER1);//50% chance
  7086. + //Spells
  7087. + if (LIVINGBOMB != 0)
  7088. + RefreshAura(GLYPH_LIVING_BOMB);
  7089. + if (POLYMORPH != 0)
  7090. + RefreshAura(GLYPH_POLYMORPH);
  7091. + }
  7092. +
  7093. + private:
  7094. + uint32
  7095. + /*Arcane*/DAMPENMAGIC, ARCANEINTELLECT, ARCANEMISSILES, POLYMORPH, COUNTERSPELL, FOCUSMAGIC,
  7096. + /*Arcane*/SPELLSTEAL, EVOCATION, BLINK, REMOVE_CURSE, /*INVISIBILITY,*/
  7097. + /*Fire*/FIREBALL, FIREBLAST, FLAMESTRIKE, PYROBLAST, COMBUSTION, BLASTWAVE, DRAGONBREATH, LIVINGBOMB,
  7098. + /*Frost*/FROSTBOLT, FROSTNOVA, CONEOFCOLD, BLIZZARD, ICEARMOR, ICEBLOCK, ICE_BARRIER, FROSTBITE;
  7099. + //Timers
  7100. +/*fire*/uint32 Pyroblast_cd, FireBlast_cd, DragonBreath_cd, Living_Bomb_cd, Combustion_cd;
  7101. +/*frst*/uint32 Ice_Barrier_cd, ConeofCold_cd, Blizzard_cd, Iceblock_cd;
  7102. +/*arcn*/uint32 CounterSpell_cd, Blink_cd, Evocation_cd;
  7103. +/*exc.*/uint32 Bolt_cd, Nova_cd;
  7104. +/*exc.*/uint32 BOLT, NOVA;
  7105. +/*exc.*/uint32 polyCheckTimer, fmCheckTimer;
  7106. + //Check
  7107. +/*exc.*/bool Polymorph, clearcast;
  7108. +
  7109. + enum MageBaseSpells// all orignals
  7110. + {
  7111. + DAMPENMAGIC_1 = 604,
  7112. + ARCANEINTELLECT_1 = 1459,
  7113. + ARCANEMISSILES_1 = 5143,
  7114. + POLYMORPH_1 = 118,
  7115. + COUNTERSPELL_1 = 2139,
  7116. + SPELLSTEAL_1 = 30449,
  7117. + EVOCATION_1 = 12051,
  7118. + BLINK_1 = 1953,
  7119. + REMOVE_CURSE_1 = 475,
  7120. + //INVISIBILITY_1 = 0,
  7121. + FIREBALL_1 = 133,
  7122. + BLASTWAVE_1 = 11113,
  7123. + DRAGONBREATH_1 = 31661,
  7124. + FIREBLAST_1 = 2136,
  7125. + PYROBLAST_1 = 11366,
  7126. + LIVINGBOMB_1 = 44457,
  7127. + FLAMESTRIKE_1 = 2120,
  7128. + COMBUSTION_1 = 11129,
  7129. + FROSTBOLT_1 = 116,
  7130. + FROSTNOVA_1 = 122,
  7131. + CONEOFCOLD_1 = 120,
  7132. + BLIZZARD_1 = 10,
  7133. + FROSTARMOR_1 = 168,
  7134. + ICEARMOR_1 = 7302,
  7135. + ICE_BARRIER_1 = 11426,
  7136. + ICEBLOCK_1 = 45438,
  7137. + FOCUSMAGIC_1 = 54646,
  7138. + };
  7139. +
  7140. + enum MagePassives
  7141. + {
  7142. + SHATTERED_BARRIER = 54787,//rank 2
  7143. + ARCTIC_WINDS = 31678,//rank 5
  7144. + WINTERS_CHILL1 = 11180,
  7145. + WINTERS_CHILL2 = 28592,
  7146. + WINTERS_CHILL3 = 28593,
  7147. + FROSTBITE1 = 11071,
  7148. + FROSTBITE2 = 12496,
  7149. + FROSTBITE3 = 12497,
  7150. + IMPROVED_BLIZZARD = 12488,//rank 3
  7151. + CLEARCAST /*Arcane Concentration*/ = 12577,//rank 5
  7152. + ARCANE_POTENCY1 = 31571,
  7153. + ARCANE_POTENCY2 = 31572,
  7154. + SHATTER1 = 11170,
  7155. + SHATTER2 = 12982,
  7156. + SHATTER3 = 12983,
  7157. + INCANTERS_ABSORPTION1 = 44394,
  7158. + INCANTERS_ABSORPTION2 = 44395,
  7159. + INCANTERS_ABSORPTION3 = 44396,
  7160. + FINGERS_OF_FROST = 44545,//rank 2
  7161. + ARCANE_INSTABILITY = 15060,//rank 3
  7162. + IMPROVED_COUNTERSPELL1 = 11255,
  7163. + IMPROVED_COUNTERSPELL2 = 12598,
  7164. + IGNITE = 12848,
  7165. + FIRESTARTER1 = 44442,
  7166. + FIRESTARTER2 = 44443,
  7167. + IMPACT = 12358,
  7168. + GLYPH_LIVING_BOMB = 63091,
  7169. + GLYPH_POLYMORPH = 56375,
  7170. + };
  7171. + enum MageSpecial
  7172. + {
  7173. + CLEARCASTBUFF = 12536,
  7174. + IMPACT_BUFF = 64343,
  7175. + FIRESTARTERBUFF = 54741,
  7176. + ARCANE_POTENCY_BUFF1 = 57529,
  7177. + ARCANE_POTENCY_BUFF2 = 57531,
  7178. + COMBUSTION_BUFF = 28682
  7179. + };
  7180. + };
  7181. +};
  7182. +
  7183. +void AddSC_mage_bot()
  7184. +{
  7185. + new mage_bot();
  7186. +}
  7187. diff --git a/src/server/game/AI/NpcBots/bot_paladin_ai.cpp b/src/server/game/AI/NpcBots/bot_paladin_ai.cpp
  7188. new file mode 100644
  7189. index 0000000..9bc0ffa
  7190. --- /dev/null
  7191. +++ b/src/server/game/AI/NpcBots/bot_paladin_ai.cpp
  7192. @@ -0,0 +1,1014 @@
  7193. +#include "bot_ai.h"
  7194. +#include "Group.h"
  7195. +#include "Player.h"
  7196. +#include "ScriptMgr.h"
  7197. +#include "SpellAuraEffects.h"
  7198. +#include "WorldSession.h"
  7199. +/*
  7200. +Paladin NpcBot (reworked by Graff [email protected])
  7201. +Complete - Around 45-50%
  7202. +TODO: Repentance Work Improve, Tanking, Shield Abilities, Auras
  7203. +*/
  7204. +class paladin_bot : public CreatureScript
  7205. +{
  7206. +public:
  7207. + paladin_bot() : CreatureScript("paladin_bot") { }
  7208. +
  7209. + CreatureAI* GetAI(Creature* creature) const
  7210. + {
  7211. + return new paladin_botAI(creature);
  7212. + }
  7213. +
  7214. + bool OnGossipHello(Player* player, Creature* creature)
  7215. + {
  7216. + return bot_minion_ai::OnGossipHello(player, creature);
  7217. + }
  7218. +
  7219. + bool OnGossipSelect(Player* player, Creature* creature, uint32 sender, uint32 action)
  7220. + {
  7221. + if (bot_minion_ai* ai = creature->GetBotMinionAI())
  7222. + return ai->OnGossipSelect(player, creature, sender, action);
  7223. + return true;
  7224. + }
  7225. +
  7226. + struct paladin_botAI : public bot_minion_ai
  7227. + {
  7228. + paladin_botAI(Creature* creature) : bot_minion_ai(creature) { }
  7229. +
  7230. + bool doCast(Unit* victim, uint32 spellId, bool triggered = false)
  7231. + {
  7232. + if (checkBotCast(victim, spellId, CLASS_PALADIN) != SPELL_CAST_OK)
  7233. + return false;
  7234. + return bot_ai::doCast(victim, spellId, triggered);
  7235. + }
  7236. +
  7237. + void HOFGroup(Player* pTarget, uint32 diff)
  7238. + {
  7239. + if (!HOF || HOF_Timer > diff || GC_Timer > diff || Rand() > 60) return;
  7240. + if (IsCasting()) return;//I'm busy
  7241. +
  7242. + if (Group* pGroup = pTarget->GetGroup())
  7243. + {
  7244. + for (GroupReference* itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
  7245. + {
  7246. + Player* tPlayer = itr->getSource();
  7247. + if (!tPlayer) continue;
  7248. + if (HOFTarget(tPlayer, diff))
  7249. + return;
  7250. + }
  7251. + for (GroupReference* itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
  7252. + {
  7253. + Player* tPlayer = itr->getSource();
  7254. + if (!tPlayer || !tPlayer->HaveBot()) continue;
  7255. + for (uint8 i = 0; i != tPlayer->GetMaxNpcBots(); ++i)
  7256. + {
  7257. + Creature* cre = tPlayer->GetBotMap(i)->_Cre();
  7258. + if (!cre || !cre->IsInWorld()) continue;
  7259. + if (HOFTarget(cre, diff))
  7260. + return;
  7261. + }
  7262. + }
  7263. + }
  7264. + }
  7265. +
  7266. + bool HOFTarget(Unit* target, uint32 diff)
  7267. + {
  7268. + if (!HOF || HOF_Timer > diff || GC_Timer > diff) return false;
  7269. + if (!target || target->isDead()) return false;
  7270. + if (target->ToCreature() && Rand() > 25) return false;
  7271. + if (me->GetExactDist(target) > 30) return false;//too far away
  7272. + if (HasAuraName(target, HOF)) return false; //Alredy has HOF
  7273. +
  7274. + Unit::AuraMap const &auras = target->GetOwnedAuras();
  7275. + for (Unit::AuraMap::const_iterator i = auras.begin(); i != auras.end(); ++i)
  7276. + {
  7277. + Aura* aura = i->second;
  7278. + if (aura->IsPassive()) continue;//most
  7279. + if (aura->GetDuration() < 2000) continue;
  7280. + if (AuraApplication* app = aura->GetApplicationOfTarget(target->GetGUID()))
  7281. + if (app->IsPositive()) continue;
  7282. + SpellInfo const* spellInfo = aura->GetSpellInfo();
  7283. + if (spellInfo->AttributesEx & SPELL_ATTR0_HIDDEN_CLIENTSIDE) continue;
  7284. + if (me->getLevel() >= 40 && (spellInfo->GetAllEffectsMechanicMask() & (1<<MECHANIC_STUN)))
  7285. + {
  7286. + if (doCast(target, HOF))
  7287. + {
  7288. + if (target->ToCreature())
  7289. + HOF_Timer = 10000;//10 sec for selfcast after stun
  7290. + else
  7291. + HOF_Timer = 15000;//improved
  7292. + HOFGuid = target->GetGUID();
  7293. + return true;
  7294. + }
  7295. + }
  7296. + /*else */if (spellInfo->GetAllEffectsMechanicMask() & (1<<MECHANIC_SNARE) ||
  7297. + spellInfo->GetAllEffectsMechanicMask() & (1<<MECHANIC_ROOT))
  7298. + {
  7299. + uint32 spell = (spellInfo->Dispel == DISPEL_MAGIC || spellInfo->Dispel == DISPEL_DISEASE || spellInfo->Dispel == DISPEL_POISON) && CLEANSE ? CLEANSE : HOF;
  7300. + if (doCast(target, spell))
  7301. + {
  7302. + if (spell == HOF)
  7303. + {
  7304. + if (target->ToCreature())
  7305. + HOF_Timer = 5000;//5 sec for bots
  7306. + else
  7307. + HOF_Timer = 15000;//improved
  7308. + if (me->getLevel() >= 40)
  7309. + HOFGuid = target->GetGUID();
  7310. + }
  7311. + return true;
  7312. + }
  7313. + }
  7314. + }
  7315. + return false;
  7316. + }
  7317. +
  7318. + void HOSGroup(Player* hTarget, uint32 diff)
  7319. + {
  7320. + if (!HOS || HOS_Timer > diff || GC_Timer > diff || Rand() > 30) return;
  7321. + if (IsCasting()) return;
  7322. + if (Group* pGroup = hTarget->GetGroup())
  7323. + {
  7324. + bool bots = false;
  7325. + float threat;
  7326. + for (GroupReference* itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
  7327. + {
  7328. + Player* HOSPlayer = itr->getSource();
  7329. + if (!HOSPlayer) continue;
  7330. + if (HOSPlayer->HaveBot())
  7331. + bots = true;
  7332. + if (HOSPlayer->isDead()) continue;
  7333. + if (tank && HOSPlayer == tank) continue;//tanks do not need it
  7334. + if (master->GetMap() != HOSPlayer->FindMap() || !HOSPlayer->IsInWorld() || me->GetExactDist(HOSPlayer) > 30) continue;
  7335. + if (HasAuraName(HOSPlayer, HOS)) continue;
  7336. + AttackerSet h_attackers = HOSPlayer->getAttackers();
  7337. + if (h_attackers.empty()) continue;
  7338. + for (AttackerSet::iterator iter = h_attackers.begin(); iter != h_attackers.end(); ++iter)
  7339. + {
  7340. + if (!(*iter)) continue;
  7341. + if ((*iter)->isDead()) continue;
  7342. + if (!(*iter)->CanHaveThreatList()) continue;
  7343. + threat = (*iter)->getThreatManager().getThreat(HOSPlayer);
  7344. + if (threat < 25.f) continue;//too small threat
  7345. + if ((*iter)->getThreatManager().getThreat(tank) < threat * 0.33f) continue;//would be useless
  7346. + if (HOSPlayer->GetDistance((*iter)) > 10) continue;
  7347. + if (HOSTarget(HOSPlayer, diff)) return;
  7348. + }//end for
  7349. + }//end for
  7350. + if (!bots) return;
  7351. + for (GroupReference* itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
  7352. + {
  7353. + Player* pl = itr->getSource();
  7354. + if (!pl) continue;
  7355. + if (!pl->HaveBot()) continue;
  7356. + if (master->GetMap() != pl->FindMap()) continue;
  7357. + if (!pl->IsInWorld() || pl->IsBeingTeleported()) continue;
  7358. + for (uint8 i = 0; i != pl->GetMaxNpcBots(); ++i)
  7359. + {
  7360. + Creature* cre = pl->GetBotMap(i)->_Cre();
  7361. + if (!cre || cre->isDead()) continue;
  7362. + if (tank && cre == tank) continue;
  7363. + if (me->GetExactDist(cre) > 30) continue;
  7364. + if (HasAuraName(cre, HOS)) continue; //Alredy has HOS
  7365. + AttackerSet h_attackers = cre->getAttackers();
  7366. + if (h_attackers.empty()) continue;
  7367. + for (AttackerSet::iterator iter = h_attackers.begin(); iter != h_attackers.end(); ++iter)
  7368. + {
  7369. + if (!(*iter)) continue;
  7370. + if ((*iter)->isDead()) continue;
  7371. + if (!(*iter)->CanHaveThreatList()) continue;
  7372. + threat = (*iter)->getThreatManager().getThreat(cre);
  7373. + if (threat < 25.f) continue;//too small threat
  7374. + if ((*iter)->getThreatManager().getThreat(tank) < threat * 0.33f) continue;//would be useless
  7375. + if (cre->GetDistance((*iter)) > 10) continue;
  7376. + if (HOSTarget(cre, diff)) return;
  7377. + }//end for
  7378. + }//end for
  7379. + }//end for
  7380. + }//end if
  7381. + }
  7382. +
  7383. + bool HOSTarget(Unit* target, uint32 diff)
  7384. + {
  7385. + if (!target || target->isDead()) return false;
  7386. + if (!HOS || HOS_Timer > diff || GC_Timer > diff || Rand() > 50) return false;
  7387. + if (tank && target == tank) return false; //tanks do not need it
  7388. + if (IsCasting()) return false; //I'm busy casting
  7389. + if (me->GetExactDist(target) > 30) return false; //too far away
  7390. + if (HasAuraName(target, HOS)) return false; //Alredy has HOS
  7391. +
  7392. + AttackerSet h_attackers = target->getAttackers();
  7393. + if (h_attackers.empty()) return false; //no aggro
  7394. + float threat;
  7395. + uint8 Tattackers = 0;
  7396. + for (AttackerSet::iterator iter = h_attackers.begin(); iter != h_attackers.end(); ++iter)
  7397. + {
  7398. + if (!(*iter)) continue;
  7399. + if ((*iter)->isDead()) continue;
  7400. + if (!(*iter)->CanHaveThreatList()) continue;
  7401. + threat = (*iter)->getThreatManager().getThreat(target);
  7402. + if (threat < 25.f) continue;//too small threat
  7403. + if ((*iter)->getThreatManager().getThreat(tank) < threat * 0.33f) continue;//would be useless
  7404. + if (target->GetDistance((*iter)) <= 10)
  7405. + Tattackers++;
  7406. + }
  7407. + if (Tattackers > 0 && doCast(target, HOS))
  7408. + {
  7409. + for (AttackerSet::iterator iter = h_attackers.begin(); iter != h_attackers.end(); ++iter)
  7410. + if ((*iter)->getThreatManager().getThreat(target) > 0.f)
  7411. + (*iter)->getThreatManager().modifyThreatPercent(target, -(30 + 50*(target->HasAura(586))));//Fade
  7412. + HOS_Timer = 25000 - 20000*IS_CREATURE_GUID(target->GetGUID());
  7413. + return true;
  7414. + }
  7415. + return false;
  7416. + }
  7417. + //Holy_Shock setup (Modify HERE)
  7418. + bool HS(Unit* target, uint32 diff)
  7419. + {
  7420. + if (!target || target->isDead()) return false;
  7421. + if (!HOLY_SHOCK || HS_Timer > diff || GC_Timer > diff) return false;
  7422. + if (IsCasting()) return false;
  7423. + if (target->GetTypeId() == TYPEID_PLAYER && (target->isCharmed() || target->isPossessed())) return false;//do not damage friends under control
  7424. + if (me->GetExactDist(target) > 40) return false;
  7425. +
  7426. + if (doCast(target, HOLY_SHOCK))
  7427. + {
  7428. + HS_Timer = target->ToCreature() ? 3500 : 5000;
  7429. + return true;
  7430. + }
  7431. + return false;
  7432. + }
  7433. +
  7434. + bool HealTarget(Unit* target, uint8 hp, uint32 diff)
  7435. + {
  7436. + if (!target || target->isDead()) return false;
  7437. + if (hp > 97) return false;
  7438. + //sLog->outBasic("HealTarget() by %s on %s", me->GetName().c_str(), target->GetName().c_str());
  7439. + if (Rand() > 40 + 20*target->isInCombat() + 50*master->GetMap()->IsRaid()) return false;
  7440. + if (me->GetExactDist(target) > 35) return false;
  7441. + if (IsCasting()) return false;
  7442. + if (HAND_OF_PROTECTION && BOP_Timer <= diff && IS_PLAYER_GUID(target->GetGUID()) &&
  7443. + (master->GetGroup() && master->GetGroup()->IsMember(target->GetGUID()) || target == master) &&
  7444. + ((hp < 30 && !target->getAttackers().empty()) || (hp < 50 && target->getAttackers().size() > 3)) &&
  7445. + me->GetExactDist(target) < 30 &&
  7446. + !HasAuraName(target, HAND_OF_PROTECTION) &&
  7447. + !HasAuraName(target, "Forbearance"))
  7448. + {
  7449. + if (doCast(target, HAND_OF_PROTECTION))
  7450. + {
  7451. + if (target->GetTypeId() == TYPEID_PLAYER)
  7452. + me->MonsterWhisper("BOP on you!", target->GetGUID());
  7453. + BOP_Timer = 60000; //1 min
  7454. + if (!HasAuraName(target, "Forbearance"))
  7455. + me->AddAura(25771, target);//Forbearance
  7456. + if (HasAuraName(target, "Forbearance") && !target->HasAura(HAND_OF_PROTECTION))
  7457. + me->AddAura(HAND_OF_PROTECTION, target);
  7458. + }
  7459. + return true;
  7460. + }
  7461. + else if (hp < 20 && !HasAuraName(target, HAND_OF_PROTECTION))
  7462. + {
  7463. + // 20% to cast loh, else just do a Shock
  7464. + switch (rand()%3)
  7465. + {
  7466. + case 1:
  7467. + if (LAY_ON_HANDS && LOH_Timer <= diff && hp < 20 &&
  7468. + target->GetTypeId() == TYPEID_PLAYER &&
  7469. + (target->isInCombat() || !target->getAttackers().empty()) &&
  7470. + !HasAuraName(target, "Forbearance"))
  7471. + {
  7472. + if (doCast(target, LAY_ON_HANDS))
  7473. + {
  7474. + me->MonsterWhisper("Lay of Hands on you!", target->GetGUID());
  7475. + LOH_Timer = 60000; //1 min
  7476. + return true;
  7477. + }
  7478. + }
  7479. + case 2:
  7480. + if (GC_Timer > diff) return false;
  7481. + if (FLASH_OF_LIGHT && doCast(target, FLASH_OF_LIGHT))
  7482. + return true;
  7483. + case 3:
  7484. + if (GC_Timer > diff) return false;
  7485. + if (HOLY_SHOCK && HS_Timer <= diff && HS(target, diff))
  7486. + return true;
  7487. + }
  7488. + }
  7489. + if (GC_Timer > diff) return false;
  7490. + Unit* u = target->getVictim();
  7491. + if (SACRED_SHIELD && SSH_Timer <= diff && target->GetTypeId() == TYPEID_PLAYER &&
  7492. + (hp < 65 || target->getAttackers().size() > 1 || (u && u->GetMaxHealth() > target->GetMaxHealth()*10 && target->isInCombat())) &&
  7493. + !target->HasAura(SACRED_SHIELD) &&
  7494. + ((master->GetGroup() && master->GetGroup()->IsMember(target->GetGUID())) || target == master))
  7495. + {
  7496. + Unit* aff = FindAffectedTarget(SACRED_SHIELD, me->GetGUID(), 50, 1);//use players since we cast only on them
  7497. + if ((!aff || (aff->getAttackers().empty() && tank != aff)) &&
  7498. + doCast(target, SACRED_SHIELD))
  7499. + {
  7500. + SSH_Timer = 3000;
  7501. + return true;
  7502. + }
  7503. + }
  7504. + if (HOLY_SHOCK && (hp < 85 || GetLostHP(target) > 6000) && HS_Timer <= diff)
  7505. + if (HS(target, diff))
  7506. + return true;
  7507. + if ((hp > 35 && (hp < 75 || GetLostHP(target) > 8000)) || (!FLASH_OF_LIGHT && hp < 85))
  7508. + if (doCast(target, HOLY_LIGHT))
  7509. + return true;
  7510. + if (FLASH_OF_LIGHT && (hp < 90 || GetLostHP(target) > 1500))
  7511. + if (doCast(target, FLASH_OF_LIGHT))
  7512. + return true;
  7513. + return false;
  7514. + }//end HealTarget
  7515. +
  7516. + void StartAttack(Unit* u, bool force = false)
  7517. + {
  7518. + if (GetBotCommandState() == COMMAND_ATTACK && !force) return;
  7519. + Aggro(u);
  7520. + SetBotCommandState(COMMAND_ATTACK);
  7521. + GetInPosition(force, false);
  7522. + }
  7523. +
  7524. + void EnterCombat(Unit*) { }
  7525. + void Aggro(Unit*) { }
  7526. + void AttackStart(Unit*) { }
  7527. + void KilledUnit(Unit*) { }
  7528. + void EnterEvadeMode() { }
  7529. + void MoveInLineOfSight(Unit*) { }
  7530. + void JustDied(Unit*) { master->SetNpcBotDied(me->GetGUID()); }
  7531. +
  7532. + void UpdateAI(uint32 diff)
  7533. + {
  7534. + ReduceCD(diff);
  7535. + if (HOFGuid != 0)
  7536. + {
  7537. + if (Unit* ally = sObjectAccessor->FindUnit(HOFGuid))
  7538. + if (Aura* hof = ally->GetAura(HOF, me->GetGUID()))
  7539. + hof->SetDuration(hof->GetDuration() + 4000);//Guardian's Favor part 2 (handled separately)
  7540. + HOFGuid = 0;
  7541. + }
  7542. + if (IAmDead()) return;
  7543. + if (me->getVictim())
  7544. + DoMeleeAttackIfReady();
  7545. + else
  7546. + Evade();
  7547. + if (wait == 0)
  7548. + wait = GetWait();
  7549. + else
  7550. + return;
  7551. + CheckAuras();
  7552. + BreakCC(diff);
  7553. + //HOFTarget(me, diff);//self stun cure goes FIRST
  7554. + if (CCed(me)) return;
  7555. +
  7556. + if (GetManaPCT(me) < 30 && Potion_cd <= diff)
  7557. + {
  7558. + temptimer = GC_Timer;
  7559. + if (doCast(me, MANAPOTION))
  7560. + Potion_cd = POTION_CD;
  7561. + GC_Timer = temptimer;
  7562. + }
  7563. + if (GetManaPCT(me) < 40 && DIVINE_PLEA && Divine_Plea_Timer <= diff)
  7564. + if (doCast(me, DIVINE_PLEA))
  7565. + Divine_Plea_Timer = 45000;
  7566. +
  7567. + CureTarget(me, CLEANSE, diff);//maybe unnecessary but this goes FIRST
  7568. + HOFTarget(master, diff);//maybe unnecessary
  7569. + CureTarget(master, CLEANSE, diff);//maybe unnecessary
  7570. + BuffAndHealGroup(master, diff);
  7571. + HOSTarget(master, diff);
  7572. + CureGroup(master, CLEANSE, diff);
  7573. + HOFGroup(master, diff);
  7574. + HOSGroup(master, diff);
  7575. +
  7576. + if (GetHealthPCT(me) < 50 && Potion_cd <= diff)
  7577. + {
  7578. + temptimer = GC_Timer;
  7579. + if (doCast(me, HEALINGPOTION))
  7580. + Potion_cd = POTION_CD;
  7581. + GC_Timer = temptimer;
  7582. + }
  7583. + if (!me->isInCombat())
  7584. + DoNonCombatActions(diff);
  7585. + //buff
  7586. + if (SEAL_OF_COMMAND && GC_Timer <= diff && !me->HasAura(SEAL_OF_COMMAND) &&
  7587. + doCast(me, SEAL_OF_COMMAND))
  7588. + GC_Timer = 500;
  7589. +
  7590. + // Heal myself
  7591. + if (GetHealthPCT(me) < 80)
  7592. + HealTarget(me, GetHealthPCT(me), diff);
  7593. +
  7594. + if (!CheckAttackTarget(CLASS_PALADIN))
  7595. + return;
  7596. +
  7597. + Repentance(diff);
  7598. + //Counter(diff);
  7599. + DoNormalAttack(diff);
  7600. + }
  7601. +
  7602. + void DoNonCombatActions(uint32 diff)
  7603. + {
  7604. + if (GC_Timer > diff || me->IsMounted()) return;
  7605. +
  7606. + RezGroup(REDEMPTION, master);
  7607. +
  7608. + if (Feasting()) return;
  7609. +
  7610. + //aura
  7611. + if (master->isAlive() && me->GetExactDist(master) < 20)
  7612. + {
  7613. + uint8 myAura;
  7614. + if (me->HasAura(DEVOTION_AURA, me->GetGUID()))
  7615. + myAura = DEVOTIONAURA;
  7616. + else if (me->HasAura(CONCENTRATION_AURA, me->GetGUID()))
  7617. + myAura = CONCENTRATIONAURA;
  7618. + else myAura = NOAURA;
  7619. +
  7620. + if (myAura != NOAURA)
  7621. + return; //do not bother
  7622. +
  7623. + Aura* concAura = master->GetAura(CONCENTRATION_AURA);
  7624. + Aura* devAura = master->GetAura(DEVOTION_AURA);
  7625. + if (devAura && concAura) return;
  7626. + if (devAura && devAura->GetCasterGUID() == me->GetGUID()) return;
  7627. + if (concAura && concAura->GetCasterGUID() == me->GetGUID()) return;
  7628. +
  7629. + if ((master->getClass() == CLASS_MAGE ||
  7630. + master->getClass() == CLASS_PRIEST ||
  7631. + master->getClass() == CLASS_WARLOCK ||
  7632. + master->getClass() == CLASS_DRUID || devAura) &&
  7633. + !concAura &&
  7634. + doCast(me, CONCENTRATION_AURA))
  7635. + {
  7636. + /*GC_Timer = 800;*/
  7637. + return;
  7638. + }
  7639. + if (!devAura && doCast(me, DEVOTION_AURA))
  7640. + {
  7641. + /*GC_Timer = 800;*/
  7642. + return;
  7643. + }
  7644. + }
  7645. + }
  7646. +
  7647. + bool BuffTarget(Unit* target, uint32 diff)
  7648. + {
  7649. + if (!target || target->isDead() || GC_Timer > diff || Rand() > 30) return false;
  7650. + if (me->isInCombat() && !master->GetMap()->IsRaid()) return false;
  7651. + if (me->GetExactDist(target) > 30) return false;
  7652. + if (HasAuraName(target, "Blessing of Wisdom", me->GetGUID()) ||
  7653. + HasAuraName(target, "Blessing of Might", me->GetGUID()) ||
  7654. + HasAuraName(target, "Blessing of Kings", me->GetGUID()) ||
  7655. + HasAuraName(target, "Blessing of Sanctuary", me->GetGUID()))
  7656. + return false;
  7657. + //if (HasAuraName(target, "Greater Blessing of Wisdom", me->GetGUID()) ||
  7658. + // HasAuraName(target, "Greater Blessing of Might", me->GetGUID()) ||
  7659. + // HasAuraName(target, "Greater Blessing of Kings", me->GetGUID()) ||
  7660. + // HasAuraName(target, "Greater Blessing of Sanctuary", me->GetGUID()))
  7661. + // return false;
  7662. + bool wisdom = HasAuraName(target, BLESSING_OF_WISDOM) || HasAuraName(target, "Greater Blessing of Wisdom");
  7663. + bool kings = HasAuraName(target, BLESSING_OF_KINGS) || HasAuraName(target, "Greater Blessing of Kings");
  7664. + bool sanctuary = HasAuraName(target, BLESSING_OF_SANCTUARY) || HasAuraName(target, "Greater Blessing of Sanctuary");
  7665. + bool might = (HasAuraName(target, BLESSING_OF_MIGHT) || HasAuraName(target, "Greater Blessing of Might") || HasAuraName(target, "Battle Shout"));
  7666. +
  7667. + uint8 Class = 0;
  7668. + if (target->GetTypeId() == TYPEID_PLAYER)
  7669. + Class = target->ToPlayer()->getClass();
  7670. + else if (target->ToCreature())
  7671. + Class = target->ToCreature()->GetBotClass();
  7672. + switch (Class)
  7673. + {
  7674. + case CLASS_PRIEST:
  7675. + if (BLESSING_OF_WISDOM && !wisdom && doCast(target, BLESSING_OF_WISDOM))
  7676. + return true;
  7677. + else if (BLESSING_OF_KINGS && !kings && doCast(target, BLESSING_OF_KINGS))
  7678. + return true;
  7679. + break;
  7680. + case CLASS_DEATH_KNIGHT:
  7681. + case CLASS_WARRIOR:
  7682. + case CLASS_PALADIN:
  7683. + case CLASS_ROGUE:
  7684. + case CLASS_HUNTER:
  7685. + case CLASS_SHAMAN:
  7686. + if (BLESSING_OF_KINGS && !kings && doCast(target, BLESSING_OF_KINGS))
  7687. + return true;
  7688. + else if (!might && doCast(target, BLESSING_OF_MIGHT))
  7689. + return true;
  7690. + else if (BLESSING_OF_SANCTUARY && !sanctuary && doCast(target, BLESSING_OF_SANCTUARY))
  7691. + return true;
  7692. + else if (BLESSING_OF_WISDOM && !wisdom && target->getPowerType() == POWER_MANA && doCast(target, BLESSING_OF_WISDOM))
  7693. + return true;
  7694. + break;
  7695. + default:
  7696. + if (BLESSING_OF_KINGS && !kings && doCast(target, BLESSING_OF_KINGS))
  7697. + return true;
  7698. + else if (BLESSING_OF_WISDOM && !wisdom && target->getPowerType() == POWER_MANA && doCast(target, BLESSING_OF_WISDOM))
  7699. + return true;
  7700. + else if (BLESSING_OF_SANCTUARY && !sanctuary && doCast(target, BLESSING_OF_SANCTUARY))
  7701. + return true;
  7702. + else if (!might && doCast(target, BLESSING_OF_MIGHT))
  7703. + return true;
  7704. + break;
  7705. + }
  7706. + return false;
  7707. + }
  7708. +
  7709. + void Repentance(uint32 diff, Unit* target = NULL)
  7710. + {
  7711. + if (target && Repentance_Timer < 25000 && doCast(target, REPENTANCE))
  7712. + {
  7713. + temptimer = GC_Timer;
  7714. + Repentance_Timer = 45000;
  7715. + GC_Timer = temptimer;
  7716. + return;
  7717. + }
  7718. + if (REPENTANCE && Repentance_Timer <= diff)
  7719. + {
  7720. + Unit* u = FindRepentanceTarget();
  7721. + if (u && u->getVictim() != me && doCast(u, REPENTANCE))
  7722. + Repentance_Timer = 45000;
  7723. + }
  7724. + }
  7725. +
  7726. + void Counter(uint32 diff)
  7727. + {
  7728. + if (Rand() > 60 || IsCasting()) return;
  7729. + Unit* target = Repentance_Timer < 25000 ? FindCastingTarget(20, false, REPENTANCE) : NULL;
  7730. + if (target)
  7731. + Repentance(diff, target);//first check repentance
  7732. + else if (TURN_EVIL && Turn_Evil_Timer < 1500)
  7733. + {
  7734. + target = FindCastingTarget(20, false, TURN_EVIL);
  7735. + temptimer = GC_Timer;
  7736. + if (target && doCast(target, TURN_EVIL, true))
  7737. + Turn_Evil_Timer = 3000;
  7738. + GC_Timer = temptimer;
  7739. + }
  7740. + else if (HOLY_WRATH && Holy_Wrath_Timer < 8000)
  7741. + {
  7742. + target = FindCastingTarget(8, false, TURN_EVIL);//here we check target as with turn evil cuz of same requirements
  7743. + temptimer = GC_Timer;
  7744. + if (target && doCast(me, HOLY_WRATH))
  7745. + Holy_Wrath_Timer = 23000 - me->getLevel() * 100; //23 - 0...8 sec (15 sec on 80 as with glyph)
  7746. + GC_Timer = temptimer;
  7747. + }
  7748. + else if (HAMMER_OF_JUSTICE && HOJ_Timer <= 7000/* && GC_Timer <= diff*/)
  7749. + {
  7750. + target = FindCastingTarget(10);
  7751. + if (target && doCast(opponent, HAMMER_OF_JUSTICE))
  7752. + HOJ_Timer = 65000 - master->getLevel()*500; //25 sec on 80
  7753. + }
  7754. + }
  7755. +
  7756. + void TurnEvil(uint32 diff)
  7757. + {
  7758. + if (!TURN_EVIL || Turn_Evil_Timer > diff || GC_Timer > diff || Rand() > 50 ||
  7759. + FindAffectedTarget(TURN_EVIL, me->GetGUID(), 50))
  7760. + return;
  7761. + Unit* target = FindUndeadCCTarget(20, TURN_EVIL);
  7762. + if (target &&
  7763. + (target != me->getVictim() || GetHealthPCT(me) < 70 || target->getVictim() == master) &&
  7764. + doCast(target, TURN_EVIL, true))
  7765. + {
  7766. + Turn_Evil_Timer = 3000;
  7767. + return;
  7768. + }
  7769. + else
  7770. + if ((opponent->GetCreatureType() == CREATURE_TYPE_UNDEAD || opponent->GetCreatureType() == CREATURE_TYPE_DEMON) &&
  7771. + !CCed(opponent) &&
  7772. + opponent->getVictim() && tank && opponent->getVictim() != tank && opponent->getVictim() != me &&
  7773. + GetHealthPCT(me) < 90 &&
  7774. + doCast(opponent, TURN_EVIL, true))
  7775. + Turn_Evil_Timer = 3000;
  7776. + }
  7777. +
  7778. + void Wrath(uint32 diff)
  7779. + {
  7780. + if (!HOLY_WRATH || Holy_Wrath_Timer > diff || GC_Timer > diff || Rand() > 50)
  7781. + return;
  7782. + if ((opponent->GetCreatureType() == CREATURE_TYPE_UNDEAD || opponent->GetCreatureType() == CREATURE_TYPE_DEMON) &&
  7783. + me->GetExactDist(opponent) <= 8 && doCast(me, HOLY_WRATH))
  7784. + Holy_Wrath_Timer = 23000 - me->getLevel() * 100; //23 - 0...8 sec (15 sec on 80 as with glyph)
  7785. + else
  7786. + {
  7787. + Unit* target = FindUndeadCCTarget(8, HOLY_WRATH);
  7788. + if (target && doCast(me, HOLY_WRATH))
  7789. + Holy_Wrath_Timer = 23000 - me->getLevel() * 100; //23 - 0...8 sec (15 sec on 80 as with glyph)
  7790. + }
  7791. + }
  7792. +
  7793. + void DoNormalAttack(uint32 diff)
  7794. + {
  7795. + opponent = me->getVictim();
  7796. + if (opponent)
  7797. + {
  7798. + if (!IsCasting())
  7799. + StartAttack(opponent, true);
  7800. + }
  7801. + else
  7802. + return;
  7803. +
  7804. + Counter(diff);
  7805. + TurnEvil(diff);
  7806. +
  7807. + if (MoveBehind(*opponent))
  7808. + wait = 5;
  7809. + //{ wait = 5; return; }
  7810. +
  7811. + if (HOW && HOW_Timer <= diff && GC_Timer <= diff && Rand() < 50 && GetHealthPCT(opponent) < 20 &&
  7812. + me->GetExactDist(opponent) < 30)
  7813. + if (doCast(opponent, HOW))
  7814. + HOW_Timer = 6000; //6 sec
  7815. +
  7816. + Unit* u = opponent->getVictim();
  7817. + if (Rand() < 50 && HANDOFRECKONING && Hand_Of_Reckoning_Timer <= diff && me->GetExactDist(opponent) < 30 &&
  7818. + u && u != me && u != tank && (IsInBotParty(u) || tank == me))//No GCD
  7819. + {
  7820. + Creature* cre = opponent->ToCreature();
  7821. + temptimer = GC_Timer;
  7822. + if (((cre && cre->isWorldBoss() && !isMeleeClass(u->getClass())) ||
  7823. + GetHealthPCT(u) < GetHealthPCT(me) - 5 || tank == me) &&
  7824. + doCast(opponent, HANDOFRECKONING))
  7825. + Hand_Of_Reckoning_Timer = 8000 - (me == tank)*2000;
  7826. + GC_Timer = temptimer;
  7827. + }
  7828. +
  7829. + if (Rand() < 20 && HAMMER_OF_JUSTICE && HOJ_Timer <= diff && GC_Timer <= diff &&
  7830. + !CCed(opponent) && me->GetExactDist(opponent) < 10)
  7831. + if (doCast(opponent, HAMMER_OF_JUSTICE))
  7832. + HOJ_Timer = 65000 - master->getLevel()*500; //25 sec on 80
  7833. +
  7834. + if (JUDGEMENT_OF_LIGHT && JOL_Timer <= diff && GC_Timer <= diff && Rand() < 50 &&
  7835. + me->GetExactDist(opponent) < 10 && me->HasAura(SEAL_OF_COMMAND))
  7836. + if (doCast(opponent, JUDGEMENT_OF_LIGHT))
  7837. + JOL_Timer = 8000;
  7838. +
  7839. + if (Rand() < 50 && CONSECRATION && Consecration_cd <= diff && GC_Timer <= diff &&
  7840. + me->GetDistance(opponent) < 7 && !opponent->isMoving())
  7841. + if (doCast(me, CONSECRATION))
  7842. + Consecration_cd = 8000;
  7843. +
  7844. + if (Rand() < 25 && AVENGING_WRATH && AW_Timer <= diff &&
  7845. + (opponent->GetHealth() > master->GetMaxHealth()*2/3))
  7846. + {
  7847. + temptimer = GC_Timer;
  7848. + if (doCast(me, AVENGING_WRATH))
  7849. + AW_Timer = 60000; //1 min
  7850. + GC_Timer = temptimer;
  7851. + }
  7852. +
  7853. + if (CRUSADER_STRIKE && Crusader_cd <= diff && GC_Timer <= diff && me->GetDistance(opponent) < 5)
  7854. + if (doCast(opponent, CRUSADER_STRIKE))
  7855. + Crusader_cd = 12000 - me->getLevel() * 100;//4 sec on 80
  7856. +
  7857. + if (EXORCISM && Exorcism_Timer <= diff && GC_Timer <= diff && me->GetExactDist(opponent) < 30 &&
  7858. + (tank != me || opponent->getVictim() == me || opponent->IsVehicle() || opponent->ToPlayer()))
  7859. + if (doCast(opponent, EXORCISM/*, true)*/))//possible instacast here
  7860. + Exorcism_Timer = 15000;
  7861. +
  7862. + Wrath(diff);
  7863. +
  7864. + if (DIVINE_STORM && DS_Timer <= diff && GC_Timer <= diff && me->GetExactDist(opponent) < 7)
  7865. + if (doCast(opponent, DIVINE_STORM))
  7866. + DS_Timer = 10000 - me->getLevel()/4 * 100; //10 - 2 sec
  7867. + }
  7868. +
  7869. + void ApplyClassDamageMultiplierMelee(int32& damage, SpellNonMeleeDamage& /*damageinfo*/, SpellInfo const* spellInfo, WeaponAttackType /*attackType*/, bool& crit) const
  7870. + {
  7871. + uint32 spellId = spellInfo->Id;
  7872. + uint8 lvl = me->getLevel();
  7873. + float fdamage = float(damage);
  7874. + //1) apply additional crit chance. This additional chance roll will replace original (balance safe)
  7875. + if (!crit)
  7876. + {
  7877. + float aftercrit = 0.f;
  7878. + //Fanaticism: 18% additional critical chance for all Judgements (not shure which check is right)
  7879. + if (lvl >= 45 && (spellInfo->Category == SPELLCATEGORY_JUDGEMENT || spellInfo->GetSpellSpecific() == SPELL_SPECIFIC_JUDGEMENT))
  7880. + aftercrit += 18.f;
  7881. +
  7882. + if (aftercrit > 0.f)
  7883. + crit = roll_chance_f(aftercrit);
  7884. + }
  7885. +
  7886. + //2) apply bonus damage mods
  7887. + float pctbonus = 0.0f;
  7888. + //if (crit)
  7889. + //{
  7890. + //}
  7891. + //Sanctity of Battle: 15% bonus damage for Exorcism and Crusader Strike
  7892. + if (lvl >= 25 && spellId == EXORCISM)
  7893. + pctbonus += 0.15f;
  7894. + //The Art of War (damage part): 10% bonus damage for Judgements, Crusader Strike and Divine Storm
  7895. + if (lvl >= 40 &&
  7896. + (spellInfo->Category == SPELLCATEGORY_JUDGEMENT ||
  7897. + spellInfo->GetSpellSpecific() == SPELL_SPECIFIC_JUDGEMENT ||
  7898. + spellId == CRUSADER_STRIKE ||
  7899. + spellId == DIVINE_STORM))
  7900. + pctbonus += 0.1f;
  7901. +
  7902. + damage = int32(fdamage * (1.0f + pctbonus));
  7903. + }
  7904. +
  7905. + void ApplyClassDamageMultiplierSpell(int32& damage, SpellNonMeleeDamage& /*damageinfo*/, SpellInfo const* spellInfo, WeaponAttackType /*attackType*/, bool& crit) const
  7906. + {
  7907. + uint32 spellId = spellInfo->Id;
  7908. + uint8 lvl = me->getLevel();
  7909. + float fdamage = float(damage);
  7910. + //1) apply additional crit chance. This additional chance roll will replace original (balance safe)
  7911. + if (!crit)
  7912. + {
  7913. + float aftercrit = 0.f;
  7914. + //Sanctified Wrath: 50% additional critical chance for Hammer of Wrath
  7915. + if (lvl >= 45 && spellId == HOW)
  7916. + aftercrit += 50.f;
  7917. +
  7918. + if (aftercrit > 0.f)
  7919. + crit = roll_chance_f(aftercrit);
  7920. + }
  7921. +
  7922. + //2) apply bonus damage mods
  7923. + float pctbonus = 0.0f;
  7924. + //if (crit)
  7925. + //{
  7926. + //}
  7927. +
  7928. + damage = int32(fdamage * (1.0f + pctbonus));
  7929. + }
  7930. +
  7931. + void SpellHit(Unit* caster, SpellInfo const* spell)
  7932. + {
  7933. + OnSpellHit(caster, spell);
  7934. + }
  7935. +
  7936. + void DamageTaken(Unit* u, uint32& /*damage*/)
  7937. + {
  7938. + OnOwnerDamagedBy(u);
  7939. + }
  7940. +
  7941. + void OwnerAttackedBy(Unit* u)
  7942. + {
  7943. + OnOwnerDamagedBy(u);
  7944. + }
  7945. +
  7946. + void Reset()
  7947. + {
  7948. + Crusader_cd = 0;
  7949. + Consecration_cd = 0;
  7950. + LOH_Timer = 0;
  7951. + HOJ_Timer = 0;
  7952. + HOF_Timer = 0;
  7953. + JOL_Timer = 0;
  7954. + HS_Timer = 0;
  7955. + BOP_Timer = 0;
  7956. + HOW_Timer = 0;
  7957. + DS_Timer = 0;
  7958. + AW_Timer = 10000;
  7959. + HOS_Timer = 0;
  7960. + SSH_Timer = 0;
  7961. + Hand_Of_Reckoning_Timer = 0;
  7962. + Divine_Plea_Timer = 0;
  7963. + Repentance_Timer = 0;
  7964. + Exorcism_Timer = 0;
  7965. + Holy_Wrath_Timer = 0;
  7966. + Turn_Evil_Timer = 0;
  7967. +
  7968. + HOFGuid = 0;
  7969. +
  7970. + if (master)
  7971. + {
  7972. + setStats(CLASS_PALADIN, me->getRace(), master->getLevel(), true);
  7973. + ApplyClassPassives();
  7974. + ApplyPassives(CLASS_PALADIN);
  7975. + }
  7976. + }
  7977. +
  7978. + void ReduceCD(uint32 diff)
  7979. + {
  7980. + CommonTimers(diff);
  7981. + if (HOW_Timer > diff) HOW_Timer -= diff;
  7982. + if (DS_Timer > diff) DS_Timer -= diff;
  7983. + if (AW_Timer > diff) AW_Timer -= diff;
  7984. + if (HOS_Timer > diff) HOS_Timer -= diff;
  7985. + if (HS_Timer > diff) HS_Timer -= diff;
  7986. + if (BOP_Timer > diff) BOP_Timer -= diff;
  7987. + if (Consecration_cd > diff) Consecration_cd -= diff;
  7988. + if (Crusader_cd > diff) Crusader_cd -= diff;
  7989. + if (LOH_Timer > diff) LOH_Timer -= diff;
  7990. + if (HOJ_Timer > diff) HOJ_Timer -= diff;
  7991. + if (HOF_Timer > diff) HOF_Timer -= diff;
  7992. + if (JOL_Timer > diff) JOL_Timer -= diff;
  7993. + if (SSH_Timer > diff) SSH_Timer -= diff;
  7994. + if (Hand_Of_Reckoning_Timer > diff) Hand_Of_Reckoning_Timer -= diff;
  7995. + if (Divine_Plea_Timer > diff) Divine_Plea_Timer -= diff;
  7996. + if (Repentance_Timer > diff) Repentance_Timer -= diff;
  7997. + if (Exorcism_Timer > diff) Exorcism_Timer -= diff;
  7998. + if (Holy_Wrath_Timer > diff) Holy_Wrath_Timer -= diff;
  7999. + if (Turn_Evil_Timer > diff) Turn_Evil_Timer -= diff;
  8000. + }
  8001. +
  8002. + bool CanRespawn()
  8003. + {return false;}
  8004. +
  8005. + void InitSpells()
  8006. + {
  8007. + uint8 lvl = me->getLevel();
  8008. + FLASH_OF_LIGHT = InitSpell(me, FLASH_OF_LIGHT_1);
  8009. + HOLY_LIGHT = InitSpell(me, HOLY_LIGHT_1);
  8010. + LAY_ON_HANDS = InitSpell(me, LAY_ON_HANDS_1);
  8011. + SACRED_SHIELD = InitSpell(me, SACRED_SHIELD_1);
  8012. + HOLY_SHOCK = lvl >= 40 ? InitSpell(me, HOLY_SHOCK_1) : 0;
  8013. + CLEANSE = InitSpell(me, CLEANSE_1);
  8014. + REDEMPTION = InitSpell(me, REDEMPTION_1);
  8015. + HAMMER_OF_JUSTICE = InitSpell(me, HAMMER_OF_JUSTICE_1);
  8016. + REPENTANCE = lvl >= 45 ? REPENTANCE_1 : 0;
  8017. + TURN_EVIL = InitSpell(me, TURN_EVIL_1);
  8018. + HOLY_WRATH = InitSpell(me, HOLY_WRATH_1);
  8019. + EXORCISM = InitSpell(me, EXORCISM_1);
  8020. + SEAL_OF_COMMAND = lvl >= 25 ? SEAL_OF_COMMAND_1 : 0;
  8021. + CRUSADER_STRIKE = lvl >= 20 ? CRUSADER_STRIKE_1 : 0;//exception
  8022. + JUDGEMENT_OF_LIGHT = InitSpell(me, JUDGEMENT_OF_LIGHT_1);
  8023. + CONSECRATION = InitSpell(me, CONSECRATION_1);
  8024. + DIVINE_STORM = lvl >= 60 ? DIVINE_STORM_1 : 0;
  8025. + HOW /*Hammer of Wrath*/ = InitSpell(me, HOW_1);
  8026. + AVENGING_WRATH = InitSpell(me, AVENGING_WRATH_1);
  8027. + BLESSING_OF_MIGHT = InitSpell(me, BLESSING_OF_MIGHT_1);
  8028. + BLESSING_OF_WISDOM = InitSpell(me, BLESSING_OF_WISDOM_1);
  8029. + BLESSING_OF_KINGS = InitSpell(me, BLESSING_OF_KINGS_1);
  8030. + BLESSING_OF_SANCTUARY = lvl >= 30 ? BLESSING_OF_SANCTUARY_1 : 0;
  8031. + DEVOTION_AURA = InitSpell(me, DEVOTION_AURA_1);
  8032. + CONCENTRATION_AURA = InitSpell(me, CONCENTRATION_AURA_1);
  8033. + DIVINE_PLEA = InitSpell(me, DIVINE_PLEA_1);
  8034. + HAND_OF_PROTECTION = InitSpell(me, HAND_OF_PROTECTION_1);
  8035. + HOF/*Hand of Freedom*/ = InitSpell(me, HOF_1);
  8036. + HOS /*Hand of salvation*/ = InitSpell(me, HOS_1);
  8037. + HANDOFRECKONING = InitSpell(me, HANDOFRECKONING_1);
  8038. + }
  8039. +
  8040. + void ApplyClassPassives()
  8041. + {
  8042. + uint8 level = master->getLevel();
  8043. + //1 - SPD 3% crit 3%
  8044. + if (level >= 78)
  8045. + RefreshAura(SPELLDMG,5); //+15%
  8046. + else if (level >= 75)
  8047. + RefreshAura(SPELLDMG,4); //+12%
  8048. + else if (level >= 55)
  8049. + RefreshAura(SPELLDMG,3); //+9%
  8050. + else if (level >= 35)
  8051. + RefreshAura(SPELLDMG,2); //+6%
  8052. + else if (level >= 15)
  8053. + RefreshAura(SPELLDMG); //+3%
  8054. + //2 - SPD 6%
  8055. + if (level >= 55)
  8056. + RefreshAura(SPELLDMG2,3); //+18%
  8057. + else if (level >= 35)
  8058. + RefreshAura(SPELLDMG2,2); //+12%
  8059. + else if (level >= 15)
  8060. + RefreshAura(SPELLDMG2); //+6%
  8061. + //Talents
  8062. + if (level >= 55)
  8063. + RefreshAura(PURE);
  8064. + if (level >= 35)
  8065. + RefreshAura(WISE);
  8066. + if (level >= 50)
  8067. + RefreshAura(RECKONING5); //10%
  8068. + else if (level >= 45)
  8069. + RefreshAura(RECKONING4); //8%
  8070. + else if (level >= 40)
  8071. + RefreshAura(RECKONING3); //6%
  8072. + else if (level >= 35)
  8073. + RefreshAura(RECKONING2); //4%
  8074. + else if (level >= 30)
  8075. + RefreshAura(RECKONING1); //2%
  8076. + //if (level >= 50)
  8077. + // RefreshAura(RIGHTEOUS_VENGEANCE3);
  8078. + //else if (level >= 47)
  8079. + // RefreshAura(RIGHTEOUS_VENGEANCE2);
  8080. + //else if (level >= 45)
  8081. + // RefreshAura(RIGHTEOUS_VENGEANCE1);
  8082. + if (level >= 30)
  8083. + RefreshAura(VENGEANCE3);
  8084. + else if (level >= 27)
  8085. + RefreshAura(VENGEANCE2);
  8086. + else if (level >= 25)
  8087. + RefreshAura(VENGEANCE1);
  8088. + if (level >= 60)
  8089. + RefreshAura(SHOFL3);
  8090. + else if (level >= 55)
  8091. + RefreshAura(SHOFL2);
  8092. + else if (level >= 50)
  8093. + RefreshAura(SHOFL1);
  8094. + if (level >= 45)
  8095. + RefreshAura(SACRED_CLEANSING);
  8096. + if (level >= 35)
  8097. + RefreshAura(DIVINE_PURPOSE);
  8098. + if (level >= 25)
  8099. + RefreshAura(VINDICATION2);
  8100. + else if (level >= 20)
  8101. + RefreshAura(VINDICATION1);
  8102. + if (level >= 30)
  8103. + RefreshAura(LAYHANDS);
  8104. + if (level >= 20)
  8105. + RefreshAura(FANATICISM,2); //-60% aggro
  8106. + if (level >= 15)
  8107. + RefreshAura(GLYPH_HOLY_LIGHT); //10% heal
  8108. + //if (level >= 70)
  8109. + // RefreshAura(PALADIN_T9_2P_BONUS); //Righteous Vengeance Crits
  8110. + }
  8111. +
  8112. + private:
  8113. + uint32
  8114. + /*Heals*/FLASH_OF_LIGHT, HOLY_LIGHT, HOLY_SHOCK, LAY_ON_HANDS, SACRED_SHIELD,
  8115. + /*CC*/HAMMER_OF_JUSTICE, REPENTANCE, TURN_EVIL,
  8116. + /*Damage*/SEAL_OF_COMMAND, HOLY_WRATH, EXORCISM, CRUSADER_STRIKE, JUDGEMENT_OF_LIGHT,
  8117. + /*Damage*/CONSECRATION, DIVINE_STORM, AVENGING_WRATH, HOW,//hammer of wrath
  8118. +/*Blessing*/BLESSING_OF_MIGHT, BLESSING_OF_WISDOM, BLESSING_OF_KINGS, BLESSING_OF_SANCTUARY,
  8119. + /*Auras*/DEVOTION_AURA, CONCENTRATION_AURA,
  8120. + /*Hands*/HAND_OF_PROTECTION, HOF, HOS, HANDOFRECKONING,
  8121. + /*Misc*/CLEANSE, REDEMPTION, DIVINE_PLEA;
  8122. + //Timers
  8123. + uint32 Crusader_cd, Consecration_cd, Exorcism_Timer, Holy_Wrath_Timer, JOL_Timer, HOF_Timer,
  8124. + HS_Timer, HOW_Timer, DS_Timer, HOS_Timer, SSH_Timer, Hand_Of_Reckoning_Timer, Turn_Evil_Timer,
  8125. + LOH_Timer, HOJ_Timer, BOP_Timer, AW_Timer, Divine_Plea_Timer, Repentance_Timer;
  8126. + uint64 HOFGuid;
  8127. +
  8128. + enum PaladinBaseSpells// all orignals
  8129. + {
  8130. + FLASH_OF_LIGHT_1 = 19750,
  8131. + HOLY_LIGHT_1 = 635,
  8132. + LAY_ON_HANDS_1 = 633,
  8133. + REDEMPTION_1 = 7328,
  8134. + HOF_1 /*Hand of Freedom*/ = 1044,
  8135. + SACRED_SHIELD_1 = 53601,
  8136. + HOLY_SHOCK_1 = 20473,
  8137. + CLEANSE_1 = 4987,
  8138. + HAND_OF_PROTECTION_1 = 1022,
  8139. + HOS_1 /*Hand of salvation*/ = 1038,
  8140. + SEAL_OF_COMMAND_1 = 20375,
  8141. + HANDOFRECKONING_1 = 62124,
  8142. + DIVINE_PLEA_1 = 54428,
  8143. + REPENTANCE_1 = 20066,
  8144. + TURN_EVIL_1 = 10326,
  8145. + CRUSADER_STRIKE_1 = 35395,
  8146. + JUDGEMENT_OF_LIGHT_1 = 20271,
  8147. + CONSECRATION_1 = 26573,
  8148. + HAMMER_OF_JUSTICE_1 = 853,
  8149. + DIVINE_STORM_1 = 53385,
  8150. + HOW_1 /*Hammer of Wrath*/ = 24275,
  8151. + EXORCISM_1 = 879,
  8152. + HOLY_WRATH_1 = 2812,
  8153. + AVENGING_WRATH_1 = 31884,
  8154. + BLESSING_OF_MIGHT_1 = 19740,
  8155. + BLESSING_OF_WISDOM_1 = 19742,
  8156. + BLESSING_OF_KINGS_1 = 20217,
  8157. + BLESSING_OF_SANCTUARY_1 = 20911,
  8158. + DEVOTION_AURA_1 = 465,
  8159. + CONCENTRATION_AURA_1 = 19746,
  8160. + };
  8161. + enum PaladinPassives
  8162. + {
  8163. + //Talents
  8164. + DIVINE_PURPOSE = 31872,
  8165. + PURE/*Judgements of the Pure*/ = 54155,
  8166. + WISE/*Judgements of the Wise*/ = 31878,
  8167. + SACRED_CLEANSING = 53553,//rank 3
  8168. + RECKONING1 = 20177,
  8169. + RECKONING2 = 20179,
  8170. + RECKONING3 = 20181,
  8171. + RECKONING4 = 20180,
  8172. + RECKONING5 = 20182,
  8173. + VINDICATION1 = 9452 ,//rank 1
  8174. + VINDICATION2 = 26016,//rank 2
  8175. + LAYHANDS /*Improved LOH rank 2*/ = 20235,
  8176. + FANATICISM = 31881,//rank 3
  8177. + //RIGHTEOUS_VENGEANCE1 = 53380,//rank 1
  8178. + //RIGHTEOUS_VENGEANCE2 = 53381,//rank 2
  8179. + //RIGHTEOUS_VENGEANCE3 = 53382,//rank 3
  8180. + VENGEANCE1 = 20049,//rank 1
  8181. + VENGEANCE2 = 20056,//rank 2
  8182. + VENGEANCE3 = 20057,//rank 3
  8183. + SHOFL1 /*Sheath of Light*/ = 53501,//rank 1
  8184. + SHOFL2 = 53502,//rank 2
  8185. + SHOFL3 = 53503,//rank 3
  8186. + //Glyphs
  8187. + GLYPH_HOLY_LIGHT = 54937,
  8188. + //other
  8189. + SPELLDMG/*Arcane Instability-mage*/ = 15060,//rank3 3% dam/crit
  8190. + SPELLDMG2/*Earth and Moon - druid*/ = 48511,//rank3 6% dam
  8191. + //PALADIN_T9_2P_BONUS = 67188,//Righteous Vengeance Crits
  8192. + };
  8193. +
  8194. + enum PaladinSpecial
  8195. + {
  8196. + NOAURA,
  8197. + DEVOTIONAURA,
  8198. + CONCENTRATIONAURA,
  8199. + };
  8200. + };
  8201. +};
  8202. +
  8203. +void AddSC_paladin_bot()
  8204. +{
  8205. + new paladin_bot();
  8206. +}
  8207. diff --git a/src/server/game/AI/NpcBots/bot_priest_ai.cpp b/src/server/game/AI/NpcBots/bot_priest_ai.cpp
  8208. new file mode 100644
  8209. index 0000000..f47aa65
  8210. --- /dev/null
  8211. +++ b/src/server/game/AI/NpcBots/bot_priest_ai.cpp
  8212. @@ -0,0 +1,859 @@
  8213. +#include "bot_ai.h"
  8214. +#include "Group.h"
  8215. +#include "Player.h"
  8216. +#include "ScriptMgr.h"
  8217. +//#include "SpellAuras.h"
  8218. +#include "WorldSession.h"
  8219. +/*
  8220. +Priest NpcBot (reworked by Graff [email protected])
  8221. +Complete - Around 50%
  8222. +TODO: maybe remove Divine Spirit or so, too much buffs
  8223. +*/
  8224. +class priest_bot : public CreatureScript
  8225. +{
  8226. +public:
  8227. + priest_bot() : CreatureScript("priest_bot") { }
  8228. +
  8229. + CreatureAI* GetAI(Creature* creature) const
  8230. + {
  8231. + return new priest_botAI(creature);
  8232. + }
  8233. +
  8234. + bool OnGossipHello(Player* player, Creature* creature)
  8235. + {
  8236. + return bot_minion_ai::OnGossipHello(player, creature);
  8237. + }
  8238. +
  8239. + bool OnGossipSelect(Player* player, Creature* creature, uint32 sender, uint32 action)
  8240. + {
  8241. + if (bot_minion_ai* ai = creature->GetBotMinionAI())
  8242. + return ai->OnGossipSelect(player, creature, sender, action);
  8243. + return true;
  8244. + }
  8245. +
  8246. + struct priest_botAI : public bot_minion_ai
  8247. + {
  8248. + priest_botAI(Creature* creature) : bot_minion_ai(creature) { }
  8249. +
  8250. + bool doCast(Unit* victim, uint32 spellId, bool triggered = false)
  8251. + {
  8252. + if (checkBotCast(victim, spellId, CLASS_PRIEST) != SPELL_CAST_OK)
  8253. + return false;
  8254. + return bot_ai::doCast(victim, spellId, triggered);
  8255. + }
  8256. +
  8257. + bool MassGroupHeal(Player* player, uint32 diff)
  8258. + {
  8259. + if (!PRAYER_OF_HEALING && !DIVINE_HYMN) return false;
  8260. + if (!player->GetGroup()) return false;
  8261. + if (Rand() > 30) return false;
  8262. + if (IsCasting()) return false;
  8263. +
  8264. + if (DIVINE_HYMN && Divine_Hymn_Timer <= diff)
  8265. + {
  8266. + Group* gr = player->GetGroup();
  8267. + uint8 LHPcount = 0;
  8268. + for (GroupReference* itr = gr->GetFirstMember(); itr != NULL; itr = itr->next())
  8269. + {
  8270. + Player* tPlayer = itr->getSource();
  8271. + if (!tPlayer || me->GetMap() != tPlayer->FindMap() ||
  8272. + !tPlayer->IsInWorld() || tPlayer->IsBeingTeleported() ||
  8273. + tPlayer->isPossessed() || tPlayer->isCharmed()) continue;
  8274. + if (tPlayer->isAlive())
  8275. + {
  8276. + if (me->GetExactDist(tPlayer) > 35) continue;
  8277. + uint8 pct = 50 + tPlayer->getAttackers().size()*10;
  8278. + pct = pct < 80 ? pct : 80;
  8279. + if (GetHealthPCT(tPlayer) < pct && GetLostHP(tPlayer) > 4000)
  8280. + ++LHPcount;
  8281. + }
  8282. + if (LHPcount > 1)
  8283. + break;
  8284. + if (!tPlayer->HaveBot()) continue;
  8285. + for (uint8 i = 0; i != tPlayer->GetMaxNpcBots(); ++i)
  8286. + {
  8287. + Creature* bot = tPlayer->GetBotMap(i)->_Cre();
  8288. + if (bot && GetHealthPCT(bot) < 40 && me->GetExactDist(bot) < 30)
  8289. + ++LHPcount;
  8290. + if (LHPcount > 1)
  8291. + break;
  8292. + }
  8293. + }
  8294. + if (LHPcount > 1 && doCast(me, DIVINE_HYMN))
  8295. + {
  8296. + Divine_Hymn_Timer = 180000; //3 min
  8297. + return true;
  8298. + }
  8299. + }
  8300. + if (PRAYER_OF_HEALING)
  8301. + {
  8302. + Group* gr = player->GetGroup();
  8303. + Unit* castTarget = NULL;
  8304. + uint8 LHPcount = 0;
  8305. + for (GroupReference* itr = gr->GetFirstMember(); itr != NULL; itr = itr->next())
  8306. + {
  8307. + uint8 lowestPCT = 100;
  8308. + Player* tPlayer = itr->getSource();
  8309. + if (!tPlayer || me->GetMap() != tPlayer->GetMap() ||
  8310. + !tPlayer->IsInWorld() || tPlayer->IsBeingTeleported() ||
  8311. + tPlayer->isPossessed() || tPlayer->isCharmed()) continue;
  8312. + if (tPlayer->isAlive())
  8313. + {
  8314. + if (me->GetExactDist(tPlayer) > 25) continue;
  8315. + if (GetHealthPCT(tPlayer) < 85)
  8316. + {
  8317. + ++LHPcount;
  8318. + if (GetHealthPCT(tPlayer) < lowestPCT)
  8319. + lowestPCT = GetHealthPCT(tPlayer);
  8320. + castTarget = tPlayer;
  8321. + }
  8322. + }
  8323. + if (LHPcount > 2)
  8324. + break;
  8325. + if (!tPlayer->HaveBot()) continue;
  8326. + for (uint8 i = 0; i != tPlayer->GetMaxNpcBots(); ++i)
  8327. + {
  8328. + Creature* bot = tPlayer->GetBotMap(i)->_Cre();
  8329. + if (bot && GetHealthPCT(bot) < 70 && me->GetExactDist(bot) < 15)
  8330. + {
  8331. + ++LHPcount;
  8332. + if (GetHealthPCT(bot) < lowestPCT)
  8333. + lowestPCT = GetHealthPCT(bot);
  8334. + castTarget = bot;
  8335. + }
  8336. + if (LHPcount > 2)
  8337. + break;
  8338. + }
  8339. + }
  8340. + if (LHPcount > 2 && castTarget && doCast(castTarget, PRAYER_OF_HEALING))
  8341. + return true;
  8342. + }
  8343. + return false;
  8344. + }//end MassGroupHeal
  8345. +
  8346. + bool ShieldTarget(Unit* target, uint32 diff)
  8347. + {
  8348. + if (PWS_Timer > diff || Rand() > 50 || IsCasting()) return false;
  8349. + if (target->HasAura(WEAKENED_SOUL)) return false;
  8350. + if (HasAuraName(target, PW_SHIELD)) return false;
  8351. + //if (me->GetExactDist(target) > 40) return false;//checked already in HealTarget()
  8352. +
  8353. + if (!target->getAttackers().empty() || GetHealthPCT(target) < 33 || target->HasAuraType(SPELL_AURA_PERIODIC_DAMAGE))
  8354. + {
  8355. + if (doCast(target, PW_SHIELD))
  8356. + {
  8357. + if (me->getLevel() >= 30 || // improved
  8358. + (target->ToCreature() && target->ToCreature()->GetIAmABot()))
  8359. + PWS_Timer = 0;
  8360. + else
  8361. + PWS_Timer = 4000;
  8362. + GC_Timer = 800;
  8363. + return true;
  8364. + }
  8365. + }
  8366. + return false;
  8367. + }
  8368. +
  8369. + void StartAttack(Unit* u, bool force = false)
  8370. + {
  8371. + if (GetBotCommandState() == COMMAND_ATTACK && !force) return;
  8372. + Aggro(u);
  8373. + GetInPosition(force, true);
  8374. + SetBotCommandState(COMMAND_ATTACK);
  8375. + }
  8376. +
  8377. + void EnterCombat(Unit*) { }
  8378. + void Aggro(Unit*) { }
  8379. + void AttackStart(Unit*) { }
  8380. + void KilledUnit(Unit*) { }
  8381. + void EnterEvadeMode() { }
  8382. + void MoveInLineOfSight(Unit*) { }
  8383. + void JustDied(Unit*) { master->SetNpcBotDied(me->GetGUID()); }
  8384. +
  8385. + void UpdateAI(uint32 diff)
  8386. + {
  8387. + ReduceCD(diff);
  8388. + if (IAmDead()) return;
  8389. + if (!me->getVictim())
  8390. + Evade();
  8391. + if (wait == 0)
  8392. + wait = GetWait();
  8393. + else
  8394. + return;
  8395. + CheckAuras();
  8396. + Disperse(diff);
  8397. + BreakCC(diff);
  8398. + if (CCed(me)) return;
  8399. + DoDevCheck(diff);
  8400. +
  8401. + if (GetManaPCT(me) < 33 && Potion_cd <= diff)
  8402. + {
  8403. + temptimer = GC_Timer;
  8404. + if (doCast(me, MANAPOTION))
  8405. + Potion_cd = POTION_CD;
  8406. + GC_Timer = temptimer;
  8407. + }
  8408. + //check possible fear
  8409. + doDefend(diff);
  8410. + //buff and heal master's group
  8411. + MassGroupHeal(master, diff);
  8412. + BuffAndHealGroup(master, diff);
  8413. + CureGroup(master, DISPELMAGIC, diff);
  8414. + CureGroup(master, CURE_DISEASE, diff);
  8415. + //ShieldGroup(master);
  8416. + if (master->isInCombat() || me->isInCombat())
  8417. + {
  8418. + CheckDispel(diff);
  8419. + CheckSilence(diff);
  8420. + }
  8421. + if (me->isInCombat())
  8422. + CheckShackles(diff);
  8423. +
  8424. + if (!me->isInCombat())
  8425. + DoNonCombatActions(diff);
  8426. +
  8427. + if (!CheckAttackTarget(CLASS_PRIEST))
  8428. + return;
  8429. +
  8430. + AttackerSet m_attackers = master->getAttackers();
  8431. + AttackerSet b_attackers = me->getAttackers();
  8432. +
  8433. + if (GetHealthPCT(master) > 90 && GetManaPCT(me) > 35 && GetHealthPCT(me) > 90 &&
  8434. + (m_attackers.size() < 4 || b_attackers.size() + m_attackers.size() < 3) &&
  8435. + !IsCasting())
  8436. + //general rule
  8437. + {
  8438. + opponent = me->getVictim();
  8439. + if (opponent)
  8440. + {
  8441. + if (!IsCasting())
  8442. + StartAttack(opponent);
  8443. + }
  8444. + else
  8445. + return;
  8446. + bool isBoss = opponent->GetTypeId() == TYPEID_UNIT ? opponent->ToCreature()->isWorldBoss() : false;
  8447. + if (me->GetExactDist(opponent) < 30)
  8448. + {
  8449. + if (SW_DEATH && Rand() < 50 && SW_Death_Timer <= diff &&
  8450. + (GetHealthPCT(opponent) < 15 || opponent->GetHealth() < me->GetMaxHealth()/6) &&
  8451. + doCast(opponent, SW_DEATH))
  8452. + {
  8453. + SW_Death_Timer = 10000;
  8454. + return;
  8455. + }
  8456. + if (Rand() < 30 && GC_Timer <= diff && !opponent->HasAura(SW_PAIN, me->GetGUID()) &&
  8457. + opponent->GetHealth() > me->GetMaxHealth()/4 &&
  8458. + doCast(opponent, SW_PAIN))
  8459. + return;
  8460. + if (VAMPIRIC_TOUCH && GC_Timer <= diff && !isBoss && Rand() < 50 && !opponent->HasAura(VAMPIRIC_TOUCH, me->GetGUID()) &&
  8461. + opponent->GetHealth() > me->GetMaxHealth()/4 &&
  8462. + doCast(opponent, VAMPIRIC_TOUCH))
  8463. + return;
  8464. + if (DEVOURING_PLAGUE && GC_Timer <= diff && !isBoss && Rand() < 30 && !Devcheck && !opponent->HasAura(DEVOURING_PLAGUE, me->GetGUID()) &&
  8465. + opponent->GetHealth() > me->GetMaxHealth()/3 &&
  8466. + doCast(opponent, DEVOURING_PLAGUE))
  8467. + return;
  8468. + if (Mind_Blast_Timer <= diff && GC_Timer <= 300 && !isBoss && Rand() < 50 && (!VAMPIRIC_TOUCH || HasAuraName(opponent, VAMPIRIC_TOUCH)) &&
  8469. + doCast(opponent, MIND_BLAST))
  8470. + {
  8471. + Mind_Blast_Timer = 7500 - me->getLevel()/4*100;//5.5 sec on 80 lvl (as improved)
  8472. + return;
  8473. + }
  8474. + if (MIND_FLAY && Mind_Flay_Timer <= diff && GC_Timer <= 300 && !isBoss && !me->isMoving() && Rand() < 40 && me->GetExactDist(opponent) < 30 &&
  8475. +
  8476. + (opponent->isMoving() || opponent->GetHealth() < me->GetMaxHealth()/3 ||
  8477. + (opponent->HasAura(SW_PAIN, me->GetGUID()) &&
  8478. + opponent->HasAura(DEVOURING_PLAGUE, me->GetGUID()))) &&
  8479. + doCast(opponent, MIND_FLAY))
  8480. + {
  8481. + Mind_Flay_Timer = 3000;
  8482. + return;
  8483. + }
  8484. + if (MIND_SEAR && GC_Timer <= diff && !me->isMoving() && !opponent->isMoving() && Rand() < 50 &&
  8485. + opponent->HasAura(SW_PAIN, me->GetGUID()) &&
  8486. + opponent->HasAura(DEVOURING_PLAGUE, me->GetGUID()))
  8487. + if (Unit* u = FindSplashTarget(30, opponent))
  8488. + if (doCast(u, MIND_SEAR))
  8489. + return;
  8490. + }//endif opponent
  8491. + }//endif damage
  8492. + //check horror after dots/damage
  8493. + if (PSYCHIC_HORROR && Psychic_Horror_Timer <= diff && Rand() < 30 &&
  8494. + opponent->GetCreatureType() != CREATURE_TYPE_UNDEAD &&
  8495. + opponent->GetHealth() > me->GetMaxHealth()/5 &&
  8496. + me->GetExactDist(opponent) < 30 && !HasAuraName(opponent, PSYCHIC_HORROR) &&
  8497. + !CCed(opponent))
  8498. + {
  8499. + if (doCast(opponent, PSYCHIC_HORROR))
  8500. + {
  8501. + Psychic_Horror_Timer = 60000;
  8502. + return;
  8503. + }
  8504. + }
  8505. + }//end UpdateAI
  8506. +
  8507. + bool HealTarget(Unit* target, uint8 hp, uint32 diff)
  8508. + {
  8509. + if (hp > 98) return false;
  8510. + if (!target || target->isDead() || me->GetExactDist(target) > 40)
  8511. + return false;
  8512. + if (Rand() > 50 + 20*target->isInCombat() + 50*master->GetMap()->IsRaid()) return false;
  8513. +
  8514. + //GUARDIAN SPIRIT
  8515. + if (GUARDIAN_SPIRIT && Guardian_Spirit_Timer <= diff && Rand() < 70 &&
  8516. + target->isInCombat() && !target->getAttackers().empty() &&
  8517. + hp < (5 + std::min(20, uint8(target->getAttackers().size())*5)) &&
  8518. + (master->GetGroup() && master->GetGroup()->IsMember(target->GetGUID()) || target == master) &&
  8519. + !target->HasAura(GUARDIAN_SPIRIT))
  8520. + {
  8521. + temptimer = GC_Timer;
  8522. + if (me->IsNonMeleeSpellCasted(true))
  8523. + me->InterruptNonMeleeSpells(true);
  8524. + if (doCast(target, GUARDIAN_SPIRIT))
  8525. + {
  8526. + if (target->GetTypeId() == TYPEID_PLAYER)
  8527. + {
  8528. + if (target->HasAura(GUARDIAN_SPIRIT, me->GetGUID()))
  8529. + me->MonsterWhisper("Guardin Spirit on you!", target->GetGUID());
  8530. + Guardian_Spirit_Timer = 90000;//1.5 min
  8531. + }
  8532. + else
  8533. + Guardian_Spirit_Timer = 30000;//30 sec for creatures
  8534. + GC_Timer = temptimer;
  8535. + return true;
  8536. + }
  8537. + }
  8538. +
  8539. + if (IsCasting()) return false;
  8540. +
  8541. + //PAIN SUPPRESSION
  8542. + if (hp < 35 && PAIN_SUPPRESSION && Pain_Suppression_Timer <= diff && Rand() < 50 &&
  8543. + (target->isInCombat() || !target->getAttackers().empty()) &&
  8544. + !target->HasAura(PAIN_SUPPRESSION))
  8545. + {
  8546. + temptimer = GC_Timer;
  8547. + if (doCast(target, PAIN_SUPPRESSION))
  8548. + {
  8549. + if (target->GetTypeId() == TYPEID_PLAYER)
  8550. + {
  8551. + if (target->HasAura(PAIN_SUPPRESSION, me->GetGUID()))
  8552. + me->MonsterWhisper("Pain Suppression on you!", target->GetGUID());
  8553. + Pain_Suppression_Timer = 45000;//45 sec
  8554. + }
  8555. + else
  8556. + Pain_Suppression_Timer = 15000;//15 sec for creatures
  8557. + GC_Timer = temptimer;
  8558. + return true;
  8559. + }
  8560. + }
  8561. +
  8562. + //Now Heals Requires GCD
  8563. + if ((hp < 80 || !target->getAttackers().empty()) &&
  8564. + PWS_Timer <= diff && ShieldTarget(target, diff))
  8565. + return true;
  8566. +
  8567. + //PENANCE/Greater Heal
  8568. + if (hp < 75 || GetLostHP(target) > 4000)
  8569. + {
  8570. + if (PENANCE && Penance_Timer <= diff &&
  8571. + !me->isMoving() && //better check then stop moving every try (furthermore it doesn't always work properly)
  8572. + (target->GetTypeId() != TYPEID_PLAYER || !(target->ToPlayer()->isCharmed() || target->ToPlayer()->isPossessed())) &&
  8573. + doCast(target, PENANCE))
  8574. + {
  8575. + Penance_Timer = 8000;
  8576. + return true;
  8577. + }
  8578. + else if (Heal_Timer <= diff && GC_Timer <= diff && hp > 50 && doCast(target, HEAL))
  8579. + {
  8580. + Heal_Timer = 2500;
  8581. + return true;
  8582. + }
  8583. + }
  8584. + //Flash Heal
  8585. + if (((hp > 75 && hp < 90) || hp < 50 || GetLostHP(target) > 1500) &&
  8586. + GC_Timer <= diff && FLASH_HEAL &&
  8587. + doCast(target, FLASH_HEAL))
  8588. + return true;
  8589. + //maintain HoTs
  8590. + Unit* u = target->getVictim();
  8591. + Creature* boss = u && u->ToCreature() && u->ToCreature()->isWorldBoss() ? u->ToCreature() : NULL;
  8592. + bool tanking = tank == target && boss;
  8593. + //Renew
  8594. + if (((hp < 98 && hp > 70) || GetLostHP(target) > 500 || tanking) &&
  8595. + !HasAuraName(target, RENEW, me->GetGUID()) &&
  8596. + GC_Timer <= diff && doCast(target, RENEW))
  8597. + {
  8598. + GC_Timer = 800;
  8599. + return true;
  8600. + }
  8601. +
  8602. + return false;
  8603. + }
  8604. +
  8605. + bool BuffTarget(Unit* target, uint32 diff)
  8606. + {
  8607. + if (GC_Timer > diff || Rand() > 60) return false;
  8608. + if (!target || target->isDead() || me->GetExactDist(target) > 30) return false;
  8609. +
  8610. + if (Fear_Ward_Timer <= diff && !HasAuraName(target, FEAR_WARD) && doCast(target, FEAR_WARD))
  8611. + {
  8612. + Fear_Ward_Timer = target->GetTypeId() == TYPEID_PLAYER ? 60000 : 30000;//30sec for bots
  8613. + GC_Timer = 800;
  8614. + return true;
  8615. + }
  8616. +
  8617. + if (target == me)
  8618. + {
  8619. + if (!me->HasAura(INNER_FIRE) && doCast(me, INNER_FIRE))
  8620. + {
  8621. + GC_Timer = 800;
  8622. + return true;
  8623. + }
  8624. + if (VAMPIRIC_EMBRACE && !me->HasAura(VAMPIRIC_EMBRACE) && doCast(me, VAMPIRIC_EMBRACE))
  8625. + {
  8626. + GC_Timer = 800;
  8627. + return true;
  8628. + }
  8629. + }
  8630. +
  8631. + if (me->isInCombat() && !master->GetMap()->IsRaid()) return false;
  8632. +
  8633. + if (Rand() < 70 && !HasAuraName(target, PW_FORTITUDE) && doCast(target, PW_FORTITUDE))
  8634. + {
  8635. + /*GC_Timer = 800;*/
  8636. + return true;
  8637. + }
  8638. + if (Rand() < 30 && !HasAuraName(target, SHADOW_PROTECTION) && doCast(target, SHADOW_PROTECTION))
  8639. + {
  8640. + /*GC_Timer = 800;*/
  8641. + return true;
  8642. + }
  8643. + if (Rand() < 30 && !HasAuraName(target, DIVINE_SPIRIT) && doCast(target, DIVINE_SPIRIT))
  8644. + {
  8645. + /*GC_Timer = 800;*/
  8646. + return true;
  8647. + }
  8648. + return false;
  8649. + }
  8650. +
  8651. + void DoNonCombatActions(uint32 diff)
  8652. + {
  8653. + if (Rand() > 50 || GC_Timer > diff || me->IsMounted()) return;
  8654. +
  8655. + RezGroup(RESURRECTION, master);
  8656. +
  8657. + if (Feasting()) return;
  8658. +
  8659. + if (BuffTarget(master, diff))
  8660. + return;
  8661. + if (BuffTarget(me, diff))
  8662. + return;
  8663. + }
  8664. +
  8665. + void CheckDispel(uint32 diff)
  8666. + {
  8667. + if (CheckDispelTimer > diff || Rand() > 25 || IsCasting())
  8668. + return;
  8669. + Unit* target = FindHostileDispelTarget();
  8670. + if (target && doCast(target, DISPELMAGIC))
  8671. + {
  8672. + CheckDispelTimer = 3000;
  8673. + GC_Timer = 500;
  8674. + }
  8675. + CheckDispelTimer = 1000;
  8676. + }
  8677. +
  8678. + void CheckShackles(uint32 diff)
  8679. + {
  8680. + if (!SHACKLE_UNDEAD || ShackleTimer > diff || GC_Timer > diff || IsCasting())
  8681. + return;
  8682. + if (FindAffectedTarget(SHACKLE_UNDEAD, me->GetGUID()))
  8683. + return;
  8684. + Unit* target = FindUndeadCCTarget(30, SHACKLE_UNDEAD);
  8685. + if (target && doCast(target, SHACKLE_UNDEAD))
  8686. + {
  8687. + ShackleTimer = 3000;
  8688. + GC_Timer = 800;
  8689. + }
  8690. + }
  8691. +
  8692. + void CheckSilence(uint32 diff)
  8693. + {
  8694. + if (IsCasting()) return;
  8695. + temptimer = GC_Timer;
  8696. + if (SILENCE && Silence_Timer <= diff)
  8697. + {
  8698. + if (Unit* target = FindCastingTarget(30))
  8699. + if (doCast(target, SILENCE))
  8700. + Silence_Timer = 30000;
  8701. + }
  8702. + else if (PSYCHIC_HORROR && Psychic_Horror_Timer <= 20000)
  8703. + {
  8704. + if (Unit* target = FindCastingTarget(30))
  8705. + if (doCast(target, PSYCHIC_HORROR))
  8706. + Psychic_Horror_Timer = 60000;
  8707. + }
  8708. + GC_Timer = temptimer;
  8709. + }
  8710. +
  8711. + void doDefend(uint32 diff)
  8712. + {
  8713. + AttackerSet m_attackers = master->getAttackers();
  8714. + AttackerSet b_attackers = me->getAttackers();
  8715. + //fear master's attackers
  8716. + if (!m_attackers.empty() && PSYCHIC_SCREAM && Fear_Timer <= diff &&
  8717. + (master != tank || GetHealthPCT(master) < 75))
  8718. + {
  8719. + uint8 tCount = 0;
  8720. + for (AttackerSet::iterator iter = m_attackers.begin(); iter != m_attackers.end(); ++iter)
  8721. + {
  8722. + if (!(*iter)) continue;
  8723. + if ((*iter)->GetCreatureType() == CREATURE_TYPE_UNDEAD) continue;
  8724. + if (me->GetExactDist((*iter)) > 7) continue;
  8725. + if (CCed(*iter) && me->GetExactDist((*iter)) > 5) continue;
  8726. + if (me->IsValidAttackTarget(*iter))
  8727. + ++tCount;
  8728. + }
  8729. + if (tCount > 1 && doCast(me, PSYCHIC_SCREAM))
  8730. + {
  8731. + Fear_Timer = 24000;//with improved 24 sec
  8732. + return;
  8733. + }
  8734. + }
  8735. +
  8736. + // Defend myself (psychic horror)
  8737. + if (!b_attackers.empty() && PSYCHIC_SCREAM && Fear_Timer <= diff)
  8738. + {
  8739. + uint8 tCount = 0;
  8740. + for (AttackerSet::iterator iter = b_attackers.begin(); iter != b_attackers.end(); ++iter)
  8741. + {
  8742. + if (!(*iter)) continue;
  8743. + if ((*iter)->GetCreatureType() == CREATURE_TYPE_UNDEAD) continue;
  8744. + if (me->GetExactDist((*iter)) > 7) continue;
  8745. + if (CCed(*iter) && me->GetExactDist((*iter)) > 5) continue;
  8746. + if (me->IsValidAttackTarget(*iter))
  8747. + ++tCount;
  8748. + }
  8749. + if (tCount > 0 && doCast(me, PSYCHIC_SCREAM))
  8750. + {
  8751. + Fear_Timer = 24000;//with improved 24 sec
  8752. + return;
  8753. + }
  8754. + }
  8755. + // Heal myself
  8756. + if (GetHealthPCT(me) < 99 && !b_attackers.empty())
  8757. + {
  8758. + if (ShieldTarget(me, diff)) return;
  8759. +
  8760. + if (FADE && Fade_Timer <= diff && me->isInCombat())
  8761. + {
  8762. + if (b_attackers.empty()) return; //no aggro
  8763. + uint8 Tattackers = 0;
  8764. + for (AttackerSet::iterator iter = b_attackers.begin(); iter != b_attackers.end(); ++iter)
  8765. + {
  8766. + if (!(*iter)) continue;
  8767. + if ((*iter)->isDead()) continue;
  8768. + if (!(*iter)->ToCreature()) continue;
  8769. + if (!(*iter)->CanHaveThreatList()) continue;
  8770. + if (me->GetExactDist((*iter)) <= 15)
  8771. + Tattackers++;
  8772. + }
  8773. + if (Tattackers > 0)
  8774. + {
  8775. + temptimer = GC_Timer;
  8776. + if (doCast(me, FADE))
  8777. + {
  8778. + for (AttackerSet::iterator iter = b_attackers.begin(); iter != b_attackers.end(); ++iter)
  8779. + if ((*iter)->getThreatManager().getThreat(me) > 0.f)
  8780. + (*iter)->getThreatManager().modifyThreatPercent(me, -50);
  8781. + Fade_Timer = 6000;
  8782. + GC_Timer = temptimer;
  8783. + return;
  8784. + }
  8785. + }
  8786. + }
  8787. + if (GetHealthPCT(me) < 90 && HealTarget(me, GetHealthPCT(me), diff))
  8788. + return;
  8789. + }
  8790. + }
  8791. +
  8792. + void DoDevCheck(uint32 diff)
  8793. + {
  8794. + if (DevcheckTimer <= diff)
  8795. + {
  8796. + Devcheck = FindAffectedTarget(DEVOURING_PLAGUE, me->GetGUID());
  8797. + DevcheckTimer = 5000;
  8798. + }
  8799. + }
  8800. +
  8801. + void Disperse(uint32 diff)
  8802. + {
  8803. + if (!DISPERSION || GC_Timer > diff || Dispersion_Timer > diff || IsCasting()) return;
  8804. + //attackers case
  8805. + if ((me->getAttackers().size() > 3 && Fade_Timer > diff && GetHealthPCT(me) < 90) ||
  8806. + (GetHealthPCT(me) < 20 && me->HasAuraType(SPELL_AURA_PERIODIC_DAMAGE)) ||
  8807. + (GetManaPCT(me) < 30) ||
  8808. + (me->getAttackers().size() > 1 && me->HasAuraWithMechanic((1<<MECHANIC_SNARE)|(1<<MECHANIC_ROOT))))
  8809. + {
  8810. + temptimer = GC_Timer;
  8811. + if (doCast(me, DISPERSION))
  8812. + Dispersion_Timer = 75000;//with glyph
  8813. + GC_Timer = temptimer;
  8814. + return;
  8815. + }
  8816. + Dispersion_Timer = 2000;//fail
  8817. + }
  8818. +
  8819. + void SpellHit(Unit* caster, SpellInfo const* spell)
  8820. + {
  8821. + OnSpellHit(caster, spell);
  8822. + }
  8823. +
  8824. + void DamageTaken(Unit* u, uint32& /*damage*/)
  8825. + {
  8826. + OnOwnerDamagedBy(u);
  8827. + }
  8828. +
  8829. + void OwnerAttackedBy(Unit* u)
  8830. + {
  8831. + OnOwnerDamagedBy(u);
  8832. + }
  8833. +
  8834. + void Reset()
  8835. + {
  8836. + Heal_Timer = 0;
  8837. + Divine_Hymn_Timer = 0;
  8838. + Pain_Suppression_Timer = 0;
  8839. + Guardian_Spirit_Timer = 0;
  8840. + PWS_Timer = 0;
  8841. + Fade_Timer = 0;
  8842. + Fear_Timer = 0;
  8843. + Mind_Blast_Timer = 0;
  8844. + SW_Death_Timer = 0;
  8845. + Fear_Ward_Timer = 0;
  8846. + Psychic_Horror_Timer = 0;
  8847. + Silence_Timer = 0;
  8848. + Dispersion_Timer = 0;
  8849. + Mind_Flay_Timer = 0;
  8850. + Penance_Timer = 0;
  8851. + CheckDispelTimer = 0;
  8852. + ShackleTimer = 0;
  8853. + DevcheckTimer = 20;
  8854. + Devcheck = false;
  8855. +
  8856. + if (master)
  8857. + {
  8858. + setStats(CLASS_PRIEST, me->getRace(), master->getLevel(), true);
  8859. + ApplyClassPassives();
  8860. + ApplyPassives(CLASS_PRIEST);
  8861. + }
  8862. + }
  8863. +
  8864. + void ReduceCD(uint32 diff)
  8865. + {
  8866. + CommonTimers(diff);
  8867. + if (Heal_Timer > diff) Heal_Timer -= diff;
  8868. + if (Fade_Timer > diff) Fade_Timer -= diff;
  8869. + if (Divine_Hymn_Timer > diff) Divine_Hymn_Timer -= diff;
  8870. + if (Pain_Suppression_Timer > diff) Pain_Suppression_Timer -= diff;
  8871. + if (Guardian_Spirit_Timer > diff) Guardian_Spirit_Timer -= diff;
  8872. + if (PWS_Timer > diff) PWS_Timer -= diff;
  8873. + if (Fear_Timer > diff) Fear_Timer -= diff;
  8874. + if (Mind_Blast_Timer > diff) Mind_Blast_Timer -= diff;
  8875. + if (SW_Death_Timer > diff) SW_Death_Timer -= diff;
  8876. + if (Fear_Ward_Timer > diff) Fear_Ward_Timer -= diff;
  8877. + if (Psychic_Horror_Timer > diff) Psychic_Horror_Timer -= diff;
  8878. + if (Silence_Timer > diff) Silence_Timer -= diff;
  8879. + if (Dispersion_Timer > diff) Dispersion_Timer -= diff;
  8880. + if (Mind_Flay_Timer > diff) Mind_Flay_Timer -= diff;
  8881. + if (Penance_Timer > diff) Penance_Timer -= diff;
  8882. + if (CheckDispelTimer > diff) CheckDispelTimer -= diff;
  8883. + if (ShackleTimer > diff) ShackleTimer -= diff;
  8884. + if (DevcheckTimer > diff) DevcheckTimer -= diff;
  8885. + }
  8886. +
  8887. + bool CanRespawn()
  8888. + {return false;}
  8889. +
  8890. + void InitSpells()
  8891. + {
  8892. + uint8 lvl = me->getLevel();
  8893. + DISPELMAGIC = lvl >= 70 ? MASS_DISPEL_1 : InitSpell(me, DISPEL_MAGIC_1);
  8894. + CURE_DISEASE = InitSpell(me, CURE_DISEASE_1);
  8895. + FEAR_WARD = InitSpell(me, FEAR_WARD_1);
  8896. + /*Talent*/PAIN_SUPPRESSION = lvl >= 50 ? PAIN_SUPPRESSION_1 : 0;
  8897. + PSYCHIC_SCREAM = InitSpell(me, PSYCHIC_SCREAM_1);
  8898. + FADE = InitSpell(me, FADE_1);
  8899. + /*Talent*/PSYCHIC_HORROR = lvl >= 50 ? PSYCHIC_HORROR_1 : 0;
  8900. + /*Talent*/SILENCE = lvl >= 30 ? SILENCE_1 : 0;
  8901. + /*Talent*/PENANCE = lvl >= 60 ? InitSpell(me, PENANCE_1) : 0;
  8902. + /*Talent*/VAMPIRIC_EMBRACE = lvl >= 30 ? VAMPIRIC_EMBRACE_1 : 0;
  8903. + /*Talent*/DISPERSION = lvl >= 60 ? DISPERSION_1 : 0;
  8904. + MIND_SEAR = InitSpell(me, MIND_SEAR_1);
  8905. + /*Talent*/GUARDIAN_SPIRIT = lvl >= 60 ? GUARDIAN_SPIRIT_1 : 0;
  8906. + SHACKLE_UNDEAD = InitSpell(me, SHACKLE_UNDEAD_1);
  8907. + HEAL = lvl >= 40 ? InitSpell(me, GREATER_HEAL_1) : lvl >= 16 ? InitSpell(me, NORMAL_HEAL_1) : InitSpell(me, LESSER_HEAL_1);
  8908. + RENEW = InitSpell(me, RENEW_1);
  8909. + FLASH_HEAL = InitSpell(me, FLASH_HEAL_1);
  8910. + PRAYER_OF_HEALING = InitSpell(me, PRAYER_OF_HEALING_1);
  8911. + DIVINE_HYMN = InitSpell(me, DIVINE_HYMN_1);
  8912. + RESURRECTION = InitSpell(me, RESURRECTION_1);
  8913. + PW_SHIELD = InitSpell(me, PW_SHIELD_1);
  8914. + INNER_FIRE = InitSpell(me, INNER_FIRE_1);
  8915. + PW_FORTITUDE = InitSpell(me, PW_FORTITUDE_1);
  8916. + SHADOW_PROTECTION = InitSpell(me, SHADOW_PROTECTION_1);
  8917. + DIVINE_SPIRIT = InitSpell(me, DIVINE_SPIRIT_1);
  8918. + SW_PAIN = InitSpell(me, SW_PAIN_1);
  8919. + MIND_BLAST = InitSpell(me, MIND_BLAST_1);
  8920. + SW_DEATH = InitSpell(me, SW_DEATH_1);
  8921. + DEVOURING_PLAGUE = InitSpell(me, DEVOURING_PLAGUE_1);
  8922. + /*Talent*/MIND_FLAY = lvl >= 20 ? InitSpell(me, MIND_FLAY_1) : 0;
  8923. + /*Talent*/VAMPIRIC_TOUCH = lvl >= 50 ? InitSpell(me, VAMPIRIC_TOUCH_1) : 0;
  8924. + }
  8925. + void ApplyClassPassives()
  8926. + {
  8927. + uint8 level = master->getLevel();
  8928. + if (level >= 65)
  8929. + RefreshAura(BORROWED_TIME); //25%haste/40%bonus
  8930. + if (level >= 55)
  8931. + RefreshAura(DIVINE_AEGIS); //30%
  8932. + if (level >= 55)
  8933. + RefreshAura(EMPOWERED_RENEW3); //15%
  8934. + else if (level >= 50)
  8935. + RefreshAura(EMPOWERED_RENEW2); //10%
  8936. + else if (level >= 45)
  8937. + RefreshAura(EMPOWERED_RENEW1); //5%
  8938. + if (level >= 45)
  8939. + RefreshAura(BODY_AND_SOUL1); //30%
  8940. + if (level >= 50)
  8941. + RefreshAura(PAINANDSUFFERING3); //100%
  8942. + else if (level >= 48)
  8943. + RefreshAura(PAINANDSUFFERING2); //66%
  8944. + else if (level >= 45)
  8945. + RefreshAura(PAINANDSUFFERING1); //33%
  8946. + if (level >= 50)
  8947. + RefreshAura(MISERY3); //3%
  8948. + else if (level >= 48)
  8949. + RefreshAura(MISERY2); //2%
  8950. + else if (level >= 45)
  8951. + RefreshAura(MISERY1); //1%
  8952. + if (level >= 45)
  8953. + RefreshAura(GRACE); //100%
  8954. + if (level >= 35)
  8955. + RefreshAura(IMP_DEV_PLAG); //30%
  8956. + if (level >= 25)
  8957. + RefreshAura(INSPIRATION3); //10%
  8958. + else if (level >= 23)
  8959. + RefreshAura(INSPIRATION2); //6%
  8960. + else if (level >= 20)
  8961. + RefreshAura(INSPIRATION1); //3%
  8962. + if (level >= 30)
  8963. + RefreshAura(SHADOW_WEAVING3); //100%
  8964. + else if (level >= 28)
  8965. + RefreshAura(SHADOW_WEAVING2); //66%
  8966. + else if (level >= 25)
  8967. + RefreshAura(SHADOW_WEAVING1); //33%
  8968. + if (level >= 15)
  8969. + {
  8970. + RefreshAura(GLYPH_SW_PAIN);
  8971. + RefreshAura(GLYPH_PW_SHIELD); //20% heal
  8972. + }
  8973. + if (level >= 40)
  8974. + RefreshAura(SHADOWFORM); //allows dots to crit, passive
  8975. + if (level >= 70)
  8976. + RefreshAura(PRIEST_T10_2P_BONUS);
  8977. + }
  8978. +
  8979. + private:
  8980. + uint32
  8981. + /*Buffs*/INNER_FIRE, PW_FORTITUDE, DIVINE_SPIRIT, SHADOW_PROTECTION,
  8982. + /*Disc*/FEAR_WARD, PAIN_SUPPRESSION, SHACKLE_UNDEAD, PW_SHIELD, DISPELMAGIC, CURE_DISEASE, PENANCE,
  8983. + /*Holy*/HEAL, FLASH_HEAL, RENEW, PRAYER_OF_HEALING, DIVINE_HYMN, GUARDIAN_SPIRIT, RESURRECTION,
  8984. + /*Shadow*/SW_PAIN, MIND_BLAST, SW_DEATH, DEVOURING_PLAGUE, MIND_FLAY, VAMPIRIC_TOUCH,
  8985. + /*Shadow*/PSYCHIC_SCREAM, FADE, PSYCHIC_HORROR, VAMPIRIC_EMBRACE, DISPERSION, MIND_SEAR, SILENCE;
  8986. + //Timers/other
  8987. +/*Disc*/uint32 Penance_Timer, PWS_Timer, Pain_Suppression_Timer, Fear_Ward_Timer;
  8988. +/*Holy*/uint32 Heal_Timer, Divine_Hymn_Timer, Guardian_Spirit_Timer;
  8989. +/*Shdw*/uint32 Fade_Timer, Fear_Timer, Mind_Blast_Timer, SW_Death_Timer, Mind_Flay_Timer,
  8990. +/*Shdw*/ Psychic_Horror_Timer, Silence_Timer, Dispersion_Timer;
  8991. +/*Misc*/uint16 CheckDispelTimer, ShackleTimer, DevcheckTimer;
  8992. +/*Misc*/bool Devcheck;
  8993. +
  8994. + enum PriestBaseSpells
  8995. + {
  8996. + DISPEL_MAGIC_1 = 527,
  8997. + MASS_DISPEL_1 = 32375,
  8998. + CURE_DISEASE_1 = 528,
  8999. + FEAR_WARD_1 = 6346,
  9000. + /*Talent*/PAIN_SUPPRESSION_1 = 33206,
  9001. + PSYCHIC_SCREAM_1 = 8122,
  9002. + FADE_1 = 586,
  9003. + /*Talent*/PSYCHIC_HORROR_1 = 64044,
  9004. + /*Talent*/SILENCE_1 = 15487,
  9005. + /*Talent*/PENANCE_1 = 47540,
  9006. + /*Talent*/VAMPIRIC_EMBRACE_1 = 15286,
  9007. + /*Talent*/DISPERSION_1 = 47585,
  9008. + MIND_SEAR_1 = 48045,
  9009. + /*Talent*/GUARDIAN_SPIRIT_1 = 47788,
  9010. + SHACKLE_UNDEAD_1 = 9484,
  9011. + LESSER_HEAL_1 = 2050,
  9012. + NORMAL_HEAL_1 = 2054,
  9013. + GREATER_HEAL_1 = 2060,
  9014. + RENEW_1 = 139,
  9015. + FLASH_HEAL_1 = 2061,
  9016. + PRAYER_OF_HEALING_1 = 596,
  9017. + DIVINE_HYMN_1 = 64843,
  9018. + RESURRECTION_1 = 2006,
  9019. + PW_SHIELD_1 = 17,
  9020. + INNER_FIRE_1 = 588,
  9021. + PW_FORTITUDE_1 = 1243,
  9022. + SHADOW_PROTECTION_1 = 976,
  9023. + DIVINE_SPIRIT_1 = 14752,
  9024. + SW_PAIN_1 = 589,
  9025. + MIND_BLAST_1 = 8092,
  9026. + SW_DEATH_1 = 32379,
  9027. + DEVOURING_PLAGUE_1 = 2944,
  9028. + /*Talent*/MIND_FLAY_1 = 15407,
  9029. + /*Talent*/VAMPIRIC_TOUCH_1 = 34914,
  9030. + };
  9031. + enum PriestPassives
  9032. + {
  9033. + SHADOWFORM /*For DOT crits*/ = 49868,
  9034. + //Talents
  9035. + IMP_DEV_PLAG = 63627,//rank 3
  9036. + MISERY1 = 33191,
  9037. + MISERY2 = 33192,
  9038. + MISERY3 = 33193,
  9039. + PAINANDSUFFERING1 = 47580,
  9040. + PAINANDSUFFERING2 = 47581,
  9041. + PAINANDSUFFERING3 = 47582,
  9042. + SHADOW_WEAVING1 = 15257,
  9043. + SHADOW_WEAVING2 = 15331,
  9044. + SHADOW_WEAVING3 = 15332,
  9045. + DIVINE_AEGIS = 47515,//rank 3
  9046. + BORROWED_TIME = 52800,//rank 5
  9047. + GRACE = 47517,//rank 2
  9048. + EMPOWERED_RENEW1 = 63534,
  9049. + EMPOWERED_RENEW2 = 63542,
  9050. + EMPOWERED_RENEW3 = 63543,
  9051. + INSPIRATION1 = 14892,
  9052. + INSPIRATION2 = 15362,
  9053. + INSPIRATION3 = 15363,
  9054. + BODY_AND_SOUL1 = 64127,
  9055. + //Glyphs
  9056. + GLYPH_SW_PAIN = 55681,
  9057. + GLYPH_PW_SHIELD = 55672,
  9058. + //other
  9059. + PRIEST_T10_2P_BONUS = 70770,//33% renew
  9060. + };
  9061. + enum PriestSpecial
  9062. + {
  9063. + WEAKENED_SOUL = 6788,
  9064. + };
  9065. + }; //end priest_bot
  9066. +};
  9067. +
  9068. +void AddSC_priest_bot()
  9069. +{
  9070. + new priest_bot();
  9071. +}
  9072. diff --git a/src/server/game/AI/NpcBots/bot_rogue_ai.cpp b/src/server/game/AI/NpcBots/bot_rogue_ai.cpp
  9073. new file mode 100644
  9074. index 0000000..daa7b10
  9075. --- /dev/null
  9076. +++ b/src/server/game/AI/NpcBots/bot_rogue_ai.cpp
  9077. @@ -0,0 +1,894 @@
  9078. +#include "bot_ai.h"
  9079. +//#include "Group.h"
  9080. +#include "Player.h"
  9081. +#include "ScriptMgr.h"
  9082. +#include "SpellAuras.h"
  9083. +/*
  9084. +Rogue NpcBot (reworked by Graff [email protected])
  9085. +Complete - 25% maybe...
  9086. +TODO:
  9087. +*/
  9088. +#define DMGMIN 1
  9089. +#define DMGMAX 2
  9090. +#define MAX_COMBO_POINTS 5
  9091. +#define EVISCERATE_MAX_RANK 12
  9092. +const uint32 EVSCRDamage[EVISCERATE_MAX_RANK+1][MAX_COMBO_POINTS+1][DMGMAX+1] =
  9093. +{
  9094. + { { 0,0,0 }, { 0,0,0 }, { 0,0,0 }, { 0,0,0 }, { 0,0,0 }, { 0,0,0 } },
  9095. + { { 0,0,0 }, { 0,6,11 }, { 0,12,16 }, { 0,17,22 }, { 0,22,28 }, { 0,28,34 } },
  9096. + { { 0,0,0 }, { 0,14,23 }, { 0,26,34 }, { 0,37,46 }, { 0,48,58 }, { 0,60,70 } },
  9097. + { { 0,0,0 }, { 0,25,49 }, { 0,45,59 }, { 0,64,79 }, { 0,83,99 }, { 0,103,119 } },
  9098. + { { 0,0,0 }, { 0,41,62 }, { 0,73,93 }, { 0,104,125 }, { 0,135,157 }, { 0,167,189 } },
  9099. + { { 0,0,0 }, { 0,60,91 }, { 0,106,136 }, { 0,151,182 }, { 0,196,228 }, { 0,242,274 } },
  9100. + { { 0,0,0 }, { 0,93,138 }, { 0,165,209 }, { 0,236,281 }, { 0,307,353 }, { 0,379,425 } },
  9101. + { { 0,0,0 }, { 0,144,213 }, { 0,255,323 }, { 0,365,434 }, { 0,475,545 }, { 0,586,656 } },
  9102. + { { 0,0,0 }, { 0,199,296 }, { 0,351,447 }, { 0,502,599 }, { 0,653,751 }, { 0,805,903 } },
  9103. + { { 0,0,0 }, { 0,224,333 }, { 0,395,503 }, { 0,565,674 }, { 0,735,845 }, { 0,906,1016 } },
  9104. + { { 0,0,0 }, { 0,245,366 }, { 0,431,551 }, { 0,616,737 }, { 0,801,923 }, { 0,987,1109 } },
  9105. + { { 0,0,0 }, { 0,405,614 }, { 0,707,915 }, { 0,1008,1217 }, { 0,1309,1519 }, { 0,1611,1821 } },
  9106. + { { 0,0,0 }, { 0,497,752 }, { 0,868,1122 }, { 0,1238,1493 }, { 0,1608,1864 }, { 0,1979,2235 } }
  9107. +};
  9108. +#define RUPTURE_MAX_RANK 9
  9109. +const uint32 RuptureDamage[RUPTURE_MAX_RANK+1][MAX_COMBO_POINTS+1] =
  9110. +{
  9111. + { 0, 0, 0, 0, 0, 0 },
  9112. + { 0, 41, 61, 86, 114, 147 },
  9113. + { 0, 61, 91, 128, 170, 219 },
  9114. + { 0, 89, 131, 182, 240, 307 },
  9115. + { 0, 129, 186, 254, 331, 419 },
  9116. + { 0, 177, 256, 350, 457, 579 },
  9117. + { 0, 273, 381, 506, 646, 803 },
  9118. + { 0, 325, 461, 620, 800, 1003 },
  9119. + { 0, 489, 686, 914, 1171, 1459 },
  9120. + { 0, 581, 816, 1088, 1395, 1739 }
  9121. +};
  9122. +
  9123. +class rogue_bot : public CreatureScript
  9124. +{
  9125. +public:
  9126. + rogue_bot() : CreatureScript("rogue_bot") { }
  9127. +
  9128. + CreatureAI* GetAI(Creature* creature) const
  9129. + {
  9130. + return new rogue_botAI(creature);
  9131. + }
  9132. +
  9133. + bool OnGossipHello(Player* player, Creature* creature)
  9134. + {
  9135. + return bot_minion_ai::OnGossipHello(player, creature);
  9136. + }
  9137. +
  9138. + bool OnGossipSelect(Player* player, Creature* creature, uint32 sender, uint32 action)
  9139. + {
  9140. + if (bot_minion_ai* ai = creature->GetBotMinionAI())
  9141. + return ai->OnGossipSelect(player, creature, sender, action);
  9142. + return true;
  9143. + }
  9144. +
  9145. + struct rogue_botAI : public bot_minion_ai
  9146. + {
  9147. + rogue_botAI(Creature* creature) : bot_minion_ai(creature)
  9148. + {
  9149. + Reset();
  9150. + }
  9151. +
  9152. + bool doCast(Unit* victim, uint32 spellId, bool triggered = false)
  9153. + {
  9154. + if (checkBotCast(victim, spellId, CLASS_ROGUE) != SPELL_CAST_OK)
  9155. + return false;
  9156. + return bot_ai::doCast(victim, spellId, triggered);
  9157. + }
  9158. +
  9159. + void StartAttack(Unit* u, bool force = false)
  9160. + {
  9161. + if (GetBotCommandState() == (COMMAND_ATTACK) && !force) return;
  9162. + Aggro(u);
  9163. + SetBotCommandState(COMMAND_ATTACK);
  9164. + GetInPosition(force, false);
  9165. + }
  9166. +
  9167. + void EnterCombat(Unit*) { }
  9168. + void Aggro(Unit*) { }
  9169. + void AttackStart(Unit*) { }
  9170. + void KilledUnit(Unit*) { }
  9171. + void EnterEvadeMode() { }
  9172. + void MoveInLineOfSight(Unit*) { }
  9173. + void JustDied(Unit*) { comboPoints = 0; tempComboPoints = 0; master->SetNpcBotDied(me->GetGUID()); }
  9174. + void DoNonCombatActions(const uint32 /*diff*/)
  9175. + {}
  9176. +
  9177. + //This method should be used to emulate energy usage reduction
  9178. + void modenergy(int32 mod, bool set = false)
  9179. + {
  9180. + //can't set enery to -x (2 cases)
  9181. + if (set && mod < 0)
  9182. + return;
  9183. + if (mod < 0 && energy < uint32(abs(mod)))
  9184. + return;
  9185. +
  9186. + if (set)
  9187. + energy = mod;
  9188. + else
  9189. + energy += mod;
  9190. +
  9191. + energy = std::min<uint32>(energy, 100);
  9192. + me->SetPower(POWER_ENERGY, energy);
  9193. + }
  9194. +
  9195. + uint32 getenergy()
  9196. + {
  9197. + energy = me->GetPower(POWER_ENERGY);
  9198. + return energy;
  9199. + }
  9200. +
  9201. + void UpdateAI(uint32 diff)
  9202. + {
  9203. + ReduceCD(diff);
  9204. + if (KidneyTarget)
  9205. + {
  9206. + //kidney shot is casted as rank 1 (1 or 2 secs) so add duration accordingly
  9207. + //i.e. kedney rank 1, points = 5, we have aura with duration 1 sec so add 4 more secs
  9208. + //tempComboPoints -= 1;
  9209. + if (tempComboPoints)
  9210. + {
  9211. + if (Unit* u = sObjectAccessor->FindUnit(KidneyTarget))
  9212. + {
  9213. + if (Aura* kidney = u->GetAura(KIDNEY_SHOT, me->GetGUID()))
  9214. + {
  9215. + uint32 dur = kidney->GetDuration() + tempComboPoints*1000;
  9216. + kidney->SetDuration(dur);
  9217. + kidney->SetMaxDuration(dur);
  9218. + }
  9219. + }
  9220. + else//spell is failed to hit: restore cp
  9221. + {
  9222. + if (comboPoints == 0)
  9223. + comboPoints = tempComboPoints;
  9224. + }
  9225. + tempComboPoints = 0;
  9226. + }
  9227. + KidneyTarget = 0;
  9228. + }
  9229. + else if (RuptureTarget)
  9230. + {
  9231. + //tempComboPoints -= 1;
  9232. + if (tempComboPoints)
  9233. + {
  9234. + if (Unit* u = sObjectAccessor->FindUnit(RuptureTarget))
  9235. + {
  9236. + if (Aura* rupture = u->GetAura(RUPTURE, me->GetGUID()))
  9237. + {
  9238. + uint32 dur = rupture->GetDuration() + tempComboPoints*2000;
  9239. + rupture->SetDuration(dur);
  9240. + rupture->SetMaxDuration(dur);
  9241. + }
  9242. + }
  9243. + else//spell is failed to hit: restore cp
  9244. + {
  9245. + if (comboPoints == 0)
  9246. + comboPoints = tempComboPoints;
  9247. + }
  9248. + tempComboPoints = 0;
  9249. + }
  9250. + RuptureTarget = 0;
  9251. + }
  9252. + else if (tempDICE)
  9253. + {
  9254. + //tempComboPoints -= 1;
  9255. + if (tempComboPoints)
  9256. + {
  9257. + if (Aura* dice = me->GetAura(SLICE_DICE))
  9258. + {
  9259. + uint32 dur = (dice->GetDuration()*3)/2 + tempComboPoints*4500;//with Improved Slice and Dice
  9260. + dice->SetDuration(dur);
  9261. + dice->SetMaxDuration(dur);
  9262. + }
  9263. + tempComboPoints = 0;
  9264. + }
  9265. + tempDICE = false;
  9266. + }
  9267. + if (IAmDead()) return;
  9268. + if (me->getVictim())
  9269. + DoMeleeAttackIfReady();
  9270. + else
  9271. + Evade();
  9272. + if (wait == 0)
  9273. + wait = GetWait();
  9274. + else
  9275. + return;
  9276. + if (checkAurasTimer == 0)
  9277. + CheckAuras();
  9278. + BreakCC(diff);
  9279. + if (CCed(me)) return;
  9280. +
  9281. + if (GetHealthPCT(me) < 33 && Potion_cd <= diff)
  9282. + {
  9283. + temptimer = GC_Timer;
  9284. + if (doCast(me, HEALINGPOTION))
  9285. + {
  9286. + Potion_cd = POTION_CD;
  9287. + GC_Timer = temptimer;
  9288. + }
  9289. + }
  9290. +
  9291. + if (!me->isInCombat())
  9292. + DoNonCombatActions(diff);
  9293. +
  9294. + if (!CheckAttackTarget(CLASS_ROGUE))
  9295. + return;
  9296. +
  9297. + Attack(diff);
  9298. + }
  9299. +
  9300. + void Attack(const uint32 diff)
  9301. + {
  9302. + opponent = me->getVictim();
  9303. + if (opponent)
  9304. + {
  9305. + if (!IsCasting())
  9306. + StartAttack(opponent, true);
  9307. + }
  9308. + else
  9309. + return;
  9310. +
  9311. + comboPoints = std::min<uint8>(comboPoints, 5);
  9312. + //AttackerSet m_attackers = master->getAttackers();
  9313. + AttackerSet b_attackers = me->getAttackers();
  9314. + float dist = me->GetExactDist(opponent);
  9315. + float meleedist = me->GetDistance(opponent);
  9316. +
  9317. + if (BLADE_FLURRY && Blade_Flurry_Timer <= diff && meleedist <= 5 &&
  9318. + Rand() < 30 && getenergy() >= 25 && FindSplashTarget(7, opponent))
  9319. + {
  9320. + temptimer = GC_Timer;
  9321. + if (doCast(me, BLADE_FLURRY))
  9322. + {
  9323. + Blade_Flurry_Timer = 90000;
  9324. + GC_Timer = temptimer;
  9325. + return;//return here to allow cast only on next update
  9326. + }
  9327. + }
  9328. +
  9329. + if (MoveBehind(*opponent))
  9330. + wait = 5;
  9331. +
  9332. + //KICK
  9333. + if (KICK && Kick_Timer <= diff && meleedist <= 5 && Rand() < 80 && getenergy() >= 25 &&
  9334. + opponent->IsNonMeleeSpellCasted(false))
  9335. + {
  9336. + temptimer = GC_Timer;
  9337. + if (doCast(opponent, KICK))
  9338. + {
  9339. + Kick_Timer = 8000;//improved
  9340. + GC_Timer = temptimer;
  9341. + //return;
  9342. + }
  9343. + }
  9344. + //SHADOWSTEP
  9345. + if (SHADOWSTEP && Shadowstep_Timer <= diff && dist < 25 &&
  9346. + (opponent->getVictim() != me || opponent->GetTypeId() == TYPEID_PLAYER) &&
  9347. + Rand() < 30 && getenergy() >= 10)
  9348. + {
  9349. + temptimer = GC_Timer;
  9350. + if (doCast(opponent, SHADOWSTEP))
  9351. + {
  9352. + Shadowstep_Timer = 20000;
  9353. + GC_Timer = temptimer;
  9354. + //return;
  9355. + }
  9356. + //doCast(opponent, BACKSTAB, true);
  9357. + }
  9358. + //BACKSTAB
  9359. + if (BACKSTAB && GC_Timer <= diff && meleedist <= 5 && comboPoints < 5 &&
  9360. + /*Rand() < 90 && */getenergy() >= 60 && !opponent->HasInArc(M_PI, me))
  9361. + {
  9362. + if (doCast(opponent, BACKSTAB))
  9363. + return;
  9364. + }
  9365. + //SINISTER STRIKE
  9366. + if (SINISTER_STRIKE && GC_Timer <= diff && meleedist <= 5 && comboPoints < 5 &&
  9367. + Rand() < 25 && getenergy() >= 45)
  9368. + {
  9369. + if (doCast(opponent, SINISTER_STRIKE))
  9370. + return;
  9371. + }
  9372. + //SLICE AND DICE
  9373. + if (SLICE_DICE && Slice_Dice_Timer <= diff && GC_Timer <= diff && dist < 20 && comboPoints > 1 &&
  9374. + (b_attackers.size() <= 1 || Blade_Flurry_Timer > 80000) && Rand() < 30 && getenergy() >= 25)
  9375. + {
  9376. + if (doCast(opponent, SLICE_DICE))
  9377. + {
  9378. + //DICE = true;
  9379. + //tempDICE = true;
  9380. + //tempComboPoints = comboPoints;
  9381. + ////comboPoints = 0;
  9382. + return;
  9383. + }
  9384. + }
  9385. + //KIDNEY SHOT
  9386. + if (KIDNEY_SHOT && GC_Timer <= diff && Kidney_Timer <= diff && meleedist <= 5 && comboPoints > 0 &&
  9387. + !CCed(opponent) && getenergy() >= 25 && ((Rand() < 15 + comboPoints*15 && opponent->getVictim() == me && comboPoints > 2) || opponent->IsNonMeleeSpellCasted(false)))
  9388. + {
  9389. + if (doCast(opponent, KIDNEY_SHOT))
  9390. + {
  9391. + KidneyTarget = opponent->GetGUID();
  9392. + tempComboPoints = comboPoints;
  9393. + //comboPoints = 0;
  9394. + Kidney_Timer = 20000;
  9395. + return;
  9396. + }
  9397. + }
  9398. + //EVISCERATE
  9399. + if (EVISCERATE && GC_Timer <= diff && meleedist <= 5 && comboPoints > 2 &&
  9400. + getenergy() >= 35 && Rand() < comboPoints*15)
  9401. + {
  9402. + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(EVISCERATE);
  9403. + uint8 rank = spellInfo->GetRank();
  9404. + float ap = me->GetTotalAttackPowerValue(BASE_ATTACK);
  9405. + float combo = float(comboPoints);
  9406. + int32 damage = int32(urand(EVSCRDamage[rank][comboPoints][DMGMIN], EVSCRDamage[rank][comboPoints][DMGMAX]));//base damage
  9407. + damage += irand(int32(ap*combo*0.03f), int32(ap*combo*0.07f));//ap bonus
  9408. +
  9409. + //PlaceHolder::damage bonuses
  9410. + // Eviscerate and Envenom Bonus Damage (Deathmantle item set effect)
  9411. + //if (me->HasAura(37169))
  9412. + // damage += comboPoints*100;
  9413. +
  9414. + currentSpell = EVISCERATE;
  9415. + me->CastCustomSpell(opponent, EVISCERATE, &damage, NULL, NULL, false);
  9416. + return;
  9417. + }
  9418. + //MUTILATE
  9419. + //if (isTimerReady(Mutilate_Timer) && energy>60)
  9420. + //{
  9421. + // // TODO: calculate correct dmg for mutilate (dont forget poison bonus)
  9422. + // // for now use same formula as evicerate
  9423. + // uint32 base_attPower = me->GetUInt32Value(UNIT_FIELD_ATTACK_POWER);
  9424. + // //float minDmg = me->GetFloatValue(UNIT_FIELD_MINDAMAGE);
  9425. + // float minDmg = me->GetWeaponDamageRange(BASE_ATTACK, MINDAMAGE);
  9426. + // int damage = irand(int32(base_attPower*7*0.03f),int32(base_attPower*7*0.08f))+minDmg+me->getLevel();
  9427. +
  9428. + // // compensate for lack of attack power
  9429. + // damage = damage*(rand()%4+1);
  9430. +
  9431. + // me->CastCustomSpell(opponent, MUTILATE, &damage, NULL, NULL, false, NULL, NULL);
  9432. +
  9433. + // //doCast (me, MUTILATE);
  9434. + // Mutilate_Timer = 10;
  9435. + // comboPoints+=3;
  9436. + // energy -= 60;
  9437. + //}
  9438. +
  9439. + //RUPTURE
  9440. + if (RUPTURE && Rupture_Timer <= diff && GC_Timer <= diff && meleedist <= 5 && comboPoints > 2 &&
  9441. + opponent->GetHealth() > me->GetMaxHealth()/3 && getenergy() >= 25 && Rand() < 50 + 50*opponent->isMoving())
  9442. + {
  9443. + //no damage range for rupture
  9444. + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(RUPTURE);
  9445. + uint8 rank = spellInfo->GetRank();
  9446. + float ap = me->GetTotalAttackPowerValue(BASE_ATTACK);
  9447. + float AP_per_combo[6] = {0.0f, 0.015f, 0.024f, 0.03f, 0.03428571f, 0.0375f};
  9448. + float divider[6] = {0.0f, 4.f, 5.f, 6.f, 7.f, 8.f};//duration/2 = number of ticks
  9449. + int32 damage = int32(RuptureDamage[rank][comboPoints]/divider[comboPoints]);//base damage
  9450. + damage += int32(ap*AP_per_combo[comboPoints]);//ap bonus is strict - applied to every tick
  9451. +
  9452. + currentSpell = RUPTURE;
  9453. + me->CastCustomSpell(opponent, RUPTURE, &damage, NULL, NULL, false);
  9454. + return;
  9455. + //if (doCast(opponent, RUPTURE))
  9456. + //{
  9457. + // RuptureTarget = opponent->GetGUID();
  9458. + // tempComboPoints = comboPoints;
  9459. + // Rupture_Timer = 6000 + (comboPoints-1)*2000;
  9460. + // //comboPoints = 0;
  9461. + // return;
  9462. + //}
  9463. + }
  9464. + //DISMANTLE
  9465. + if (DISMANTLE && Dismantle_Timer <= diff && meleedist <= 5 &&
  9466. + opponent->GetTypeId() == TYPEID_PLAYER &&
  9467. + Rand() < 20 && getenergy() >= 25 && !CCed(opponent) &&
  9468. + !opponent->HasAuraType(SPELL_AURA_MOD_DISARM) &&
  9469. + opponent->ToPlayer()->GetWeaponForAttack(BASE_ATTACK))
  9470. + {
  9471. + temptimer = GC_Timer;
  9472. + if (doCast(opponent, DISMANTLE))
  9473. + {
  9474. + Dismantle_Timer = 60000;
  9475. + GC_Timer = temptimer;
  9476. + }
  9477. + }
  9478. + }
  9479. +
  9480. + void ApplyClassDamageMultiplierMelee(int32& damage, SpellNonMeleeDamage& /*damageinfo*/, SpellInfo const* spellInfo, WeaponAttackType /*attackType*/, bool& crit) const
  9481. + {
  9482. + uint32 spellId = spellInfo->Id;
  9483. + uint8 lvl = me->getLevel();
  9484. + float fdamage = float(damage);
  9485. + //1) apply additional crit chance. This additional chance roll will replace original (balance safe)
  9486. + if (!crit)
  9487. + {
  9488. + float aftercrit = 0.f;
  9489. + //Puncturing Wounds: 30% additional critical chance for Backstab
  9490. + if (lvl >= 15 && spellId == BACKSTAB)
  9491. + aftercrit += 30.f;
  9492. + //Puncturing Wounds: 15% additional critical chance for Mutilate
  9493. + else if (spellId == MUTILATE)
  9494. + aftercrit += 15.f;
  9495. + //Glyph of Eviscerate: 10% additional critical chance for Eviscerate
  9496. + else if (spellId == EVISCERATE)
  9497. + aftercrit += 10.f;
  9498. + //Improved Ambush: 50% additional critical chance for Ambush
  9499. + //else if (spellId == AMBUSH)
  9500. + // crit_chance += 50.f;
  9501. + if (lvl >= 50 && spellInfo->HasEffect(SPELL_EFFECT_ADD_COMBO_POINTS) && me->HasAura(TURN_THE_TABLES_EFFECT))
  9502. + aftercrit += 6.f;
  9503. +
  9504. + //second roll (may be illogical)
  9505. + if (aftercrit > 0.f)
  9506. + crit = roll_chance_f(aftercrit);
  9507. + }
  9508. +
  9509. + //2) apply bonus damage mods
  9510. + float pctbonus = 0.0f;
  9511. + if (crit)
  9512. + {
  9513. + //!!!Melee spell damage is not yet critical, all reduced by half
  9514. + //Lethality: 30% crit damage bonus for non-stealth combo-generating abilities (on 25 lvl)
  9515. + if (lvl >= 25 && !(spellInfo->Attributes & SPELL_ATTR0_ONLY_STEALTHED) &&
  9516. + spellInfo->HasEffect(SPELL_EFFECT_ADD_COMBO_POINTS))
  9517. + pctbonus += 0.15f;
  9518. + }
  9519. + //Shadowstep: 20% bonus damage to all abilities once
  9520. + //if (shadowstep == true)
  9521. + //{
  9522. + // shadowstep = false;
  9523. + // me->RemoveAurasDueToSpell(SHADOWSTEP_EFFECT_DAMAGE);
  9524. + // pctbonus += 0.2f;
  9525. + //}
  9526. + //Find Weakness: 6% bonus damage to all abilities
  9527. + if (lvl >= 45)
  9528. + pctbonus += 0.06f;
  9529. + //DeathDealer set bonus: 15% damage bonus for Eviscerate
  9530. + if (lvl >= 60 && spellId == EVISCERATE)
  9531. + pctbonus += 0.15f;
  9532. + //Imoroved Eviscerate: 20% damage bonus for Eviscerate
  9533. + if (spellId == EVISCERATE)
  9534. + pctbonus += 0.2f;
  9535. + //Opportunity: 20% damage bonus for Backstab, Mutilate, Garrote and Ambush
  9536. + if (spellId == BACKSTAB || spellId == MUTILATE/* ||
  9537. + spellId == GARROTE || spellId == AMBUSH*/)
  9538. + pctbonus += 0.2f;
  9539. + //Aggression: 15% damage bonus for Sinister Strike, Backstab and Eviscerate
  9540. + if (lvl >= 30 && (spellId == SINISTER_STRIKE || spellId == BACKSTAB || spellId == EVISCERATE))
  9541. + pctbonus += 0.15f;
  9542. + //Blood Spatter: 30% bonus damage for Rupture and Garrote
  9543. + if (lvl >= 15 && (spellId == RUPTURE/* || spellId == GARROTE*/))
  9544. + pctbonus += 0.3f;
  9545. + //Serrated Blades: 30% bonus damage for Rupture
  9546. + if (lvl >= 20 && spellId == RUPTURE)
  9547. + pctbonus += 0.3f;
  9548. + //Surprise Attacks: 10% bonus damage for Sinister Strike, Backstab, Shiv, Hemmorhage and Gouge
  9549. + if (lvl >= 50 && (spellId == SINISTER_STRIKE || spellId == BACKSTAB/* ||
  9550. + spellId == SHIV || spellId == HEMMORHAGE || spellId == GOUGE*/))
  9551. + pctbonus += 0.1f;
  9552. +
  9553. + damage = int32(fdamage * (1.0f + pctbonus));
  9554. + }
  9555. +
  9556. + void DamageDealt(Unit* victim, uint32& /*damage*/, DamageEffectType damageType)
  9557. + {
  9558. + if (!WOUND_POISON && !MIND_NUMBING_POISON)
  9559. + return;
  9560. +
  9561. + if (damageType == DIRECT_DAMAGE/* || damageType == SPELL_DIRECT_DAMAGE*/)
  9562. + {
  9563. + if (victim && me->GetExactDist(victim) <= 30)
  9564. + {
  9565. + switch (rand()%2)
  9566. + {
  9567. + case 0:
  9568. + break;
  9569. + case 1:
  9570. + {
  9571. + switch (rand()%2)
  9572. + {
  9573. + case 0:
  9574. + if (WOUND_POISON)
  9575. + {
  9576. + currentSpell = WOUND_POISON;
  9577. + DoCast(victim, WOUND_POISON, true);
  9578. + }
  9579. + break;
  9580. + case 1:
  9581. + if (MIND_NUMBING_POISON)
  9582. + {
  9583. + currentSpell = MIND_NUMBING_POISON;
  9584. + DoCast(victim, MIND_NUMBING_POISON, true);
  9585. + }
  9586. + break;
  9587. + }
  9588. + }
  9589. + }
  9590. + }
  9591. + }
  9592. + }
  9593. +
  9594. + void SpellHit(Unit* caster, SpellInfo const* spell)
  9595. + {
  9596. + OnSpellHit(caster, spell);
  9597. + }
  9598. +
  9599. + void SpellHitTarget(Unit* target, SpellInfo const* spell)
  9600. + {
  9601. + uint32 spellId = spell->Id;
  9602. + if (currentSpell == 0)
  9603. + return;
  9604. +
  9605. + if (spellId == SLICE_DICE)
  9606. + {
  9607. + tempDICE = true;
  9608. + tempComboPoints = comboPoints;
  9609. + Slice_Dice_Timer = 15000 + (comboPoints-1)*4500;
  9610. + }
  9611. + else if (spellId == RUPTURE)
  9612. + {
  9613. + RuptureTarget = target->GetGUID();
  9614. + tempComboPoints = comboPoints;
  9615. + Rupture_Timer = 8000 + (comboPoints-1)*2000;
  9616. + GC_Timer = 800;
  9617. + }
  9618. + else if (spellId == EVISCERATE)
  9619. + GC_Timer = 800;
  9620. +
  9621. + //if (spellId == EVISCERATE || spellId == KIDNEY_SHOT || spellId == SLICE_DICE || spellId == RUPTURE/* || spellId == EXPOSE_ARMOR || spellId == ENVENOM*/)
  9622. + //Relentless Strikes
  9623. + if (spell->NeedsComboPoints())
  9624. + {
  9625. + //std::ostringstream msg;
  9626. + //msg << "casting ";
  9627. + //if (spellId == EVISCERATE)
  9628. + // msg << "Eviscerate, ";
  9629. + //else if (spellId == RUPTURE)
  9630. + // msg << "Rupture, ";
  9631. + //else if (spellId == SLICE_DICE)
  9632. + // msg << "Slice and Dice, ";
  9633. + //else if (spellId == KIDNEY_SHOT)
  9634. + // msg << "Kidney Shot, ";
  9635. + ////else if (spellId == EXPOSE_ARMOR)
  9636. + //// msg << "Expose Armor, ";
  9637. + ////else if (spellId == ENVENOM)
  9638. + //// msg << "Envenom, ";
  9639. + //msg << "combo points: " << uint32(std::min<uint32>(comboPoints,5));
  9640. + //me->MonsterWhisper(msg.str().c_str(), master->GetGUID());
  9641. + if (rand()%100 < 1 + 20*(comboPoints > 5 ? 5 : comboPoints))
  9642. + DoCast(me, RELENTLESS_STRIKES_EFFECT, true);
  9643. + tempComboPoints = comboPoints;
  9644. + //CP adding effects are handled before actual finisher so use temp value
  9645. + //std::ostringstream msg2;
  9646. + //msg2 << "cp set to 0";
  9647. + if (tempAddCP)
  9648. + {
  9649. + //msg2 << " + " << uint32(tempAddCP) << " (temp)";
  9650. + comboPoints = tempAddCP;
  9651. + tempAddCP = 0;
  9652. + }
  9653. + else
  9654. + comboPoints = 0;
  9655. + //me->MonsterWhisper(msg2.str().c_str(), master->GetGUID());
  9656. + }
  9657. + else if (spellId == SINISTER_STRIKE ||
  9658. + spellId == BACKSTAB/* ||
  9659. + spellId == GOUGE ||
  9660. + spellId == HEMORRHAGE ||
  9661. + spellId == SHOSTLY_STRIKE*/)
  9662. + {
  9663. + ++comboPoints;
  9664. + //std::ostringstream msg;
  9665. + //msg << "1 cp generated ";
  9666. + //if (spellId == SINISTER_STRIKE)
  9667. + // msg << "(Sinister Strike)";
  9668. + //else if (spellId == BACKSTAB)
  9669. + // msg << "(Backstab)";
  9670. + //msg << " set to " << uint32(comboPoints);
  9671. + //if (tempAddCP)
  9672. + // msg << " + " << uint32(tempAddCP) << " (triggered)";
  9673. + //me->MonsterWhisper(msg.str().c_str(), master->GetGUID());
  9674. + if (tempAddCP)
  9675. + {
  9676. + comboPoints += tempAddCP;
  9677. + tempAddCP = 0;
  9678. + }
  9679. + }
  9680. + else if (spellId == MUTILATE/* ||
  9681. + spellId == AMBUSH*/)
  9682. + {
  9683. + comboPoints += 2;
  9684. + //std::ostringstream msg;
  9685. + //msg << "2 cp generated (Mutilate), set to " << uint32(comboPoints);
  9686. + //if (tempAddCP)
  9687. + // msg << " + " << uint32(tempAddCP) << " (triggered)";
  9688. + //me->MonsterWhisper(msg.str().c_str(), master->GetGUID());
  9689. + if (tempAddCP)
  9690. + {
  9691. + comboPoints += tempAddCP;
  9692. + tempAddCP = 0;
  9693. + }
  9694. + }
  9695. + else if (spellId == SEAL_FATE_EFFECT ||
  9696. + spellId == RUTHLESSNESS_EFFECT)
  9697. + {
  9698. + ++tempAddCP;
  9699. + //std::ostringstream msg;
  9700. + //msg << "1 temp cp generated ";
  9701. + //if (spellId == SEAL_FATE_EFFECT)
  9702. + // msg << "(Seal Fate)";
  9703. + //else if (spellId == RUTHLESSNESS_EFFECT)
  9704. + // msg << "(Ruthleness)";
  9705. + //me->MonsterWhisper(msg.str().c_str(), master->GetGUID());
  9706. + }
  9707. +
  9708. + if (spellId == SINISTER_STRIKE)
  9709. + {
  9710. + //Improved Sinister Strike
  9711. + //instead of restoring energy we should override current value
  9712. + if (me->getLevel() >= 10)
  9713. + modenergy(-40, true);//45 - 5
  9714. + }
  9715. + //Glyph of Sinister Strike (50% to add cp on crit)
  9716. + //Seal Fate means crit so this glyph is enabled from lvl 35)
  9717. + if (spellId == SEAL_FATE_EFFECT && currentSpell == SINISTER_STRIKE && rand()%100 >= 50)
  9718. + {
  9719. + ++tempAddCP;
  9720. + //me->MonsterWhisper("1 temp cp generated (glyph of SS)", master->GetGUID());
  9721. + }
  9722. + //Slaughter from the Shadows energy restore
  9723. + //instead of restoring energy we should override current value
  9724. + if (me->getLevel() >= 55)
  9725. + {
  9726. + if (spellId == BACKSTAB/* || spellId == AMBUSH*/)
  9727. + modenergy(-40, true);
  9728. + //else if (spellId == HEMORRHAGE)
  9729. + // modenergy(-30, true);
  9730. + }
  9731. +
  9732. + //if (spellId == SHADOWSTEP)
  9733. + //{
  9734. + // Shadowstep_eff_Timer = 10000;
  9735. + // shadowstep = true;
  9736. + //}
  9737. +
  9738. + //move behind on Kidney Shot and Gouge (optionally)
  9739. + if (spellId == KIDNEY_SHOT/* || spellId == GOUGE*/)
  9740. + if (MoveBehind(*target))
  9741. + wait = 3;
  9742. +
  9743. + if (spellId == currentSpell)
  9744. + currentSpell = 0;
  9745. + }
  9746. +
  9747. + void DamageTaken(Unit* u, uint32& /*damage*/)
  9748. + {
  9749. + OnOwnerDamagedBy(u);
  9750. + }
  9751. +
  9752. + void OwnerAttackedBy(Unit* u)
  9753. + {
  9754. + OnOwnerDamagedBy(u);
  9755. + }
  9756. +
  9757. + void Reset()
  9758. + {
  9759. + Mutilate_Timer = 0;
  9760. + Rupture_Timer = 0;
  9761. + Dismantle_Timer = 0;
  9762. + Kick_Timer = 0;
  9763. + Kidney_Timer = 0;
  9764. + Shadowstep_Timer = 0;
  9765. + Blade_Flurry_Timer = 0;
  9766. + Slice_Dice_Timer = 0;
  9767. + //Shadowstep_eff_Timer = 0;
  9768. +
  9769. + comboPoints = 0;
  9770. + tempComboPoints = 0;
  9771. + tempAddCP = 0;
  9772. +
  9773. + KidneyTarget = 0;
  9774. + RuptureTarget = 0;
  9775. +
  9776. + tempDICE = false;
  9777. + spellHitTarget = true;
  9778. + //shadowstep = false;
  9779. +
  9780. + me->setPowerType(POWER_ENERGY);
  9781. + //10 energy gained per stack
  9782. + RefreshAura(GLADIATOR_VIGOR, 10);
  9783. +
  9784. + if (master)
  9785. + {
  9786. + setStats(CLASS_ROGUE, me->getRace(), master->getLevel(), true);
  9787. + ApplyClassPassives();
  9788. + ApplyPassives(CLASS_ROGUE);
  9789. + }
  9790. +
  9791. + me->SetPower(POWER_ENERGY, me->GetMaxPower(POWER_ENERGY));
  9792. + }
  9793. +
  9794. + void ReduceCD(const uint32 diff)
  9795. + {
  9796. + CommonTimers(diff);
  9797. + if (Kick_Timer > diff) Kick_Timer -= diff;
  9798. + if (Rupture_Timer > diff) Rupture_Timer -= diff;
  9799. + if (Shadowstep_Timer > diff) Shadowstep_Timer -= diff;
  9800. + if (Mutilate_Timer > diff) Mutilate_Timer -= diff;
  9801. + if (Kidney_Timer > diff) Kidney_Timer -= diff;
  9802. + if (Dismantle_Timer > diff) Dismantle_Timer -= diff;
  9803. + if (Blade_Flurry_Timer > diff) Blade_Flurry_Timer -= diff;
  9804. + if (Slice_Dice_Timer > diff) Slice_Dice_Timer -= diff;
  9805. + //if (Shadowstep_eff_Timer > diff) Shadowstep_eff_Timer -= diff;
  9806. + //else if (shadowstep) shadowstep = false;
  9807. + }
  9808. +
  9809. + bool CanRespawn()
  9810. + {return false;}
  9811. +
  9812. + void InitSpells()
  9813. + {
  9814. + uint8 lvl = me->getLevel();
  9815. + BACKSTAB = InitSpell(me, BACKSTAB_1);
  9816. + SINISTER_STRIKE = InitSpell(me, SINISTER_STRIKE_1);
  9817. + SLICE_DICE = InitSpell(me, SLICE_DICE_1);
  9818. + EVISCERATE = InitSpell(me, EVISCERATE_1);
  9819. + KICK = InitSpell(me, KICK_1);
  9820. + RUPTURE = InitSpell(me, RUPTURE_1);
  9821. + KIDNEY_SHOT = InitSpell(me, KIDNEY_SHOT_1);
  9822. + MUTILATE = lvl >= 50 ? InitSpell(me, MUTILATE_1) : 0;
  9823. + SHADOWSTEP = lvl >= 50 ? SHADOWSTEP_1 : 0;
  9824. + DISMANTLE = InitSpell(me, DISMANTLE_1);
  9825. + BLADE_FLURRY = lvl >= 30 ? BLADE_FLURRY_1 : 0;
  9826. +
  9827. + WOUND_POISON = InitSpell(me, WOUND_POISON_1);
  9828. + MIND_NUMBING_POISON = InitSpell(me, MIND_NUMBING_POISON_1);
  9829. + }
  9830. +
  9831. + void ApplyClassPassives()
  9832. + {
  9833. + uint8 level = master->getLevel();
  9834. +
  9835. + //if (level >= 78)
  9836. + // RefreshAura(ROGUE_ARMOR_ENERGIZE,2);
  9837. + //else if (level >= 60)
  9838. + // RefreshAura(ROGUE_ARMOR_ENERGIZE);
  9839. + if (level >= 70)
  9840. + RefreshAura(COMBAT_POTENCY5,2);
  9841. + else if (level >= 55)
  9842. + RefreshAura(COMBAT_POTENCY5);
  9843. + else if (level >= 52)
  9844. + RefreshAura(COMBAT_POTENCY4);
  9845. + else if (level >= 49)
  9846. + RefreshAura(COMBAT_POTENCY3);
  9847. + else if (level >= 47)
  9848. + RefreshAura(COMBAT_POTENCY2);
  9849. + else if (level >= 45)
  9850. + RefreshAura(COMBAT_POTENCY1);
  9851. + if (level >= 35)
  9852. + RefreshAura(SEAL_FATE5);
  9853. + else if (level >= 32)
  9854. + RefreshAura(SEAL_FATE4);
  9855. + else if (level >= 29)
  9856. + RefreshAura(SEAL_FATE3);
  9857. + else if (level >= 27)
  9858. + RefreshAura(SEAL_FATE2);
  9859. + else if (level >= 25)
  9860. + RefreshAura(SEAL_FATE1);
  9861. + if (level >= 78)
  9862. + RefreshAura(VITALITY,4);
  9863. + else if (level >= 70)
  9864. + RefreshAura(VITALITY,3);
  9865. + else if (level >= 55)
  9866. + RefreshAura(VITALITY,2);
  9867. + else if (level >= 40)
  9868. + RefreshAura(VITALITY);
  9869. + if (level >= 55)
  9870. + RefreshAura(TURN_THE_TABLES);
  9871. + if (level >= 40)
  9872. + RefreshAura(DEADLY_BREW);
  9873. + if (level >= 35)
  9874. + RefreshAura(BLADE_TWISTING1);//allow rank 1 only
  9875. + if (level >= 35)
  9876. + RefreshAura(QUICK_RECOVERY2);
  9877. + else if (level >= 30)
  9878. + RefreshAura(QUICK_RECOVERY1);
  9879. + if (level >= 30)
  9880. + RefreshAura(IMPROVED_KIDNEY_SHOT);
  9881. + if (level >= 10)
  9882. + RefreshAura(GLYPH_BACKSTAB);
  9883. + if (level >= 10)
  9884. + RefreshAura(SURPRISE_ATTACKS);
  9885. +
  9886. + //On 25 get Glyph of Vigor
  9887. + if (level >= 25)
  9888. + RefreshAura(ROGUE_VIGOR,2);
  9889. + else if (level >= 20)
  9890. + RefreshAura(ROGUE_VIGOR);
  9891. + }
  9892. +
  9893. + private:
  9894. + uint32
  9895. + BACKSTAB, SINISTER_STRIKE, SLICE_DICE, EVISCERATE, KICK, RUPTURE, KIDNEY_SHOT, MUTILATE,
  9896. + SHADOWSTEP, DISMANTLE, BLADE_FLURRY,
  9897. + WOUND_POISON, MIND_NUMBING_POISON;
  9898. + //Timers/other
  9899. + uint64 KidneyTarget, RuptureTarget;
  9900. + uint32 Rupture_Timer, Dismantle_Timer,
  9901. + Kick_Timer, Shadowstep_Timer, Mutilate_Timer, Kidney_Timer,
  9902. + Blade_Flurry_Timer, Slice_Dice_Timer/*, Shadowstep_eff_Timer*/;
  9903. + uint32 energy;
  9904. + uint8 comboPoints, tempComboPoints, tempAddCP;
  9905. + bool tempDICE, spellHitTarget/*, shadowstep*/;
  9906. +
  9907. + enum RogueBaseSpells
  9908. + {
  9909. + BACKSTAB_1 = 53,
  9910. + SINISTER_STRIKE_1 = 1757,
  9911. + SLICE_DICE_1 = 5171,
  9912. + EVISCERATE_1 = 2098,//11300
  9913. + KICK_1 = 1766,
  9914. + RUPTURE_1 = 1943,
  9915. + KIDNEY_SHOT_1 = 408,//8643
  9916. + /*Talent*/MUTILATE_1 = 1329,//48666
  9917. + /*Talent*/SHADOWSTEP_1 = 36554,
  9918. + DISMANTLE_1 = 51722,
  9919. + BLADE_FLURRY_1 = 33735,
  9920. +
  9921. + WOUND_POISON_1 = 13218,
  9922. + MIND_NUMBING_POISON_1 = 5760
  9923. + };
  9924. +
  9925. + enum RoguePassives
  9926. + {
  9927. + //Talents
  9928. + SEAL_FATE1 = 14189,
  9929. + SEAL_FATE2 = 14190,
  9930. + SEAL_FATE3 = 14193,
  9931. + SEAL_FATE4 = 14194,
  9932. + SEAL_FATE5 = 14195,
  9933. + COMBAT_POTENCY1 = 35541,
  9934. + COMBAT_POTENCY2 = 35550,
  9935. + COMBAT_POTENCY3 = 35551,
  9936. + COMBAT_POTENCY4 = 35552,
  9937. + COMBAT_POTENCY5 = 35553,
  9938. + QUICK_RECOVERY1 = 31244,
  9939. + QUICK_RECOVERY2 = 31245,
  9940. + BLADE_TWISTING1 = 31124,
  9941. + //BLADE_TWISTING2 = 31126,
  9942. + VITALITY = 61329,//rank 3
  9943. + DEADLY_BREW = 51626,//rank 2
  9944. + IMPROVED_KIDNEY_SHOT = 14176,//rank 3
  9945. + TURN_THE_TABLES = 51629,//rank 3
  9946. + SURPRISE_ATTACKS = 32601,
  9947. + ROGUE_VIGOR = 14983,
  9948. + //Other
  9949. + //ROGUE_ARMOR_ENERGIZE/*Deathmantle*/ = 27787,
  9950. + GLADIATOR_VIGOR = 21975,
  9951. + GLYPH_BACKSTAB = 56800
  9952. + };
  9953. +
  9954. + enum RogueSpecial
  9955. + {
  9956. + //WOUND_POISON = 13218,
  9957. + //DEADLY_POISON = 2818,
  9958. + //MIND_NUMBING_POISON = 5760,
  9959. + RELENTLESS_STRIKES_EFFECT = 14181,
  9960. + RUTHLESSNESS_EFFECT = 14157,
  9961. + SEAL_FATE_EFFECT = 14189,
  9962. + //SHADOWSTEP_EFFECT_DAMAGE = 36563,
  9963. + TURN_THE_TABLES_EFFECT = 52910
  9964. + };
  9965. + };
  9966. +};
  9967. +
  9968. +void AddSC_rogue_bot()
  9969. +{
  9970. + new rogue_bot();
  9971. +}
  9972. diff --git a/src/server/game/AI/NpcBots/bot_shaman_ai.cpp b/src/server/game/AI/NpcBots/bot_shaman_ai.cpp
  9973. new file mode 100644
  9974. index 0000000..f9e7e8b
  9975. --- /dev/null
  9976. +++ b/src/server/game/AI/NpcBots/bot_shaman_ai.cpp
  9977. @@ -0,0 +1,390 @@
  9978. +#include "bot_ai.h"
  9979. +//#include "Group.h"
  9980. +#include "Player.h"
  9981. +#include "ScriptMgr.h"
  9982. +#include "SpellAuras.h"
  9983. +/*
  9984. +Shaman NpcBot (reworked by Graff [email protected])
  9985. +Complete - 1%
  9986. +TODO:
  9987. +*/
  9988. +class shaman_bot : public CreatureScript
  9989. +{
  9990. +public:
  9991. + shaman_bot() : CreatureScript("shaman_bot") { }
  9992. +
  9993. + CreatureAI* GetAI(Creature* creature) const
  9994. + {
  9995. + return new shaman_botAI(creature);
  9996. + }
  9997. +
  9998. + bool OnGossipHello(Player* player, Creature* creature)
  9999. + {
  10000. + return bot_minion_ai::OnGossipHello(player, creature);
  10001. + }
  10002. +
  10003. + bool OnGossipSelect(Player* player, Creature* creature, uint32 sender, uint32 action)
  10004. + {
  10005. + if (bot_minion_ai* ai = creature->GetBotMinionAI())
  10006. + return ai->OnGossipSelect(player, creature, sender, action);
  10007. + return true;
  10008. + }
  10009. +
  10010. + struct shaman_botAI : public bot_minion_ai
  10011. + {
  10012. + shaman_botAI(Creature* creature) : bot_minion_ai(creature)
  10013. + {
  10014. + Reset();
  10015. + }
  10016. +
  10017. + bool doCast(Unit* victim, uint32 spellId, bool triggered = false)
  10018. + {
  10019. + if (checkBotCast(victim, spellId, CLASS_SHAMAN) != SPELL_CAST_OK)
  10020. + return false;
  10021. + uint64 originalCaster = 0;
  10022. + if (spellId == STONESKIN_TOTEM ||
  10023. + spellId == SEARING_TOTEM ||
  10024. + spellId == WINDFURY_TOTEM ||
  10025. + spellId == HEALINGSTREAM_TOTEM ||
  10026. + spellId == MANASPRING_TOTEM)
  10027. + originalCaster = master->GetGUID();
  10028. + return bot_ai::doCast(victim, spellId, triggered, originalCaster);
  10029. + }
  10030. +
  10031. + void StartAttack(Unit* u, bool force = false)
  10032. + {
  10033. + if (GetBotCommandState() == COMMAND_ATTACK && !force) return;
  10034. + Aggro(u);
  10035. + GetInPosition(force, false/*STYLE == MELEE*//*SPEC == ENCHANCEMENT*/);
  10036. + SetBotCommandState(COMMAND_ATTACK);
  10037. + }
  10038. +
  10039. + void EnterCombat(Unit*) { }
  10040. + void Aggro(Unit*) { }
  10041. + void AttackStart(Unit*) { }
  10042. + void KilledUnit(Unit*) { }
  10043. + void EnterEvadeMode() { }
  10044. + void MoveInLineOfSight(Unit*) { }
  10045. + void JustDied(Unit*) { master->SetNpcBotDied(me->GetGUID()); }
  10046. +
  10047. + void CheckTotems(uint32 diff)
  10048. + {
  10049. + if (GC_Timer > diff || Rand() > 50 || IsCasting() || me->GetDistance(master) > 20 || Feasting())
  10050. + return;
  10051. + if (me->isInCombat())
  10052. + {
  10053. + if (WINDFURY_TOTEM && !HasAuraName(master, WINDFURY_TOTEM))
  10054. + {
  10055. + if (doCast(me, WINDFURY_TOTEM))
  10056. + return;
  10057. + }
  10058. + else if (STONESKIN_TOTEM && !HasAuraName(master, STONESKIN_AURA))
  10059. + {
  10060. + if (doCast(me, STONESKIN_TOTEM))
  10061. + return;
  10062. + }
  10063. +
  10064. + if (Unit* u = me->getVictim())
  10065. + {
  10066. + if (SEARING_TOTEM && Searing_Totem_Timer <= diff)
  10067. + {
  10068. + if (me->GetExactDist(u) < (u->isMoving() ? 10 : 25))
  10069. + {
  10070. + if (doCast(me, SEARING_TOTEM))
  10071. + {
  10072. + Searing_Totem_Timer = 30000;
  10073. + return;
  10074. + }
  10075. + }
  10076. + }
  10077. + }
  10078. + }
  10079. + else if (!me->isMoving() && !master->isMoving())
  10080. + {
  10081. + uint8 manapct = GetManaPCT(master);
  10082. + uint8 hppct = GetHealthPCT(master);
  10083. + if (HEALINGSTREAM_TOTEM &&
  10084. + (master->getPowerType() != POWER_MANA || hppct < 25 || manapct > hppct) &&
  10085. + hppct < 98 && !HasAuraName(master, HEALINGSTREAM_AURA))
  10086. + {
  10087. + if (doCast(me, HEALINGSTREAM_TOTEM))
  10088. + return;
  10089. + }
  10090. + else if (MANASPRING_TOTEM && manapct < 98 && !HasAuraName(master, MANASPRING_AURA))
  10091. + {
  10092. + if (doCast(me, MANASPRING_TOTEM))
  10093. + return;
  10094. + }
  10095. + }
  10096. + }
  10097. +
  10098. + void UpdateAI(uint32 diff)
  10099. + {
  10100. + ReduceCD(diff);
  10101. + if (IAmDead()) return;
  10102. + if (!me->getVictim()) Evade();
  10103. + if (me->getVictim())
  10104. + DoMeleeAttackIfReady();
  10105. + else
  10106. + Evade();
  10107. + if (wait == 0)
  10108. + wait = GetWait();
  10109. + else
  10110. + return;
  10111. + CheckAuras();
  10112. + BreakCC(diff);
  10113. + if (CCed(me)) return;
  10114. +
  10115. + if (GetManaPCT(me) < 30 && Potion_cd <= diff)
  10116. + {
  10117. + temptimer = GC_Timer;
  10118. + if (doCast(me, MANAPOTION))
  10119. + Potion_cd = POTION_CD;
  10120. + GC_Timer = temptimer;
  10121. + }
  10122. +
  10123. + BuffAndHealGroup(master, diff);
  10124. + CheckTotems(diff);
  10125. +
  10126. + if (!me->isInCombat())
  10127. + DoNonCombatActions(diff);
  10128. + //buff myself
  10129. + if (LIGHTNING_SHIELD && GC_Timer <= diff && !me->HasAura(LIGHTNING_SHIELD))
  10130. + if (doCast(me, LIGHTNING_SHIELD))
  10131. + GC_Timer = 500;
  10132. +
  10133. + if (!CheckAttackTarget(CLASS_SHAMAN))
  10134. + return;
  10135. +
  10136. + //Counter(diff);
  10137. + DoNormalAttack(diff);
  10138. + }
  10139. +
  10140. + void Counter(uint32 const /*diff*/)
  10141. + {}
  10142. +
  10143. + void DoNormalAttack(uint32 diff)
  10144. + {
  10145. + opponent = me->getVictim();
  10146. + if (opponent)
  10147. + {
  10148. + if (!IsCasting())
  10149. + StartAttack(opponent, true);
  10150. + }
  10151. + else
  10152. + return;
  10153. +
  10154. + Counter(diff);
  10155. +
  10156. + //AttackerSet m_attackers = master->getAttackers();
  10157. + //AttackerSet b_attackers = me->getAttackers();
  10158. + float dist = me->GetExactDist(opponent);
  10159. + //float meleedist = me->GetDistance(opponent);
  10160. +
  10161. + if (MoveBehind(*opponent))
  10162. + wait = 5;
  10163. +
  10164. + if (Shock_Timer <= diff && GC_Timer <= diff && dist < 20)
  10165. + {
  10166. + if (!opponent->HasAura(FLAME_SHOCK, me->GetGUID()))
  10167. + {
  10168. + if (doCast(opponent, FLAME_SHOCK))
  10169. + {
  10170. + Shock_Timer = 9000;
  10171. + return;
  10172. + }
  10173. + }
  10174. + else if (!opponent->HasAura(EARTH_SHOCK))
  10175. + {
  10176. + if (doCast(opponent, EARTH_SHOCK))
  10177. + {
  10178. + Shock_Timer = 9000;
  10179. + return;
  10180. + }
  10181. + }
  10182. + }
  10183. +
  10184. + if (Lightning_Bolt_Timer <= diff && GC_Timer <= diff && dist < 25)
  10185. + {
  10186. + if (doCast(opponent, LIGHTNING_BOLT))
  10187. + {
  10188. + Lightning_Bolt_Timer = uint32(float(sSpellMgr->GetSpellInfo(LIGHTNING_BOLT)->CalcCastTime()/100) * me->GetFloatValue(UNIT_MOD_CAST_SPEED) + 200);
  10189. + return;
  10190. + }
  10191. + }
  10192. + }
  10193. +
  10194. + void DoNonCombatActions(uint32 diff)
  10195. + {
  10196. + if (GC_Timer > diff || Rand() > 50 || me->IsMounted()) return;
  10197. +
  10198. + RezGroup(ANCESTRAL_SPIRIT, master);
  10199. + //BuffAndHealGroup(master, diff);
  10200. + // CureGroup(master, diff);
  10201. + }
  10202. +
  10203. + bool HealTarget(Unit* target, uint8 hp, uint32 diff)
  10204. + {
  10205. + if (hp > 95) return false;
  10206. + if (!target || target->isDead() || me->GetExactDist(target) > 40)
  10207. + return false;
  10208. + if (Rand() > 50 + 20*target->isInCombat() + 50*master->GetMap()->IsRaid()) return false;
  10209. +
  10210. + //PLACEHOLDER: Instant spell req. interrupt current spell
  10211. +
  10212. + if (IsCasting()) return false;
  10213. +
  10214. +
  10215. + /*if (hp < 70 && Heal_Timer <= diff)
  10216. + {
  10217. + doCast(target, HEALING_WAVE);
  10218. + }
  10219. + else */if (hp < 90 && Heal_Timer <= diff)
  10220. + {
  10221. + doCast(target, CHAIN_HEAL);
  10222. + }
  10223. + else if (hp < 95)
  10224. + {
  10225. + doCast(target, LESSER_HEAL);
  10226. + return true;
  10227. + }
  10228. + return true;
  10229. + }
  10230. +
  10231. + //void ApplyBotDamageMultiplierMelee(int32& damage, SpellNonMeleeDamage& /*damageinfo*/, SpellInfo const* spellInfo, WeaponAttackType /*attackType*/, bool& /*crit*/) const
  10232. + //{
  10233. + // uint32 spellId = spellInfo->Id;
  10234. + // uint8 lvl = me->getLevel();
  10235. + // float fdamage = float(damage);
  10236. + // //1) apply additional crit chance. This new chance roll will replace original (balance unsafe)
  10237. + // if (!crit)
  10238. + // {
  10239. + // float crit_chance = me->GetUnitCriticalChance(BASE_ATTACK, me);
  10240. + // float aftercrit = crit_chance;
  10241. +
  10242. + // //second roll (may be illogical)
  10243. + // if (aftercrit > crit_chance)
  10244. + // crit = roll_chance_f(aftercrit);
  10245. + // }
  10246. +
  10247. + // 2) apply bonus damage mods
  10248. + // float pctbonus = 0.0f;
  10249. + // if (crit)
  10250. + // {
  10251. + // }
  10252. +
  10253. + // fdamage *= (1.0f + pctbonus);
  10254. + // damage = int32(fdamage);
  10255. + // //last: overall multiplier
  10256. + // bot_ai::ApplyBotDamageMultiplierMelee(damage, damageinfo, spellInfo, attackType, crit);
  10257. + //}
  10258. +
  10259. + void DamageDealt(Unit* /*victim*/, uint32& /*damage*/, DamageEffectType /*damageType*/)
  10260. + {
  10261. + }
  10262. +
  10263. + void SpellHit(Unit* caster, SpellInfo const* spell)
  10264. + {
  10265. + OnSpellHit(caster, spell);
  10266. + }
  10267. +
  10268. + void DamageTaken(Unit* u, uint32& /*damage*/)
  10269. + {
  10270. + OnOwnerDamagedBy(u);
  10271. + }
  10272. +
  10273. + void OwnerAttackedBy(Unit* u)
  10274. + {
  10275. + OnOwnerDamagedBy(u);
  10276. + }
  10277. +
  10278. + void Reset()
  10279. + {
  10280. + Heal_Timer = 0;
  10281. + Shock_Timer = 0;
  10282. + Lightning_Bolt_Timer = 0;
  10283. + Searing_Totem_Timer = 0;
  10284. +
  10285. + if (master)
  10286. + {
  10287. + setStats(CLASS_SHAMAN, me->getRace(), master->getLevel(), true);
  10288. + ApplyClassPassives();
  10289. + ApplyPassives(CLASS_SHAMAN);
  10290. + }
  10291. + }
  10292. +
  10293. + void ReduceCD(uint32 diff)
  10294. + {
  10295. + CommonTimers(diff);
  10296. + if (Heal_Timer > diff) Heal_Timer -= diff;
  10297. + if (Shock_Timer > diff) Shock_Timer -= diff;
  10298. + if (Lightning_Bolt_Timer > diff) Lightning_Bolt_Timer -= diff;
  10299. + if (Searing_Totem_Timer > diff) Searing_Totem_Timer -= diff;
  10300. + }
  10301. +
  10302. + bool CanRespawn()
  10303. + {return false;}
  10304. +
  10305. + void InitSpells()
  10306. + {
  10307. + //uint8 lvl = me->getLevel();
  10308. + CHAIN_HEAL = InitSpell(me, CHAIN_HEAL_1);
  10309. + LESSER_HEAL = InitSpell(me, LESSER_HEAL_1);
  10310. + ANCESTRAL_SPIRIT = InitSpell(me, ANCESTRAL_SPIRIT_1);
  10311. + FLAME_SHOCK = InitSpell(me, FLAME_SHOCK_1);
  10312. + EARTH_SHOCK = InitSpell(me, EARTH_SHOCK_1);
  10313. + LIGHTNING_BOLT = InitSpell(me, LIGHTNING_BOLT_1);
  10314. + LIGHTNING_SHIELD = InitSpell(me, LIGHTNING_SHIELD_1);
  10315. + STONESKIN_TOTEM = InitSpell(me, STONESKIN_TOTEM_1);
  10316. + HEALINGSTREAM_TOTEM = InitSpell(me, HEALINGSTREAM_TOTEM_1);
  10317. + MANASPRING_TOTEM = InitSpell(me, MANASPRING_TOTEM_1);
  10318. + SEARING_TOTEM = InitSpell(me, SEARING_TOTEM_1);
  10319. + WINDFURY_TOTEM = InitSpell(me, WINDFURY_TOTEM_1);
  10320. + }
  10321. +
  10322. + void ApplyClassPassives()
  10323. + {
  10324. + }
  10325. +
  10326. + private:
  10327. + uint32
  10328. + CHAIN_HEAL, LESSER_HEAL, ANCESTRAL_SPIRIT,
  10329. + FLAME_SHOCK, EARTH_SHOCK, LIGHTNING_BOLT, LIGHTNING_SHIELD,
  10330. + STONESKIN_TOTEM, HEALINGSTREAM_TOTEM, MANASPRING_TOTEM, SEARING_TOTEM, WINDFURY_TOTEM;
  10331. + //Timers
  10332. + uint32 Heal_Timer, Shock_Timer, Lightning_Bolt_Timer, Searing_Totem_Timer;
  10333. +
  10334. + enum ShamanBaseSpells
  10335. + {
  10336. + CHAIN_HEAL_1 = 1064,
  10337. + LESSER_HEAL_1 = 8004,
  10338. + ANCESTRAL_SPIRIT_1 = 2008,
  10339. + FLAME_SHOCK_1 = 8050,
  10340. + EARTH_SHOCK_1 = 8042,
  10341. + LIGHTNING_BOLT_1 = 403,
  10342. + LIGHTNING_SHIELD_1 = 324,
  10343. + STONESKIN_TOTEM_1 = 8071,
  10344. + HEALINGSTREAM_TOTEM_1 = 5394,
  10345. + MANASPRING_TOTEM_1 = 5675,
  10346. + SEARING_TOTEM_1 = 3599,
  10347. + WINDFURY_TOTEM_1 = 8512,
  10348. + };
  10349. +
  10350. + enum ShamanPassives
  10351. + {
  10352. + };
  10353. +
  10354. + enum ShamanSpecial
  10355. + {
  10356. + STONESKIN_AURA = 8072,
  10357. + HEALINGSTREAM_AURA = 5672,
  10358. + MANASPRING_AURA = 5677,
  10359. + };
  10360. + };
  10361. +};
  10362. +
  10363. +
  10364. +void AddSC_shaman_bot()
  10365. +{
  10366. + new shaman_bot();
  10367. +}
  10368. diff --git a/src/server/game/AI/NpcBots/bot_warlock_ai.cpp b/src/server/game/AI/NpcBots/bot_warlock_ai.cpp
  10369. new file mode 100644
  10370. index 0000000..b5fc80b
  10371. --- /dev/null
  10372. +++ b/src/server/game/AI/NpcBots/bot_warlock_ai.cpp
  10373. @@ -0,0 +1,458 @@
  10374. +#include "bot_ai.h"
  10375. +//#include "Group.h"
  10376. +#include "Player.h"
  10377. +#include "ScriptMgr.h"
  10378. +#include "SpellAuras.h"
  10379. +
  10380. +/*
  10381. +Warlock NpcBot (reworked by Graff [email protected])
  10382. +Voidwalker pet AI included
  10383. +Complete - 3%
  10384. +TODO:
  10385. +*/
  10386. +class warlock_bot : public CreatureScript
  10387. +{
  10388. +public:
  10389. + warlock_bot() : CreatureScript("warlock_bot") { }
  10390. +
  10391. + CreatureAI* GetAI(Creature* creature) const
  10392. + {
  10393. + return new warlock_botAI(creature);
  10394. + }
  10395. +
  10396. + bool OnGossipHello(Player* player, Creature* creature)
  10397. + {
  10398. + return bot_minion_ai::OnGossipHello(player, creature);
  10399. + }
  10400. +
  10401. + bool OnGossipSelect(Player* player, Creature* creature, uint32 sender, uint32 action)
  10402. + {
  10403. + if (bot_minion_ai* ai = creature->GetBotMinionAI())
  10404. + return ai->OnGossipSelect(player, creature, sender, action);
  10405. + return true;
  10406. + }
  10407. +
  10408. + struct warlock_botAI : public bot_minion_ai
  10409. + {
  10410. + warlock_botAI(Creature* creature) : bot_minion_ai(creature) { }
  10411. +
  10412. + bool doCast(Unit* victim, uint32 spellId, bool triggered = false)
  10413. + {
  10414. + if (checkBotCast(victim, spellId, CLASS_WARRIOR) != SPELL_CAST_OK)
  10415. + return false;
  10416. + return bot_ai::doCast(victim, spellId, triggered);
  10417. + }
  10418. +
  10419. + void EnterCombat(Unit*) { }
  10420. + void Aggro(Unit*) { }
  10421. + void AttackStart(Unit*) { }
  10422. + void KilledUnit(Unit*) { }
  10423. + void EnterEvadeMode() { }
  10424. + void MoveInLineOfSight(Unit*) { }
  10425. + void JustDied(Unit*) { me->SetBotsPetDied(); master->SetNpcBotDied(me->GetGUID()); }
  10426. + void DoNonCombatActions()
  10427. + {}
  10428. +
  10429. + void StartAttack(Unit* u, bool force = false)
  10430. + {
  10431. + if (GetBotCommandState() == COMMAND_ATTACK && !force) return;
  10432. + Aggro(u);
  10433. + GetInPosition(force, true);
  10434. + SetBotCommandState(COMMAND_ATTACK);
  10435. + fear_cd = std::max<uint32>(fear_cd, 1000);
  10436. + }
  10437. +
  10438. + void UpdateAI(uint32 diff)
  10439. + {
  10440. + ReduceCD(diff);
  10441. + if (IAmDead()) return;
  10442. + if (!me->getVictim())
  10443. + Evade();
  10444. + if (wait == 0)
  10445. + wait = GetWait();
  10446. + else
  10447. + return;
  10448. + CheckAuras();
  10449. + if (CCed(me)) return;
  10450. +
  10451. + //if pet is dead or unreachable
  10452. + Creature* m_botsPet = me->GetBotsPet();
  10453. + if (!m_botsPet || m_botsPet->FindMap() != master->GetMap() || (me->GetDistance2d(m_botsPet) > sWorld->GetMaxVisibleDistanceOnContinents() - 20.f))
  10454. + if (master->getLevel() >= 10 && !me->isInCombat() && !IsCasting() && !me->IsMounted())
  10455. + SummonBotsPet(PET_VOIDWALKER);
  10456. +
  10457. + //TODO: implement healthstone
  10458. + if (GetHealthPCT(me) < 50 && Potion_cd <= diff)
  10459. + {
  10460. + temptimer = GC_Timer;
  10461. + if (doCast(me, HEALINGPOTION))
  10462. + Potion_cd = POTION_CD;
  10463. + GC_Timer = temptimer;
  10464. + }
  10465. + if (GetManaPCT(me) < 50 && Potion_cd <= diff)
  10466. + {
  10467. + temptimer = GC_Timer;
  10468. + if (doCast(me, MANAPOTION))
  10469. + Potion_cd = POTION_CD;
  10470. + GC_Timer = temptimer;
  10471. + }
  10472. + if (!me->isInCombat())
  10473. + DoNonCombatActions();
  10474. +
  10475. + if (!CheckAttackTarget(CLASS_WARLOCK))
  10476. + return;
  10477. +
  10478. + DoNormalAttack(diff);
  10479. + }
  10480. +
  10481. + void DoNormalAttack(uint32 diff)
  10482. + {
  10483. + opponent = me->getVictim();
  10484. + if (opponent)
  10485. + {
  10486. + if (!IsCasting())
  10487. + StartAttack(opponent);
  10488. + }
  10489. + else
  10490. + return;
  10491. +
  10492. + //TODO: add more damage spells
  10493. +
  10494. + if (fear_cd <= diff && GC_Timer <= diff)
  10495. + { CheckFear(); fear_cd = 2000; }
  10496. +
  10497. + if (RAIN_OF_FIRE && Rand() < 25 && Rain_of_fire_cd <= diff && GC_Timer <= diff)
  10498. + {
  10499. + Unit* blizztarget = FindAOETarget(30, true);
  10500. + if (blizztarget && doCast(blizztarget, RAIN_OF_FIRE))
  10501. + {
  10502. + Rain_of_fire_cd = 5000;
  10503. + return;
  10504. + }
  10505. + Rain_of_fire_cd = 2000;//fail
  10506. + }
  10507. +
  10508. + if (Rand() < 25 && CURSE_OF_THE_ELEMENTS && GC_Timer <= diff && !HasAuraName(opponent, CURSE_OF_THE_ELEMENTS) &&
  10509. + doCast(opponent, CURSE_OF_THE_ELEMENTS))
  10510. + {
  10511. + GC_Timer = 800;
  10512. + return;
  10513. + }
  10514. +
  10515. + if (GC_Timer <= 0 && Rand() < 25 && !opponent->HasAura(CORRUPTION, me->GetGUID()) &&
  10516. + doCast(opponent, CORRUPTION))
  10517. + { return; }
  10518. +
  10519. + if (HAUNT && Rand() < 25 && Haunt_cd <= diff && GC_Timer <= diff && !opponent->HasAura(HAUNT, me->GetGUID()) &&
  10520. + doCast(opponent, HAUNT))
  10521. + {
  10522. + Haunt_cd = 8000;
  10523. + return;
  10524. + }
  10525. +
  10526. + if (GC_Timer <= diff && Rand() < 15 && !Afflicted(opponent))
  10527. + {
  10528. + if (conflagarate_cd <= 8000 && doCast(opponent, IMMOLATE))
  10529. + return;
  10530. + else if (UNSTABLE_AFFLICTION && doCast(opponent, UNSTABLE_AFFLICTION))
  10531. + return;
  10532. + }
  10533. +
  10534. + if (CONFLAGRATE && Rand() < 35 && conflagarate_cd <= diff && GC_Timer <= diff &&
  10535. + HasAuraName(opponent, IMMOLATE) &&
  10536. + doCast(opponent, CONFLAGRATE))
  10537. + {
  10538. + conflagarate_cd = 10000;
  10539. + return;
  10540. + }
  10541. +
  10542. + if (CHAOS_BOLT && Rand() < 50 && chaos_bolt_cd <= diff && GC_Timer <= diff && doCast(opponent, CHAOS_BOLT))
  10543. + {
  10544. + chaos_bolt_cd = 16000 - me->getLevel() * 100;
  10545. + return;
  10546. + }
  10547. +
  10548. + if (GC_Timer <= diff && doCast(opponent, SHADOW_BOLT))
  10549. + return;
  10550. +
  10551. + }
  10552. +
  10553. + uint8 Afflicted(Unit* target)
  10554. + {
  10555. + if (!target || target->isDead()) return 0;
  10556. + bool aff = HasAuraName(target, UNSTABLE_AFFLICTION, me->GetGUID());
  10557. + bool imm = HasAuraName(target, IMMOLATE, me->GetGUID());
  10558. + if (imm) return 1;
  10559. + if (aff) return 2;
  10560. + return 0;
  10561. + }
  10562. +
  10563. + void CheckFear()
  10564. + {
  10565. + if (Unit* u = FindAffectedTarget(FEAR, me->GetGUID()))
  10566. + if (Aura* aura = u->GetAura(FEAR, me->GetGUID()))
  10567. + if (aura->GetDuration() > 3000)
  10568. + return;
  10569. + Unit* feartarget = FindFearTarget();
  10570. + if (feartarget && doCast(feartarget, FEAR)) {}
  10571. + }
  10572. +
  10573. + //void SummonedCreatureDies(Creature* summon, Unit* /*killer*/)
  10574. + //{
  10575. + // if (summon == me->GetBotsPet())
  10576. + // me->SetBotsPetDied();
  10577. + //}
  10578. +
  10579. + //void SummonedCreatureDespawn(Creature* summon)
  10580. + //{
  10581. + // if (summon == me->GetBotsPet())
  10582. + // me->SetBotsPet(NULL);
  10583. + //}
  10584. +
  10585. + void SpellHit(Unit* caster, SpellInfo const* spell)
  10586. + {
  10587. + OnSpellHit(caster, spell);
  10588. + }
  10589. +
  10590. + void DamageTaken(Unit* u, uint32& /*damage*/)
  10591. + {
  10592. + OnOwnerDamagedBy(u);
  10593. + }
  10594. +
  10595. + void OwnerAttackedBy(Unit* u)
  10596. + {
  10597. + OnOwnerDamagedBy(u);
  10598. + }
  10599. +
  10600. + void Reset()
  10601. + {
  10602. + Rain_of_fire_cd = 0;
  10603. + Haunt_cd = 0;
  10604. + conflagarate_cd = 0;
  10605. + chaos_bolt_cd = 0;
  10606. + fear_cd = 0;
  10607. +
  10608. + if (master)
  10609. + {
  10610. + setStats(CLASS_WARLOCK, me->getRace(), master->getLevel(), true);
  10611. + //TODO: passives
  10612. + ApplyClassPassives();
  10613. + ApplyPassives(CLASS_WARLOCK);
  10614. + }
  10615. + }
  10616. +
  10617. + void ReduceCD(uint32 diff)
  10618. + {
  10619. + CommonTimers(diff);
  10620. + if (Rain_of_fire_cd > diff) Rain_of_fire_cd -= diff;
  10621. + if (Haunt_cd > diff) Haunt_cd -= diff;
  10622. + if (conflagarate_cd > diff) conflagarate_cd -= diff;
  10623. + if (chaos_bolt_cd > diff) chaos_bolt_cd -= diff;
  10624. + if (fear_cd > diff) fear_cd -= diff;
  10625. + }
  10626. +
  10627. + bool CanRespawn()
  10628. + {return false;}
  10629. +
  10630. + void InitSpells()
  10631. + {
  10632. + uint8 lvl = me->getLevel();
  10633. + CURSE_OF_THE_ELEMENTS = InitSpell(me, CURSE_OF_THE_ELEMENTS_1);
  10634. + SHADOW_BOLT = InitSpell(me, SHADOW_BOLT_1);
  10635. + IMMOLATE = InitSpell(me, IMMOLATE_1);
  10636. + CONFLAGRATE = lvl >= 40 ? CONFLAGRATE_1 : 0;
  10637. + /*Talent*/CHAOS_BOLT = lvl >= 60 ? InitSpell(me, CHAOS_BOLT_1) : 0;
  10638. + RAIN_OF_FIRE = InitSpell(me, RAIN_OF_FIRE_1);
  10639. + /*Talent*/HAUNT = lvl >= 60 ? InitSpell(me, HAUNT_1) : 0;
  10640. + CORRUPTION = InitSpell(me, CORRUPTION_1);
  10641. + /*Talent*/UNSTABLE_AFFLICTION = lvl >= 50 ? InitSpell(me, UNSTABLE_AFFLICTION_1) : 0;
  10642. + FEAR = InitSpell(me, FEAR_1);
  10643. + }
  10644. +
  10645. + void ApplyClassPassives() {}
  10646. +
  10647. + private:
  10648. + uint32
  10649. + /*Curses*/CURSE_OF_THE_ELEMENTS,
  10650. +/*Destruct*/SHADOW_BOLT, IMMOLATE, CONFLAGRATE, CHAOS_BOLT, RAIN_OF_FIRE,
  10651. + /*Afflict*/HAUNT, CORRUPTION, UNSTABLE_AFFLICTION,
  10652. + /*Other*/FEAR;
  10653. + //Timers
  10654. + uint32 Rain_of_fire_cd, Haunt_cd, conflagarate_cd, chaos_bolt_cd, fear_cd;
  10655. +
  10656. + enum WarlockBaseSpells// all orignals
  10657. + {
  10658. + CURSE_OF_THE_ELEMENTS_1 = 1490,
  10659. + SHADOW_BOLT_1 = 686,
  10660. + IMMOLATE_1 = 348,
  10661. + CONFLAGRATE_1 = 17962,
  10662. + CHAOS_BOLT_1 = 50796,
  10663. + RAIN_OF_FIRE_1 = 5740,
  10664. + HAUNT_1 = 59164,
  10665. + CORRUPTION_1 = 172,
  10666. + UNSTABLE_AFFLICTION_1 = 30404,
  10667. + FEAR_1 = 6215,
  10668. + };
  10669. + enum WarlockPassives
  10670. + {
  10671. + };
  10672. + };
  10673. +};
  10674. +
  10675. +class voidwalker_bot : public CreatureScript
  10676. +{
  10677. +public:
  10678. + voidwalker_bot() : CreatureScript("voidwalker_bot") { }
  10679. +
  10680. + CreatureAI* GetAI(Creature* creature) const
  10681. + {
  10682. + return new voidwalker_botAI(creature);
  10683. + }
  10684. +
  10685. + struct voidwalker_botAI : public bot_pet_ai
  10686. + {
  10687. + voidwalker_botAI(Creature* creature) : bot_pet_ai(creature) { }
  10688. +
  10689. + bool doCast(Unit* victim, uint32 spellId, bool triggered = false)
  10690. + {
  10691. + if (checkBotCast(victim, spellId, CLASS_NONE) != SPELL_CAST_OK)
  10692. + return false;
  10693. + return bot_ai::doCast(victim, spellId, triggered);
  10694. + }
  10695. +
  10696. + void EnterCombat(Unit*) { }
  10697. + void Aggro(Unit*) { }
  10698. + void AttackStart(Unit*) { }
  10699. + void KilledUnit(Unit*) { }
  10700. + void EnterEvadeMode() { }
  10701. + void MoveInLineOfSight(Unit*) { }
  10702. + void JustDied(Unit*) { m_creatureOwner->SetBotsPetDied(); }
  10703. +
  10704. + void DoNonCombatActions()
  10705. + {}
  10706. +
  10707. + void StartAttack(Unit* u, bool force = false)
  10708. + {
  10709. + if (GetBotCommandState() == COMMAND_ATTACK && !force) return;
  10710. + Aggro(u);
  10711. + GetInPosition(force, false);
  10712. + SetBotCommandState(COMMAND_ATTACK);
  10713. + }
  10714. +
  10715. + void UpdateAI(uint32 diff)
  10716. + {
  10717. + ReduceCD(diff);
  10718. + if (IAmDead()) return;
  10719. + if (me->getVictim())
  10720. + DoMeleeAttackIfReady();
  10721. + if (wait == 0)
  10722. + wait = GetWait();
  10723. + else
  10724. + return;
  10725. + CheckAuras();
  10726. + if (CCed(me)) return;
  10727. +
  10728. + //TODO: add checks to help owner
  10729. +
  10730. + if (!me->isInCombat())
  10731. + DoNonCombatActions();
  10732. +
  10733. + if (!CheckAttackTarget(PET_TYPE_VOIDWALKER))
  10734. + return;
  10735. +
  10736. + DoNormalAttack(diff);
  10737. + }
  10738. +
  10739. + void DoNormalAttack(uint32 diff)
  10740. + {
  10741. + opponent = me->getVictim();
  10742. + if (opponent)
  10743. + {
  10744. + if (!IsCasting())
  10745. + StartAttack(opponent, true);
  10746. + }
  10747. + else
  10748. + return;
  10749. + if (MoveBehind(*opponent))
  10750. + wait = 5;
  10751. +
  10752. + //TORMENT
  10753. + if (Torment_cd <= diff && me->GetDistance(opponent) <= 5 && (!tank || tank == me || opponent->getVictim() == m_creatureOwner))
  10754. + {
  10755. + temptimer = GC_Timer;
  10756. + if (doCast(opponent, TORMENT))
  10757. + Torment_cd = 5000;
  10758. + GC_Timer = temptimer;
  10759. + }
  10760. + }
  10761. +
  10762. + void SpellHit(Unit* caster, SpellInfo const* spell)
  10763. + {
  10764. + OnSpellHit(caster, spell);
  10765. + }
  10766. +
  10767. + void DamageTaken(Unit* u, uint32& /*damage*/)
  10768. + {
  10769. + if (m_creatureOwner->IsAIEnabled)
  10770. + if (bot_minion_ai* ai = m_creatureOwner->GetBotMinionAI())
  10771. + ai->OnOwnerDamagedBy(u);
  10772. + }
  10773. +
  10774. + //debug
  10775. + //void ListSpells(ChatHandler* ch) const
  10776. + //{
  10777. + // ch->PSendSysMessage("Spells list:");
  10778. + // ch->PSendSysMessage("Torment: %u", TORMENT);
  10779. + // ch->PSendSysMessage("End of spells list.");
  10780. + //}
  10781. +
  10782. + void Reset()
  10783. + {
  10784. + Torment_cd = 0;
  10785. +
  10786. + if (master && m_creatureOwner)
  10787. + {
  10788. + setStats(master->getLevel(), PET_TYPE_VOIDWALKER, true);
  10789. + ApplyPassives(PET_TYPE_VOIDWALKER);
  10790. + ApplyClassPassives();
  10791. + SetBaseArmor(162 * master->getLevel());
  10792. + }
  10793. + }
  10794. +
  10795. + void ReduceCD(uint32 diff)
  10796. + {
  10797. + CommonTimers(diff);
  10798. + if (Torment_cd > diff) Torment_cd -= diff;
  10799. + }
  10800. +
  10801. + bool CanRespawn()
  10802. + {return false;}
  10803. +
  10804. + void InitSpells()
  10805. + {
  10806. + TORMENT = InitSpell(me, TORMENT_1);
  10807. + }
  10808. +
  10809. + void ApplyClassPassives() {}
  10810. +
  10811. + private:
  10812. + uint32
  10813. + TORMENT;
  10814. + //Timers
  10815. + uint32 Torment_cd;
  10816. +
  10817. + enum VoidwalkerBaseSpells
  10818. + {
  10819. + TORMENT_1 = 3716,
  10820. + };
  10821. + enum VoidwalkerPassives
  10822. + {
  10823. + };
  10824. + };
  10825. +};
  10826. +
  10827. +void AddSC_warlock_bot()
  10828. +{
  10829. + new warlock_bot();
  10830. + new voidwalker_bot();
  10831. +}
  10832. diff --git a/src/server/game/AI/NpcBots/bot_warrior_ai.cpp b/src/server/game/AI/NpcBots/bot_warrior_ai.cpp
  10833. new file mode 100644
  10834. index 0000000..e54ddce
  10835. --- /dev/null
  10836. +++ b/src/server/game/AI/NpcBots/bot_warrior_ai.cpp
  10837. @@ -0,0 +1,1192 @@
  10838. +#include "bot_ai.h"
  10839. +#include "Group.h"
  10840. +#include "Player.h"
  10841. +#include "ScriptMgr.h"
  10842. +#include "SpellAuras.h"
  10843. +#include "WorldSession.h"
  10844. +/*
  10845. +Warrior NpcBot (reworked by Graff [email protected])
  10846. +Complete - Around 50-55%
  10847. +TODO: Solve 'DeathWish + Enrage', Thunder Clap, Piercing Howl, Challenging Shout, other tanking stuff
  10848. +*/
  10849. +class warrior_bot : public CreatureScript
  10850. +{
  10851. +public:
  10852. + warrior_bot() : CreatureScript("warrior_bot") { }
  10853. +
  10854. + CreatureAI* GetAI(Creature* creature) const
  10855. + {
  10856. + return new warrior_botAI(creature);
  10857. + }
  10858. +
  10859. + bool OnGossipHello(Player* player, Creature* creature)
  10860. + {
  10861. + return bot_minion_ai::OnGossipHello(player, creature);
  10862. + }
  10863. +
  10864. + bool OnGossipSelect(Player* player, Creature* creature, uint32 sender, uint32 action)
  10865. + {
  10866. + if (bot_minion_ai* ai = creature->GetBotMinionAI())
  10867. + return ai->OnGossipSelect(player, creature, sender, action);
  10868. + return true;
  10869. + }
  10870. +
  10871. + struct warrior_botAI : public bot_minion_ai
  10872. + {
  10873. + warrior_botAI(Creature* creature) : bot_minion_ai(creature) { }
  10874. +
  10875. + bool doCast(Unit* victim, uint32 spellId, bool triggered = false)
  10876. + {
  10877. + if (checkBotCast(victim, spellId, CLASS_WARRIOR) != SPELL_CAST_OK)
  10878. + return false;
  10879. + return bot_ai::doCast(victim, spellId, triggered);
  10880. + }
  10881. +
  10882. + void UpdateAI(uint32 diff)
  10883. + {
  10884. + ReduceCD(diff);
  10885. + if (rendTarget)
  10886. + {
  10887. + //Glyph of Rending: Increases Rend duration by 6 sec.
  10888. + if (Unit* u = sObjectAccessor->FindUnit(rendTarget))
  10889. + {
  10890. + if (Aura* rend = u->GetAura(REND, me->GetGUID()))
  10891. + {
  10892. + uint32 dur = rend->GetDuration() + 6000;
  10893. + rend->SetDuration(dur);
  10894. + rend->SetMaxDuration(dur);
  10895. + }
  10896. + }
  10897. + rendTarget = 0;
  10898. + }
  10899. + if (IAmDead()) return;
  10900. + if (me->getVictim())
  10901. + DoMeleeAttackIfReady();
  10902. + else
  10903. + Evade();
  10904. +
  10905. + if (ragetimer2 <= diff)
  10906. + {
  10907. + if (me->isInCombat() && me->getLevel() >= 20)
  10908. + {
  10909. + if (getrage() < 990)
  10910. + me->SetPower(POWER_RAGE, rage + uint32(10.f*rageIncomeMult));//1 rage per 2 sec
  10911. + else
  10912. + me->SetPower(POWER_RAGE, 1000);//max
  10913. + }
  10914. + ragetimer2 = 2000;
  10915. + }
  10916. + if (ragetimer <= diff)
  10917. + {
  10918. + if (!me->isInCombat() && !HasAuraName(me, BLOODRAGE_1))
  10919. + {
  10920. + if (getrage() > uint32(10.f*rageLossMult))
  10921. + me->SetPower(POWER_RAGE, rage - uint32(10.f*rageLossMult));//-1 rage per 1.5 sec
  10922. + else
  10923. + me->SetPower(POWER_RAGE, 0);//min
  10924. + }
  10925. + ragetimer = 1500;
  10926. + //if (getrage() > 1000) me->SetPower(POWER_RAGE, 1000);
  10927. + //if (getrage() < 10) me->SetPower(POWER_RAGE, 0);
  10928. + }
  10929. +
  10930. + if (wait == 0)
  10931. + wait = GetWait();
  10932. + else
  10933. + return;
  10934. + if (checkAurasTimer == 0)
  10935. + {
  10936. + SS = SWEEPING_STRIKES && me->HasAura(SWEEPING_STRIKES);
  10937. + CheckAuras();
  10938. + }
  10939. + BreakCC(diff);
  10940. + if (CCed(me)) return;
  10941. +
  10942. + if (GetHealthPCT(me) < 67 && Potion_cd <= diff)
  10943. + {
  10944. + temptimer = GC_Timer;
  10945. + if (doCast(me, HEALINGPOTION))
  10946. + {
  10947. + Potion_cd = POTION_CD;
  10948. + GC_Timer = temptimer;
  10949. + }
  10950. + }
  10951. + CheckIntervene(diff);
  10952. + if (!me->isInCombat())
  10953. + DoNonCombatActions(diff);
  10954. +
  10955. + if (!CheckAttackTarget(CLASS_WARRIOR))
  10956. + {
  10957. + if (tank != me && !me->isInCombat() && battleStance != true && master->getAttackers().empty() && stancetimer <= diff)
  10958. + stanceChange(diff, 1);
  10959. + return;
  10960. + }
  10961. +
  10962. + if (Rand() < 30 && battleShout_cd <= diff && GC_Timer <= diff && getrage() > 100 &&
  10963. + !HasAuraName(master, BATTLESHOUT) &&
  10964. + !HasAuraName(master, "Blessing of Might") &&
  10965. + !HasAuraName(master, "Greater Blessing of Might") &&
  10966. + master->IsWithinLOSInMap(me) &&
  10967. + doCast(me, BATTLESHOUT))
  10968. + battleShout_cd = BATTLESHOUT_CD;
  10969. +
  10970. + if (Rand() < 20 && BLOODRAGE && bloodrage_cd <= diff && me->isInCombat() && getrage() < 500 &&
  10971. + !me->HasAura(ENRAGED_REGENERATION))
  10972. + {
  10973. + temptimer = GC_Timer;
  10974. + if (doCast(me, BLOODRAGE))
  10975. + {
  10976. + bloodrage_cd = BLOODRAGE_CD;
  10977. + GC_Timer = temptimer;
  10978. + }
  10979. + }
  10980. +
  10981. + Attack(diff);
  10982. + }
  10983. +
  10984. + void StartAttack(Unit* u, bool force = false)
  10985. + {
  10986. + if (GetBotCommandState() == (COMMAND_ATTACK) && !force) return;
  10987. + Aggro(u);
  10988. + SetBotCommandState(COMMAND_ATTACK);
  10989. + GetInPosition(force, false);
  10990. + }
  10991. +
  10992. + void EnterCombat(Unit*) { }
  10993. + void Aggro(Unit*) { }
  10994. + void AttackStart(Unit*) { }
  10995. + void KilledUnit(Unit*) { }
  10996. + void EnterEvadeMode() { }
  10997. + void MoveInLineOfSight(Unit*) { }
  10998. + void JustDied(Unit*) { master->SetNpcBotDied(me->GetGUID()); }
  10999. + void DoNonCombatActions(uint32 const /*diff*/)
  11000. + {}
  11001. +
  11002. + void modrage(int32 mod, bool set = false)
  11003. + {
  11004. + if (set && mod < 0)
  11005. + return;
  11006. + if (mod < 0 && rage < uint32(abs(mod)))
  11007. + return;
  11008. +
  11009. + if (set)
  11010. + rage = mod*10;
  11011. + else
  11012. + rage += mod*10;
  11013. + me->SetPower(POWER_RAGE, rage);
  11014. + }
  11015. +
  11016. + uint32 getrage()
  11017. + {
  11018. + rage = me->GetPower(POWER_RAGE);
  11019. + return rage;
  11020. + }
  11021. +
  11022. + void BreakCC(uint32 diff)
  11023. + {
  11024. + if (me->HasAuraWithMechanic((1<<MECHANIC_FEAR)|(1<<MECHANIC_SAPPED)|(1<<MECHANIC_DISORIENTED)))
  11025. + {
  11026. + if (BERSERKERRAGE && !me->HasAura(ENRAGED_REGENERATION) &&
  11027. + berserkerRage_cd <= diff && GC_Timer <= diff && doCast(me, BERSERKERRAGE))
  11028. + {
  11029. + berserkerRage_cd = BERSERKERRAGE_CD;
  11030. + if (me->getLevel() >= 40)
  11031. + modrage(20);
  11032. + return;
  11033. + }
  11034. + }
  11035. + bot_minion_ai::BreakCC(diff);
  11036. + }
  11037. +
  11038. + void Attack(uint32 diff)
  11039. + {
  11040. + opponent = me->getVictim();
  11041. + if (opponent)
  11042. + {
  11043. + if (!IsCasting())
  11044. + StartAttack(opponent, true);
  11045. + }
  11046. + else
  11047. + return;
  11048. + //Keep stance in raid if tank
  11049. + if (master->GetBotTankGuid() == me->GetGUID() &&
  11050. + defensiveStance != true && stancetimer <= diff/* &&
  11051. + (!master->GetGroup() || master->GetGroup()->isRaidGroup())*/)
  11052. + stanceChange(diff, 2);
  11053. + //SelfHeal
  11054. + if (ENRAGED_REGENERATION)
  11055. + {
  11056. + if (Rand() < 20 && GetHealthPCT(me) < 40 && getrage() > 150 && regen_cd <= diff &&
  11057. + me->HasAuraWithMechanic(MECHANIC_ENRAGED))//no GC_Timer
  11058. + {
  11059. + temptimer = 0;
  11060. + if (doCast(me, ENRAGED_REGENERATION))
  11061. + {
  11062. + regen_cd = ENRAGED_REGENERATION_CD;
  11063. + GC_Timer = temptimer;
  11064. + return;
  11065. + }
  11066. + }
  11067. + //maybe not needed part
  11068. + if (me->HasAura(ENRAGED_REGENERATION))
  11069. + {
  11070. + if (HasAuraName(me, "Enrage"))
  11071. + me->RemoveAurasWithMechanic(MECHANIC_ENRAGED);
  11072. + if (HasAuraName(me, BLOODRAGE))
  11073. + me->RemoveAurasDueToSpell(BLOODRAGE);
  11074. + if (HasAuraName(me, DEATHWISH))
  11075. + me->RemoveAurasDueToSpell(DEATHWISH);
  11076. + if (HasAuraName(me, BERSERKERRAGE))
  11077. + me->RemoveAurasDueToSpell(BERSERKERRAGE);
  11078. + }
  11079. + }//end SelfHeal
  11080. +
  11081. + AttackerSet m_attackers = master->getAttackers();
  11082. + AttackerSet b_attackers = me->getAttackers();
  11083. + float dist = me->GetExactDist(opponent);
  11084. + float meleedist = me->GetDistance(opponent);
  11085. + //charge + warbringer
  11086. + if (CHARGE && charge_cd <= diff && dist > 11 && dist < 25 && me->HasInArc(M_PI, opponent) &&
  11087. + (me->getLevel() >= 50 ||
  11088. + (!me->isInCombat() && (battleStance == true || stanceChange(diff, 1)))))
  11089. + {
  11090. + temptimer = GC_Timer;
  11091. + if (me->getLevel() >= 50)
  11092. + me->RemoveMovementImpairingAuras();
  11093. + if (doCast(opponent, CHARGE, me->isInCombat()))
  11094. + {
  11095. + charge_cd = CHARGE_CD;
  11096. + GC_Timer = temptimer;
  11097. + return;
  11098. + }
  11099. + }
  11100. + //intercept
  11101. + if (INTERCEPT && intercept_cd <= diff &&
  11102. + getrage() > 100 && dist > 11 && dist < 25 && me->HasInArc(M_PI, opponent) &&
  11103. + !CCed(opponent) && (berserkerStance == true || stanceChange(diff, 3)))
  11104. + {
  11105. + if (doCast(opponent, INTERCEPT))
  11106. + {
  11107. + intercept_cd = INTERCEPT_CD;
  11108. + //modrage(-10);
  11109. + return;
  11110. + }
  11111. + }
  11112. + //FEAR
  11113. + if (Rand() < 70 && INTIMIDATING_SHOUT && getrage() > 250 && intimidatingShout_cd <= diff && GC_Timer <= diff)
  11114. + {
  11115. + if (opponent->IsNonMeleeSpellCasted(false, false, true) && dist <= 8 &&
  11116. + !(opponent->ToCreature() && opponent->ToCreature()->GetCreatureType() == CREATURE_TYPE_UNDEAD))
  11117. + {
  11118. + if (doCast(opponent, INTIMIDATING_SHOUT))
  11119. + {
  11120. + intimidatingShout_cd = INTIMIDATINGSHOUT_CD;
  11121. + return;
  11122. + }
  11123. + }
  11124. + Unit* fearTarget = NULL;
  11125. + bool triggered = false;
  11126. + uint8 tCount = 0;
  11127. + //fear master's attackers
  11128. + if (!m_attackers.empty() &&
  11129. + ((master->getClass() != CLASS_DEATH_KNIGHT &&
  11130. + master->getClass() != CLASS_WARRIOR &&
  11131. + master->getClass() != CLASS_PALADIN) ||
  11132. + GetHealthPCT(master) < 70))
  11133. + {
  11134. + for(AttackerSet::iterator iter = m_attackers.begin(); iter != m_attackers.end(); ++iter)
  11135. + {
  11136. + if (!(*iter)) continue;
  11137. + if ((*iter)->GetCreatureType() == CREATURE_TYPE_UNDEAD) continue;
  11138. + if (me->GetExactDist((*iter)) <= 8 && (*iter)->isTargetableForAttack())
  11139. + {
  11140. + ++tCount;
  11141. + fearTarget = (*iter);
  11142. + if (tCount > 1) break;
  11143. + }
  11144. + }
  11145. + if (tCount > 0 && !fearTarget)
  11146. + {
  11147. + fearTarget = opponent;
  11148. + triggered = true;
  11149. + }
  11150. + if (tCount > 1 && doCast(fearTarget, INTIMIDATING_SHOUT, triggered))
  11151. + {
  11152. + intimidatingShout_cd = INTIMIDATINGSHOUT_CD;
  11153. + if (triggered)
  11154. + modrage(-25);
  11155. + return;
  11156. + }
  11157. + }
  11158. + //Defend myself
  11159. + if (b_attackers.size() > 1)
  11160. + {
  11161. + tCount = 0;
  11162. + fearTarget = NULL;
  11163. + triggered = false;
  11164. + for(AttackerSet::iterator iter = b_attackers.begin(); iter != b_attackers.end(); ++iter)
  11165. + {
  11166. + if (!(*iter)) continue;
  11167. + if ((*iter)->GetCreatureType() == CREATURE_TYPE_UNDEAD) continue;
  11168. + if (me->GetExactDist((*iter)) <= 8 && (*iter)->isTargetableForAttack())
  11169. + {
  11170. + ++tCount;
  11171. + fearTarget = (*iter);
  11172. + if (tCount > 0) break;
  11173. + }
  11174. + }
  11175. + if (tCount > 0 && !fearTarget)
  11176. + {
  11177. + fearTarget = opponent;
  11178. + triggered = true;
  11179. + }
  11180. + if (tCount > 0 && doCast(fearTarget, INTIMIDATING_SHOUT, triggered))
  11181. + {
  11182. + intimidatingShout_cd = INTIMIDATINGSHOUT_CD;
  11183. + if (triggered)
  11184. + modrage(-25);
  11185. + return;
  11186. + }
  11187. + }
  11188. + }//end FEAR
  11189. + //OVERPOWER
  11190. + if (OVERPOWER && getrage() > 50 && meleedist <= 5 && GC_Timer <= diff && (battleStance == true || stancetimer <= diff))
  11191. + {
  11192. + bool blood = me->HasAura(TASTE_FOR_BLOOD_BUFF);
  11193. + if ((((opponent->GetTypeId() == TYPEID_PLAYER && UNRELENTING_ASSAULT && blood && opponent->IsNonMeleeSpellCasted(false) && overpower_cd <= 3000) ||
  11194. + (opponent->IsNonMeleeSpellCasted(false) && blood) ||
  11195. + (overpower_cd <= diff && blood))))
  11196. + {
  11197. + if (battleStance == true || stanceChange(diff, 1))
  11198. + {
  11199. + if (doCast(opponent, OVERPOWER))
  11200. + {
  11201. + overpower_cd = 5000;
  11202. + if (blood)
  11203. + me->RemoveAura(TASTE_FOR_BLOOD_BUFF);
  11204. + return;
  11205. + }
  11206. + }
  11207. + }
  11208. + }
  11209. +
  11210. + if (MoveBehind(*opponent))
  11211. + wait = 5;
  11212. + //{ wait = 5; return; }
  11213. + //HAMSTRING
  11214. + if (HAMSTRING && Rand() < 50 && getrage() > 100 && GC_Timer <= diff && meleedist <= 5 && opponent->isMoving() &&
  11215. + (battleStance == true || berserkerStance == true || stancetimer <= diff) &&
  11216. + !opponent->HasAuraWithMechanic((1<<MECHANIC_SNARE)|(1<<MECHANIC_ROOT)))
  11217. + {
  11218. + if (battleStance == true || berserkerStance == true || stanceChange(diff, 5))
  11219. + if (doCast(opponent, HAMSTRING))
  11220. + return;
  11221. + }
  11222. + //UBERS
  11223. + //Dont use RETA unless capable circumstances
  11224. + if (Rand() < 20 && uber_cd <= diff && GC_Timer <= diff)//mod here
  11225. + {
  11226. + if (RETALIATION && b_attackers.size() > 4 &&
  11227. + (battleStance == true || stanceChange(diff, 1)))
  11228. + {
  11229. + if (doCast(me, RETALIATION))
  11230. + {
  11231. + uber_cd = UBER_CD;
  11232. + return;
  11233. + }
  11234. + }
  11235. + //Dont use RECKL unless capable circumstances
  11236. + if (RECKLESSNESS && tank != me &&
  11237. + (m_attackers.size() > 3 || opponent->GetHealth() > me->GetHealth()*10) &&
  11238. + (berserkerStance == true || stanceChange(diff, 3)))
  11239. + {
  11240. + if (doCast(me, RECKLESSNESS))
  11241. + {
  11242. + uber_cd = UBER_CD;
  11243. + return;
  11244. + }
  11245. + }
  11246. + }
  11247. + //DEATHWISH
  11248. + if (DEATHWISH && Rand() < 20 && deathwish_cd <= diff && GC_Timer <= diff &&
  11249. + getrage() > 100 && opponent->GetHealth() > me->GetHealth()/2 &&
  11250. + !me->HasAura(ENRAGED_REGENERATION))//mod here
  11251. + {
  11252. + if (doCast(me, DEATHWISH))
  11253. + {
  11254. + //modrage(-10);
  11255. + deathwish_cd = DEATHWISH_CD;
  11256. + return;
  11257. + }
  11258. + }
  11259. + //TAUNT
  11260. + Unit* u = opponent->getVictim();
  11261. + if (TAUNT && taunt_cd <= diff && u && u != me && u != tank && dist <= 30 &&
  11262. + (IsInBotParty(u) || tank == me) && !CCed(opponent) &&
  11263. + (defensiveStance == true || (stancetimer <= diff && stanceChange(diff, 2))))//No GCD
  11264. + {
  11265. + temptimer = GC_Timer;
  11266. + if (doCast(opponent, TAUNT, true))
  11267. + {
  11268. + taunt_cd = TAUNT_CD;
  11269. + GC_Timer = temptimer;
  11270. + return;
  11271. + }
  11272. + }
  11273. + //EXECUTE
  11274. + if (EXECUTE && tank != me && Rand() < 70 && GC_Timer <= diff && getrage() > 150 && meleedist <= 5 && GetHealthPCT(opponent) < 20 &&
  11275. + (battleStance == true || berserkerStance == true || stancetimer <= diff))
  11276. + {
  11277. + if ((battleStance == true || berserkerStance == true || stanceChange(diff, 5)) &&
  11278. + doCast(opponent, EXECUTE))
  11279. + {
  11280. + //sudden death
  11281. + if (me->getLevel() >= 50 && getrage() <= 400)
  11282. + modrage(10, true);
  11283. + else if (getrage() > 300)
  11284. + modrage(-30);
  11285. + else
  11286. + modrage(0, true);
  11287. + return;
  11288. + }
  11289. + }
  11290. + //SUNDER
  11291. + if (SUNDER && Rand() < 35 && meleedist <= 5 && tank == me &&
  11292. + opponent->GetHealth() > me->GetMaxHealth() &&
  11293. + GC_Timer <= diff && getrage() > 150 && (sunder_cd <= diff || getrage() > 500))
  11294. + {
  11295. + Aura* sunder = opponent->GetAura(SUNDER, me->GetGUID());
  11296. + if ((!sunder || sunder->GetStackAmount() < 5 || sunder->GetDuration() < 15000) &&
  11297. + doCast(opponent, SUNDER))
  11298. + {
  11299. + sunder_cd = SUNDER_CD;
  11300. + GC_Timer = 1000;
  11301. + return;
  11302. + }
  11303. + }
  11304. + //SS
  11305. + if (SWEEPING_STRIKES && sweeping_strikes_cd <= diff && tank != me && Rand() < 20 &&
  11306. + (battleStance == true || berserkerStance == true || stancetimer <= diff) &&
  11307. + (b_attackers.size() > 1 || FindSplashTarget(7, opponent)))
  11308. + {
  11309. + temptimer = GC_Timer;
  11310. + if ((battleStance == true || berserkerStance == true || stanceChange(diff, 5)) &&
  11311. + doCast(me, SWEEPING_STRIKES, true))//no rage use as with glyph
  11312. + {
  11313. + SS = true;
  11314. + sweeping_strikes_cd = SWEEPING_STRIKES_CD;
  11315. + GC_Timer = temptimer;
  11316. + return;
  11317. + }
  11318. + }
  11319. + //WHIRLWIND
  11320. + if (WHIRLWIND && Rand() < 50 && whirlwind_cd <= diff && GC_Timer <= diff && getrage() >= 250 &&
  11321. + (FindSplashTarget(7, opponent) || (getrage() > 500 && dist <= 7)) &&
  11322. + (berserkerStance == true || stancetimer <= diff))
  11323. + {
  11324. + if ((berserkerStance == true || stanceChange(diff, 3)) &&
  11325. + doCast(me, WHIRLWIND))
  11326. + {
  11327. + whirlwind_cd = WHIRLWIND_CD;
  11328. + return;
  11329. + }
  11330. + }
  11331. + //BLADESTORM
  11332. + if (BLADESTORM && bladestorm_cd <= diff && GC_Timer <= diff &&
  11333. + getrage() >= 250 && (Rand() < 20 || me->HasAura(RECKLESSNESS)) &&
  11334. + (b_attackers.size() > 1 || opponent->GetHealth() > me->GetMaxHealth()))
  11335. + {
  11336. + if (doCast(me, BLADESTORM))
  11337. + {
  11338. + bladestorm_cd = BLADESTORM_CD;
  11339. + return;
  11340. + }
  11341. + }
  11342. + //Mortal Strike
  11343. + if (MORTALSTRIKE && getrage() > 300 && meleedist <= 5 && mortalStrike_cd <= diff && GC_Timer <= diff)
  11344. + {
  11345. + if (doCast(opponent, MORTALSTRIKE/*, true*/))
  11346. + {
  11347. + mortalStrike_cd = MORTALSTRIKE_CD;
  11348. + slam_cd = 0;//reset here
  11349. + }
  11350. + }
  11351. + //Slam
  11352. + bool triggered = mortalStrike_cd == 7000;
  11353. + if (SLAM && Rand() < (30 + triggered*60) && slam_cd <= diff && getrage() > 150 && meleedist <= 5)
  11354. + {
  11355. + if (doCast(opponent, SLAM, triggered))
  11356. + {
  11357. + slam_cd = 4500;//4.5sec (must be > MORTALSTRIKE_CD/2)
  11358. + if (triggered)
  11359. + modrage(-15);
  11360. + return;
  11361. + }
  11362. + }
  11363. + //PUMMEL
  11364. + if (PUMMEL && Rand() < 80 && pummel_cd <= diff && getrage() > 100 && meleedist <= 5 &&
  11365. + opponent->IsNonMeleeSpellCasted(false) &&
  11366. + (berserkerStance == true || stancetimer <= diff))
  11367. + {
  11368. + temptimer = GC_Timer;
  11369. + if ((berserkerStance == true || stanceChange(diff, 3)) &&
  11370. + doCast(opponent, PUMMEL))
  11371. + {
  11372. + pummel_cd = PUMMEL_CD;
  11373. + GC_Timer = temptimer;
  11374. + return;
  11375. + }
  11376. + }
  11377. + //REND
  11378. + if (REND && Rand() < 30 && getrage() > 100 && GC_Timer <= diff && meleedist <= 5 &&
  11379. + opponent->GetHealth() > me->GetHealth()/2 &&
  11380. + (battleStance == true || defensiveStance == true || stancetimer <= diff) &&
  11381. + !opponent->HasAura(REND, me->GetGUID()))
  11382. + {
  11383. + if ((battleStance == true || defensiveStance == true || stanceChange(diff, 1)) &&
  11384. + doCast(opponent, REND))
  11385. + {
  11386. + rendTarget = opponent->GetGUID();
  11387. + return;
  11388. + }
  11389. + }
  11390. + //Cleave
  11391. + if (Rand() < 30 && CLEAVE && cleave_cd <= diff && me->getLevel() >= 20 && getrage() > 200 && meleedist <= 5)//noGC_Timer
  11392. + {
  11393. + temptimer = GC_Timer;
  11394. + u = FindSplashTarget(5);
  11395. + if (u && doCast(opponent, CLEAVE))
  11396. + {
  11397. + rage -= 200;//not visible
  11398. + cleave_cd = me->getAttackTimer(BASE_ATTACK);//once per swing, prevents rage loss
  11399. + GC_Timer = temptimer;
  11400. + return;
  11401. + }
  11402. + }
  11403. + else {}//HEROIC STRIKE placeholder
  11404. + //DISARM DEPRECATED
  11405. + /*if (disarm_cd <= diff && meleedist < 5 &&
  11406. + (opponent->getVictim()->GetGUID() == master->GetGUID() ||
  11407. + opponent->getVictim()->GetGUID() == m_creature->GetGUID()) &&
  11408. + getrage() > 15 &&
  11409. + !HasAuraName(opponent, GetSpellName(DISARM)) &&
  11410. + GC_Timer <= diff)
  11411. + {
  11412. + if (opponent->getClass() == CLASS_ROGUE ||
  11413. + opponent->getClass() == CLASS_WARRIOR ||
  11414. + opponent->getClass() == CLASS_SHAMAN ||
  11415. + opponent->getClass() == CLASS_PALADIN)
  11416. + {
  11417. + if (defensiveStance == true)
  11418. + {
  11419. + doCast(opponent, DISARM, true);
  11420. + //rage -= 100;
  11421. + disarm_cd = DISARM_CD;
  11422. + }
  11423. + else stanceChange(diff, 2);
  11424. + }
  11425. + }*/
  11426. + }//end Attack
  11427. +
  11428. + void CheckIntervene(uint32 diff)
  11429. + {
  11430. + if (INTERVENE && intervene_cd <= diff && getrage() > 100 && Rand() < ((tank == me) ? 80 : 30) &&
  11431. + (defensiveStance == true || (stancetimer <= diff && !SS)))
  11432. + {
  11433. + if (!master->isInCombat() && master->getAttackers().empty() && master->isMoving())
  11434. + {
  11435. + float mydist = me->GetExactDist(master);
  11436. + if (mydist < 25 && mydist > 18 && (defensiveStance == true || stanceChange(diff, 2)))
  11437. + {
  11438. + temptimer = GC_Timer;
  11439. + if (doCast(master, INTERVENE))
  11440. + {
  11441. + //modrage(-10);
  11442. + intervene_cd = INTERVENE_CD;
  11443. + GC_Timer = temptimer;
  11444. + Follow(true);
  11445. + return;
  11446. + }
  11447. + }
  11448. + }
  11449. + //sLog->outBasic("%s: Intervene check.", me->GetName().c_str());
  11450. + Group* gr = master->GetGroup();
  11451. + if (!gr)
  11452. + {
  11453. + if (GetHealthPCT(master) < 95 && !master->getAttackers().empty() &&
  11454. + me->getAttackers().size() <= master->getAttackers().size())
  11455. + {
  11456. + float dist = me->GetExactDist(master);
  11457. + if (dist > 25 || dist < 10) return;
  11458. + if (!(defensiveStance == true || stanceChange(diff, 2))) return;
  11459. + temptimer = GC_Timer;
  11460. + if (doCast(master, INTERVENE))
  11461. + {
  11462. + //modrage(-10);
  11463. + intervene_cd = INTERVENE_CD;
  11464. + GC_Timer = temptimer;
  11465. + return;
  11466. + }
  11467. + }
  11468. + }
  11469. + else
  11470. + {
  11471. + bool Bots = false;
  11472. + float dist;
  11473. + for (GroupReference* itr = gr->GetFirstMember(); itr != NULL; itr = itr->next())
  11474. + {
  11475. + Player* tPlayer = itr->getSource();
  11476. + if (!tPlayer) continue;
  11477. + if (tPlayer->FindMap() != me->GetMap()) continue;
  11478. + if (!tPlayer->IsInWorld() || tPlayer->IsBeingTeleported()) continue;
  11479. + //sLog->outBasic("checking player %s", tPlayer->GetName().c_str());
  11480. + if (tPlayer->HaveBot())
  11481. + Bots = true;
  11482. + if (tPlayer->isDead() || GetHealthPCT(tPlayer) > 90 || tank == tPlayer) continue;
  11483. + //sLog->outBasic("alive and health < 80%");
  11484. + if (tPlayer->getAttackers().size() < me->getAttackers().size()) continue;
  11485. + //sLog->outBasic("attackers checked");
  11486. + dist = me->GetExactDist(tPlayer);
  11487. + if (dist > 25 || dist < 10) continue;
  11488. + //sLog->outBasic("and whithin reach");
  11489. + if ((defensiveStance == true || stanceChange(diff, 2)))
  11490. + {
  11491. + //sLog->outBasic("defensive stance acuired, attempt cast");
  11492. + temptimer = GC_Timer;
  11493. + if (doCast(tPlayer, INTERVENE))
  11494. + {
  11495. + //sLog->outBasic("cast succeed");
  11496. + //modrage(-10);
  11497. + intervene_cd = INTERVENE_CD;
  11498. + GC_Timer = temptimer;
  11499. + return;
  11500. + }
  11501. + }
  11502. + }
  11503. + if (!Bots) return;
  11504. + for (GroupReference* itr = gr->GetFirstMember(); itr != NULL; itr = itr->next())
  11505. + {
  11506. + Player* tPlayer = itr->getSource();
  11507. + if (!tPlayer || !tPlayer->HaveBot()) continue;
  11508. + if (tPlayer->FindMap() != me->GetMap()) continue;
  11509. + if (!tPlayer->IsInWorld() || tPlayer->IsBeingTeleported()) continue;
  11510. + for (uint8 i = 0; i != tPlayer->GetMaxNpcBots(); ++i)
  11511. + {
  11512. + Creature* bot = tPlayer->GetBotMap(i)->_Cre();
  11513. + if (!bot || bot == me || bot->isDead()) continue;
  11514. + if (GetHealthPCT(bot) > 90 || tank == bot) continue;
  11515. + dist = me->GetExactDist(bot);
  11516. + if (dist > 25 || dist < 10) continue;
  11517. + if (bot->getAttackers().size() <= me->getAttackers().size()) continue;
  11518. + if ((defensiveStance == true || stanceChange(diff, 2)))
  11519. + {
  11520. + //sLog->outBasic("defensive stance acuired, attempt cast");
  11521. + temptimer = GC_Timer;
  11522. + if (doCast(bot, INTERVENE))
  11523. + {
  11524. + //sLog->outBasic("cast succeed");
  11525. + //modrage(-10);
  11526. + intervene_cd = INTERVENE_CD/2; //half for bot
  11527. + GC_Timer = temptimer;
  11528. + return;
  11529. + }
  11530. + }
  11531. + }
  11532. + }
  11533. + }
  11534. + }
  11535. + }
  11536. +
  11537. + void SpellHitTarget(Unit* target, SpellInfo const* spell)
  11538. + {
  11539. + switch (spell->Id)
  11540. + {
  11541. + case OVERPOWER_1:
  11542. + if (target->GetTypeId() != TYPEID_UNIT || //only creatures lol
  11543. + !UNRELENTING_ASSAULT)
  11544. + return;
  11545. + if (target->HasUnitState(UNIT_STATE_CASTING))
  11546. + {
  11547. + uint32 spell = 0;
  11548. + if (me->HasAura(UNRELENTING_ASSAULT2))
  11549. + spell = UNRELENTING_ASSAULT_SPELL2;
  11550. + else if (me->HasAura(UNRELENTING_ASSAULT1))
  11551. + spell = UNRELENTING_ASSAULT_SPELL1;
  11552. + if (!spell)
  11553. + return;
  11554. + target->CastSpell(target, spell, true);
  11555. + }
  11556. + break;
  11557. + default:
  11558. + break;
  11559. + }
  11560. + }
  11561. +
  11562. + bool stanceChange(uint32 diff, uint8 stance)
  11563. + {
  11564. + if (!(stancetimer <= diff) || !stance)
  11565. + return false;
  11566. +
  11567. + if (stance == 5)
  11568. + {
  11569. + switch (urand(0,1))
  11570. + {
  11571. + case 0:
  11572. + stance = 1;
  11573. + break;
  11574. + case 1:
  11575. + stance = me->getLevel() < 30 ? 1 : 3;
  11576. + break;
  11577. + }
  11578. + }
  11579. + if (stance == 2 && (me->getLevel() < 10 || SS))
  11580. + return false;
  11581. + if (stance == 3 && me->getLevel() < 30)
  11582. + return false;
  11583. +
  11584. + temptimer = GC_Timer;
  11585. + uint32 temprage = 0;
  11586. + uint32 myrage = me->GetPower(POWER_RAGE);
  11587. + if (me->getLevel() >= 15)
  11588. + temprage = myrage > 250 ? 250 : myrage;
  11589. + else if (me->getLevel() >= 10)
  11590. + temprage = myrage > 100 ? 100 : myrage;
  11591. + switch (stance)
  11592. + {
  11593. + case 1:
  11594. + if (doCast(me, BATTLESTANCE))
  11595. + {
  11596. + if (me->HasAura(BATTLESTANCE))
  11597. + {
  11598. + battleStance = true;
  11599. + defensiveStance = false;
  11600. + berserkerStance = false;
  11601. + me->SetPower(POWER_RAGE, temprage);
  11602. + stancetimer = 2100 - me->getLevel()*20;//2100-1600 on 80
  11603. + GC_Timer = temptimer;
  11604. + return true;
  11605. + }
  11606. + }
  11607. + break;
  11608. + case 2:
  11609. + if (doCast(me, DEFENSIVESTANCE))
  11610. + {
  11611. + if (me->HasAura(DEFENSIVESTANCE))
  11612. + {
  11613. + defensiveStance = true;
  11614. + battleStance = false;
  11615. + berserkerStance = false;
  11616. + me->SetPower(POWER_RAGE, temprage);
  11617. + stancetimer = 2100 - me->getLevel()*20;//2100-1600 on 80
  11618. + GC_Timer = temptimer;
  11619. + return true;
  11620. + }
  11621. + }
  11622. + break;
  11623. + case 3:
  11624. + if (doCast(me, BERSERKERSTANCE))
  11625. + {
  11626. + if (me->HasAura(BERSERKERSTANCE))
  11627. + {
  11628. + berserkerStance = true;
  11629. + battleStance = false;
  11630. + defensiveStance = false;
  11631. + me->SetPower(POWER_RAGE, temprage);
  11632. + stancetimer = 2100 - me->getLevel()*20;//2100-1600 on 80
  11633. + GC_Timer = temptimer;
  11634. + return true;
  11635. + }
  11636. + }
  11637. + break;
  11638. + default:
  11639. + break;
  11640. + }
  11641. + GC_Timer = temptimer;
  11642. + return false;
  11643. + }
  11644. +
  11645. + void ApplyClassDamageMultiplierMelee(int32& damage, SpellNonMeleeDamage& /*damageinfo*/, SpellInfo const* spellInfo, WeaponAttackType /*attackType*/, bool& crit) const
  11646. + {
  11647. + uint32 spellId = spellInfo->Id;
  11648. + uint8 lvl = me->getLevel();
  11649. + float fdamage = float(damage);
  11650. + //1) apply additional crit chance. This additional chance roll will replace original (balance safe)
  11651. + if (!crit)
  11652. + {
  11653. + float aftercrit = 0.f;
  11654. + //Incite: 15% additional critical chance for Cleave, Heroic Strike and Thunder Clap
  11655. + if (lvl >= 15 && spellId == CLEAVE /*|| spellId == HEROICSTRIKE || spellId == THUNDERCLAP*/)
  11656. + aftercrit += 15.f;
  11657. + //Improved Overpower: 50% additional critical chance for Overpower
  11658. + if (lvl >= 20 && spellId == OVERPOWER)
  11659. + aftercrit += 50.f;
  11660. +
  11661. + //second roll (may be illogical)
  11662. + if (aftercrit > 0.f)
  11663. + crit = roll_chance_f(aftercrit);
  11664. + }
  11665. +
  11666. + //2) apply bonus damage mods
  11667. + float pctbonus = 0.0f;
  11668. + if (crit)
  11669. + {
  11670. + //!!!Melee spell damage is not yet critical, all reduced by half
  11671. + //Impale: 20% crit damage bonus for all abilities
  11672. + if (lvl >= 20)
  11673. + pctbonus += 0.10f;
  11674. + }
  11675. + //Improved Rend: 20% bonus damage for Rend
  11676. + if (spellId == REND)
  11677. + pctbonus += 0.2f;
  11678. + //Improved Whirlwind: 20% bonus damage for Whirlwind
  11679. + if (lvl >= 40 && spellId == WHIRLWIND)
  11680. + pctbonus += 0.2f;
  11681. + //Glyph of Mortal Strike: 10% bonus damage for Mortal Strike
  11682. + if (lvl >= 40 && spellId == MORTALSTRIKE)
  11683. + pctbonus += 0.1f;
  11684. + //Unrelenting Assault (part 2): 20% bonus damage for Overpower and Revenge
  11685. + if (lvl >= 45 && (spellId == OVERPOWER/* || spellId == REVENGE*/))
  11686. + pctbonus += 0.2f;
  11687. + //Improved Mortal Strike: 10% bonus damage for Mortal Strike
  11688. + if (lvl >= 45 && spellId == MORTALSTRIKE)
  11689. + pctbonus += 0.1f;
  11690. + //Undending Fury: 10% bonus damage for Whirlwind, Slam and Bloodthirst
  11691. + if (lvl >= 55 && (spellId == WHIRLWIND || spellId == SLAM /*|| spellId == BLOODTHIRST*/))
  11692. + pctbonus += 0.1f;
  11693. +
  11694. + damage = int32(fdamage * (1.0f + pctbonus));
  11695. + }
  11696. +
  11697. + void SpellHit(Unit* caster, SpellInfo const* spell)
  11698. + {
  11699. + OnSpellHit(caster, spell);
  11700. + }
  11701. +
  11702. + void DamageTaken(Unit* u, uint32& /*damage*/)
  11703. + {
  11704. + OnOwnerDamagedBy(u);
  11705. + }
  11706. +
  11707. + void OwnerAttackedBy(Unit* u)
  11708. + {
  11709. + OnOwnerDamagedBy(u);
  11710. + }
  11711. +
  11712. + void Reset()
  11713. + {
  11714. + slam_cd = 0;
  11715. + regen_cd = 20000;
  11716. + sweeping_strikes_cd = 0;
  11717. + charge_cd = 0;
  11718. + deathwish_cd = 0;
  11719. + mortalStrike_cd = 0;
  11720. + overpower_cd = 0;
  11721. + uber_cd = 0;
  11722. + berserkerRage_cd = 0;
  11723. + battleShout_cd = 0;
  11724. + intercept_cd = 0;
  11725. + intimidatingShout_cd = 0;
  11726. + pummel_cd = 0;
  11727. + whirlwind_cd = 5000;
  11728. + cleave_cd = 0;
  11729. + bladestorm_cd = 10000;
  11730. + bloodrage_cd = 0;
  11731. + intervene_cd = 0;
  11732. + taunt_cd = 0;
  11733. + sunder_cd = 0;
  11734. + stancetimer = 0;
  11735. + ragetimer = 1500;
  11736. + ragetimer2 = 3000;
  11737. +
  11738. + rendTarget = 0;
  11739. +
  11740. + battleStance = true;
  11741. + defensiveStance = false;
  11742. + berserkerStance = false;
  11743. +
  11744. + rageIncomeMult = sWorld->getRate(RATE_POWER_RAGE_INCOME);
  11745. + rageLossMult = sWorld->getRate(RATE_POWER_RAGE_LOSS);
  11746. + me->setPowerType(POWER_RAGE);
  11747. + rage = 0;
  11748. +
  11749. + if (master)
  11750. + {
  11751. + setStats(CLASS_WARRIOR, me->getRace(), master->getLevel(), true);
  11752. + ApplyClassPassives();
  11753. + ApplyPassives(CLASS_WARRIOR);
  11754. + //mob generates abnormal amounts rage so increase/reduce rate with level(from 188% down to 30% at level 80)//not seems to work
  11755. + //for (int8 i = 0; i < 3; ++i)
  11756. + // me->ApplyEffectModifiers(sSpellMgr->GetSpellInfo(29623), i, float(90 - master->getLevel()*2));
  11757. + }
  11758. + }
  11759. +
  11760. + void ReduceCD(uint32 diff)
  11761. + {
  11762. + CommonTimers(diff);
  11763. + if (regen_cd > diff) regen_cd -= diff;
  11764. + if (slam_cd > diff) slam_cd -= diff;
  11765. + if (battleShout_cd > diff) battleShout_cd -= diff;
  11766. + if (sweeping_strikes_cd > diff) sweeping_strikes_cd -= diff;
  11767. + if (deathwish_cd > diff) deathwish_cd -= diff;
  11768. + if (mortalStrike_cd > diff) mortalStrike_cd -= diff;
  11769. + if (overpower_cd > diff) overpower_cd -= diff;
  11770. + if (uber_cd > diff) uber_cd -= diff;
  11771. + if (berserkerRage_cd > diff) berserkerRage_cd -= diff;
  11772. + if (charge_cd > diff) charge_cd -= diff;
  11773. + if (intercept_cd > diff) intercept_cd -= diff;
  11774. + if (intimidatingShout_cd > diff) intimidatingShout_cd -= diff;
  11775. + if (pummel_cd > diff) pummel_cd -= diff;
  11776. + if (whirlwind_cd > diff) whirlwind_cd -= diff;
  11777. + if (bladestorm_cd > diff) bladestorm_cd -= diff;
  11778. + if (cleave_cd > diff) cleave_cd -= diff;
  11779. + if (bloodrage_cd > diff) bloodrage_cd -= diff;
  11780. + if (intervene_cd > diff) intervene_cd -= diff;
  11781. + if (taunt_cd > diff) taunt_cd -= diff;
  11782. + if (sunder_cd > diff) sunder_cd -= diff;
  11783. +
  11784. + if (stancetimer > diff) stancetimer -= diff;
  11785. + if (ragetimer > diff) ragetimer -= diff;
  11786. + if (ragetimer2 > diff) ragetimer2 -= diff;
  11787. + }
  11788. +
  11789. + bool CanRespawn()
  11790. + {return false;}
  11791. +
  11792. + void InitSpells()
  11793. + {
  11794. + uint8 lvl = me->getLevel();
  11795. + //CHALLENGING_SHOUT = InitSpell(me, CHALLENGING_SHOUT_1);
  11796. + INTIMIDATING_SHOUT = InitSpell(me, INTIMIDATING_SHOUT_1);
  11797. + ENRAGED_REGENERATION = InitSpell(me, ENRAGED_REGENERATION_1);
  11798. + CHARGE = InitSpell(me, CHARGE_1);
  11799. + OVERPOWER = InitSpell(me, OVERPOWER_1);
  11800. + /*Quest*/TAUNT = lvl >= 10 ? TAUNT_1 : 0;
  11801. + //DISARM = InitSpell(DISARM_1);
  11802. + BLOODRAGE = InitSpell(me, BLOODRAGE_1);
  11803. + BERSERKERRAGE = InitSpell(me, BERSERKERRAGE_1);
  11804. + INTERCEPT = InitSpell(me, INTERCEPT_1);
  11805. + CLEAVE = InitSpell(me, CLEAVE_1);
  11806. + HAMSTRING = InitSpell(me, HAMSTRING_1);
  11807. + INTERVENE = InitSpell(me, INTERVENE_1);
  11808. + WHIRLWIND = InitSpell(me, WHIRLWIND_1);
  11809. + /*Talent*/BLADESTORM = lvl >= 60 ? BLADESTORM_1 : 0;
  11810. + BATTLESHOUT = InitSpell(me, BATTLESHOUT_1);
  11811. + REND = InitSpell(me, REND_1);
  11812. + EXECUTE = InitSpell(me, EXECUTE_1);
  11813. + PUMMEL = InitSpell(me, PUMMEL_1);
  11814. + /*Talent*/MORTALSTRIKE = lvl >= 40 ? InitSpell(me, MORTALSTRIKE_1) : 0;
  11815. + SLAM = InitSpell(me, SLAM_1);
  11816. + /*Quest*/SUNDER = lvl >= 10 ? InitSpell(me, SUNDER_1) : 0;
  11817. + /*Talent*/SWEEPING_STRIKES = lvl >= 30 ? SWEEPING_STRIKES_1 : 0;
  11818. + BATTLESTANCE = BATTLESTANCE_1;
  11819. + /*Quest*/DEFENSIVESTANCE = lvl >= 10 ? DEFENSIVESTANCE_1 : 0;
  11820. + /*Quest*/BERSERKERSTANCE = lvl >= 30 ? BERSERKERSTANCE_1 : 0;
  11821. + RECKLESSNESS = InitSpell(me, RECKLESSNESS_1);
  11822. + RETALIATION = InitSpell(me, RETALIATION_1);
  11823. + /*Talent*/DEATHWISH = lvl >= 30 ? DEATHWISH_1 : 0;
  11824. + }
  11825. +
  11826. + void ApplyClassPassives()
  11827. + {
  11828. + uint8 level = master->getLevel();
  11829. + if (level >= 70)
  11830. + RefreshAura(WC5); //10%
  11831. + else if (level >= 68)
  11832. + RefreshAura(WC4); //8%
  11833. + else if (level >= 66)
  11834. + RefreshAura(WC3); //6%
  11835. + else if (level >= 64)
  11836. + RefreshAura(WC2); //4%
  11837. + else if (level >= 62)
  11838. + RefreshAura(WC1); //2%
  11839. + if (level >= 39)
  11840. + RefreshAura(FLURRY5); //30%
  11841. + else if (level >= 38)
  11842. + RefreshAura(FLURRY4); //24%
  11843. + else if (level >= 37)
  11844. + RefreshAura(FLURRY3); //18%
  11845. + else if (level >= 36)
  11846. + RefreshAura(FLURRY2); //12%
  11847. + else if (level >= 35)
  11848. + RefreshAura(FLURRY1); //6%
  11849. + if (level >= 60)
  11850. + RefreshAura(SWORD_SPEC5,2);//twice
  11851. + else if (level >= 50)
  11852. + RefreshAura(SWORD_SPEC5);//once
  11853. + else if (level >= 45)
  11854. + RefreshAura(SWORD_SPEC4);//once
  11855. + else if (level >= 40)
  11856. + RefreshAura(SWORD_SPEC3);//once
  11857. + else if (level >= 35)
  11858. + RefreshAura(SWORD_SPEC2);//once
  11859. + else if (level >= 30)
  11860. + RefreshAura(SWORD_SPEC1);//once
  11861. + if (level >= 60)
  11862. + RefreshAura(RAMPAGE);
  11863. + if (level >= 55)
  11864. + RefreshAura(TRAUMA2);//30%
  11865. + else if (level >= 35)
  11866. + RefreshAura(TRAUMA1);//15%
  11867. + if (level >= 50)
  11868. + {
  11869. + RefreshAura(UNRELENTING_ASSAULT2);
  11870. + UNRELENTING_ASSAULT = true;
  11871. + }
  11872. + else if (level >= 45)
  11873. + {
  11874. + RefreshAura(UNRELENTING_ASSAULT1);
  11875. + UNRELENTING_ASSAULT = true;
  11876. + }
  11877. + if (level >= 45)
  11878. + RefreshAura(BLOOD_FRENZY);
  11879. + if (level >= 40)
  11880. + RefreshAura(SECOND_WIND);
  11881. + if (level >= 40)
  11882. + RefreshAura(TOUGHNESS,2);//-60%
  11883. + else if (level >= 15)
  11884. + RefreshAura(TOUGHNESS);//-30%
  11885. + if (level >= 40)
  11886. + RefreshAura(IMP_HAMSTRING,2);//30%
  11887. + else if (level >= 35)
  11888. + RefreshAura(IMP_HAMSTRING);//15%
  11889. + if (level >= 30)
  11890. + RefreshAura(TASTE_FOR_BLOOD3);//100%
  11891. + else if (level >= 28)
  11892. + RefreshAura(TASTE_FOR_BLOOD2);//66%
  11893. + else if (level >= 25)
  11894. + RefreshAura(TASTE_FOR_BLOOD1);//33%
  11895. + if (level >= 30)
  11896. + RefreshAura(BLOOD_CRAZE3);
  11897. + else if (level >= 25)
  11898. + RefreshAura(BLOOD_CRAZE2);
  11899. + else if (level >= 20)
  11900. + RefreshAura(BLOOD_CRAZE1);
  11901. + //BloodRage Absorb
  11902. + if (level >= 60)
  11903. + RefreshAura(WARRIOR_T10_4P);
  11904. + }
  11905. +
  11906. + private:
  11907. + uint32
  11908. + /*Shouts*/INTIMIDATING_SHOUT, BATTLESHOUT, CHALLENGING_SHOUT,
  11909. + /*Charges*/CHARGE, INTERCEPT, INTERVENE,
  11910. + /*Damage*/OVERPOWER, CLEAVE, REND, EXECUTE, WHIRLWIND, BLADESTORM, MORTALSTRIKE, SLAM,
  11911. + /*Stances*/BATTLESTANCE, DEFENSIVESTANCE, BERSERKERSTANCE,
  11912. + /*Ubers*/RECKLESSNESS, RETALIATION, DEATHWISH,
  11913. + /*Others*/TAUNT, DISARM, BLOODRAGE, ENRAGED_REGENERATION, BERSERKERRAGE, HAMSTRING, PUMMEL, SUNDER, SWEEPING_STRIKES;
  11914. +
  11915. + //CDs/Timers/misc
  11916. +/*shts*/uint32 battleShout_cd, intimidatingShout_cd;
  11917. +/*chrg*/uint32 charge_cd, intercept_cd, intervene_cd;;
  11918. + /*Dmg*/uint32 mortalStrike_cd, overpower_cd, slam_cd, whirlwind_cd, cleave_cd, bladestorm_cd;
  11919. +/*else*/uint32 regen_cd, sweeping_strikes_cd, deathwish_cd, uber_cd, berserkerRage_cd, pummel_cd,
  11920. + bloodrage_cd, taunt_cd, sunder_cd;
  11921. +/*tmrs*/uint32 stancetimer, ragetimer, ragetimer2;
  11922. +/*misc*/uint64 rendTarget;
  11923. +/*misc*/uint32 rage;
  11924. +/*misc*/float rageIncomeMult, rageLossMult;
  11925. +/*Chck*/bool battleStance, defensiveStance, berserkerStance, SS, UNRELENTING_ASSAULT;
  11926. +
  11927. + enum WarriorBaseSpells
  11928. + {
  11929. + //CHALLENGING_SHOUT_1 = 1161,
  11930. + INTIMIDATING_SHOUT_1 = 5246,
  11931. + ENRAGED_REGENERATION_1 = 55694,
  11932. + CHARGE_1 = 11578,
  11933. + OVERPOWER_1 = 7384,
  11934. + TAUNT_1 = 355,
  11935. + //DISARM_1 = 676,
  11936. + BLOODRAGE_1 = 2687,
  11937. + BERSERKERRAGE_1 = 18499,
  11938. + INTERCEPT_1 = 20252,
  11939. + CLEAVE_1 = 845,//59992,
  11940. + HAMSTRING_1 = 1715,
  11941. + INTERVENE_1 = 3411,
  11942. + WHIRLWIND_1 = 1680,
  11943. + BLADESTORM_1 = 46924,//67541,
  11944. + BATTLESHOUT_1 = 6673,
  11945. + REND_1 = 772,
  11946. + EXECUTE_1 = 5308,
  11947. + PUMMEL_1 = 6552,
  11948. + MORTALSTRIKE_1 = 12294,
  11949. + SLAM_1 = 1464,
  11950. + SUNDER_1 = 7386,//16145,
  11951. + SWEEPING_STRIKES_1 = 12328,
  11952. + BATTLESTANCE_1 = 2457,//7165, //2457, original warrior one
  11953. + DEFENSIVESTANCE_1 = 71,//71, original warrior one
  11954. + BERSERKERSTANCE_1 = 2458,//7366, //2458, original warrior spell
  11955. + RECKLESSNESS_1 = 13847,//1719, original warrior spell
  11956. + RETALIATION_1 = 22857,//20230, original warrior spell
  11957. + DEATHWISH_1 = 12292,
  11958. + };
  11959. + enum WarriorPassives
  11960. + {
  11961. + //Talents
  11962. + WC1 /*WRECKING CREW1*/ = 46867,
  11963. + WC2 /*WRECKING CREW2*/ = 56611,
  11964. + WC3 /*WRECKING CREW3*/ = 56612,
  11965. + WC4 /*WRECKING CREW4*/ = 56613,
  11966. + WC5 /*WRECKING CREW5*/ = 56614,
  11967. + FLURRY1 = 16256,
  11968. + FLURRY2 = 16281,
  11969. + FLURRY3 = 16282,
  11970. + FLURRY4 = 16283,
  11971. + FLURRY5 = 16284,
  11972. + SWORD_SPEC1 = 12281,
  11973. + SWORD_SPEC2 = 12812,
  11974. + SWORD_SPEC3 = 12813,
  11975. + SWORD_SPEC4 = 12814,
  11976. + SWORD_SPEC5 = 12815,
  11977. + BLOOD_CRAZE1 = 16487,
  11978. + BLOOD_CRAZE2 = 16489,
  11979. + BLOOD_CRAZE3 = 16492,
  11980. + TASTE_FOR_BLOOD1 = 56636,
  11981. + TASTE_FOR_BLOOD2 = 56637,
  11982. + TASTE_FOR_BLOOD3 = 56638,
  11983. + UNRELENTING_ASSAULT1 = 46859,
  11984. + UNRELENTING_ASSAULT2 = 46860,
  11985. + TRAUMA1 = 46854,
  11986. + TRAUMA2 = 46855,
  11987. + BLOOD_FRENZY = 29859,
  11988. + RAMPAGE = 29801,
  11989. + SECOND_WIND = 29838,//rank 2
  11990. + TOUGHNESS = 12764,//rank 5
  11991. + IMP_HAMSTRING = 23695,//rank 3
  11992. + //other
  11993. + WARRIOR_T10_4P = 70844,
  11994. + };
  11995. + enum WarriorSpecial
  11996. + {
  11997. + TASTE_FOR_BLOOD_BUFF = 60503,
  11998. + UNRELENTING_ASSAULT_SPELL1 = 64849,
  11999. + UNRELENTING_ASSAULT_SPELL2 = 64850,
  12000. + };
  12001. + enum WarriorCooldowns
  12002. + {
  12003. + ENRAGED_REGENERATION_CD = 90000, //1.5 min
  12004. + SWEEPING_STRIKES_CD = 30000,
  12005. + CHARGE_CD = 15000,
  12006. + DEATHWISH_CD = 90000, //1.5 min
  12007. + MORTALSTRIKE_CD = 7000,
  12008. + UBER_CD = 150000, //RETALIATION_RECKLESSNESS_SHIELDWALL 2.5 min NEED SEPARATE
  12009. + BERSERKERRAGE_CD = 25000,
  12010. + INTERCEPT_CD = 15000,
  12011. + INTIMIDATINGSHOUT_CD = 45000,
  12012. + PUMMEL_CD = 10000,
  12013. + WHIRLWIND_CD = 8000,
  12014. + BLADESTORM_CD = 60000,
  12015. + BLOODRAGE_CD = 40000,
  12016. + //DISARM_CD = 40000,
  12017. + INTERVENE_CD = 25000,
  12018. + BATTLESHOUT_CD = 25000,
  12019. + //SPELLREFLECTION_CD = 8000,
  12020. + TAUNT_CD = 8000,
  12021. + SUNDER_CD = 7000,
  12022. + };
  12023. + };
  12024. +};
  12025. +
  12026. +void AddSC_warrior_bot()
  12027. +{
  12028. + new warrior_bot();
  12029. +}
  12030. diff --git a/src/server/game/AI/NpcBots/botcommands.cpp b/src/server/game/AI/NpcBots/botcommands.cpp
  12031. new file mode 100644
  12032. index 0000000..bc106d6
  12033. --- /dev/null
  12034. +++ b/src/server/game/AI/NpcBots/botcommands.cpp
  12035. @@ -0,0 +1,851 @@
  12036. +/*
  12037. +Name: script_bot_commands
  12038. +%Complete: ???
  12039. +Comment: Playerbot and Npcbot related commands
  12040. +Category: commandscripts/custom/
  12041. +*/
  12042. +
  12043. +#include "bot_ai.h"
  12044. +#include "bp_ai.h"
  12045. +#include "bp_mgr.h"
  12046. +#include "Chat.h"
  12047. +#include "Config.h"
  12048. +#include "Group.h"
  12049. +#include "Language.h"
  12050. +#include "Player.h"
  12051. +#include "ScriptMgr.h"
  12052. +
  12053. +class script_bot_commands : public CommandScript
  12054. +{
  12055. +public:
  12056. + script_bot_commands() : CommandScript("script_bot_commands") { }
  12057. +
  12058. + ChatCommand* GetCommands() const
  12059. + {
  12060. + static ChatCommand npcbotCommandTable[] =
  12061. + {
  12062. + { "info", SEC_PLAYER, false, &HandleNpcBotInfoCommand, "", NULL },
  12063. + { "add", SEC_PLAYER, false, &HandleNpcBotAddCommand, "", NULL },
  12064. + { "revive", SEC_MODERATOR, false, &HandleNpcBotReviveCommand, "", NULL },
  12065. + { "remove", SEC_PLAYER, false, &HandleNpcBotRemoveCommand, "", NULL },
  12066. + { "reset", SEC_PLAYER, false, &HandleNpcBotResetCommand, "", NULL },
  12067. + { "command", SEC_PLAYER, false, &HandleNpcBotCommandCommand, "", NULL },
  12068. + { "distance", SEC_PLAYER, false, &HandleNpcBotDistanceCommand, "", NULL },
  12069. + //{ "reloadequips", SEC_ADMINISTRATOR, false, &HandleReloadEquipsCommand, "", NULL },
  12070. + { NULL, 0, false, NULL, "", NULL }
  12071. + };
  12072. + static ChatCommand commandTable[] =
  12073. + {
  12074. + { "bot", SEC_ADMINISTRATOR, false, &HandlePlayerbotCommand, "", NULL },
  12075. + { "maintank", SEC_PLAYER, false, &HandleMainTankCommand, "", NULL },
  12076. + { "mt", SEC_PLAYER, false, &HandleMainTankCommand, "", NULL },
  12077. + { "npcbot", SEC_PLAYER, false, NULL, "", npcbotCommandTable },
  12078. + { NULL, 0, false, NULL, "", NULL }
  12079. + };
  12080. + return commandTable;
  12081. + }
  12082. +
  12083. + //static bool HandleReloadEquipsCommand(ChatHandler* handler, const char* /*args*/)
  12084. + //{
  12085. + // sLog->outInfo(LOG_FILTER_GENERAL, "Re-Loading Creature Equips...");
  12086. + // sObjectMgr->LoadEquipmentTemplates();
  12087. + // handler->SendGlobalGMSysMessage("DB table `creature_equip_template` (creature equipment) reloaded.");
  12088. + // return true;
  12089. + //}
  12090. +
  12091. + static bool HandlePlayerbotCommand(ChatHandler* handler, const char* args)
  12092. + {
  12093. + if (!handler->GetSession())
  12094. + {
  12095. + handler->PSendSysMessage("You may only add bots from an active session");
  12096. + handler->SetSentErrorMessage(true);
  12097. + return false;
  12098. + }
  12099. +
  12100. + bool allowPBots = ConfigMgr::GetBoolDefault("Bot.EnablePlayerBots", false);
  12101. + if (allowPBots == false)
  12102. + {
  12103. + handler->PSendSysMessage("Playerbot system is disabled");
  12104. + handler->SetSentErrorMessage(true);
  12105. + return false;
  12106. + }
  12107. +
  12108. + if (!*args)
  12109. + {
  12110. + handler->PSendSysMessage("usage: add PLAYERNAME or remove PLAYERNAME");
  12111. + handler->SetSentErrorMessage(true);
  12112. + return false;
  12113. + }
  12114. + Player* player = handler->GetSession()->GetPlayer();
  12115. +
  12116. + char* cmd = strtok ((char*)args, " ");
  12117. + if (!cmd)
  12118. + {
  12119. + handler->PSendSysMessage("usage: add PLAYERNAME or remove PLAYERNAME");
  12120. + handler->SetSentErrorMessage(true);
  12121. + return false;
  12122. + }
  12123. + std::string cmdStr = cmd;
  12124. +
  12125. + //if (cmdStr.compare("tele") == 0 || cmdStr.compare("teleport") == 0 || cmdStr.compare("summ") == 0 || cmdStr.compare("summon") == 0)
  12126. + //{
  12127. + // if (handler->GetSession()->m_playerBots.empty())
  12128. + // {
  12129. + // handler->PSendSysMessage("You Have No Playerbots!");
  12130. + // handler->SetSentErrorMessage(true);
  12131. + // return false;
  12132. + // }
  12133. + // PlayerbotChatHandler ch(player);
  12134. + // for (PlayerBotMap::const_iterator itr = handler->GetSession()->GetPlayerBotsBegin(); itr != handler->GetSession()->GetPlayerBotsEnd(); ++itr)
  12135. + // {
  12136. + // Player* botPlayer = itr->second;
  12137. + // if (!botPlayer)
  12138. + // continue;
  12139. + // ch.teleport(*botPlayer);
  12140. + // //botPlayer->TeleportTo(*player);
  12141. + // }
  12142. + // return true;
  12143. + //}
  12144. + char* charname = strtok (NULL, " ");
  12145. + if (!charname)
  12146. + {
  12147. + handler->PSendSysMessage("usage: add PLAYERNAME or remove PLAYERNAME");
  12148. + handler->SetSentErrorMessage(true);
  12149. + return false;
  12150. + }
  12151. + std::string charnameStr = charname;
  12152. +
  12153. + PlayerbotMgr* mgr = player->GetPlayerbotMgr();
  12154. + if (!mgr)
  12155. + {
  12156. + mgr = new PlayerbotMgr(player);
  12157. + player->SetPlayerbotMgr(mgr);
  12158. + }
  12159. +
  12160. + uint64 guid;
  12161. +
  12162. + if (charnameStr.compare("all") != 0)
  12163. + {
  12164. + if (!normalizePlayerName(charnameStr))
  12165. + return false;
  12166. +
  12167. + guid = sObjectMgr->GetPlayerGUIDByName(charnameStr.c_str());
  12168. + if (guid == 0 || (guid == handler->GetSession()->GetPlayer()->GetGUID()))
  12169. + {
  12170. + handler->PSendSysMessage(LANG_PLAYER_NOT_FOUND);
  12171. + handler->SetSentErrorMessage(true);
  12172. + return false;
  12173. + }
  12174. +
  12175. + uint32 accountId = sObjectMgr->GetPlayerAccountIdByGUID(guid);
  12176. + if (accountId != handler->GetSession()->GetAccountId())
  12177. + {
  12178. + handler->PSendSysMessage("You may only add bots from the same account.");
  12179. + handler->SetSentErrorMessage(true);
  12180. + return false;
  12181. + }
  12182. + }
  12183. +
  12184. + if (cmdStr.compare("add") == 0 || cmdStr.compare("login") == 0)
  12185. + {
  12186. + if (charnameStr.compare("all") == 0)
  12187. + {
  12188. + std::string plName;
  12189. + std::list<std::string>* names = new std::list<std::string>;
  12190. + uint32 accId = player->GetSession()->GetAccountId();
  12191. + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PLAYERBOTS);
  12192. + stmt->setUInt32(0, accId);
  12193. + stmt->setUInt32(1, player->GetGUIDLow());
  12194. + PreparedQueryResult results = CharacterDatabase.Query(stmt);
  12195. + //CharacterDatabase.PQuery("SELECT name FROM characters WHERE account = '%u' AND guid != '%u'", accId, player->GetGUIDLow());
  12196. + if (results)
  12197. + {
  12198. + do
  12199. + {
  12200. + Field* fields = results->Fetch();
  12201. + plName = fields[0].GetString();
  12202. + if (ObjectAccessor::FindPlayerByName(plName))
  12203. + continue;
  12204. + names->insert(names->end(), plName);
  12205. + } while(results->NextRow());
  12206. + }
  12207. + std::list<std::string>::iterator iter,next;
  12208. + for (iter = names->begin(); iter != names->end(); iter++)
  12209. + {
  12210. + std::stringstream arg;
  12211. + arg << "add " << (*iter).c_str();
  12212. + HandlePlayerbotCommand(handler, arg.str().c_str());
  12213. + }
  12214. + handler->PSendSysMessage("Bots added successfully.");
  12215. + delete names;
  12216. + return true;
  12217. + }
  12218. + else
  12219. + {
  12220. + guid = sObjectMgr->GetPlayerGUIDByName(charnameStr.c_str());
  12221. + if (mgr->GetPlayerBot(guid) != NULL)
  12222. + {
  12223. + handler->PSendSysMessage("Bot already exists in world.");
  12224. + handler->SetSentErrorMessage(true);
  12225. + return false;
  12226. + }
  12227. + mgr->AddPlayerBot(guid);
  12228. + }
  12229. + }
  12230. + else if (cmdStr.compare("remove") == 0 || cmdStr.compare("logout") == 0)
  12231. + {
  12232. + if (charnameStr.compare("all") == 0)
  12233. + {
  12234. + std::list<std::string>* names = new std::list<std::string>;
  12235. + for (PlayerBotMap::const_iterator iter = mgr->GetPlayerBotsBegin(); iter != mgr->GetPlayerBotsEnd(); ++iter)
  12236. + {
  12237. + names->push_back(iter->second->GetName());
  12238. + }
  12239. + std::list<std::string>::iterator iter, next;
  12240. + for (iter = names->begin(); iter != names->end(); iter++)
  12241. + {
  12242. + std::stringstream arg;
  12243. + arg << "remove " << (*iter).c_str();
  12244. + HandlePlayerbotCommand(handler, arg.str().c_str());
  12245. + }
  12246. + delete names;
  12247. + return true;
  12248. + }
  12249. + else
  12250. + {
  12251. + guid = sObjectMgr->GetPlayerGUIDByName(charnameStr.c_str());
  12252. + if (!mgr->GetPlayerBot(guid))
  12253. + {
  12254. + handler->PSendSysMessage("Bot can not be removed because bot does not exist in world.");
  12255. + handler->SetSentErrorMessage(true);
  12256. + return false;
  12257. + }
  12258. + mgr->LogoutPlayerBot(guid);
  12259. + handler->PSendSysMessage("Bot removed successfully.");
  12260. + return true;
  12261. + }
  12262. + }
  12263. + else if (cmdStr == "co" || cmdStr == "combatorder")
  12264. + {
  12265. + Unit* target = NULL;
  12266. + char* orderChar = strtok(NULL, " ");
  12267. + if (!orderChar || mgr->getPlayerbots().empty())
  12268. + {
  12269. + handler->PSendSysMessage("|cffff0000Syntax error:|cffffffff .bot co <botName> <order=reset|tank|assist|heal|protect> [targetPlayer]");
  12270. + handler->SetSentErrorMessage(true);
  12271. + return false;
  12272. + }
  12273. + std::string orderStr = orderChar;
  12274. + if (orderStr == "protect" || orderStr == "assist")
  12275. + {
  12276. + char* targetChar = strtok(NULL, " ");
  12277. + uint64 targetGUID = handler->GetSession()->GetPlayer()->GetSelection();
  12278. + if (!targetChar && !targetGUID)
  12279. + {
  12280. + handler->PSendSysMessage("|cffff0000Combat orders protect and assist expect a target either by selection or by giving target player in command string!");
  12281. + handler->SetSentErrorMessage(true);
  12282. + return false;
  12283. + }
  12284. + if (targetChar)
  12285. + {
  12286. + std::string targetStr = targetChar;
  12287. + targetGUID = sObjectMgr->GetPlayerGUIDByName(targetStr.c_str());
  12288. + }
  12289. + target = ObjectAccessor::GetUnit(*handler->GetSession()->GetPlayer(), targetGUID);
  12290. + if (!target)
  12291. + {
  12292. + handler->PSendSysMessage("|cffff0000Invalid target for combat order protect or assist!");
  12293. + handler->SetSentErrorMessage(true);
  12294. + return false;
  12295. + }
  12296. + }
  12297. + //if (handler->GetSession()->GetPlayerBot(guid) == NULL)
  12298. + //{
  12299. + // handler->PSendSysMessage("|cffff0000Bot can not receive combat order because bot does not exist in world.");
  12300. + // handler->SetSentErrorMessage(true);
  12301. + // return false;
  12302. + //}
  12303. + //if (mgr)
  12304. + //for (PlayerBotMap::const_iterator itr = mgr->GetPlayerBotsBegin(); itr != mgr->GetPlayerBotsEnd(); ++itr)
  12305. + // if (Player* bot = ObjectAccessor::GetPlayer(*player, itr->first))
  12306. + // bot->GetPlayerbotAI()->SetCombatOrderByStr(orderStr, target);
  12307. + }
  12308. + return true;
  12309. + }
  12310. +
  12311. + static bool HandleMainTankCommand(ChatHandler* handler, const char* args)
  12312. + {
  12313. + Group* group = handler->GetSession()->GetPlayer()->GetGroup();
  12314. + if (!group)
  12315. + {
  12316. + handler->PSendSysMessage("Must be in a group to use main tank command.");
  12317. + handler->SetSentErrorMessage(true);
  12318. + return false;
  12319. + }
  12320. + uint64 myguid = handler->GetSession()->GetPlayer()->GetGUID();
  12321. + if (!group->IsLeader(myguid) && !group->IsAssistant(myguid))
  12322. + {
  12323. + handler->PSendSysMessage("you have no permission to set main tank.");
  12324. + handler->SetSentErrorMessage(true);
  12325. + return false;
  12326. + }
  12327. +
  12328. + if (!*args)
  12329. + {
  12330. + if (uint64 selection = handler->GetSession()->GetPlayer()->GetSelection())
  12331. + {
  12332. + if (group->IsMember(selection))
  12333. + {
  12334. + if (Unit* u = ObjectAccessor::FindUnit(selection))
  12335. + {
  12336. + bool isabot = u->GetTypeId() == TYPEID_UNIT && u->ToCreature()->GetIAmABot();
  12337. + if (isabot && group->GetMemberSlots().size() < 3 && handler->GetSession()->GetSecurity() == SEC_PLAYER)
  12338. + {
  12339. + handler->PSendSysMessage("Your party is too small to set a npcbot main tank.");
  12340. + handler->SetSentErrorMessage(true);
  12341. + return false;
  12342. + }
  12343. + group->RemoveUniqueGroupMemberFlag(MEMBER_FLAG_MAINTANK);
  12344. + Group::MemberSlotList const& members = group->GetMemberSlots();
  12345. + for (Group::MemberSlotList::const_iterator itr = members.begin(); itr != members.end(); ++itr)
  12346. + {
  12347. + uint8 flags = itr->flags;
  12348. + if (group->isRaidGroup())
  12349. + {
  12350. + //try to set flags in group (will fail if not raid)
  12351. + group->SetGroupMemberFlag(itr->guid, itr->guid == selection, MEMBER_FLAG_MAINTANK);
  12352. + }
  12353. + else //force flags for non-raid group (DB only) this will allow bots to find tank
  12354. + {
  12355. + if (itr->guid == selection && !(flags & MEMBER_FLAG_MAINTANK))
  12356. + flags |= MEMBER_FLAG_MAINTANK;
  12357. + }
  12358. + //store result if DB
  12359. + if (itr->guid != selection || !group->isRaidGroup())
  12360. + {
  12361. + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GROUP_MEMBER_FLAG);
  12362. + stmt->setUInt8(0, flags);
  12363. + stmt->setUInt32(1, GUID_LOPART(itr->guid));
  12364. + CharacterDatabase.Execute(stmt);
  12365. + }
  12366. + //send result to players and their bots
  12367. + if (!IS_PLAYER_GUID(itr->guid))
  12368. + continue;
  12369. + if (Player* player = ObjectAccessor::FindPlayer(itr->guid))
  12370. + {
  12371. + ChatHandler chp(player->GetSession());
  12372. + chp.PSendSysMessage("Main tank is set to %s.", u->GetName().c_str());
  12373. + player->SetBotTank(selection);
  12374. + if (player->HaveBot())
  12375. + {
  12376. + for (uint8 i = 0; i != player->GetMaxNpcBots(); ++i)
  12377. + {
  12378. + Creature* cre = player->GetBotMap(i)->_Cre();
  12379. + if (cre)
  12380. + cre->SetBotTank(u);
  12381. + }
  12382. + }
  12383. + }
  12384. + }
  12385. + u->HandleEmoteCommand(EMOTE_ONESHOT_CHEER);
  12386. + handler->SetSentErrorMessage(true);
  12387. + return true;
  12388. + }
  12389. + }
  12390. + }
  12391. + if (Unit* unit = bot_ai::GetBotGroupMainTank(group))
  12392. + {
  12393. + bool bot = unit->GetTypeId() == TYPEID_UNIT && unit->ToCreature()->GetIAmABot();
  12394. + handler->PSendSysMessage("Main tank is %s (%s%s).", unit->GetName().c_str(), (bot ? "npcbot" : "player"), (unit->isAlive() ? "" : ", dead"));
  12395. + handler->SetSentErrorMessage(true);
  12396. + return true;
  12397. + }
  12398. + handler->PSendSysMessage(".maintank");
  12399. + handler->PSendSysMessage("Allows to set a main tank in bot party (can be used on npcbots). Determines npcbots' actions");
  12400. + handler->PSendSysMessage("Npcbot maintank also receives damage reduction, avoidance and threat generation bonus");
  12401. + handler->SetSentErrorMessage(true);
  12402. + return true;
  12403. + }
  12404. + else
  12405. + {
  12406. + //clear tank in whole bot party
  12407. + std::string cmdStr = strtok((char*)args, " ");
  12408. + if (!cmdStr.compare("clear") || !cmdStr.compare("cl") || !cmdStr.compare("cle") ||
  12409. + !cmdStr.compare("reset") || !cmdStr.compare("res"))
  12410. + {
  12411. + Group::MemberSlotList const& members = group->GetMemberSlots();
  12412. + for (Group::MemberSlotList::const_iterator itr = members.begin(); itr != members.end(); ++itr)
  12413. + {
  12414. + uint8 flags = itr->flags;
  12415. + if (group->isRaidGroup())
  12416. + {
  12417. + if (flags & MEMBER_FLAG_MAINTANK)
  12418. + group->SetGroupMemberFlag(itr->guid, false, MEMBER_FLAG_MAINTANK);
  12419. + }
  12420. + else
  12421. + {
  12422. + if (itr->flags & MEMBER_FLAG_MAINTANK)
  12423. + flags &= ~MEMBER_FLAG_MAINTANK;
  12424. + }
  12425. + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GROUP_MEMBER_FLAG);
  12426. + stmt->setUInt8(0, flags);
  12427. + stmt->setUInt32(1, GUID_LOPART(itr->guid));
  12428. + CharacterDatabase.Execute(stmt);
  12429. + if (!IS_PLAYER_GUID(itr->guid))
  12430. + continue;
  12431. + Player* player = ObjectAccessor::FindPlayer(itr->guid);
  12432. + if (!player) continue;
  12433. + ChatHandler(player->GetSession()).PSendSysMessage("Main tank has been reset by %s.", handler->GetSession()->GetPlayer()->GetName().c_str());
  12434. + player->SetBotTank(0);
  12435. + if (player->HaveBot())
  12436. + {
  12437. + for (uint8 i = 0; i != player->GetMaxNpcBots(); ++i)
  12438. + {
  12439. + Creature* cre = player->GetBotMap(i)->_Cre();
  12440. + if (cre)
  12441. + cre->SetBotTank(NULL);
  12442. + }
  12443. + }
  12444. + }
  12445. + handler->SetSentErrorMessage(true);
  12446. + return true;
  12447. + }
  12448. + }
  12449. + handler->SetSentErrorMessage(true);
  12450. + return false;
  12451. + }
  12452. +
  12453. + static bool HandleNpcBotInfoCommand(ChatHandler* handler, const char* /*args*/)
  12454. + {
  12455. + Player* owner = handler->GetSession()->GetPlayer();
  12456. + if (!owner->GetSelection())
  12457. + {
  12458. + handler->PSendSysMessage(".npcbot info");
  12459. + handler->PSendSysMessage("Lists NpcBots count of each class owned by selected player. You can use this on self and your Playerbots");
  12460. + handler->SetSentErrorMessage(true);
  12461. + return false;
  12462. + }
  12463. + Player* master = owner->GetSelectedPlayer();
  12464. + if (!master || (master && master != owner && master->GetSession()->m_master != owner))
  12465. + {
  12466. + handler->PSendSysMessage("You should select one of your PlayerBots or self.");
  12467. + handler->SetSentErrorMessage(true);
  12468. + return false;
  12469. + }
  12470. + //create list
  12471. + std::set<Player*> Players;
  12472. + Players.insert(master);
  12473. + PlayerbotMgr* mgr = master->GetPlayerbotMgr();
  12474. + if (mgr && !mgr->getPlayerbots().empty())
  12475. + for (PlayerBotMap::const_iterator itr = mgr->GetPlayerBotsBegin(); itr != mgr->GetPlayerBotsEnd(); ++itr)
  12476. + Players.insert(itr->second);
  12477. + //cycle through
  12478. + for (std::set<Player*>::const_iterator it = Players.begin(); it != Players.end(); ++it)
  12479. + {
  12480. + Player* pl = *it;
  12481. + if (!pl->HaveBot())
  12482. + {
  12483. + handler->PSendSysMessage("%s has no NpcBots!", pl->GetName().c_str());
  12484. + continue;
  12485. + }
  12486. + const std::string plType = pl == owner ? ", player" : ", playerbot";
  12487. + handler->PSendSysMessage("Listing NpcBots for %s%s", pl->GetName().c_str(), plType.c_str());
  12488. + handler->PSendSysMessage("Owned NpcBots: %u", pl->GetNpcBotsCount());
  12489. + for (uint8 i = CLASS_WARRIOR; i != MAX_CLASSES; ++i)
  12490. + {
  12491. + uint8 count = 0;
  12492. + uint8 alivecount = 0;
  12493. + for (uint8 pos = 0; pos != pl->GetMaxNpcBots(); ++pos)
  12494. + {
  12495. + if (Creature* cre = pl->GetBotMap(pos)->_Cre())
  12496. + {
  12497. + if (cre->GetBotClass() == i)
  12498. + {
  12499. + ++count;
  12500. + if (cre->isAlive())
  12501. + ++alivecount;
  12502. + }
  12503. + }
  12504. + }
  12505. + char const* bclass;
  12506. + switch (i)
  12507. + {
  12508. + case CLASS_WARRIOR: bclass = "Warriors"; break;
  12509. + case CLASS_PALADIN: bclass = "Paladins"; break;
  12510. + case CLASS_MAGE: bclass = "Mages"; break;
  12511. + case CLASS_PRIEST: bclass = "Priests"; break;
  12512. + case CLASS_WARLOCK: bclass = "Warlocks"; break;
  12513. + case CLASS_DRUID: bclass = "Druids"; break;
  12514. + case CLASS_DEATH_KNIGHT: bclass = "DeathKnights"; break;
  12515. + case CLASS_ROGUE: bclass = "Rogues"; break;
  12516. + case CLASS_SHAMAN: bclass = "Shamans"; break;
  12517. + case CLASS_HUNTER: bclass = "Hunters"; break;
  12518. + default: bclass = "Unknown Class"; break;
  12519. + }
  12520. + if (count > 0)
  12521. + handler->PSendSysMessage("%s: %u (alive: %u)", bclass, count, alivecount);
  12522. + }
  12523. + }
  12524. + return true;
  12525. + }
  12526. +
  12527. + static bool HandleNpcBotDistanceCommand(ChatHandler* handler, const char* args)
  12528. + {
  12529. + Player* owner = handler->GetSession()->GetPlayer();
  12530. + if (!*args)
  12531. + {
  12532. + if (owner->HaveBot())
  12533. + {
  12534. + handler->PSendSysMessage("bot follow distance is %u", owner->GetBotFollowDist());
  12535. + handler->SetSentErrorMessage(true);
  12536. + return false;
  12537. + }
  12538. + handler->PSendSysMessage(".npcbot distance");
  12539. + handler->PSendSysMessage("Sets 'distance to target' at which bots will follow you");
  12540. + handler->PSendSysMessage("if set to 0, bots will not attack anything unless you point them");
  12541. + handler->PSendSysMessage("min: 0, max: 75");
  12542. + handler->SetSentErrorMessage(true);
  12543. + return false;
  12544. + }
  12545. + char* distance = strtok((char*)args, " ");
  12546. + int8 dist = -1;
  12547. +
  12548. + if (distance)
  12549. + dist = (int8)atoi(distance);
  12550. +
  12551. + if (dist >= 0 && dist <= 75)
  12552. + {
  12553. + owner->SetBotFollowDist(dist);
  12554. + if (!owner->isInCombat() && owner->HaveBot())
  12555. + {
  12556. + for (uint8 i = 0; i != owner->GetMaxNpcBots(); ++i)
  12557. + {
  12558. + Creature* cre = owner->GetBotMap(i)->_Cre();
  12559. + if (!cre || !cre->IsInWorld()) continue;
  12560. + owner->SendBotCommandState(cre, COMMAND_FOLLOW);
  12561. + }
  12562. + }
  12563. + PlayerbotMgr* mgr = owner->GetPlayerbotMgr();
  12564. + if (mgr && !mgr->getPlayerbots().empty())
  12565. + {
  12566. + for (PlayerBotMap::const_iterator itr = mgr->GetPlayerBotsBegin(); itr != mgr->GetPlayerBotsEnd(); ++itr)
  12567. + {
  12568. + if (Player* bot = itr->second)
  12569. + {
  12570. + bot->SetBotFollowDist(dist);
  12571. + if (!bot->isInCombat() && bot->HaveBot())
  12572. + {
  12573. + for (uint8 i = 0; i != bot->GetMaxNpcBots(); ++i)
  12574. + {
  12575. + Creature* cre = bot->GetBotMap(i)->_Cre();
  12576. + if (!cre || !cre->IsInWorld()) continue;
  12577. + bot->SendBotCommandState(cre, COMMAND_FOLLOW);
  12578. + }
  12579. + }
  12580. + }
  12581. + }
  12582. + }
  12583. + handler->PSendSysMessage("bot follow distance set to %u", dist);
  12584. + return true;
  12585. + }
  12586. + handler->SendSysMessage("follow distance should be between 0 and 75");
  12587. + handler->SetSentErrorMessage(true);
  12588. + return false;
  12589. + }
  12590. +
  12591. + static bool HandleNpcBotCommandCommand(ChatHandler* handler, const char* args)
  12592. + {
  12593. + Player* owner = handler->GetSession()->GetPlayer();
  12594. + if (!*args)
  12595. + {
  12596. + handler->PSendSysMessage(".npcbot command <command>");
  12597. + handler->PSendSysMessage("Forces npcbots to either follow you or hold position. Can be used on Playerbots");
  12598. + handler->SetSentErrorMessage(true);
  12599. + return false;
  12600. + }
  12601. + Player* master = owner->GetSelectedPlayer();
  12602. + if (!master || (master && master != owner && master->GetSession()->m_master != owner))
  12603. + master = owner;
  12604. + char* command = strtok((char*)args, " ");
  12605. + int8 state = -1;
  12606. + if (!strncmp(command, "s", 2) || !strncmp(command, "st", 3) || !strncmp(command, "stay", 5) || !strncmp(command, "stand", 6))
  12607. + state = COMMAND_STAY;
  12608. + else if (!strncmp(command, "f", 2) || !strncmp(command, "follow", 7) || !strncmp(command, "fol", 4) || !strncmp(command, "fo", 3))
  12609. + state = COMMAND_FOLLOW;
  12610. + if (state >= 0 && master->HaveBot())
  12611. + {
  12612. + for (uint8 i = 0; i != master->GetMaxNpcBots(); ++i)
  12613. + {
  12614. + Creature* cre = master->GetBotMap(i)->_Cre();
  12615. + if (!cre || !cre->IsInWorld()) continue;
  12616. + master->SendBotCommandState(cre, CommandStates(state));
  12617. + }
  12618. + return true;
  12619. + }
  12620. + return false;
  12621. + }
  12622. +
  12623. + static bool HandleNpcBotRemoveCommand(ChatHandler* handler, const char* /*args*/)
  12624. + {
  12625. + Player* owner = handler->GetSession()->GetPlayer();
  12626. + uint64 guid = owner->GetSelection();
  12627. + if (!guid)
  12628. + {
  12629. + handler->PSendSysMessage(".npcbot remove");
  12630. + handler->PSendSysMessage("Remove npcbots for selected Playerbot, you can also remove npcbots manually");
  12631. + handler->SetSentErrorMessage(true);
  12632. + return false;
  12633. + }
  12634. + Player* master = ObjectAccessor::GetPlayer(*owner, guid);
  12635. + if (master)
  12636. + {
  12637. + if (master != owner && master->GetSession()->m_master != owner)
  12638. + {
  12639. + handler->PSendSysMessage("You can only remove bots from self and your own playerbots!");
  12640. + handler->SetSentErrorMessage(true);
  12641. + return false;
  12642. + }
  12643. + if (master->HaveBot())
  12644. + {
  12645. + for (uint8 i = 0; i != master->GetMaxNpcBots(); ++i)
  12646. + {
  12647. + master->RemoveBot(master->GetBotMap(i)->_Guid(), true);
  12648. + }
  12649. + if (!master->HaveBot())
  12650. + {
  12651. + handler->PSendSysMessage("Npcbots successfully removed");
  12652. + handler->SetSentErrorMessage(true);
  12653. + return true;
  12654. + }
  12655. + handler->PSendSysMessage("Error!");
  12656. + handler->SetSentErrorMessage(true);
  12657. + return false;
  12658. + }
  12659. + handler->PSendSysMessage("Npcbots are not found!");
  12660. + handler->SetSentErrorMessage(true);
  12661. + return false;
  12662. + }
  12663. + Creature* cre = ObjectAccessor::GetCreature(*owner, guid);
  12664. + if (cre && cre->GetIAmABot())
  12665. + {
  12666. + master = cre->GetBotOwner();
  12667. + if (!master || (master && master != owner && master->GetSession()->m_master != owner))
  12668. + {
  12669. + handler->PSendSysMessage("You can only remove bots from self and your own playerbots");
  12670. + handler->SetSentErrorMessage(true);
  12671. + return false;
  12672. + }
  12673. + uint8 pos = master->GetNpcBotSlot(guid);
  12674. + master->RemoveBot(cre->GetGUID(), true);
  12675. + if (master->GetBotMap(pos)->_Cre() == NULL)
  12676. + {
  12677. + handler->PSendSysMessage("NpcBot successfully removed");
  12678. + handler->SetSentErrorMessage(true);
  12679. + return true;
  12680. + }
  12681. + handler->PSendSysMessage("NpcBot is NOT removed for some reason!");
  12682. + handler->SetSentErrorMessage(true);
  12683. + return false;
  12684. + }
  12685. + handler->PSendSysMessage("You should select Player or it's Npcbot!");
  12686. + handler->SetSentErrorMessage(true);
  12687. + return false;
  12688. + }
  12689. +
  12690. + static bool HandleNpcBotResetCommand(ChatHandler* handler, const char* /*args*/)
  12691. + {
  12692. + Player* owner = handler->GetSession()->GetPlayer();
  12693. + Player* master = NULL;
  12694. + bool all = false;
  12695. + uint64 guid = owner->GetSelection();
  12696. + if (!guid)
  12697. + {
  12698. + handler->PSendSysMessage(".npcbot reset");
  12699. + handler->PSendSysMessage("Reset selected npcbot or npcbots for selected Playerbot, you can also reset your npcbots");
  12700. + handler->SetSentErrorMessage(true);
  12701. + return false;
  12702. + }
  12703. + if (IS_PLAYER_GUID(guid))
  12704. + {
  12705. + master = ObjectAccessor::FindPlayer(guid);
  12706. + all = true;
  12707. + }
  12708. + else if (IS_CREATURE_GUID(guid))
  12709. + {
  12710. + if (Creature* cre = ObjectAccessor::GetCreature(*owner, guid))
  12711. + master = cre->GetBotOwner();
  12712. + }
  12713. + if (master && (master == owner || master->GetSession()->m_master == owner))
  12714. + {
  12715. + if (master->isInCombat() && master->GetSession()->GetSecurity() == SEC_PLAYER)
  12716. + {
  12717. + handler->PSendSysMessage("Cannot reset bots in combat!");
  12718. + handler->SetSentErrorMessage(true);
  12719. + return false;
  12720. + }
  12721. + if (!master->HaveBot())
  12722. + {
  12723. + handler->PSendSysMessage("Npcbots are not found!");
  12724. + handler->SetSentErrorMessage(true);
  12725. + return false;
  12726. + }
  12727. + for (uint8 i = 0; i != master->GetMaxNpcBots(); ++i)
  12728. + {
  12729. + if (all)
  12730. + master->RemoveBot(master->GetBotMap(i)->_Guid());
  12731. + else if (master->GetBotMap(i)->_Guid() == guid)
  12732. + {
  12733. + master->RemoveBot(guid);
  12734. + break;
  12735. + }
  12736. + }
  12737. + return true;
  12738. + }
  12739. + handler->PSendSysMessage(".npcbot reset");
  12740. + handler->PSendSysMessage("Reset selected npcbot or npcbot for selected Playerbot, you can also reset your npcbot. Cannot be used in combat");
  12741. + handler->SetSentErrorMessage(true);
  12742. + return false;
  12743. + }
  12744. + //For debug purposes only
  12745. + static bool HandleNpcBotReviveCommand(ChatHandler* handler, const char* /*args*/)
  12746. + {
  12747. + if (handler->GetSession()->GetSecurity() == SEC_PLAYER)
  12748. + {
  12749. + handler->PSendSysMessage("Revive command is disabled");
  12750. + handler->SetSentErrorMessage(true);
  12751. + return false;
  12752. + }
  12753. +
  12754. + Player* owner = handler->GetSession()->GetPlayer();
  12755. + if (owner->InBattleground())
  12756. + {
  12757. + handler->PSendSysMessage("Bot revival is disabled in pvp matches");
  12758. + handler->SetSentErrorMessage(true);
  12759. + return false;
  12760. + }
  12761. + if (owner->isInCombat())
  12762. + {
  12763. + handler->PSendSysMessage("Bot revival is disabled in combat");
  12764. + handler->SetSentErrorMessage(true);
  12765. + return false;
  12766. + }
  12767. + if (owner->isInFlight())
  12768. + {
  12769. + handler->PSendSysMessage("Bot revival is disabled in flight");
  12770. + handler->SetSentErrorMessage(true);
  12771. + return false;
  12772. + }
  12773. + if (owner->HaveBot())
  12774. + {
  12775. + for (uint8 i = 0; i != owner->GetMaxNpcBots(); ++i)
  12776. + {
  12777. + Creature* bot = owner->GetBotMap(i)->_Cre();
  12778. + if (!bot) continue;
  12779. + if (bot->isDead())
  12780. + {
  12781. + owner->SetBot(bot);
  12782. + owner->CreateBot(0, 0, 0, false, true);
  12783. + }
  12784. + }
  12785. + handler->PSendSysMessage("NpcBots revived");
  12786. + handler->SetSentErrorMessage(true);
  12787. + return true;
  12788. + }
  12789. + handler->PSendSysMessage(".npcbot revive");
  12790. + handler->PSendSysMessage("Revive your npcbots if you are all hopelessly dead");
  12791. + handler->SetSentErrorMessage(true);
  12792. + return false;
  12793. + }
  12794. +
  12795. + static bool HandleNpcBotAddCommand(ChatHandler* handler, const char* args)
  12796. + {
  12797. + Player* owner = handler->GetSession()->GetPlayer();
  12798. + Player* master = owner->GetSelectedPlayer();
  12799. + if (!master || !*args || (master != owner && master->GetSession()->m_master != owner))
  12800. + {
  12801. + handler->PSendSysMessage(".npcbot add");
  12802. + handler->PSendSysMessage("Allows to create npcbot of given class for targeted Playerbot, can be also used on self");
  12803. + handler->SetSentErrorMessage(true);
  12804. + return false;
  12805. + }
  12806. + if (master->RestrictBots())
  12807. + {
  12808. + handler->GetSession()->SendNotification("This place is restricted for NpcBots");
  12809. + handler->SetSentErrorMessage(true);
  12810. + return false;
  12811. + }
  12812. + if (master->isDead())
  12813. + {
  12814. + if (master == owner)
  12815. + owner->GetSession()->SendNotification("You're dead!");
  12816. + else
  12817. + owner->GetSession()->SendNotification("%s is dead!", master->GetName().c_str());
  12818. + handler->SetSentErrorMessage(true);
  12819. + return false;
  12820. + }
  12821. + if (master->GetGroup() && master->GetGroup()->isRaidGroup() && master->GetGroup()->IsFull())
  12822. + {
  12823. + handler->PSendSysMessage("Group is full, aborted");
  12824. + handler->SetSentErrorMessage(true);
  12825. + return false;
  12826. + }
  12827. + if (master->GetNpcBotsCount() >= master->GetMaxNpcBots())
  12828. + {
  12829. + handler->PSendSysMessage("NpcBots limit exceed");
  12830. + handler->SetSentErrorMessage(true);
  12831. + return false;
  12832. + }
  12833. +
  12834. + char* bclass = strtok((char*)args, " ");
  12835. + uint8 botclass = CLASS_NONE;
  12836. +
  12837. + if (!strncmp(bclass, "deathknight", 12) || !strncmp(bclass, "dk", 3) || !strncmp(bclass, "de", 3))
  12838. + botclass = CLASS_DEATH_KNIGHT;
  12839. + else if (!strncmp(bclass, "druid", 6) || !strncmp(bclass, "dru", 4) || !strncmp(bclass, "dr", 3))
  12840. + botclass = CLASS_DRUID;
  12841. + else if (!strncmp(bclass, "hunter", 7) || !strncmp(bclass, "hunt", 5) || !strncmp(bclass, "hu", 3))
  12842. + botclass = CLASS_HUNTER;
  12843. + else if (!strncmp(bclass, "mage", 5) || !strncmp(bclass, "ma", 3))
  12844. + botclass = CLASS_MAGE;
  12845. + else if (!strncmp(bclass, "paladin", 8) || !strncmp(bclass, "pal", 4) || !strncmp(bclass, "pa", 3))
  12846. + botclass = CLASS_PALADIN;
  12847. + else if (!strncmp(bclass, "priest", 7) || !strncmp(bclass, "pri", 4) || !strncmp(bclass, "pr", 3))
  12848. + botclass = CLASS_PRIEST;
  12849. + else if (!strncmp(bclass, "rogue", 6) || !strncmp(bclass, "rog", 4) || !strncmp(bclass, "ro", 3))
  12850. + botclass = CLASS_ROGUE;
  12851. + else if (!strncmp(bclass, "shaman", 7) || !strncmp(bclass, "sha", 4) || !strncmp(bclass, "sh", 3))
  12852. + botclass = CLASS_SHAMAN;
  12853. + else if (!strncmp(bclass, "warlock", 8) || !strncmp(bclass, "warl", 5) || !strncmp(bclass, "lock", 5))
  12854. + botclass = CLASS_WARLOCK;
  12855. + else if (!strncmp(bclass, "warrior", 8) || !strncmp(bclass, "warr", 5))
  12856. + botclass = CLASS_WARRIOR;
  12857. +
  12858. + if (botclass == CLASS_NONE)
  12859. + {
  12860. + handler->PSendSysMessage("Wrong bot class");
  12861. + handler->SetSentErrorMessage(true);
  12862. + return false;
  12863. + }
  12864. +
  12865. + uint8 bots = master->GetNpcBotsCount();
  12866. + master->CreateNPCBot(botclass);
  12867. + master->RefreshBot(0);
  12868. + if (master->GetNpcBotsCount() > bots)
  12869. + {
  12870. + if (master->isInCombat())
  12871. + handler->PSendSysMessage("NpcBot successfully created (%s). Will appear out of combat", master->GetName().c_str());
  12872. + else
  12873. + handler->PSendSysMessage("NpcBot successfully created (%s).", master->GetName().c_str());
  12874. + handler->SetSentErrorMessage(true);
  12875. + return true;
  12876. + }
  12877. + handler->PSendSysMessage("NpcBot is NOT created for some reason!");
  12878. + handler->SetSentErrorMessage(true);
  12879. + return false;
  12880. + }
  12881. +};
  12882. +
  12883. +void AddSC_script_bot_commands()
  12884. +{
  12885. + new script_bot_commands();
  12886. +}
  12887. diff --git a/src/server/game/AI/NpcBots/botgiver.cpp b/src/server/game/AI/NpcBots/botgiver.cpp
  12888. new file mode 100644
  12889. index 0000000..7fe13f8
  12890. --- /dev/null
  12891. +++ b/src/server/game/AI/NpcBots/botgiver.cpp
  12892. @@ -0,0 +1,643 @@
  12893. +#include "bp_mgr.h"
  12894. +#include "Config.h"
  12895. +#include "Group.h"
  12896. +#include "ObjectMgr.h"
  12897. +#include "Player.h"
  12898. +#include "ScriptedGossip.h"
  12899. +#include "ScriptMgr.h"
  12900. +#include "WorldSession.h"
  12901. +/*
  12902. +script_bot_giver by Graff ([email protected])
  12903. +Complete - ???
  12904. +Category - creature_cripts/custom/bots/
  12905. +*/
  12906. +const uint8 GroupIcons[TARGETICONCOUNT] =
  12907. +{
  12908. + /*STAR = */0x001,
  12909. + /*CIRCLE = */0x002,
  12910. + /*DIAMOND = */0x004,
  12911. + /*TRIANGLE = */0x008,
  12912. + /*MOON = */0x010,
  12913. + /*SQUARE = */0x020,
  12914. + /*CROSS = */0x040,
  12915. + /*SKULL = */0x080,
  12916. +};
  12917. +
  12918. +enum GossipActions
  12919. +{
  12920. + CREATE_NBOT_MENU = 1,
  12921. + CREATE_NBOT = 2,
  12922. + CREATE_PBOT_MENU = 3,
  12923. + CREATE_PBOT = 4,
  12924. +
  12925. + REMOVE_PBOT_MENU = 5,
  12926. + REMOVE_PBOT = 6,
  12927. + REMOVE_NBOT_MENU = 7,
  12928. + REMOVE_NBOT = 8,
  12929. +
  12930. + INFO_WHISPER = 9
  12931. +};
  12932. +
  12933. +enum BotgiverTexIDs
  12934. +{
  12935. + ABANDON_PLAYER = 1,
  12936. + RECRUIT_PLAYER = 2,
  12937. + ABANDON_MINION = 3,
  12938. + RECRUIT_MINION = 4,
  12939. + ABOUT_STR = 5,
  12940. + ADD_ALL = 6,
  12941. + REMOVE_ALL = 7,
  12942. + RECRUIT_WARRIOR = 8,
  12943. + RECRUIT_HUNTER = 9,
  12944. + RECRUIT_PALADIN = 10,
  12945. + RECRUIT_SHAMAN = 11,
  12946. + RECRUIT_ROGUE = 12,
  12947. + RECRUIT_DRUID = 13,
  12948. + RECRUIT_MAGE = 14,
  12949. + RECRUIT_PRIEST = 15,
  12950. + RECRUIT_WARLOCK = 16,
  12951. + RECRUIT_DEATH_KNIGHT = 17,
  12952. + ABOUT_BASIC_STR1 = 18,
  12953. + ABOUT_BASIC_STR2 = 19,
  12954. + ABOUT_BASIC_STR3 = 20,
  12955. + ABOUT_ICONS_STR1 = 21,
  12956. + ABOUT_ICONS_STR2 = 22,
  12957. + ICON_STRING_STAR = 23,
  12958. + ICON_STRING_CIRCLE = 24,
  12959. + ICON_STRING_DIAMOND = 25,
  12960. + ICON_STRING_TRIANGLE = 26,
  12961. + ICON_STRING_MOON = 27,
  12962. + ICON_STRING_SQUARE = 28,
  12963. + ICON_STRING_CROSS = 29,
  12964. + ICON_STRING_SKULL = 30,
  12965. + ICON_STRING_UNKNOWN = 31,
  12966. + NO_MORE_AVAILABLE = 32,
  12967. + ONE_MORE_AVAILABLE = 33,
  12968. + SOME_MORE_AVAILABLE = 34,
  12969. + ONE_AVAILABLE = 35,
  12970. + SOME_AVAILABLE = 36,
  12971. + MAX_STRINGS
  12972. +};
  12973. +
  12974. +class script_bot_giver : public CreatureScript
  12975. +{
  12976. + uint8 maxPBcount;
  12977. + uint8 maxNBcount;
  12978. +
  12979. + bool allowPBots;
  12980. + bool allowNBots;
  12981. +
  12982. +public:
  12983. + script_bot_giver() : CreatureScript("script_bot_giver") { }
  12984. +
  12985. + bool OnGossipSelect(Player* player, Creature* creature, uint32 sender, uint32 action)
  12986. + {
  12987. + switch (sender)
  12988. + {
  12989. + case CREATE_NBOT_MENU: SendCreateNPCBotMenu(player, creature, action); break;
  12990. + case CREATE_NBOT: SendCreateNPCBot(player, creature, action); break;
  12991. + case CREATE_PBOT_MENU: SendCreatePlayerBotMenu(player, creature, action); break;
  12992. + case CREATE_PBOT: SendCreatePlayerBot(player, creature, action); break;
  12993. +
  12994. + case REMOVE_PBOT_MENU: SendRemovePlayerBotMenu(player, creature, action); break;
  12995. + case REMOVE_PBOT: SendRemovePlayerBot(player, creature, action); break;
  12996. + case REMOVE_NBOT_MENU: SendRemoveNPCBotMenu(player, creature, action); break;
  12997. + case REMOVE_NBOT: SendRemoveNPCBot(player, creature, action); break;
  12998. +
  12999. + case INFO_WHISPER: SendBotHelpWhisper(player, creature, action); break;
  13000. + }
  13001. + return true;
  13002. + }
  13003. +
  13004. + bool OnGossipHello(Player* player, Creature* creature)
  13005. + {
  13006. + uint8 count = 0;
  13007. +
  13008. + maxPBcount = ConfigMgr::GetIntDefault("Bot.MaxPlayerbots", 9);
  13009. + maxNBcount = player->GetMaxNpcBots();
  13010. +
  13011. + allowPBots = ConfigMgr::GetBoolDefault("Bot.EnablePlayerBots", false);
  13012. + allowNBots = ConfigMgr::GetBoolDefault("Bot.EnableNpcBots", true) && !player->RestrictBots();
  13013. +
  13014. + std::string tempstr;
  13015. +
  13016. + if (PlayerbotMgr* mgr = player->GetPlayerbotMgr())
  13017. + {
  13018. + for (PlayerBotMap::const_iterator itr = mgr->GetPlayerBotsBegin(); itr != mgr->GetPlayerBotsEnd(); ++itr)
  13019. + {
  13020. + if (count == 0)
  13021. + {
  13022. + tempstr = "Abandon my Player";
  13023. + player->ADD_GOSSIP_ITEM(0, GetLocaleStringForTextID(tempstr, ABANDON_PLAYER, creature->GetEntry(), player->GetSession()->GetSessionDbLocaleIndex()), REMOVE_PBOT_MENU, GOSSIP_ACTION_INFO_DEF + 100);
  13024. + }
  13025. + ++count;
  13026. + }
  13027. + }
  13028. + if (count < maxPBcount && allowPBots)
  13029. + {
  13030. + tempstr = "Recruit a Player";
  13031. + player->ADD_GOSSIP_ITEM(0, GetLocaleStringForTextID(tempstr, RECRUIT_PLAYER, creature->GetEntry(), player->GetSession()->GetSessionDbLocaleIndex()), CREATE_PBOT_MENU, GOSSIP_ACTION_INFO_DEF + 1);
  13032. + }
  13033. +
  13034. + if (player->HaveBot())
  13035. + {
  13036. + count = player->GetNpcBotsCount();
  13037. + if (count > 0)
  13038. + {
  13039. + tempstr = "Abandon my Minion";
  13040. + player->ADD_GOSSIP_ITEM(0, GetLocaleStringForTextID(tempstr, ABANDON_MINION, creature->GetEntry(), player->GetSession()->GetSessionDbLocaleIndex()), REMOVE_NBOT_MENU, GOSSIP_ACTION_INFO_DEF + 101);
  13041. + }
  13042. + if (count < maxNBcount && allowNBots)
  13043. + {
  13044. + tempstr = "Recruit a Minion";
  13045. + player->ADD_GOSSIP_ITEM(0, GetLocaleStringForTextID(tempstr, RECRUIT_MINION, creature->GetEntry(), player->GetSession()->GetSessionDbLocaleIndex()), CREATE_NBOT_MENU, GOSSIP_ACTION_INFO_DEF + 2);
  13046. + }
  13047. + }
  13048. + else if (allowNBots && maxNBcount != 0)
  13049. + {
  13050. + tempstr = "Recruit a Minion";
  13051. + player->ADD_GOSSIP_ITEM(0, GetLocaleStringForTextID(tempstr, RECRUIT_MINION, creature->GetEntry(), player->GetSession()->GetSessionDbLocaleIndex()), CREATE_NBOT_MENU, GOSSIP_ACTION_INFO_DEF + 2);
  13052. + }
  13053. +
  13054. + tempstr = "Tell me about these bots";
  13055. + player->ADD_GOSSIP_ITEM(0, GetLocaleStringForTextID(tempstr, ABOUT_STR, creature->GetEntry(), player->GetSession()->GetSessionDbLocaleIndex()), INFO_WHISPER, GOSSIP_ACTION_INFO_DEF + 200);
  13056. +
  13057. + player->PlayerTalkClass->SendGossipMenu(8446, creature->GetGUID());
  13058. + return true;
  13059. + }
  13060. +
  13061. +private:
  13062. + static void SendCreatePlayerBot(Player* player, Creature* /*creature*/, uint32 action)
  13063. + {
  13064. + std::string plName;
  13065. + std::list<std::string>* names = new std::list<std::string>;
  13066. + uint32 accId = player->GetSession()->GetAccountId();
  13067. + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PLAYERBOTS);
  13068. + stmt->setUInt32(0, accId);
  13069. + stmt->setUInt32(1, player->GetGUIDLow());
  13070. + PreparedQueryResult results = CharacterDatabase.Query(stmt);
  13071. + //QueryResult results = CharacterDatabase.PQuery("SELECT name FROM characters WHERE account = '%u' AND guid != '%u'", accId, player->GetGUIDLow());
  13072. + if (results)
  13073. + {
  13074. + do
  13075. + {
  13076. + Field* fields = results->Fetch();
  13077. + plName = fields[0].GetString();
  13078. + if (ObjectAccessor::FindPlayerByName(plName))
  13079. + continue;
  13080. + names->insert(names->end(), plName);
  13081. + } while (results->NextRow());
  13082. + }
  13083. +
  13084. + if (names->empty())
  13085. + {
  13086. + delete names;
  13087. + player->CLOSE_GOSSIP_MENU();
  13088. + return;
  13089. + }
  13090. +
  13091. + PlayerbotMgr* mgr = player->GetPlayerbotMgr();
  13092. + if (!mgr)
  13093. + {
  13094. + mgr = new PlayerbotMgr(player);
  13095. + player->SetPlayerbotMgr(mgr);
  13096. + }
  13097. +
  13098. + int8 x = action - GOSSIP_ACTION_INFO_DEF - 1;
  13099. +
  13100. + for (std::list<std::string>::iterator iter = names->begin(); iter != names->end(); iter++)
  13101. + {
  13102. + if (x == 0)
  13103. + {
  13104. + uint64 guid = sObjectMgr->GetPlayerGUIDByName((*iter).c_str());
  13105. + if (mgr->GetPlayerBot(guid) != NULL)
  13106. + continue;
  13107. + mgr->AddPlayerBot(guid);
  13108. + }
  13109. + else
  13110. + {
  13111. + if (x == 1)
  13112. + {
  13113. + uint64 guid = sObjectMgr->GetPlayerGUIDByName((*iter).c_str());
  13114. + if (mgr->GetPlayerBot(guid) != NULL)
  13115. + break;
  13116. + mgr->AddPlayerBot(guid);
  13117. + break;
  13118. + }
  13119. + --x;
  13120. + }
  13121. + }
  13122. + player->CLOSE_GOSSIP_MENU();
  13123. + delete names;
  13124. + }
  13125. +
  13126. + void SendCreatePlayerBotMenu(Player* player, Creature* creature, uint32 /*action*/)
  13127. + {
  13128. + std::string plName;
  13129. + std::list<std::string>* names = new std::list<std::string>;
  13130. + uint32 accId = player->GetSession()->GetAccountId();
  13131. + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PLAYERBOTS);
  13132. + stmt->setUInt32(0, accId);
  13133. + stmt->setUInt32(1, player->GetGUIDLow());
  13134. + PreparedQueryResult results = CharacterDatabase.Query(stmt);
  13135. + //QueryResult results = CharacterDatabase.PQuery("SELECT name FROM characters WHERE account = '%u' AND guid != '%u'", accId, player->GetGUIDLow());
  13136. + if (results)
  13137. + {
  13138. + do
  13139. + {
  13140. + Field* fields = results->Fetch();
  13141. + plName = fields[0].GetString();
  13142. + if (sObjectAccessor->FindPlayerByName(plName))
  13143. + continue;
  13144. + names->insert(names->end(), plName);
  13145. + } while (results->NextRow());
  13146. + }
  13147. +
  13148. + if (names->empty())
  13149. + {
  13150. + delete names;
  13151. + player->CLOSE_GOSSIP_MENU();
  13152. + return;
  13153. + }
  13154. +
  13155. + player->PlayerTalkClass->ClearMenus();
  13156. + std::string tempstr;
  13157. + PlayerbotMgr* mgr = player->GetPlayerbotMgr();
  13158. + uint8 bots = !mgr ? 0 : mgr->GetPlayerBotsCount();
  13159. + uint32 freePBSlots = maxPBcount - bots;
  13160. + if (freePBSlots != 1 && freePBSlots >= names->size())
  13161. + {
  13162. + tempstr = "ADD ALL";
  13163. + player->ADD_GOSSIP_ITEM(9, GetLocaleStringForTextID(tempstr, ADD_ALL, creature->GetEntry(), player->GetSession()->GetSessionDbLocaleIndex()), CREATE_PBOT, GOSSIP_ACTION_INFO_DEF + 1);
  13164. + }
  13165. +
  13166. + int8 x = 2;
  13167. + for (std::list<std::string>::iterator iter = names->begin(); iter != names->end(); iter++)
  13168. + {
  13169. + player->ADD_GOSSIP_ITEM(9, (*iter).c_str() , CREATE_PBOT, GOSSIP_ACTION_INFO_DEF + x);
  13170. + ++x;
  13171. + }
  13172. +
  13173. + std::ostringstream buff;
  13174. + if (freePBSlots == 0)
  13175. + {
  13176. + tempstr = "no more bots available";
  13177. + buff << GetLocaleStringForTextID(tempstr, NO_MORE_AVAILABLE, creature->GetEntry(), player->GetSession()->GetSessionDbLocaleIndex());
  13178. + }
  13179. + else
  13180. + {
  13181. + buff << freePBSlots;
  13182. + buff << ' ';
  13183. + if (freePBSlots == 1)
  13184. + {
  13185. + if (bots == 0)
  13186. + {
  13187. + tempstr = "bot available";
  13188. + buff << GetLocaleStringForTextID(tempstr, ONE_AVAILABLE, creature->GetEntry(), player->GetSession()->GetSessionDbLocaleIndex());
  13189. + }
  13190. + else
  13191. + {
  13192. + tempstr = "more bot available";
  13193. + buff << GetLocaleStringForTextID(tempstr, ONE_MORE_AVAILABLE, creature->GetEntry(), player->GetSession()->GetSessionDbLocaleIndex());
  13194. + }
  13195. + }
  13196. + else
  13197. + {
  13198. + if (bots == 0)
  13199. + {
  13200. + tempstr = "bots available";
  13201. + buff << GetLocaleStringForTextID(tempstr, SOME_AVAILABLE, creature->GetEntry(), player->GetSession()->GetSessionDbLocaleIndex());
  13202. + }
  13203. + else
  13204. + {
  13205. + tempstr = "more bots available";
  13206. + buff << GetLocaleStringForTextID(tempstr, SOME_MORE_AVAILABLE, creature->GetEntry(), player->GetSession()->GetSessionDbLocaleIndex());
  13207. + }
  13208. + }
  13209. + }
  13210. +
  13211. + player->ADD_GOSSIP_ITEM(0, buff.str(), CREATE_PBOT_MENU, GOSSIP_ACTION_INFO_DEF + 1);
  13212. +
  13213. + player->PlayerTalkClass->SendGossipMenu(8446, creature->GetGUID());
  13214. +
  13215. + delete names;
  13216. + } //end SendCreatePlayerBotMenu
  13217. +
  13218. + static void SendRemovePlayerBotAll(Player* player, Creature* creature)
  13219. + {
  13220. + for (int8 x = 2; x<=10; x++ )
  13221. + SendRemovePlayerBot(player, creature, GOSSIP_ACTION_INFO_DEF + 2);
  13222. + }
  13223. +
  13224. + static void SendRemovePlayerBot(Player* player, Creature* creature, uint32 action)
  13225. + {
  13226. + int8 x = action - GOSSIP_ACTION_INFO_DEF - 1;
  13227. +
  13228. + if (x == 0)
  13229. + {
  13230. + SendRemovePlayerBotAll(player, creature);
  13231. + return;
  13232. + }
  13233. +
  13234. + if (PlayerbotMgr* mgr = player->GetPlayerbotMgr())
  13235. + {
  13236. + for (PlayerBotMap::const_iterator itr = mgr->GetPlayerBotsBegin(); itr != mgr->GetPlayerBotsEnd(); ++itr)
  13237. + {
  13238. + if (x == 1 && itr->second)
  13239. + {
  13240. + mgr->LogoutPlayerBot(itr->second->GetGUID());
  13241. + break;
  13242. + }
  13243. + --x;
  13244. + }
  13245. + }
  13246. + player->CLOSE_GOSSIP_MENU();
  13247. + } //end SendRemovePlayerBot
  13248. +
  13249. + static void SendRemovePlayerBotMenu(Player* player, Creature* creature, uint32 /*action*/)
  13250. + {
  13251. + player->PlayerTalkClass->ClearMenus();
  13252. + PlayerbotMgr* mgr = player->GetPlayerbotMgr();
  13253. + if (mgr->GetPlayerBotsCount() != 1)
  13254. + {
  13255. + std::string tempstr = "REMOVE ALL";
  13256. + player->ADD_GOSSIP_ITEM(9, GetLocaleStringForTextID(tempstr, REMOVE_ALL, creature->GetEntry(), player->GetSession()->GetSessionDbLocaleIndex()), REMOVE_PBOT, GOSSIP_ACTION_INFO_DEF + 1);
  13257. + }
  13258. +
  13259. + uint8 x = 2;
  13260. + for (PlayerBotMap::const_iterator itr = mgr->GetPlayerBotsBegin(); itr != mgr->GetPlayerBotsEnd(); ++itr)
  13261. + {
  13262. + Player* bot = itr->second;
  13263. + player->ADD_GOSSIP_ITEM(9, bot->GetName(), REMOVE_PBOT, GOSSIP_ACTION_INFO_DEF + x);
  13264. + ++x;
  13265. + }
  13266. + player->PlayerTalkClass->SendGossipMenu(8446, creature->GetGUID());
  13267. + } //end SendRemovePlayerBotMenu
  13268. +
  13269. + static void SendRemoveNPCBot(Player* player, Creature* /*creature*/, uint32 action)
  13270. + {
  13271. + int8 x = action - GOSSIP_ACTION_INFO_DEF;
  13272. + if (x == 1)
  13273. + {
  13274. + player->CLOSE_GOSSIP_MENU();
  13275. + for (uint8 i = 0; i != player->GetMaxNpcBots(); ++i)
  13276. + player->RemoveBot(player->GetBotMap(i)->_Guid(), true);
  13277. + return;
  13278. + }
  13279. + for (uint8 i = 0; i != player->GetMaxNpcBots(); ++i)
  13280. + {
  13281. + if (!player->GetBotMap(i)->_Cre())
  13282. + continue;
  13283. + if (x == 2)
  13284. + {
  13285. + player->RemoveBot(player->GetBotMap(i)->_Guid(), true);
  13286. + break;
  13287. + }
  13288. + --x;
  13289. + }
  13290. + player->CLOSE_GOSSIP_MENU();
  13291. + }
  13292. +
  13293. + static void SendRemoveNPCBotMenu(Player* player, Creature* creature, uint32 /*action*/)
  13294. + {
  13295. + player->PlayerTalkClass->ClearMenus();
  13296. + if (player->GetNpcBotsCount() == 1)
  13297. + {
  13298. + for (uint8 i = 0; i != player->GetMaxNpcBots(); ++i)
  13299. + player->RemoveBot(player->GetBotMap(i)->_Guid(), true);
  13300. + player->CLOSE_GOSSIP_MENU();
  13301. + return;
  13302. + }
  13303. + std::string tempstr = "REMOVE ALL";
  13304. + player->ADD_GOSSIP_ITEM(9, GetLocaleStringForTextID(tempstr, REMOVE_ALL, creature->GetEntry(), player->GetSession()->GetSessionDbLocaleIndex()), REMOVE_NBOT, GOSSIP_ACTION_INFO_DEF + 1);
  13305. +
  13306. + uint8 x = 2;
  13307. + for (uint8 i = 0; i != player->GetMaxNpcBots(); ++i)
  13308. + {
  13309. + Creature* bot = player->GetBotMap(i)->_Cre();
  13310. + if (!bot) continue;
  13311. + player->ADD_GOSSIP_ITEM(9, bot->GetName(), REMOVE_NBOT, GOSSIP_ACTION_INFO_DEF + x);
  13312. + ++x;
  13313. + }
  13314. + player->PlayerTalkClass->SendGossipMenu(8446, creature->GetGUID());
  13315. + }
  13316. +
  13317. + static void SendCreateNPCBot(Player* player, Creature* /*creature*/, uint32 action)
  13318. + {
  13319. + uint8 bot_class = 0;
  13320. + if (action == GOSSIP_ACTION_INFO_DEF + 1)//"Back"
  13321. + {
  13322. + player->CLOSE_GOSSIP_MENU();
  13323. + return;
  13324. + }
  13325. + else if (action == GOSSIP_ACTION_INFO_DEF + 2)
  13326. + bot_class = CLASS_WARRIOR;
  13327. + //else if (action == GOSSIP_ACTION_INFO_DEF + 3)
  13328. + // bot_class = CLASS_HUNTER;
  13329. + else if (action == GOSSIP_ACTION_INFO_DEF + 4)
  13330. + bot_class = CLASS_PALADIN;
  13331. + //else if (action == GOSSIP_ACTION_INFO_DEF + 5)
  13332. + // bot_class = CLASS_SHAMAN;
  13333. + else if (action == GOSSIP_ACTION_INFO_DEF + 6)
  13334. + bot_class = CLASS_ROGUE;
  13335. + else if (action == GOSSIP_ACTION_INFO_DEF + 7)
  13336. + bot_class = CLASS_DRUID;
  13337. + else if (action == GOSSIP_ACTION_INFO_DEF + 8)
  13338. + bot_class = CLASS_MAGE;
  13339. + else if (action == GOSSIP_ACTION_INFO_DEF + 9)
  13340. + bot_class = CLASS_PRIEST;
  13341. + else if (action == GOSSIP_ACTION_INFO_DEF + 10)
  13342. + bot_class = CLASS_WARLOCK;
  13343. + //else if (action == GOSSIP_ACTION_INFO_DEF + 11)
  13344. + // bot_class = CLASS_DEATH_KNIGHT;
  13345. +
  13346. + if (bot_class != 0)
  13347. + player->CreateNPCBot(bot_class);
  13348. + player->CLOSE_GOSSIP_MENU();
  13349. + return;
  13350. + }
  13351. +
  13352. + void SendCreateNPCBotMenu(Player* player, Creature* creature, uint32 /*action*/)
  13353. + {
  13354. + std::string cost = player->GetNpcBotCostStr();
  13355. + player->PlayerTalkClass->ClearMenus();
  13356. +
  13357. + std::string tempstr = "Recruit a Warrior ";
  13358. + player->ADD_GOSSIP_ITEM(9, GetLocaleStringForTextID(tempstr, RECRUIT_WARRIOR, creature->GetEntry(), player->GetSession()->GetSessionDbLocaleIndex()) + cost, CREATE_NBOT, GOSSIP_ACTION_INFO_DEF + 2);
  13359. + //tempstr = "Recruit a Hunter ";
  13360. + //player->ADD_GOSSIP_ITEM(9, GetLocaleStringForTextID(tempstr, RECRUIT_HUNTER, creature->GetEntry(), player->GetSession()->GetSessionDbLocaleIndex()) + cost, CREATE_NBOT, GOSSIP_ACTION_INFO_DEF + 3);
  13361. + tempstr = "Recruit a Paladin ";
  13362. + player->ADD_GOSSIP_ITEM(9, GetLocaleStringForTextID(tempstr, RECRUIT_PALADIN, creature->GetEntry(), player->GetSession()->GetSessionDbLocaleIndex()) + cost, CREATE_NBOT, GOSSIP_ACTION_INFO_DEF + 4);
  13363. + //tempstr = "Recruit a Shaman ";
  13364. + //player->ADD_GOSSIP_ITEM(9, GetLocaleStringForTextID(tempstr, RECRUIT_SHAMAN, creature->GetEntry(), player->GetSession()->GetSessionDbLocaleIndex()) + cost, CREATE_NBOT, GOSSIP_ACTION_INFO_DEF + 5);
  13365. + tempstr = "Recruit a Rogue ";
  13366. + player->ADD_GOSSIP_ITEM(9, GetLocaleStringForTextID(tempstr, RECRUIT_ROGUE, creature->GetEntry(), player->GetSession()->GetSessionDbLocaleIndex()) + cost, CREATE_NBOT, GOSSIP_ACTION_INFO_DEF + 6);
  13367. + tempstr = "Recruit a Druid ";
  13368. + player->ADD_GOSSIP_ITEM(3, GetLocaleStringForTextID(tempstr, RECRUIT_DRUID, creature->GetEntry(), player->GetSession()->GetSessionDbLocaleIndex()) + cost, CREATE_NBOT, GOSSIP_ACTION_INFO_DEF + 7);
  13369. + tempstr = "Recruit a Mage ";
  13370. + player->ADD_GOSSIP_ITEM(3, GetLocaleStringForTextID(tempstr, RECRUIT_MAGE, creature->GetEntry(), player->GetSession()->GetSessionDbLocaleIndex()) + cost, CREATE_NBOT, GOSSIP_ACTION_INFO_DEF + 8);
  13371. + tempstr = "Recruit a Priest ";
  13372. + player->ADD_GOSSIP_ITEM(3, GetLocaleStringForTextID(tempstr, RECRUIT_PRIEST, creature->GetEntry(), player->GetSession()->GetSessionDbLocaleIndex()) + cost, CREATE_NBOT, GOSSIP_ACTION_INFO_DEF + 9);
  13373. + tempstr = "Recruit a Warlock ";
  13374. + player->ADD_GOSSIP_ITEM(3, GetLocaleStringForTextID(tempstr, RECRUIT_WARLOCK, creature->GetEntry(), player->GetSession()->GetSessionDbLocaleIndex()) + cost, CREATE_NBOT, GOSSIP_ACTION_INFO_DEF + 10);
  13375. + //tempstr = "Recruit a Death Knight ";
  13376. + //player->ADD_GOSSIP_ITEM(9, GetLocaleStringForTextID(tempstr, RECRUIT_DEATH_KNIGHT, creature->GetEntry(), player->GetSession()->GetSessionDbLocaleIndex()) + cost, CREATE_NBOT, GOSSIP_ACTION_INFO_DEF + 11);
  13377. +
  13378. + std::ostringstream buff;
  13379. + uint8 bots = player->GetNpcBotsCount();
  13380. + uint32 freeNBSlots = maxNBcount - bots;
  13381. +
  13382. + if (freeNBSlots == 0)
  13383. + {
  13384. + tempstr = "no more bots available";
  13385. + buff << GetLocaleStringForTextID(tempstr, NO_MORE_AVAILABLE, creature->GetEntry(), player->GetSession()->GetSessionDbLocaleIndex());
  13386. + }
  13387. + else
  13388. + {
  13389. + buff << freeNBSlots;
  13390. + buff << ' ';
  13391. + if (freeNBSlots == 1)
  13392. + {
  13393. + if (bots == 0)
  13394. + {
  13395. + tempstr = "bot available";
  13396. + buff << GetLocaleStringForTextID(tempstr, ONE_AVAILABLE, creature->GetEntry(), player->GetSession()->GetSessionDbLocaleIndex());
  13397. + }
  13398. + else
  13399. + {
  13400. + tempstr = "more bot available";
  13401. + buff << GetLocaleStringForTextID(tempstr, ONE_MORE_AVAILABLE, creature->GetEntry(), player->GetSession()->GetSessionDbLocaleIndex());
  13402. + }
  13403. + }
  13404. + else
  13405. + {
  13406. + if (bots == 0)
  13407. + {
  13408. + tempstr = "bots available";
  13409. + buff << GetLocaleStringForTextID(tempstr, SOME_AVAILABLE, creature->GetEntry(), player->GetSession()->GetSessionDbLocaleIndex());
  13410. + }
  13411. + else
  13412. + {
  13413. + tempstr = "more bots available";
  13414. + buff << GetLocaleStringForTextID(tempstr, SOME_MORE_AVAILABLE, creature->GetEntry(), player->GetSession()->GetSessionDbLocaleIndex());
  13415. + }
  13416. + }
  13417. + }
  13418. + player->ADD_GOSSIP_ITEM(0, buff.str(), CREATE_NBOT_MENU, GOSSIP_ACTION_INFO_DEF + 2);
  13419. +
  13420. + player->PlayerTalkClass->SendGossipMenu(8446, creature->GetGUID());
  13421. + }
  13422. +
  13423. + static void SendBotHelpWhisper(Player* player, Creature* creature, uint32 /*action*/)
  13424. + {
  13425. + player->CLOSE_GOSSIP_MENU();
  13426. + //Basic
  13427. + std::string tempstr = "To see list of Playerbot commands whisper 'help' to one of your playerbots";
  13428. + std::string msg1 = GetLocaleStringForTextID(tempstr, ABOUT_BASIC_STR1, creature->GetEntry(), player->GetSession()->GetSessionDbLocaleIndex());
  13429. + tempstr = "To see list of available npcbot commands type .npcbot or .npcb";
  13430. + std::string msg2 = GetLocaleStringForTextID(tempstr, ABOUT_BASIC_STR2, creature->GetEntry(), player->GetSession()->GetSessionDbLocaleIndex());
  13431. + tempstr = "You can also use .maintank (or .mt or .main) command on any party member (even npcbot) so your bots will stick to your plan";
  13432. + std::string msg3 = GetLocaleStringForTextID(tempstr, ABOUT_BASIC_STR3, creature->GetEntry(), player->GetSession()->GetSessionDbLocaleIndex());
  13433. + creature->MonsterWhisper(msg1.c_str(), player->GetGUID());
  13434. + creature->MonsterWhisper(msg2.c_str(), player->GetGUID());
  13435. + creature->MonsterWhisper(msg3.c_str(), player->GetGUID());
  13436. + //Heal Icons
  13437. + uint8 mask = ConfigMgr::GetIntDefault("Bot.HealTargetIconsMask", 8);
  13438. + std::string msg4 = "";
  13439. + if (mask == 255)
  13440. + {
  13441. + tempstr = "If you want your npcbots to heal someone out of your party set any raid target icon on them";
  13442. + msg4 = GetLocaleStringForTextID(tempstr, ABOUT_ICONS_STR1, creature->GetEntry(), player->GetSession()->GetSessionDbLocaleIndex());
  13443. + creature->MonsterWhisper(msg4.c_str(), player->GetGUID());
  13444. + }
  13445. + else if (mask != 0)
  13446. + {
  13447. + tempstr = "If you want your npcbots to heal someone out of your party set proper raid target icon on them, one of these: ";
  13448. + msg4 = GetLocaleStringForTextID(tempstr, ABOUT_ICONS_STR2, creature->GetEntry(), player->GetSession()->GetSessionDbLocaleIndex());
  13449. + std::string iconrow = "";
  13450. + uint8 count = 0;
  13451. + for (uint8 i = 0; i != TARGETICONCOUNT; ++i)
  13452. + {
  13453. + if (mask & GroupIcons[i])
  13454. + {
  13455. + if (count != 0)
  13456. + iconrow += ", ";
  13457. + ++count;
  13458. + switch (i)
  13459. + {
  13460. + case 0:
  13461. + tempstr = "star";
  13462. + iconrow += GetLocaleStringForTextID(tempstr, ICON_STRING_STAR, creature->GetEntry(), player->GetSession()->GetSessionDbLocaleIndex());
  13463. + break;
  13464. + case 1:
  13465. + tempstr = "circle";
  13466. + iconrow += GetLocaleStringForTextID(tempstr, ICON_STRING_CIRCLE, creature->GetEntry(), player->GetSession()->GetSessionDbLocaleIndex());
  13467. + break;
  13468. + case 2:
  13469. + tempstr = "diamond";
  13470. + iconrow += GetLocaleStringForTextID(tempstr, ICON_STRING_DIAMOND, creature->GetEntry(), player->GetSession()->GetSessionDbLocaleIndex());
  13471. + break;
  13472. + case 3:
  13473. + tempstr = "triangle";
  13474. + iconrow += GetLocaleStringForTextID(tempstr, ICON_STRING_TRIANGLE, creature->GetEntry(), player->GetSession()->GetSessionDbLocaleIndex());
  13475. + break;
  13476. + case 4:
  13477. + tempstr = "moon";
  13478. + iconrow += GetLocaleStringForTextID(tempstr, ICON_STRING_MOON, creature->GetEntry(), player->GetSession()->GetSessionDbLocaleIndex());
  13479. + break;
  13480. + case 5:
  13481. + tempstr = "square";
  13482. + iconrow += GetLocaleStringForTextID(tempstr, ICON_STRING_SQUARE, creature->GetEntry(), player->GetSession()->GetSessionDbLocaleIndex());
  13483. + break;
  13484. + case 6:
  13485. + tempstr = "cross";
  13486. + iconrow += GetLocaleStringForTextID(tempstr, ICON_STRING_CROSS, creature->GetEntry(), player->GetSession()->GetSessionDbLocaleIndex());
  13487. + break;
  13488. + case 7:
  13489. + tempstr = "skull";
  13490. + iconrow += GetLocaleStringForTextID(tempstr, ICON_STRING_SKULL, creature->GetEntry(), player->GetSession()->GetSessionDbLocaleIndex());
  13491. + break;
  13492. + default:
  13493. + tempstr = "unknown icon";
  13494. + iconrow += GetLocaleStringForTextID(tempstr, ICON_STRING_UNKNOWN, creature->GetEntry(), player->GetSession()->GetSessionDbLocaleIndex());
  13495. + break;
  13496. + }
  13497. + }
  13498. + }
  13499. + msg4 += iconrow;
  13500. + creature->MonsterWhisper(msg4.c_str(), player->GetGUID());
  13501. + }
  13502. + }
  13503. +
  13504. + static std::string GetLocaleStringForTextID(std::string& textValue, uint32 textId, uint32 botgiverEntry, int32 localeIdx)
  13505. + {
  13506. + if (textId >= MAX_STRINGS)
  13507. + {
  13508. + sLog->outError(LOG_FILTER_PLAYER, "botgiver:GetLocaleStringForTextID:: unknown text id: %u!", uint32(textId));
  13509. + return textValue;
  13510. + }
  13511. +
  13512. + if (localeIdx == DEFAULT_LOCALE)
  13513. + return textValue; //use default
  13514. +
  13515. + if (localeIdx < 0)
  13516. + {
  13517. + sLog->outError(LOG_FILTER_PLAYER, "botgiver:GetLocaleStringForTextID:: unknown locale: %i! Sending default locale text...", localeIdx);
  13518. + return textValue;
  13519. + }
  13520. +
  13521. + uint32 idxEntry = MAKE_PAIR32(botgiverEntry, textId);
  13522. + if (GossipMenuItemsLocale const* no = sObjectMgr->GetGossipMenuItemsLocale(idxEntry))
  13523. + {
  13524. + ObjectMgr::GetLocaleString(no->OptionText, localeIdx, textValue);
  13525. + //ObjectMgr::GetLocaleString(no->BoxText, locale, strBoxText);
  13526. + }
  13527. + return textValue;
  13528. + }
  13529. +};
  13530. +
  13531. +//This function is called when the player clicks an option on the gossip menu
  13532. +void AddSC_script_bot_giver()
  13533. +{
  13534. + new script_bot_giver();
  13535. +}
  13536. diff --git a/src/server/game/AI/PlayerBots/bp_ai.cpp b/src/server/game/AI/PlayerBots/bp_ai.cpp
  13537. new file mode 100644
  13538. index 0000000..ea78334
  13539. --- /dev/null
  13540. +++ b/src/server/game/AI/PlayerBots/bp_ai.cpp
  13541. @@ -0,0 +1,1888 @@
  13542. +/*
  13543. +Playerbot System by Graff ([email protected])
  13544. +Original source: https://github.com/blueboy/portal/commits/new-ai
  13545. +Type: Rewrite project
  13546. +Complete: ~10%
  13547. +TODO:
  13548. +Everything but this
  13549. +*/
  13550. +
  13551. +//#include "Common.h"
  13552. +#include "ItemPrototype.h"
  13553. +//#include "World.h"
  13554. +//#include "SpellMgr.h"
  13555. +//#include "GridNotifiers.h"
  13556. +#include "GridNotifiersImpl.h"
  13557. +#include "bot_GridNotifiers.h"
  13558. +#include "CellImpl.h"
  13559. +//#include "ProgressBar.h"
  13560. +#include "Chat.h"
  13561. +#include "bp_ai.h"
  13562. +#include "bp_mgr.h"
  13563. +#include "bp_dk_ai.h"
  13564. +#include "bp_dru_ai.h"
  13565. +#include "bp_hun_ai.h"
  13566. +#include "bp_mag_ai.h"
  13567. +#include "bp_pal_ai.h"
  13568. +#include "bp_pri_ai.h"
  13569. +#include "bp_rog_ai.h"
  13570. +#include "bp_sha_ai.h"
  13571. +#include "bp_warl_ai.h"
  13572. +#include "bp_warr_ai.h"
  13573. +#include "InstanceSaveMgr.h"
  13574. +#include "Player.h"
  13575. +#include "Group.h"
  13576. +#include "GroupMgr.h"
  13577. +#include "Pet.h"
  13578. +//#include "ReputationMgr.h"
  13579. +#include "ObjectMgr.h"
  13580. +#include "WorldSession.h"
  13581. +//#include "Spell.h"
  13582. +//#include "Unit.h"
  13583. +//#include "SpellAuras.h"
  13584. +#include "SpellAuraEffects.h"
  13585. +//#include "SharedDefines.h"
  13586. +//#include "Log.h"
  13587. +#include "GossipDef.h"
  13588. +//#include "MotionMaster.h"
  13589. +#include "AuctionHouseMgr.h"
  13590. +//#include "Mail.h"
  13591. +#include "Guild.h"
  13592. +//#include "GuildMgr.h"
  13593. +#include "Language.h"
  13594. +#include <iomanip>
  13595. +//#include <iostream>
  13596. +
  13597. +const uint32 VENDOR_MASK = (UNIT_NPC_FLAG_VENDOR | UNIT_NPC_FLAG_VENDOR_AMMO | UNIT_NPC_FLAG_VENDOR_FOOD | UNIT_NPC_FLAG_VENDOR_POISON | UNIT_NPC_FLAG_VENDOR_REAGENT);
  13598. +Player::BoundInstancesMap _botBoundInstances[MAX_DIFFICULTY];
  13599. +
  13600. +AfterCast::AfterCast()
  13601. +{
  13602. + _afterCastTarget = NULL;
  13603. +}
  13604. +inline Unit* AfterCast::GetTarget() const
  13605. +{
  13606. + return _afterCastTarget;
  13607. +}
  13608. +inline void AfterCast::SetTarget(Unit* target)
  13609. +{
  13610. + _afterCastTarget = target;
  13611. +}
  13612. +inline void AfterCast::SetAfterCastCommand(void (*newAfterCast)(Unit*))
  13613. +{
  13614. + afterCast = newAfterCast;
  13615. +}
  13616. +inline void AfterCast::LaunchAfterCastCommand()
  13617. +{
  13618. + ASSERT(_afterCastTarget);
  13619. +
  13620. + return (*afterCast)(_afterCastTarget);
  13621. +}
  13622. +
  13623. +class PlayerbotChatHandler : public ChatHandler
  13624. +{
  13625. +public:
  13626. + explicit PlayerbotChatHandler(WorldSession* masterSession) : ChatHandler(masterSession) {}
  13627. + bool revive(Player& botPlayer) { return HandleReviveCommand(this, botPlayer.GetName().c_str()); }
  13628. + bool teleport(Player& botPlayer) { return HandleSummonCommand(this, botPlayer.GetName().c_str()); }
  13629. + //bool teleport(Player& botPlayer, WorldObject &obj) { return botPlayer.TeleportTo(obj); }
  13630. + void sysmessage(char const* str) { SendSysMessage(str); }
  13631. + bool dropQuest(char const* str) { return HandleQuestRemove(this, str); }
  13632. +private:
  13633. + static bool HandleReviveCommand(ChatHandler* handler, char const* args)
  13634. + {
  13635. + Player* target;
  13636. + uint64 targetGuid;
  13637. + std::string targetName;
  13638. + if (!handler->extractPlayerTarget((char*)args, &target, &targetGuid, &targetName))
  13639. + return false;
  13640. +
  13641. + //Player* _player = handler->GetSession()->GetPlayer();
  13642. + if (!target || target->isAlive() || target->InArena())
  13643. + {
  13644. + handler->PSendSysMessage("Resurrection player %s, failed", uint32(GUID_LOPART(targetGuid)));
  13645. + handler->SetSentErrorMessage(true);
  13646. + return false;
  13647. + }
  13648. +
  13649. + target->ResurrectPlayer(target->InBattleground() ? 1.0f : 0.5f);
  13650. + target->SpawnCorpseBones();
  13651. +
  13652. + handler->SetSentErrorMessage(true);
  13653. + return true;
  13654. + }
  13655. +
  13656. + static bool HandleSummonCommand(ChatHandler* handler, char const* args)
  13657. + {
  13658. + Player* target;
  13659. + uint64 targetGuid;
  13660. + std::string targetName;
  13661. + if (!handler->extractPlayerTarget((char*)args, &target, &targetGuid, &targetName))
  13662. + return false;
  13663. +
  13664. + Player* _player = handler->GetSession()->GetPlayer();
  13665. + if (target == _player || targetGuid == _player->GetGUID())
  13666. + {
  13667. + handler->PSendSysMessage(LANG_CANT_TELEPORT_SELF);
  13668. + handler->SetSentErrorMessage(true);
  13669. + return false;
  13670. + }
  13671. +
  13672. + if (target)
  13673. + {
  13674. + std::string nameLink = handler->playerLink(targetName);
  13675. +
  13676. + if (target->IsBeingTeleported())
  13677. + {
  13678. + handler->PSendSysMessage(LANG_IS_TELEPORTED, nameLink.c_str());
  13679. + handler->SetSentErrorMessage(true);
  13680. + return false;
  13681. + }
  13682. +
  13683. + Map* map = handler->GetSession()->GetPlayer()->GetMap();
  13684. +
  13685. + if (map->IsBattlegroundOrArena())
  13686. + {
  13687. + //// only allow if gm mode is on
  13688. + //if (!_player->isGameMaster())
  13689. + //{
  13690. + // handler->PSendSysMessage(LANG_CANNOT_GO_TO_BG_GM, nameLink.c_str());
  13691. + // handler->SetSentErrorMessage(true);
  13692. + // return false;
  13693. + //}
  13694. + // if both players are in different bgs
  13695. + /*else */if (target->GetBattlegroundId() && handler->GetSession()->GetPlayer()->GetBattlegroundId() != target->GetBattlegroundId())
  13696. + target->LeaveBattleground(false); // Note: should be changed so target gets no Deserter debuff
  13697. +
  13698. + // all's well, set bg id
  13699. + // when porting out from the bg, it will be reset to 0
  13700. + target->SetBattlegroundId(handler->GetSession()->GetPlayer()->GetBattlegroundId(), handler->GetSession()->GetPlayer()->GetBattlegroundTypeId());
  13701. + // remember current position as entry point for return at bg end teleportation
  13702. + if (!target->GetMap()->IsBattlegroundOrArena())
  13703. + target->SetBattlegroundEntryPoint();
  13704. + }
  13705. + else if (map->IsDungeon())
  13706. + {
  13707. + Map* map = target->GetMap();
  13708. +
  13709. + if (map->Instanceable() && map->GetInstanceId() != map->GetInstanceId())
  13710. + target->UnbindInstance(map->GetInstanceId(), target->GetDungeonDifficulty(), true);
  13711. +
  13712. + //// we are in instance, and can summon only player in our group with us as lead
  13713. + //if (!handler->GetSession()->GetPlayer()->GetGroup() || !target->GetGroup() ||
  13714. + // (target->GetGroup()->GetLeaderGUID() != handler->GetSession()->GetPlayer()->GetGUID()) ||
  13715. + // (handler->GetSession()->GetPlayer()->GetGroup()->GetLeaderGUID() != handler->GetSession()->GetPlayer()->GetGUID()))
  13716. + // // the last check is a bit excessive, but let it be, just in case
  13717. + //{
  13718. + // handler->PSendSysMessage(LANG_CANNOT_SUMMON_TO_INST, nameLink.c_str());
  13719. + // handler->SetSentErrorMessage(true);
  13720. + // return false;
  13721. + //}
  13722. + }
  13723. +
  13724. + //handler->PSendSysMessage(LANG_SUMMONING, nameLink.c_str(), "");
  13725. + //if (handler->needReportToTarget(target))
  13726. + // ChatHandler(target).PSendSysMessage(LANG_SUMMONED_BY, handler->playerLink(_player->GetName()).c_str());
  13727. +
  13728. + // stop flight if need
  13729. + if (target->isInFlight())
  13730. + {
  13731. + target->GetMotionMaster()->MovementExpired();
  13732. + target->CleanupAfterTaxiFlight();
  13733. + }
  13734. + // save only in non-flight case
  13735. + else
  13736. + target->SaveRecallPosition();
  13737. +
  13738. + // before GM
  13739. + float x, y, z;
  13740. + handler->GetSession()->GetPlayer()->GetClosePoint(x, y, z, target->GetObjectSize());
  13741. + target->TeleportTo(handler->GetSession()->GetPlayer()->GetMapId(), x, y, z, target->GetOrientation(), TELE_TO_GM_MODE);
  13742. + target->SetPhaseMask(handler->GetSession()->GetPlayer()->GetPhaseMask(), true);
  13743. + }
  13744. + else
  13745. + {
  13746. + //std::string nameLink = handler->playerLink(targetName);
  13747. +
  13748. + //handler->PSendSysMessage(LANG_SUMMONING, nameLink.c_str(), handler->GetTrinityString(LANG_OFFLINE));
  13749. +
  13750. + // in point where GM stay
  13751. + Player::SavePositionInDB(handler->GetSession()->GetPlayer()->GetMapId(),
  13752. + handler->GetSession()->GetPlayer()->GetPositionX(),
  13753. + handler->GetSession()->GetPlayer()->GetPositionY(),
  13754. + handler->GetSession()->GetPlayer()->GetPositionZ(),
  13755. + handler->GetSession()->GetPlayer()->GetOrientation(),
  13756. + handler->GetSession()->GetPlayer()->GetZoneId(),
  13757. + targetGuid);
  13758. + }
  13759. +
  13760. + return true;
  13761. + }
  13762. +
  13763. + static bool HandleQuestRemove(ChatHandler* handler, const char* args)
  13764. + {
  13765. + Player* player = handler->getSelectedPlayer();
  13766. + if (!player)
  13767. + {
  13768. + handler->SendSysMessage(LANG_NO_CHAR_SELECTED);
  13769. + handler->SetSentErrorMessage(true);
  13770. + return false;
  13771. + }
  13772. +
  13773. + // .removequest #entry'
  13774. + // number or [name] Shift-click form |color|Hquest:quest_id:quest_level|h[name]|h|r
  13775. + char* cId = handler->extractKeyFromLink((char*)args, "Hquest");
  13776. + if (!cId)
  13777. + return false;
  13778. +
  13779. + uint32 entry = atol(cId);
  13780. +
  13781. + Quest const* quest = sObjectMgr->GetQuestTemplate(entry);
  13782. +
  13783. + if (!quest)
  13784. + {
  13785. + handler->PSendSysMessage(LANG_COMMAND_QUEST_NOTFOUND, entry);
  13786. + handler->SetSentErrorMessage(true);
  13787. + return false;
  13788. + }
  13789. +
  13790. + // remove all quest entries for 'entry' from quest log
  13791. + for (uint8 slot = 0; slot < MAX_QUEST_LOG_SIZE; ++slot)
  13792. + {
  13793. + uint32 logQuest = player->GetQuestSlotQuestId(slot);
  13794. + if (logQuest == entry)
  13795. + {
  13796. + player->SetQuestSlot(slot, 0);
  13797. +
  13798. + // we ignore unequippable quest items in this case, its' still be equipped
  13799. + player->TakeQuestSourceItem(logQuest, false);
  13800. + }
  13801. + }
  13802. +
  13803. + player->RemoveActiveQuest(entry);
  13804. + player->RemoveRewardedQuest(entry);
  13805. +
  13806. + handler->SendSysMessage(LANG_COMMAND_QUEST_REMOVED);
  13807. + return true;
  13808. + }
  13809. +};
  13810. +
  13811. +PlayerbotAI::PlayerbotAI(PlayerbotMgr* const mgr, Player* const bot) : _mgr(mgr), me(bot), _classAI(NULL)
  13812. +//m_combatOrder(ORDERS_NONE), m_ScenarioType(SCENARIO_PVE),
  13813. +//m_TimeDoneEating(0), m_TimeDoneDrinking(0),
  13814. +//m_CurrentlyCastingSpellId(0),
  13815. +//m_CraftSpellId(0), m_spellIdCommand(0),
  13816. +//m_targetGuidCommand(0),
  13817. +//m_taxiMaster(0)
  13818. +{
  13819. + //m_bDebugCommandChat = _mgr->m_confDebugWhisper;
  13820. +
  13821. + //m_targetChanged = false;
  13822. + //m_targetType = TARGET_NORMAL;
  13823. + //m_targetCombat = 0;
  13824. + //m_targetAssist = 0;
  13825. + //m_targetProtect = 0;
  13826. +
  13827. + // set collection options
  13828. + //m_collectionFlags = 0;
  13829. + //m_collectDist = _mgr->m_confCollectDistance;
  13830. + //if (_mgr->m_confCollectCombat)
  13831. + // SetCollectFlag(COLLECT_FLAG_COMBAT);
  13832. + //if (_mgr->m_confCollectQuest)
  13833. + // SetCollectFlag(COLLECT_FLAG_QUEST);
  13834. + //if (_mgr->m_confCollectProfession)
  13835. + // SetCollectFlag(COLLECT_FLAG_PROFESSION);
  13836. + //if (_mgr->m_confCollectLoot)
  13837. + // SetCollectFlag(COLLECT_FLAG_LOOT);
  13838. + //if (_mgr->m_confCollectSkin && m_bot->HasSkill(SKILL_SKINNING))
  13839. + // SetCollectFlag(COLLECT_FLAG_SKIN);
  13840. + //if (_mgr->m_confCollectObjects)
  13841. + // SetCollectFlag(COLLECT_FLAG_NEAROBJECT);
  13842. +
  13843. + //// set needed item list
  13844. + //SetQuestNeedItems();
  13845. + //SetQuestNeedCreatures();
  13846. +
  13847. + //// start following master (will also teleport bot to master)
  13848. + //m_dropWhite = false;
  13849. + //m_AutoEquipToggle = false;
  13850. + //m_FollowAutoGo = FOLLOWAUTOGO_OFF; //turn on bot auto follow distance can be turned off by player
  13851. + //DistOverRide = 0; //set initial adjustable follow settings
  13852. + //IsUpOrDown = 0;
  13853. + //gTempDist = 0.5f;
  13854. + //gTempDist2 = 1.0f;
  13855. + //BotDataRestore();
  13856. + //ClearActiveTalentSpec();
  13857. +
  13858. + _botStates = 0;
  13859. + _combatStates = 0;
  13860. + _movementFlags = 0;
  13861. + _followTimer = 0;
  13862. + _followTargetGUID = 0;
  13863. + _waitTimer = 0;
  13864. + _mountTimer = 0;
  13865. + _cureTimer = 0;
  13866. + _selfResTimer = 0;
  13867. + _opponent = NULL;
  13868. + _afterCast = NULL;
  13869. +
  13870. + _canSelfRes = false;
  13871. +
  13872. + //get class specific ai
  13873. + ReloadClassAI();
  13874. + //init motion
  13875. + AddBotState(BOTSTATE_FOLLOW);
  13876. + //init group (temp)
  13877. + //if (me->GetTeamId() != GetMaster()->GetTeamId())
  13878. + // me->setFaction(GetMaster()->getFaction());
  13879. + _InviteToMastersGroup();
  13880. + //save instance bounds
  13881. + for (uint8 i = REGULAR_DIFFICULTY; i != MAX_DIFFICULTY; ++i)
  13882. + {
  13883. + for (Player::BoundInstancesMap::iterator itr = me->GetBoundInstances(Difficulty(i)).begin(); itr != me->GetBoundInstances(Difficulty(i)).end(); ++itr)
  13884. + {
  13885. + _botBoundInstances[Difficulty(i)][itr->first].perm = itr->second.perm;
  13886. + //reserve current save by creating new (fake) save, any of bot's saves can become invalid in time
  13887. + _botBoundInstances[Difficulty(i)][itr->first].save = new InstanceSave(itr->second.save->GetMapId(), itr->second.save->GetInstanceId(), Difficulty(i), itr->second.save->GetResetTime(), itr->second.save->CanReset());
  13888. + }
  13889. + }
  13890. +}
  13891. +
  13892. +PlayerbotAI::~PlayerbotAI()
  13893. +{
  13894. + SQLTransaction trans = CharacterDatabase.BeginTransaction();
  13895. + for (uint8 i = REGULAR_DIFFICULTY; i != MAX_DIFFICULTY; ++i)
  13896. + {
  13897. + Difficulty diff = Difficulty(i);
  13898. + for (Player::BoundInstancesMap::iterator itr = _botBoundInstances[i].begin(); itr != _botBoundInstances[i].end(); ++itr)
  13899. + {
  13900. + if (me->GetBoundInstance(itr->first, diff))
  13901. + me->UnbindInstance(itr->first, diff);
  13902. +
  13903. + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHAR_INSTANCE);
  13904. +
  13905. + stmt->setUInt32(0, me->GetGUIDLow());
  13906. + stmt->setUInt32(1, itr->second.save->GetInstanceId());
  13907. + stmt->setBool(2, itr->second.perm);
  13908. +
  13909. + trans->Append(stmt);
  13910. + //if after unbind old save is not unloaded bind to old save, else create new save and save to instance mgr
  13911. + //if (InstanceSave* save = sInstanceSaveMgr->AddInstanceSave(itr->second.save->GetMapId(), itr->second.save->GetInstanceId(), diff, itr->second.save->GetResetTime(), itr->second.save->CanReset(), true))
  13912. + // me->BindToInstance(save, itr->second.perm);
  13913. +
  13914. + delete itr->second.save; //delete our old fake save
  13915. + }
  13916. +
  13917. + _botBoundInstances[diff].clear();
  13918. + }
  13919. +
  13920. + CharacterDatabase.CommitTransaction(trans);
  13921. +
  13922. + if (_classAI) delete _classAI;
  13923. + if (_afterCast) delete _afterCast;
  13924. +}
  13925. +
  13926. +void PlayerbotAI::ReloadClassAI()
  13927. +{
  13928. + if (_classAI)
  13929. + delete _classAI;
  13930. + _mySpec = PlayerbotMgr::GetSpec(me);
  13931. + switch (me->getClass())
  13932. + {
  13933. + case CLASS_PRIEST:
  13934. + _classAI = (PlayerbotClassAI*) new PlayerbotPriestAI(GetMaster(), me, this);
  13935. + break;
  13936. + case CLASS_MAGE:
  13937. + _classAI = (PlayerbotClassAI*) new PlayerbotMageAI(GetMaster(), me, this);
  13938. + break;
  13939. + case CLASS_WARLOCK:
  13940. + _classAI = (PlayerbotClassAI*) new PlayerbotWarlockAI(GetMaster(), me, this);
  13941. + break;
  13942. + case CLASS_WARRIOR:
  13943. + _classAI = (PlayerbotClassAI*) new PlayerbotWarriorAI(GetMaster(), me, this);
  13944. + break;
  13945. + case CLASS_SHAMAN:
  13946. + _classAI = (PlayerbotClassAI*) new PlayerbotShamanAI(GetMaster(), me, this);
  13947. + break;
  13948. + case CLASS_PALADIN:
  13949. + _classAI = (PlayerbotClassAI*) new PlayerbotPaladinAI(GetMaster(), me, this);
  13950. + break;
  13951. + case CLASS_ROGUE:
  13952. + _classAI = (PlayerbotClassAI*) new PlayerbotRogueAI(GetMaster(), me, this);
  13953. + break;
  13954. + case CLASS_DRUID:
  13955. + _classAI = (PlayerbotClassAI*) new PlayerbotDruidAI(GetMaster(), me, this);
  13956. + break;
  13957. + case CLASS_HUNTER:
  13958. + _classAI = (PlayerbotClassAI*) new PlayerbotHunterAI(GetMaster(), me, this);
  13959. + break;
  13960. + case CLASS_DEATH_KNIGHT:
  13961. + _classAI = (PlayerbotClassAI*) new PlayerbotDeathKnightAI(GetMaster(), me, this);
  13962. + break;
  13963. + }
  13964. +
  13965. + //HERB_GATHERING = initSpell(HERB_GATHERING_1);
  13966. + //MINING = initSpell(MINING_1);
  13967. + //SKINNING = initSpell(SKINNING_1);
  13968. +}
  13969. +
  13970. +void PlayerbotAI::UpdateAI(uint32 diff)
  13971. +{
  13972. + _doTimers(diff);
  13973. +
  13974. + //disabled AI check point 1
  13975. + if (!_classAI || me->IsBeingTeleported() || !me->IsInWorld() || !me->FindMap())
  13976. + return;
  13977. +
  13978. + //debug remove root
  13979. + if (!me->HasUnitState(UNIT_STATE_CASTING))
  13980. + {
  13981. + if (me->HasAura(SPELL_ROOT))
  13982. + me->RemoveAura(SPELL_ROOT);
  13983. + //process aftercast
  13984. + //Aftercast should be created in certain cast action
  13985. + //TODO: aftercast pack
  13986. + if (_afterCast != NULL)
  13987. + {
  13988. + _afterCast->LaunchAfterCastCommand();
  13989. + delete _afterCast;
  13990. + _afterCast = NULL;
  13991. + }
  13992. + }
  13993. +
  13994. + if (_waitTimer > diff)
  13995. + return;
  13996. +
  13997. + _waitTimer = 600 + (_mgr->GetPlayerBotsCount() - 1) * 50;//up to a sec
  13998. +
  13999. + //DEATH BEHAVIOUR 99.5%
  14000. + if (me->isDead())
  14001. + {
  14002. + UpdateDeadActions(diff);
  14003. + return;
  14004. + }
  14005. +
  14006. + //disabled AI check point 2
  14007. + if (me->isInFlight() || me->isCharmed() || me->GetTrader())
  14008. + return;
  14009. +
  14010. + //BreakCC(diff);
  14011. + if (CCed(me)) return;
  14012. +
  14013. + //POTIONS/FLASKS/etc. (opt)
  14014. +
  14015. + //CURE / HEAL / CLASS SPECIFIC / NON-COMBAT ACTIONS
  14016. + _classAI->UpdateGroupActions(diff);
  14017. +
  14018. + //MISC ACTIONS
  14019. +
  14020. + //INCOMBAT UPDATE PAI 70% else is CAI
  14021. + if (HasAttackTarget(me->getClass(), _mySpec))
  14022. + {
  14023. + if (me->IsMounted())
  14024. + UpdateMountedState(diff);
  14025. +
  14026. + UpdateIncombatActions();
  14027. + return;
  14028. + }
  14029. +
  14030. + //OUT OF COMBAT BEHAVIOUR 1%
  14031. + if (HasBotState(BOTSTATE_LOOTING))
  14032. + {
  14033. + //TODO: LOOT
  14034. + }
  14035. + else if (!me->isInCombat())
  14036. + {
  14037. + //TODO: UpdateNonCombatActions();
  14038. + //TODO: UpdateRations();
  14039. + AddBotState(BOTSTATE_FOLLOW);
  14040. + }
  14041. +
  14042. + if (HasBotState(BOTSTATE_FOLLOW))
  14043. + UpdateFollowActions(diff);
  14044. +
  14045. + UpdateStandState();
  14046. +
  14047. + //DELAYED OPERATIONS
  14048. +
  14049. + //TODO: TAME
  14050. +
  14051. + //TODO: DIRECT CAST
  14052. +
  14053. + //TODO: CRAFTING
  14054. +
  14055. + //TODO: ORDERS (opt)
  14056. +}
  14057. +
  14058. +void PlayerbotAI::UpdateDeadActions(uint32 diff)
  14059. +{
  14060. + if (!_canSelfRes)
  14061. + {
  14062. + _selfResTimer = GetMaster()->isInCombat() ? 2000 : 10000;
  14063. + if (Map* map = GetMaster()->FindMap())
  14064. + if (map->IsRaidOrHeroicDungeon())
  14065. + _selfResTimer /= 2;
  14066. + _canSelfRes = true;
  14067. + return;
  14068. + }
  14069. +
  14070. + if (_selfResTimer > diff)
  14071. + return;
  14072. +
  14073. + if (me->getDeathState() == CORPSE)
  14074. + {
  14075. + //debug
  14076. + //if (me->GetCorpse())
  14077. + //{
  14078. + // sLog->outError(LOG_FILTER_PLAYER, "PlayerbotAI: UpdateDeadActions - %s already has a corpse!", me->GetName().c_str());
  14079. + // //m_bot->setDeathState(DEAD);
  14080. + // return;
  14081. + //}
  14082. + //clear loot
  14083. + //m_lootTargets.clear();
  14084. + //m_lootCurrent = 0;
  14085. +
  14086. + //has selfRezz
  14087. + if (me->GetUInt32Value(PLAYER_SELF_RES_SPELL))
  14088. + {
  14089. + WorldPacket p;//not really used
  14090. + me->GetSession()->HandleSelfResOpcode(p);
  14091. + return;
  14092. + }
  14093. +
  14094. + me->BuildPlayerRepop();
  14095. + me->setDeathState(DEAD);
  14096. + //me->ResetDeathTimer();//release
  14097. + //me->RepopAtGraveyard();
  14098. + }
  14099. + //else if (m_bot->getDeathState() == DEAD) //appears to be unused for players
  14100. + else if (me->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST))
  14101. + {
  14102. + Corpse* corpse = me->GetCorpse();
  14103. + //debug
  14104. + if (!corpse)
  14105. + {
  14106. + //sLog->outError(LOG_FILTER_PLAYER, "PlayerbotAI: UpdateAI - %s has no corpse!", me->GetName().c_str());
  14107. + //m_bot->setDeathState(CORPSE);
  14108. + return;
  14109. + }
  14110. + //check reclaim distance, teleport if needed
  14111. + if (me->GetDistance(corpse) > CORPSE_RECLAIM_RADIUS)
  14112. + {
  14113. + //sLog->outError(LOG_FILTER_PLAYER, "PlayerbotAI: UpdateAI - Teleport %s to corpse...", me->GetName().c_str());
  14114. + me->TeleportTo(*corpse, TELE_TO_GM_MODE);
  14115. + }
  14116. + //check reclaim delay
  14117. + int32 delay = (corpse->GetGhostTime() + me->GetCorpseReclaimDelay(corpse->GetType() == CORPSE_RESURRECTABLE_PVP)) - time(NULL);
  14118. + if (delay > 0)
  14119. + {
  14120. + //sLog->outError(LOG_FILTER_PLAYER, "PlayerbotAI: UpdateAI - %s has to wait for %u seconds to revive...", me->GetName().c_str(), uint32(delay));
  14121. + return;
  14122. + }
  14123. + //revive by master
  14124. + //sLog->outError(LOG_FILTER_PLAYER, "PlayerbotAI: UpdateAI - Reviving %s...", me->GetName().c_str());
  14125. + if (!PlayerbotChatHandler(GetMaster()->GetSession()).revive(*me))
  14126. + {
  14127. + //sLog->outError(LOG_FILTER_PLAYER, "PlayerbotAI: %s could not be revived ...", me->GetName().c_str());
  14128. + return;
  14129. + }
  14130. + }
  14131. + _canSelfRes = false;
  14132. +}
  14133. +
  14134. +void PlayerbotAI::UpdateIncombatActions()
  14135. +{
  14136. + //SHEATH
  14137. + if (!me->HasUnitState(UNIT_STATE_CASTING) && !me->IsMounted())
  14138. + {
  14139. + SheathState sheath = ShouldRanged() ? SHEATH_STATE_RANGED : SHEATH_STATE_MELEE;
  14140. + if (me->GetSheath() != sheath)
  14141. + me->SetSheath(sheath);
  14142. + }
  14143. +
  14144. + //CC CAI full
  14145. + { }
  14146. +
  14147. + //handle target
  14148. + if (Unit* u = me->getVictim())
  14149. + {
  14150. + //POSITION
  14151. + if (me->GetDistance2d(u) > GetMinAttackRange(u) && !ShouldStay())
  14152. + {
  14153. + GetInPosition(true, ShouldRanged());
  14154. + //return;
  14155. + }
  14156. + //ANGLE (emulated)
  14157. + else if (!me->HasInArc(M_PI*0.5f, u))
  14158. + {
  14159. + float x = me->GetPositionX() - (me->GetPositionX() - u->GetPositionX())*0.05f;
  14160. + float y = me->GetPositionY() - (me->GetPositionY() - u->GetPositionY())*0.05f;
  14161. + float z = me->GetPositionZ() - (me->GetPositionZ() - u->GetPositionZ())*0.05f;
  14162. + me->GetMotionMaster()->MovePoint(me->GetMapId(), x, y, z);
  14163. + //me->SetFacingToObject(u);
  14164. + }
  14165. +
  14166. + //ATTACK CAI full
  14167. + _classAI->DoCombatActions();
  14168. +
  14169. + return;
  14170. + }
  14171. +
  14172. + //no target
  14173. + ClearBotState(BOTSTATE_COMBAT);
  14174. +}
  14175. +
  14176. +void PlayerbotAI::UpdateFollowActions(uint32 diff)
  14177. +{
  14178. + if (_followTimer > diff)
  14179. + return;
  14180. +
  14181. + _followTimer = 2000;
  14182. +
  14183. + if (me->HasUnitState(UNIT_STATE_CASTING) || CCed(me, true))
  14184. + return;
  14185. +
  14186. + if (me->getVictim())
  14187. + {
  14188. + sLog->outError(LOG_FILTER_PLAYER, "PlayerbotAI: %s tried following while still fighting!", me->GetName().c_str());
  14189. + return;
  14190. + }
  14191. +
  14192. + //debug
  14193. + if ((CanFollowOthers()) != (_followTargetGUID != 0))
  14194. + {
  14195. + if (CanFollowOthers() && _followTargetGUID == 0)
  14196. + sLog->outError(LOG_FILTER_PLAYER, "PlayerbotAI: %s should follow someone else but has no target specified!", me->GetName().c_str());
  14197. + else if (!CanFollowOthers() && _followTargetGUID != 0)
  14198. + sLog->outError(LOG_FILTER_PLAYER, "PlayerbotAI: %s has follow target specified but cannot follow it!", me->GetName().c_str());
  14199. + //force follow master
  14200. + _followTargetGUID = 0;
  14201. + ClearMovementFlag(MOVEMENT_FLAG_TARGET_CHANGED);
  14202. + }
  14203. +
  14204. + //get follow target:
  14205. + //a) current follow target (selected)
  14206. + //b) follow target set by master
  14207. + //c) master
  14208. + //if player target is dead use it's corpse as follow target
  14209. + WorldObject* fTarget = NULL;
  14210. + if (uint64 sel = me->GetSelection())
  14211. + {
  14212. + if (!me->GetGroup() || me->GetGroup()->IsMember(sel))
  14213. + fTarget = sObjectAccessor->GetPlayer(*me, sel);
  14214. + }
  14215. + else if (Group* group = me->GetGroup())
  14216. + {
  14217. + if (group == GetMaster()->GetGroup())
  14218. + fTarget = GetMaster();
  14219. + }
  14220. + else if (_followTargetGUID != 0)
  14221. + {
  14222. + fTarget = sObjectAccessor->GetPlayer(*me, _followTargetGUID);
  14223. + if (fTarget->GetTypeId() == TYPEID_PLAYER && fTarget->ToPlayer()->isDead() && fTarget->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST))
  14224. + fTarget = fTarget->ToPlayer()->GetCorpse();
  14225. + }
  14226. + else
  14227. + {
  14228. + if (GetMaster()->isDead() && GetMaster()->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST))
  14229. + fTarget = GetMaster()->GetCorpse();
  14230. + else
  14231. + fTarget = GetMaster();
  14232. + }
  14233. +
  14234. + if (!fTarget)
  14235. + {
  14236. + sLog->outError(LOG_FILTER_PLAYER, "PlayerbotAI: %s has no follow target! Set to master...", me->GetName().c_str());
  14237. + fTarget = GetMaster();
  14238. + _followTargetGUID = 0;
  14239. + ClearMovementFlag(MOVEMENT_FLAG_TARGET_CHANGED);
  14240. + }
  14241. +
  14242. + //unsafe
  14243. + if (me->GetPhaseMask() != fTarget->GetPhaseMask())
  14244. + me->SetPhaseMask(fTarget->GetPhaseMask(), true);
  14245. +
  14246. + Player* fPlayer = fTarget->ToPlayer();
  14247. +
  14248. + //flight check (all such checks should go here)
  14249. + if (!fTarget->IsInWorld() || !fTarget->FindMap() ||
  14250. + (fPlayer && (fPlayer->isInFlight() || fPlayer->IsBeingTeleported())))
  14251. + {
  14252. + _followTimer = 5000;
  14253. + return;
  14254. + }
  14255. +
  14256. + //teleport if too far away
  14257. + Map const* const mymap = me->GetMap();
  14258. + Map const* const tarmap = fTarget->GetMap();
  14259. + if (mymap != tarmap || me->GetDistance2d(fTarget) > sWorld->GetMaxVisibleDistanceOnContinents())
  14260. + {
  14261. + if (fPlayer && tarmap->Instanceable() && mymap != tarmap)
  14262. + {
  14263. + Difficulty diff = fPlayer->GetDifficulty(tarmap->IsRaid());
  14264. + if (InstancePlayerBind* bind = me->GetBoundInstance(tarmap->GetId(), diff))
  14265. + _UnbindInstance(tarmap->GetId(), diff);
  14266. + }
  14267. + me->TeleportTo(*fTarget, TELE_TO_GM_MODE);
  14268. + return;
  14269. +
  14270. + }
  14271. +
  14272. + //sheath additive
  14273. + if (me->GetSheath() != SHEATH_STATE_UNARMED && _mgr->Rand() < 30)
  14274. + me->SetSheath(SHEATH_STATE_UNARMED);
  14275. +
  14276. + if (!me->HasUnitState(UNIT_STATE_FOLLOW))
  14277. + SetMovement(fTarget);
  14278. +
  14279. + //only if following master
  14280. + if (fTarget->GetGUID() == GetMaster()->GetGUID())
  14281. + UpdateMountedState(diff);
  14282. +}
  14283. +
  14284. +void PlayerbotAI::UpdateStandState()
  14285. +{
  14286. + //only if following master
  14287. + if (_followTargetGUID != 0)
  14288. + return;
  14289. +
  14290. + if (GetMaster()->getStandState() == UNIT_STAND_STATE_STAND && me->getStandState() == UNIT_STAND_STATE_SIT &&
  14291. + !(me->GetInterruptMask() & AURA_INTERRUPT_FLAG_NOT_SEATED))
  14292. + me->SetStandState(UNIT_STAND_STATE_STAND);
  14293. + if (GetMaster()->getStandState() == UNIT_STAND_STATE_SIT && me->getStandState() == UNIT_STAND_STATE_STAND &&
  14294. + !me->isInCombat() && !me->isMoving() && !me->IsMounted())
  14295. + me->SetStandState(UNIT_STAND_STATE_SIT);
  14296. +}
  14297. +
  14298. +void PlayerbotAI::UpdateMountedState(uint32 diff)
  14299. +{
  14300. + //dismount
  14301. + if (!GetMaster()->IsMounted() || ((me->isInCombat() || !me->getAttackers().empty()) && HasBotState(BOTSTATE_COMBAT)))
  14302. + {
  14303. + if (me->HasAuraType(SPELL_AURA_MOUNTED))
  14304. + me->RemoveAurasByType(SPELL_AURA_MOUNTED);
  14305. + return;
  14306. + }
  14307. +
  14308. + if (_mountTimer > diff)
  14309. + return;
  14310. +
  14311. + _mountTimer = 500;
  14312. +
  14313. + //cannot mount in water, in combat, if mounted somehow (some encounter)
  14314. + if (me->HasUnitMovementFlag(MOVEMENTFLAG_SWIMMING) || me->isInCombat() || me->IsMounted())
  14315. + return;
  14316. +
  14317. + //TODO: HUGE
  14318. + //Player Part
  14319. + Unit::AuraEffectList const& auraList = GetMaster()->GetAuraEffectsByType(SPELL_AURA_MOUNTED);
  14320. + if (!auraList.empty())
  14321. + {
  14322. + SpellInfo const* spellInfo = auraList.front()->GetSpellInfo();
  14323. + if (!spellInfo)
  14324. + {
  14325. + sLog->outError(LOG_FILTER_PLAYER, "PlayerbotAI: %s tried to mount but master %s is mounted by spell with no spellInfo!", me->GetName().c_str(), GetMaster()->GetName().c_str());
  14326. + return;
  14327. + }
  14328. +
  14329. + //Bot Part
  14330. + uint32 spellMount = 0;
  14331. + //cheap check if we know this spell
  14332. + for (PlayerSpellMap::iterator itr = me->GetSpellMap().begin(); itr != me->GetSpellMap().end(); ++itr)
  14333. + {
  14334. + //if (itr->second->state == PLAYERSPELL_REMOVED || itr->second->disabled)
  14335. + // continue;
  14336. +
  14337. + //cheap check if we just have the same mount
  14338. + uint32 spellId = itr->first;
  14339. + if (spellInfo->Id == spellId)
  14340. + {
  14341. + spellMount = spellId;
  14342. + break;
  14343. + }
  14344. + }
  14345. + if (!spellMount)
  14346. + {
  14347. + //analyze and find proper mount spell
  14348. + for (PlayerSpellMap::iterator itr = me->GetSpellMap().begin(); itr != me->GetSpellMap().end(); ++itr)
  14349. + {
  14350. + //if (itr->second->state == PLAYERSPELL_REMOVED || itr->second->disabled)
  14351. + // continue;
  14352. + uint32 spellId = itr->first;
  14353. + SpellInfo const* bSpellInfo = sSpellMgr->GetSpellInfo(spellId);
  14354. + if (!bSpellInfo || bSpellInfo->IsPassive())
  14355. + continue;
  14356. +
  14357. + for (uint8 i = 0; i != MAX_SPELL_EFFECTS; ++i)
  14358. + {
  14359. + if (bSpellInfo->Effects[i].ApplyAuraName == SPELL_AURA_MOUNTED)
  14360. + {
  14361. + //arrange values
  14362. + int8 j = i-1, k = i+1;
  14363. + if (j < 0)// i == 0
  14364. + j = k+1;//2
  14365. + else if (k >= MAX_SPELL_EFFECTS)// i == 2
  14366. + k = j-1;//0
  14367. +
  14368. + if (bSpellInfo->Effects[j].ApplyAuraName == SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED)
  14369. + {
  14370. + for (uint8 i = 0; i != MAX_SPELL_EFFECTS; ++i)
  14371. + {
  14372. + if (spellInfo->Effects[i].BasePoints == bSpellInfo->Effects[j].BasePoints)
  14373. + {
  14374. + spellMount = spellId;
  14375. + break;
  14376. + }
  14377. + }
  14378. + if (spellMount)
  14379. + break;
  14380. + }
  14381. + else if (bSpellInfo->Effects[k].ApplyAuraName == SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED)
  14382. + {
  14383. + for (uint8 i = 0; i != MAX_SPELL_EFFECTS; ++i)
  14384. + {
  14385. + if (spellInfo->Effects[i].BasePoints == bSpellInfo->Effects[k].BasePoints)
  14386. + {
  14387. + spellMount = spellId;
  14388. + break;
  14389. + }
  14390. + }
  14391. + if (spellMount)
  14392. + break;
  14393. + }
  14394. + else if (bSpellInfo->Effects[j].ApplyAuraName == SPELL_AURA_MOD_INCREASE_MOUNTED_SPEED)
  14395. + {
  14396. + for (uint8 i = 0; i != MAX_SPELL_EFFECTS; ++i)
  14397. + {
  14398. + if (spellInfo->Effects[i].BasePoints == bSpellInfo->Effects[j].BasePoints)
  14399. + {
  14400. + spellMount = spellId;
  14401. + break;
  14402. + }
  14403. + }
  14404. + if (spellMount)
  14405. + break;
  14406. + }
  14407. + else if (bSpellInfo->Effects[k].ApplyAuraName == SPELL_AURA_MOD_INCREASE_MOUNTED_SPEED)
  14408. + {
  14409. + for (uint8 i = 0; i != MAX_SPELL_EFFECTS; ++i)
  14410. + {
  14411. + if (spellInfo->Effects[i].BasePoints == bSpellInfo->Effects[k].BasePoints)
  14412. + {
  14413. + spellMount = spellId;
  14414. + break;
  14415. + }
  14416. + }
  14417. + if (spellMount)
  14418. + break;
  14419. + }
  14420. + }
  14421. + }
  14422. + if (spellMount)
  14423. + break;
  14424. + }
  14425. + }
  14426. + if (spellMount)
  14427. + {
  14428. + CastSpell(me, spellMount);
  14429. + }
  14430. + else
  14431. + {
  14432. + sLog->outError(LOG_FILTER_PLAYER, "PlayerbotAI: %s cannot find a proper mount!", me->GetName().c_str());
  14433. + //SendWhisper("Cannot find approriate mount!", *GetMaster());
  14434. + }
  14435. + }
  14436. +}
  14437. +
  14438. +bool PlayerbotAI::HasAttackTarget(uint8 /*myclass*/, uint32 myspec)
  14439. +{
  14440. + bool ranged = false;
  14441. + bool byspell = false;
  14442. + bool reset = false;
  14443. +
  14444. + //choose proper targeting mode
  14445. + //step 1: by spec
  14446. + //step 2: check conditions
  14447. + switch (MainSpec(myspec))
  14448. + {
  14449. + case MAGE_SPEC_FIRE:
  14450. + case MAGE_SPEC_FROST:
  14451. + case MAGE_SPEC_ARCANE:
  14452. + case PRIEST_SPEC_DISCIPLINE:
  14453. + case PRIEST_SPEC_HOLY:
  14454. + case PRIEST_SPEC_SHADOW:
  14455. + case SHAMAN_SPEC_ELEMENTAL:
  14456. + case SHAMAN_SPEC_RESTORATION:
  14457. + case DRUID_SPEC_RESTORATION:
  14458. + case DRUID_SPEC_BALANCE:
  14459. + case WARLOCK_SPEC_DESTRUCTION:
  14460. + case WARLOCK_SPEC_AFFLICTION:
  14461. + case WARLOCK_SPEC_DEMONOLOGY:
  14462. + {
  14463. + ranged = true;
  14464. + byspell = true;
  14465. + break;
  14466. + }
  14467. + case PALADIN_SPEC_HOLY:
  14468. + //case PALADIN_SPEC_PROTECTION:
  14469. + {
  14470. + byspell = true;
  14471. + break;
  14472. + }
  14473. + case HUNTER_SPEC_BEASTMASTERY:
  14474. + case HUNTER_SPEC_SURVIVAL:
  14475. + case HUNTER_SPEC_MARKSMANSHIP:
  14476. + {
  14477. + ranged = true;
  14478. + break;
  14479. + }
  14480. + default:
  14481. + break;
  14482. + }
  14483. +
  14484. + //TODO: wand (casters) / melee (hunter)
  14485. + //switch (myclass)
  14486. + //{
  14487. + ////wand users
  14488. + //case CLASS_WARLOCK:
  14489. + //case CLASS_PRIEST:
  14490. + //case CLASS_MAGE:
  14491. + //{
  14492. + // if (GetManaPCT(me) < 5)
  14493. + // {
  14494. + // }
  14495. + //}
  14496. +
  14497. + //}
  14498. +
  14499. + _opponent = GetAttackTarget(byspell, ranged, reset);
  14500. +
  14501. + if (!_opponent)
  14502. + {
  14503. + me->AttackStop();
  14504. + ClearBotState(BOTSTATE_COMBAT);
  14505. + return false;
  14506. + }
  14507. +
  14508. + AddBotState(BOTSTATE_COMBAT);
  14509. + if (_opponent->GetTypeId() == TYPEID_PLAYER)
  14510. + AddCombatState(COMBAT_STATE_PVP);
  14511. + ClearBotState(BOTSTATE_FOLLOW);
  14512. + if (reset)
  14513. + AddCombatState(COMBAT_STATE_RESET);
  14514. +
  14515. + if (me->IsMounted())
  14516. + UpdateMountedState(0);//now
  14517. +
  14518. + if (!me->IsMounted() && _opponent != me->getVictim())
  14519. + me->Attack(_opponent, !ranged);
  14520. +
  14521. + return true;
  14522. +}
  14523. +
  14524. +Unit* PlayerbotAI::GetAttackTarget(bool /*ranged*/, bool byspell, bool &reset) const
  14525. +{
  14526. + //Check if need to assist master
  14527. + Unit* u = GetMaster()->getVictim();
  14528. + Unit* mytar = me->getVictim();
  14529. +
  14530. + //1) heck for common target
  14531. + if (u && u == mytar)
  14532. + {
  14533. + sLog->outError(LOG_FILTER_PLAYER, "PlayerbotAI::GetAttackTarget(): bot %s continues attack common target %s", me->GetName().c_str(), u->GetName().c_str());
  14534. + return u;
  14535. + }
  14536. +
  14537. + //TODO:
  14538. + ////Follow if...
  14539. + //uint8 followdist = master->GetBotFollowDist();
  14540. + //float foldist = _getAttackDistance(float(followdist));
  14541. + //if (!u && master->isAlive() && (me->GetDistance(master) > foldist || (mytar && master->GetDistance(mytar) > foldist && me->GetDistance(master) > foldist)))
  14542. + //{
  14543. + // //sLog->outError(LOG_FILTER_PLAYER, "bot %s cannot attack target %s, too far away", me->GetName().c_str(), mytar ? mytar->GetName().c_str() : "");
  14544. + // return NULL;
  14545. + //}
  14546. +
  14547. + //2) accuring master's target (new target, if have no target or attacking something else)
  14548. + if (u && (GetMaster()->isInCombat() || u->isInCombat()) && !IsUnitInDuel(u, GetMaster()) && !IsUnitInPlayersParty(u, GetMaster()))
  14549. + {
  14550. + sLog->outError(LOG_FILTER_PLAYER, "PlayerbotAI::GetAttackTarget(): bot %s starts attack master's target %s", me->GetName().c_str(), u->GetName().c_str());
  14551. + return u;
  14552. + }
  14553. +
  14554. + if (CanPlayerbotAttack(me, mytar, byspell)/* && !IsUnitInDuel(mytar, GetMaster())*/)
  14555. + {
  14556. + //if (me->GetDistance(mytar) > (ranged ? 20.f : 5.f) && m_botCommandState != COMMAND_STAY && m_botCommandState != COMMAND_FOLLOW)
  14557. + reset = (me->GetDistance(mytar) > GetMinAttackRange(mytar));
  14558. + sLog->outError(LOG_FILTER_PLAYER, "PlayerbotAI::GetAttackTarget(): bot %s continues attack its target %s (reset = %u)", me->GetName().c_str(), mytar->GetName().c_str(), uint8(reset));
  14559. + return mytar;
  14560. + }
  14561. +
  14562. + //if (followdist == 0 && master->isAlive())
  14563. + // return NULL; //do not bother
  14564. +
  14565. + //Check group
  14566. + //1) create playerlist
  14567. + std::set<Player*> playerSet;
  14568. + Group const* gr = GetMaster()->GetGroup();
  14569. + if (!gr)
  14570. + {
  14571. + playerSet.insert(GetMaster());
  14572. + for (PlayerBotMap::const_iterator itr = _mgr->GetPlayerBotsBegin(); itr != _mgr->GetPlayerBotsEnd(); ++itr)
  14573. + {
  14574. + Player* pl = itr->second;
  14575. + if (!pl || !pl->IsInWorld() || pl->IsBeingTeleported() || me->GetMap() != pl->FindMap() || !pl->InSamePhase(me))
  14576. + continue;
  14577. + playerSet.insert(itr->second);
  14578. + }
  14579. + }
  14580. + else
  14581. + {
  14582. + for (GroupReference const* ref = gr->GetFirstMember(); ref != NULL; ref = ref->next())
  14583. + {
  14584. + Player* pl = ref->getSource();
  14585. + if (!pl || !pl->IsInWorld() || pl->IsBeingTeleported() || me->GetMap() != pl->FindMap() || !pl->InSamePhase(me))
  14586. + continue;
  14587. + playerSet.insert(pl);
  14588. + for (PlayerBotMap::const_iterator itr = _mgr->GetPlayerBotsBegin(); itr != _mgr->GetPlayerBotsEnd(); ++itr)
  14589. + {
  14590. + pl = itr->second;
  14591. + if (!pl || !pl->IsInWorld() || pl->IsBeingTeleported() || me->GetMap() != pl->FindMap() || !pl->InSamePhase(me))
  14592. + continue;
  14593. + playerSet.insert(itr->second);
  14594. + }
  14595. + }
  14596. + }
  14597. + //2) cycle through to find proper target; check players and their npcbots (and their bot pets)
  14598. + for (std::set<Player*>::const_iterator itr = playerSet.begin(); itr != playerSet.end(); ++itr)
  14599. + {
  14600. + Player* pl = (*itr);
  14601. + u = pl->getVictim();
  14602. + if (u && pl != GetMaster() && CanPlayerbotAttack(me, u, byspell) && (pl->isInCombat() || u->isInCombat()))
  14603. + {
  14604. + sLog->outError(LOG_FILTER_PLAYER, "PlayerbotAI::GetAttackTarget(): bot %s hooked %s's victim %s", me->GetName().c_str(), pl->GetName().c_str(), u->GetName().c_str());
  14605. + return u;
  14606. + }
  14607. + if (!pl->HaveBot()) continue;
  14608. + for (uint8 i = 0; i != pl->GetMaxNpcBots(); ++i)
  14609. + {
  14610. + Creature* bot = pl->GetBotMap(i)->_Cre();
  14611. + if (!bot || !bot->IsInWorld() || me->GetMap() != bot->FindMap() || !bot->InSamePhase(me))
  14612. + continue;
  14613. + u = bot->getVictim();
  14614. + if (u && CanPlayerbotAttack(me, u, byspell) && (bot->isInCombat() || u->isInCombat()))
  14615. + {
  14616. + sLog->outError(LOG_FILTER_PLAYER, "PlayerbotAI::GetAttackTarget(): bot %s hooked %s's victim %s", me->GetName().c_str(), bot->GetName().c_str(), u->GetName().c_str());
  14617. + return u;
  14618. + }
  14619. + Creature* pet = bot->GetIAmABot() ? bot->GetBotsPet() : NULL;
  14620. + if (!pet || !pet->InSamePhase(me)) continue;
  14621. + u = pet->getVictim();
  14622. + if (u && CanPlayerbotAttack(me, u, byspell) && (pet->isInCombat() || u->isInCombat()))
  14623. + {
  14624. + sLog->outError(LOG_FILTER_PLAYER, "PlayerbotAI::GetAttackTarget(): bot %s hooked %s's victim %s", me->GetName().c_str(), pet->GetName().c_str(), u->GetName().c_str());
  14625. + return u;
  14626. + }
  14627. + }
  14628. + }
  14629. +
  14630. + //Check targets around
  14631. + Unit* t = NULL;
  14632. + float maxdist = sWorld->GetMaxVisibleDistanceOnContinents();
  14633. + //first cycle we search non-cced target, then, if not found, check all
  14634. + for (uint8 i = 0; i != 2; ++i)
  14635. + {
  14636. + if (!t)
  14637. + {
  14638. + bool attackCC = bool(i);
  14639. +
  14640. + CellCoord p(Trinity::ComputeCellCoord(me->GetPositionX(), me->GetPositionY()));
  14641. + Cell cell(p);
  14642. + cell.SetNoCreate();
  14643. +
  14644. + NearestHostileUnitCheck check(me, maxdist, byspell, NULL, attackCC);
  14645. + Trinity::UnitLastSearcher <NearestHostileUnitCheck> searcher(me, t, check);
  14646. + me->VisitNearbyWorldObject(maxdist, searcher);
  14647. +
  14648. + TypeContainerVisitor<Trinity::UnitLastSearcher <NearestHostileUnitCheck>, WorldTypeMapContainer > world_unit_searcher(searcher);
  14649. + TypeContainerVisitor<Trinity::UnitLastSearcher <NearestHostileUnitCheck>, GridTypeMapContainer > grid_unit_searcher(searcher);
  14650. + cell.Visit(p, world_unit_searcher, *me->GetMap(), *me, maxdist);
  14651. + cell.Visit(p, grid_unit_searcher, *me->GetMap(), *me, maxdist);
  14652. + }
  14653. + }
  14654. +
  14655. + //Duel check, should come after common target
  14656. + //here we don't check anything (yet)
  14657. + if (!t)
  14658. + {
  14659. + t = me->duel ? me->duel->opponent : NULL;
  14660. + }
  14661. +
  14662. + if (t)
  14663. + {
  14664. + if (_opponent && t != _opponent)
  14665. + {
  14666. + sLog->outError(LOG_FILTER_PLAYER, "PlayerbotAI::GetAttackTarget(): bot %s has Found a new target %s", me->GetName().c_str(), t->GetName().c_str());
  14667. + reset = true;
  14668. + }
  14669. + }
  14670. +
  14671. + return t;
  14672. +}
  14673. +
  14674. +void PlayerbotAI::GetInPosition(bool force, bool ranged, Unit* target, Position* /*pos*/)
  14675. +{
  14676. + if (me->HasUnitState(UNIT_STATE_CASTING) || CCed(me, true))
  14677. + return;
  14678. + if (!target)
  14679. + target = me->getVictim();
  14680. + if (!target)
  14681. + return;
  14682. + if (!target->isInCombat() && !force)
  14683. + return;
  14684. +
  14685. + me->GetMotionMaster()->MoveChase(target, ShouldRanged() ? GetMinAttackRange(target) : 0);
  14686. +
  14687. + //TODO: Position
  14688. + if (!me->HasUnitState(UNIT_STATE_MELEE_ATTACKING) || target != me->getVictim())
  14689. + me->Attack(target, !ranged);
  14690. +}
  14691. +
  14692. +void PlayerbotAI::SetMovement(WorldObject* followTarget)
  14693. +{
  14694. + if (!followTarget || !followTarget->IsInWorld() || me->GetMap() != followTarget->FindMap())
  14695. + return;
  14696. +
  14697. + //outrun case should be handled outside, with teleport
  14698. + if (me->GetDistance(followTarget) > sWorld->GetMaxVisibleDistanceOnContinents())
  14699. + return;
  14700. +
  14701. + if (me->HasUnitState(UNIT_STATE_CASTING) || CCed(me, true))
  14702. + return;
  14703. +
  14704. + //force selection
  14705. + //if (me->GetSelection() != followTarget->GetGUID())
  14706. + // me->SetSelection(followTarget->GetGUID());
  14707. +
  14708. + //can we actually move?
  14709. + bool canMove = me->isInCombat() ? !ShouldStay() : CanMove();
  14710. + if (!canMove)
  14711. + {
  14712. + if (!me->HasInArc(M_PI*0.8f, followTarget))
  14713. + {
  14714. + me->SetFacingToObject(followTarget);
  14715. + me->SendUpdateToPlayer(GetMaster());
  14716. + }
  14717. + return;
  14718. + }
  14719. +
  14720. + //pick proper follow mode
  14721. + bool chase = false;
  14722. + Unit* unit = followTarget->ToUnit();
  14723. + if (unit)
  14724. + chase = HasBotState(BOTSTATE_COMBAT);
  14725. + if (chase)
  14726. + {
  14727. + me->GetMotionMaster()->MoveChase(unit);
  14728. + }
  14729. + else
  14730. + {
  14731. + //TODO: angle
  14732. + if (unit)
  14733. + me->GetMotionMaster()->MoveFollow(unit, frand(_mgr->m_confFollowDistance[0], _mgr->m_confFollowDistance[1]), frand(0, 2*M_PI));
  14734. + else //corpse etc.
  14735. + {
  14736. + float x(0),y(0),z(0);
  14737. + followTarget->GetNearPoint(me, x, y, z, 0.f, frand(_mgr->m_confFollowDistance[0], _mgr->m_confFollowDistance[1]), frand(0, 2*M_PI));
  14738. + me->GetMotionMaster()->MovePoint(followTarget->GetMapId(), x, y, z);
  14739. + }
  14740. + }
  14741. +}
  14742. +
  14743. +uint32 PlayerbotAI::InitSpell(Player* caster, uint32 baseSpell)
  14744. +{
  14745. + if (baseSpell == 0 || !caster || !caster->HasSpell(baseSpell))
  14746. + return 0;
  14747. +
  14748. + uint32 next = 0;
  14749. + SpellChainNode const* Node = sSpellMgr->GetSpellChainNode(baseSpell);
  14750. + next = Node && Node->next ? Node->next->Id : 0;
  14751. +
  14752. + if (next == 0 || !caster->HasSpell(next)) //next spell in chain doesn't exist or unavailable
  14753. + return baseSpell;
  14754. +
  14755. + //SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(baseSpell);
  14756. + //sLog->outError(LOG_FILTER_PLAYER, "PlayerbotAI::InitSpell() proceed spell %u (%s): forwarding to %u (%s)", baseSpell, spellInfo->SpellName[0], next, sSpellMgr->GetSpellInfo(next)->SpellName[0]);
  14757. + return InitSpell(caster, next);
  14758. +}
  14759. +
  14760. +bool PlayerbotAI::CastSpell(Unit* victim, uint32 spellId) const
  14761. +{
  14762. + return CastSpell(victim, sSpellMgr->GetSpellInfo(spellId));
  14763. +}
  14764. +
  14765. +bool PlayerbotAI::CastSpell(Unit* victim, SpellInfo const* spellInfo) const
  14766. +{
  14767. + ASSERT(victim);
  14768. +
  14769. + if (!spellInfo)
  14770. + return false;
  14771. + if (!victim->IsInWorld() || me->GetPhaseMask() != victim->GetPhaseMask() || me->GetMap() != victim->FindMap())
  14772. + return false;
  14773. + //cooldown
  14774. + if (me->HasSpellCooldown(spellInfo->Id))
  14775. + return false;
  14776. + //GCD
  14777. + if (me->GetGlobalCooldownMgr().HasGlobalCooldown(spellInfo))
  14778. + return false;
  14779. + //power cost
  14780. + if (int32(me->GetPower(Powers(spellInfo->PowerType))) < spellInfo->CalcPowerCost(me, spellInfo->GetSchoolMask()))
  14781. + return false;
  14782. + //range
  14783. + bool friendly = me->IsFriendlyTo(victim);
  14784. + if (me->GetDistance(victim) > spellInfo->GetMaxRange(friendly, me))
  14785. + return false;
  14786. + //debug
  14787. + if (spellInfo->HasEffect(SPELL_EFFECT_OPEN_LOCK) ||
  14788. + spellInfo->HasEffect(SPELL_EFFECT_SKINNING) ||
  14789. + spellInfo->HasEffect(SPELL_EFFECT_ENCHANT_ITEM) ||
  14790. + spellInfo->HasEffect(SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY))
  14791. + {
  14792. + sLog->outError(LOG_FILTER_PLAYER, "PlayerbotAI: %s trying to cast wrong spell on unit! %s (%u) on %s", me->GetName().c_str(), spellInfo->SpellName[uint8(GetMaster()->GetSession()->GetSessionDbcLocale())], spellInfo->Id, victim->GetName().c_str());
  14793. + //return false;
  14794. + }
  14795. +
  14796. + sLog->outError(LOG_FILTER_PLAYER, "PlayerbotAI: %s casts: %s (%u) on %s", me->GetName().c_str(), spellInfo->SpellName[uint8(GetMaster()->GetSession()->GetSessionDbcLocale())], spellInfo->Id, victim->GetName().c_str());
  14797. +
  14798. + //everything checked, prepare character for cast
  14799. + //LoS - cheaty ;)
  14800. + if (friendly && me->IsWithinLOSInMap(victim))
  14801. + me->Relocate(victim);
  14802. + //face target - just in case
  14803. + if (victim != me/* && !me->HasInArc(M_PI * 0.25f, victim)*/)
  14804. + {
  14805. + me->SetFacingToObject(victim);
  14806. + me->SendUpdateToPlayer(GetMaster());
  14807. + }
  14808. +
  14809. + Spell* spell = new Spell(me, spellInfo, TRIGGERED_NONE);
  14810. +
  14811. + //stop moving
  14812. + if (spellInfo->CalcCastTime(me, spell) != 0)
  14813. + {
  14814. + me->StopMoving();
  14815. + //debug add root (removed in SPELL_GO)
  14816. + me->AddAura(SPELL_ROOT, me);
  14817. + }
  14818. +
  14819. + delete spell;
  14820. +
  14821. + me->CastSpell(victim, spellInfo);
  14822. + //if (spell->CheckCast(true) != SPELL_CAST_OK)
  14823. + //{
  14824. + // spell->finish(false);
  14825. + // delete spell;
  14826. + // return false;
  14827. + //}
  14828. + //SpellCastTargets targets;
  14829. + //targets.SetUnitTarget(victim);
  14830. + //spell->prepare(&targets);
  14831. +
  14832. + //check cast success
  14833. + if (GetCurrentSpell() == spell)
  14834. + {
  14835. + //after cast action setup
  14836. + //SHOULD BE MOVED TO CERTAIN AI
  14837. + //if (afterCast && _afterCast == NULL)
  14838. + //{
  14839. + // _afterCast = new AfterCast();
  14840. + // _afterCast->SetTarget(afterTarget);
  14841. + // _afterCast->SetAfterCastCommand(afterCast);
  14842. + //}
  14843. + return true;
  14844. + }
  14845. +
  14846. + return false;
  14847. +}
  14848. +
  14849. +bool PlayerbotAI::CastSpell(Item* item, uint32 spellId) const
  14850. +{
  14851. + return CastSpell(item, sSpellMgr->GetSpellInfo(spellId));
  14852. +}
  14853. +
  14854. +bool PlayerbotAI::CastSpell(Item* item, SpellInfo const* spellInfo) const
  14855. +{
  14856. + ASSERT(item);
  14857. +
  14858. + if (!spellInfo || me->HasSpellCooldown(spellInfo->Id) ||
  14859. + me->GetGlobalCooldownMgr().HasGlobalCooldown(spellInfo) ||
  14860. + int32(me->GetPower(Powers(spellInfo->PowerType))) < spellInfo->CalcPowerCost(me, spellInfo->GetSchoolMask()))
  14861. + return false;
  14862. + //debug
  14863. + if (!(spellInfo->HasEffect(SPELL_EFFECT_ENCHANT_ITEM) ||
  14864. + spellInfo->HasEffect(SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY) ||
  14865. + spellInfo->HasEffect(SPELL_EFFECT_DISENCHANT)))
  14866. + {
  14867. + sLog->outError(LOG_FILTER_PLAYER, "PlayerbotAI: %s trying to cast wrong spell on item! %s (%u) on %s", me->GetName().c_str(), spellInfo->SpellName[uint8(GetMaster()->GetSession()->GetSessionDbcLocale())], spellInfo->Id, item->GetTemplate()->Name1.c_str());
  14868. + return false;
  14869. + }
  14870. +
  14871. + sLog->outError(LOG_FILTER_PLAYER, "PlayerbotAI: %s casts: %s (%u) on %s", me->GetName().c_str(), spellInfo->SpellName[uint8(GetMaster()->GetSession()->GetSessionDbcLocale())], spellInfo->Id, item->GetTemplate()->Name1.c_str());
  14872. +
  14873. + Spell* spell = new Spell(me, spellInfo, TRIGGERED_NONE);
  14874. +
  14875. + if (spellInfo->CalcCastTime(me, spell) != 0)
  14876. + {
  14877. + me->StopMoving();
  14878. + me->AddAura(SPELL_ROOT, me);
  14879. + }
  14880. +
  14881. + delete spell;
  14882. +
  14883. + SpellCastTargets targets;
  14884. + targets.SetItemTarget(item);
  14885. + targets.SetUnitTarget(item->GetOwner());
  14886. + me->CastSpell(targets, spellInfo, NULL);
  14887. +
  14888. + return (GetCurrentSpell() == spell);
  14889. +}
  14890. +
  14891. +void PlayerbotAI::CureGroup(Player* player, uint32 cureSpell, const uint32 diff)
  14892. +{
  14893. + if (!cureSpell || !player)
  14894. + return;
  14895. +
  14896. + if (_cureTimer > diff)
  14897. + return;
  14898. +
  14899. + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(cureSpell);
  14900. + if (!spellInfo)
  14901. + return;
  14902. + if (me->HasSpellCooldown(spellInfo->Id))
  14903. + return;
  14904. + if (me->GetGlobalCooldownMgr().HasGlobalCooldown(spellInfo))
  14905. + return;
  14906. + if (int32(me->GetPower(Powers(spellInfo->PowerType))) < spellInfo->CalcPowerCost(me, spellInfo->GetSchoolMask()))
  14907. + return;
  14908. + if (!IsUnitInPlayersParty(player, GetMaster()))
  14909. + return;
  14910. +
  14911. + Group const* gr = player->GetGroup();
  14912. + if (!gr)
  14913. + {
  14914. + if (_CureTarget(player, spellInfo))
  14915. + return;
  14916. + if (Pet* pet = player->GetPet())
  14917. + if (_CureTarget(pet, spellInfo))
  14918. + return;
  14919. + if (!player->HaveBot())
  14920. + return;
  14921. +
  14922. + for (uint8 i = 0; i != player->GetMaxNpcBots(); ++i)
  14923. + if (_CureTarget(player->GetBotMap(i)->_Cre(), spellInfo))
  14924. + return;
  14925. + }
  14926. + else
  14927. + {
  14928. + bool bots = false;
  14929. + Player* tPlayer = NULL;
  14930. + for (GroupReference const* itr = gr->GetFirstMember(); itr != NULL; itr = itr->next())
  14931. + {
  14932. + tPlayer = itr->getSource();
  14933. + if (!tPlayer || (!tPlayer->isAlive() && !tPlayer->HaveBot()))
  14934. + continue;
  14935. + if (!bots && tPlayer->HaveBot())
  14936. + bots = true;
  14937. + if (_CureTarget(tPlayer, spellInfo))
  14938. + return;
  14939. + if (Pet* pet = tPlayer->GetPet())
  14940. + if (_CureTarget(pet, spellInfo))
  14941. + return;
  14942. + }
  14943. + if (bots)
  14944. + {
  14945. + for (GroupReference const* itr = gr->GetFirstMember(); itr != NULL; itr = itr->next())
  14946. + {
  14947. + tPlayer = itr->getSource();
  14948. + if (!tPlayer || !tPlayer->HaveBot())
  14949. + continue;
  14950. + for (uint8 i = 0; i != tPlayer->GetMaxNpcBots(); ++i)
  14951. + if (_CureTarget(tPlayer->GetBotMap(i)->_Cre(), spellInfo))
  14952. + return;
  14953. + }
  14954. + }
  14955. + }
  14956. +
  14957. + //if no target found (basically) set check delay to prevent useless cycle
  14958. + _cureTimer = 2000;
  14959. +}
  14960. +
  14961. +inline bool PlayerbotAI::_CureTarget(Unit* target, SpellInfo const* spellInfo) const
  14962. +{
  14963. + return _CanCureTarget(target, spellInfo) ? CastSpell(target, spellInfo) : false;
  14964. +}
  14965. +
  14966. +bool PlayerbotAI::_CanCureTarget(Unit* target, SpellInfo const* spellInfo) const
  14967. +{
  14968. + if (!target || !spellInfo)
  14969. + return false;
  14970. +
  14971. + if (!target->isAlive())
  14972. + return false;
  14973. +
  14974. + if (!target->IsInWorld() ||
  14975. + me->GetMap() != target->FindMap() ||
  14976. + me->GetPhaseMask() != target->GetPhaseMask())
  14977. + return false;
  14978. +
  14979. + if (Player* player = target->ToPlayer())
  14980. + if (player->IsBeingTeleported())
  14981. + return false;
  14982. +
  14983. + if (me->GetDistance(target) > spellInfo->RangeEntry->maxRangeFriend)
  14984. + return false;
  14985. +
  14986. + if (!IsUnitInPlayersParty(target, GetMaster()))
  14987. + return false;
  14988. +
  14989. + uint32 dispelMask = 0;
  14990. + for (uint8 i = 0; i != MAX_SPELL_EFFECTS; ++i)
  14991. + if (spellInfo->Effects[i].Effect == SPELL_EFFECT_DISPEL)
  14992. + dispelMask |= SpellInfo::GetDispelMask(DispelType(spellInfo->Effects[i].MiscValue));
  14993. +
  14994. + if (dispelMask == 0)
  14995. + return false;
  14996. +
  14997. + DispelChargesList dispel_list;
  14998. + target->GetDispellableAuraList(me, dispelMask, dispel_list);
  14999. +
  15000. + return (!dispel_list.empty());
  15001. +}
  15002. +
  15003. +void PlayerbotAI::OnBotOutgoingPacket(WorldPacket const& packet)
  15004. +{
  15005. + switch (packet.GetOpcode())
  15006. + {
  15007. + case SMSG_GROUP_INVITE:
  15008. + {
  15009. + if (me->GetGroup())
  15010. + return;
  15011. +
  15012. + if (Group* grp = me->GetGroupInvite())
  15013. + {
  15014. + //Player const* inviter = sObjectAccessor->FindPlayer(grp->GetLeaderGUID());
  15015. + //if (!inviter)
  15016. + // return;
  15017. +
  15018. + WorldPacket p;
  15019. + if (grp->GetLeaderGUID() != GetMaster()->GetGUID())//(!canObeyCommandFrom(*inviter))
  15020. + {
  15021. + std::string buf = "I can't accept your invite unless you first invite my master ";
  15022. + buf += GetMaster()->GetName();
  15023. + buf += ".";
  15024. + me->Whisper(buf, LANG_UNIVERSAL, grp->GetLeaderGUID());//SendWhisper(buf, *inviter);
  15025. + me->GetSession()->HandleGroupDeclineOpcode(p); // packet not used
  15026. + }
  15027. + else
  15028. + {
  15029. + //if (m_bot->IsFriendlyTo(inviter))
  15030. + {
  15031. + //DO NOT UNCOMMENT
  15032. + //p.read_skip<uint32>();
  15033. +
  15034. + grp->RemoveInvite(me);
  15035. +
  15036. + if (grp->IsFull())
  15037. + {
  15038. + me->GetSession()->SendPartyResult(PARTY_OP_INVITE, "", ERR_GROUP_FULL);
  15039. + return;
  15040. + }
  15041. +
  15042. + Player* leader = sObjectAccessor->FindPlayer(grp->GetLeaderGUID());
  15043. +
  15044. + if (!grp->IsCreated())
  15045. + {
  15046. + if (!leader)
  15047. + {
  15048. + grp->RemoveAllInvites();
  15049. + return;
  15050. + }
  15051. +
  15052. + grp->RemoveInvite(leader);
  15053. + grp->Create(leader);
  15054. + sGroupMgr->AddGroup(grp);
  15055. + }
  15056. +
  15057. + if (!grp->AddMember(me))
  15058. + return;
  15059. +
  15060. + grp->BroadcastGroupUpdate();
  15061. + }
  15062. + //else
  15063. + //{
  15064. + // //TODO: custom group section
  15065. + //}
  15066. + }
  15067. + }
  15068. + return;
  15069. + }
  15070. + }
  15071. +}
  15072. +
  15073. +void PlayerbotAI::HandleTeleportAck()
  15074. +{
  15075. + me->GetMotionMaster()->Clear(true);
  15076. + if (me->IsBeingTeleportedNear())
  15077. + {
  15078. + WorldPacket p = WorldPacket(MSG_MOVE_TELEPORT_ACK, 8 + 4 + 4);
  15079. + p.appendPackGUID(me->GetGUID());
  15080. + p << uint32(0); // cast flags
  15081. + p << uint32(time(NULL)); // time - not used
  15082. + me->GetSession()->HandleMoveTeleportAck(p);
  15083. + }
  15084. + else if (me->IsBeingTeleportedFar())
  15085. + me->GetSession()->HandleMoveWorldportAckOpcode();
  15086. +}
  15087. +
  15088. +bool PlayerbotAI::CanPlayerbotAttack(Player* bot, Unit* target, int8 byspell)
  15089. +{
  15090. + if (!target || !bot || !bot->IsPlayerBot()) return false;
  15091. + Player* master = bot->GetPlayerbotAI()->GetMaster();
  15092. + if (!master)
  15093. + return false;
  15094. + return
  15095. + (target->isAlive() && target->IsVisible() && target->isTargetableForAttack() && !IsUnitInPlayersParty(target, master) &&
  15096. + (target->IsHostileTo(master) || target->IsHostileTo(bot) ||
  15097. + (target->GetReactionTo(master) < REP_FRIENDLY && master->getVictim() == target && (master->isInCombat() || target->isInCombat()))) && //master has pointed this target
  15098. + //target->IsWithinLOSInMap(me) &&
  15099. + (byspell < 0 || !target->IsImmunedToDamage(byspell ? SPELL_SCHOOL_MASK_MAGIC : SPELL_SCHOOL_MASK_NORMAL)));
  15100. +}
  15101. +
  15102. +bool PlayerbotAI::IsUnitInDuel(Unit* target, Player* master)
  15103. +{
  15104. + if (!target) return false;
  15105. + bool isbot = target->GetTypeId() == TYPEID_UNIT && (target->ToCreature()->GetIAmABot() || target->ToCreature()->GetIAmABotsPet());
  15106. + Player* player = target->GetTypeId() == TYPEID_PLAYER ? target->ToPlayer() : isbot ? target->ToCreature()->GetBotOwner() : NULL;
  15107. + if (!player)
  15108. + {
  15109. + if (!target->IsControlledByPlayer())
  15110. + return false;
  15111. + player = target->GetCharmerOrOwnerPlayerOrPlayerItself();
  15112. + }
  15113. +
  15114. + return (player && player->duel && (IsUnitInPlayersParty(player, master) || IsUnitInPlayersParty(player->duel->opponent, master)));
  15115. +}
  15116. +
  15117. +bool PlayerbotAI::IsUnitInPlayersParty(Unit* unit, Player* master)
  15118. +{
  15119. + if (!unit || !master) return false;
  15120. + if (unit == master) return true;
  15121. +
  15122. + //group member case (any player / any npcbot)
  15123. + if (Group* gr = master->GetGroup())
  15124. + if (gr->IsMember(unit->GetGUID()))
  15125. + return true;
  15126. +
  15127. + //Player-controlled creature case
  15128. + if (Creature* cre = unit->ToCreature())
  15129. + {
  15130. + //npcbot/npcbot's pet case
  15131. + if (Player* owner = cre->GetBotOwner())
  15132. + {
  15133. + //master's npcbot's pet
  15134. + if (owner == master)
  15135. + return true;
  15136. + else
  15137. + {
  15138. + //playerbot's npcbot's pet
  15139. + if (owner->IsPlayerBot())
  15140. + {
  15141. + //master's playerbot's npcbot's pet
  15142. + if (PlayerbotMgr* mgr = master->GetPlayerbotMgr())
  15143. + if (mgr->GetPlayerBot(unit->GetGUID()))
  15144. + return true;
  15145. + //other player's playerbot's npcbot's pet
  15146. + if (master->GetGroup() && master->GetGroup()->IsMember(owner->GetPlayerbotAI()->GetMaster()->GetGUID()))
  15147. + return true;
  15148. + }
  15149. + }
  15150. + }
  15151. + //pets, minions, guardians etc.
  15152. + else
  15153. + {
  15154. + uint64 ownerGuid = unit->GetOwnerGUID();
  15155. + //controlled by group member
  15156. + if (Group* gr = master->GetGroup())
  15157. + if (gr->IsMember(ownerGuid))
  15158. + return true;
  15159. + //controlled by playerbot
  15160. + owner = sObjectAccessor->FindPlayer(ownerGuid);
  15161. + if (owner && owner->IsPlayerBot())
  15162. + {
  15163. + //master's playerbot
  15164. + if (PlayerbotMgr* mgr = master->GetPlayerbotMgr())
  15165. + if (mgr->GetPlayerBot(unit->GetGUID()))
  15166. + return true;
  15167. + //other player's playerbot
  15168. + if (master->GetGroup() && master->GetGroup()->IsMember(owner->GetPlayerbotAI()->GetMaster()->GetGUID()))
  15169. + return true;
  15170. + }
  15171. + }
  15172. + }
  15173. + //Player-controlled player case
  15174. + else if (Player* bot = unit->ToPlayer())
  15175. + {
  15176. + //Playerbot case
  15177. + if (bot->IsPlayerBot())
  15178. + {
  15179. + //master's playerbot
  15180. + if (PlayerbotMgr* mgr = master->GetPlayerbotMgr())
  15181. + if (mgr->GetPlayerBot(unit->GetGUID()))
  15182. + return true;
  15183. + //other player's playerbot
  15184. + if (master->GetGroup() && master->GetGroup()->IsMember(bot->GetPlayerbotAI()->GetMaster()->GetGUID()))
  15185. + return true;
  15186. + }
  15187. + }
  15188. +
  15189. + return false;
  15190. +}
  15191. +
  15192. +inline void PlayerbotAI::Tell(std::string const& text, uint64 targetGUID)
  15193. +{
  15194. + if (!targetGUID)
  15195. + targetGUID = GetMaster()->GetGUID();
  15196. + if (text.empty() || !IS_PLAYER_GUID(targetGUID))
  15197. + return;
  15198. +
  15199. + me->Whisper(text, LANG_UNIVERSAL, targetGUID);
  15200. +}
  15201. +
  15202. +Player* PlayerbotAI::GetMaster() const
  15203. +{
  15204. + return _mgr->GetMaster();
  15205. +}
  15206. +
  15207. +inline bool PlayerbotAI::IsMoving() const
  15208. +{
  15209. + return (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == IDLE_MOTION_TYPE ? false : true);
  15210. +}
  15211. +
  15212. +inline Spell* PlayerbotAI::GetCurrentSpell() const
  15213. +{
  15214. + for (uint8 i = CURRENT_FIRST_NON_MELEE_SPELL; i != CURRENT_AUTOREPEAT_SPELL; ++i)
  15215. + if (Spell* spell = me->GetCurrentSpell(CurrentSpellTypes(i)))
  15216. + return spell;
  15217. +
  15218. + return NULL;
  15219. +}
  15220. +
  15221. +inline uint32 PlayerbotAI::GetCurrentSpellId() const
  15222. +{
  15223. + Spell* spell = GetCurrentSpell();
  15224. + return spell ? spell->GetSpellInfo()->Id : 0;
  15225. +}
  15226. +
  15227. +inline bool PlayerbotAI::CCed(Unit* unit, bool root)
  15228. +{
  15229. + return unit ? unit->HasUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_STUNNED | UNIT_STATE_FLEEING | UNIT_STATE_DISTRACTED | UNIT_STATE_CONFUSED_MOVE | UNIT_STATE_FLEEING_MOVE) || (root && unit->HasUnitState(UNIT_STATE_ROOT)) : true;
  15230. +}
  15231. +uint32 PlayerbotAI::GetLostHP(Unit* unit)
  15232. +{
  15233. + return unit->GetMaxHealth() - unit->GetHealth();
  15234. +}
  15235. +
  15236. +uint8 PlayerbotAI::GetHealthPCT(Unit* unit)
  15237. +{
  15238. + return (!unit || unit->isDead()) ? 100 : (unit->GetHealth()*100/unit->GetMaxHealth());
  15239. +}
  15240. +
  15241. +uint8 PlayerbotAI::GetManaPCT(Unit* unit)
  15242. +{
  15243. + return (!unit || unit->isDead() || unit->getPowerType() != POWER_MANA) ? 100 : (unit->GetPower(POWER_MANA)*100/unit->GetMaxPower(POWER_MANA));
  15244. +}
  15245. +
  15246. +inline float PlayerbotAI::GetMinAttackRange(Unit* target) const
  15247. +{
  15248. + //TODO: range for ranged classes
  15249. + return ShouldRanged() ? 19.f : target->GetMeleeReach() * 0.8f;
  15250. +}
  15251. +
  15252. +void PlayerbotAI::_UnbindInstance(uint32 mapId, Difficulty diff)
  15253. +{
  15254. + for (uint8 i = 0; i < MAX_DIFFICULTY; ++i)
  15255. + {
  15256. + Player::BoundInstancesMap& binds = me->GetBoundInstances(Difficulty(i));
  15257. + for (Player::BoundInstancesMap::iterator itr = binds.begin(); itr != binds.end();)
  15258. + {
  15259. + InstanceSave* save = itr->second.save;
  15260. + if (itr->first != me->GetMapId() && mapId == itr->first && diff == save->GetDifficulty())
  15261. + {
  15262. + ChatHandler(GetMaster()->GetSession()).PSendSysMessage("PlayerbotAI: unbinding %s's map: %d inst: %d perm: %s diff: %d canReset: %s", me->GetName().c_str(), itr->first, save->GetInstanceId(), itr->second.perm ? "yes" : "no", save->GetDifficulty(), save->CanReset() ? "yes" : "no");
  15263. + me->UnbindInstance(itr, Difficulty(i));
  15264. + break;
  15265. + }
  15266. + else
  15267. + ++itr;
  15268. + }
  15269. + }
  15270. +}
  15271. +
  15272. +void PlayerbotAI::_InviteToMastersGroup()
  15273. +{
  15274. + if (Group* gr = GetMaster()->GetGroup())
  15275. + {
  15276. + if (!gr->IsFull())
  15277. + {
  15278. + if (!gr->AddMember(me))
  15279. + return;
  15280. + }
  15281. + else if (!gr->isRaidGroup()) //non-raid group is full
  15282. + {
  15283. + gr->ConvertToRaid();
  15284. + if (!gr->AddMember(me))
  15285. + return;
  15286. + }
  15287. + else //raid group is full
  15288. + return;
  15289. + }
  15290. + else
  15291. + {
  15292. + gr = new Group;
  15293. + if (!gr->Create(GetMaster()))
  15294. + {
  15295. + delete gr;
  15296. + return;
  15297. + }
  15298. + sGroupMgr->AddGroup(gr);
  15299. + if (!gr->AddMember(me))
  15300. + return;
  15301. + }
  15302. +}
  15303. +
  15304. +void PlayerbotAI::_SendDebugInfo()
  15305. +{
  15306. + uint8 loc = uint8(me->GetSession()->GetSessionDbcLocale());
  15307. + ChatHandler ch(GetMaster()->GetSession());
  15308. +
  15309. + ch.PSendSysMessage("Listing stats for %s:", me->GetName().c_str());
  15310. + std::ostringstream str1;
  15311. + str1 << "1) Botstates: (";
  15312. + if (HasBotState(BOTSTATE_FOLLOW))
  15313. + str1 << "BOTSTATE_FOLLOW";
  15314. + if (HasBotState(BOTSTATE_STAY))
  15315. + str1 << " | BOTSTATE_STAY";
  15316. + if (HasBotState(BOTSTATE_COMBAT))
  15317. + str1 << " | BOTSTATE_COMBAT";
  15318. + if (HasBotState(BOTSTATE_LOOTING))
  15319. + str1 << " | BOTSTATE_LOOTING";
  15320. + if (HasBotState(BOTSTATE_TAME))
  15321. + str1 << " | BOTSTATE_TAME";
  15322. + if (HasBotState(BOTSTATE_DELAYED))
  15323. + str1 << " | BOTSTATE_DELAYED";
  15324. + if (HasBotState(BOTSTATE_RESET))
  15325. + str1 << " | BOTSTATE_RESET";
  15326. + str1 << ')';
  15327. + ch.PSendSysMessage(str1.str().c_str());
  15328. +
  15329. + std::ostringstream str2;
  15330. + str2 << "2) Combatstates: (";
  15331. + if (HasCombatState(COMBAT_STATE_RANGED))
  15332. + str2 << " | COMBAT_STATE_RANGED";
  15333. + if (HasCombatState(COMBAT_STATE_STAY))
  15334. + str2 << " | COMBAT_STATE_STAY";
  15335. + if (HasCombatState(COMBAT_STATE_PVP))
  15336. + str2 << " | COMBAT_STATE_PVP";
  15337. + if (HasCombatState(COMBAT_STATE_RESET))
  15338. + str2 << " | COMBAT_STATE_RESET";
  15339. + str2 << ')';
  15340. + ch.PSendSysMessage(str2.str().c_str());
  15341. +
  15342. + std::ostringstream str3;
  15343. + str3 << "3) MovementFlags: (";
  15344. + if (HasMovementFlag(MOVEMENT_FLAG_TARGET_CHANGED))
  15345. + str3 << " | MOVEMENT_FLAG_TARGET_CHANGED";
  15346. + if (HasMovementFlag(MOVEMENT_FLAG_HOLD_GROUND))
  15347. + str3 << " | MOVEMENT_FLAG_HOLD_GROUND";
  15348. + if (HasMovementFlag(MOVEMENT_FLAG_RANDOM_MOVEMENT))
  15349. + str3 << " | MOVEMENT_FLAG_RANDOM_MOVEMENT";
  15350. + str3 << ')';
  15351. + ch.PSendSysMessage(str3.str().c_str());
  15352. +
  15353. + std::ostringstream str4;
  15354. + str4 << "My spec: " << _mySpec;
  15355. + ch.PSendSysMessage(str4.str().c_str());
  15356. +
  15357. + ch.PSendSysMessage("Listing real spells:");
  15358. + PlayerSpellMap const& spellMap = me->GetSpellMap();
  15359. + for (PlayerSpellMap::const_iterator itr = spellMap.begin(); itr != spellMap.end(); ++itr)
  15360. + {
  15361. + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(itr->first);
  15362. + if (!spellInfo)
  15363. + continue;
  15364. + PlayerSpell* spell = itr->second;
  15365. + if (!spell)
  15366. + continue;
  15367. + if (spell->disabled || spell->state == PLAYERSPELL_REMOVED || spell->state == PLAYERSPELL_TEMPORARY)
  15368. + continue;
  15369. + //triggered or server-side spells
  15370. + if (spellInfo->SpellLevel == 0)
  15371. + continue;
  15372. + //passive spell
  15373. + if (spellInfo->IsPassive())
  15374. + continue;
  15375. + //spell is learned as other class spell
  15376. + if (!me->IsSpellFitByClassAndRace(spellInfo->Id))
  15377. + continue;
  15378. + //is talent
  15379. + if (GetTalentSpellPos(spellInfo->Id))
  15380. + continue;
  15381. + //not hightest rank known
  15382. + if (spellInfo->GetNextRankSpell() && me->HasSpell(spellInfo->GetNextRankSpell()->Id))
  15383. + continue;
  15384. + ////check class family for spells
  15385. + //ChrClassesEntry const* classEntry = sChrClassesStore.LookupEntry(me->getClass());
  15386. + //if (spellInfo->SpellFamilyName != classEntry->spellfamily)
  15387. + // continue;
  15388. +
  15389. + std::ostringstream str5;
  15390. + str5 << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  15391. + str5 << ' ' << localeNames[loc] << "]|h|r";
  15392. + uint32 rank = !spellInfo->ChainEntry ? 0 : spellInfo->GetRank();
  15393. + if (rank > 0)
  15394. + str5 << " Rank " << rank;
  15395. + ch.PSendSysMessage(str5.str().c_str());
  15396. + }
  15397. +
  15398. + ch.PSendSysMessage("Listing classAI spells:");
  15399. + _classAI->SendClassDebugInfo();
  15400. +
  15401. + ch.PSendSysMessage("Listing bot instance binds:");
  15402. +
  15403. + for (uint8 i = 0; i != MAX_DIFFICULTY; ++i)
  15404. + {
  15405. + Player::BoundInstancesMap &binds = me->GetBoundInstances(Difficulty(i));
  15406. + for (Player::BoundInstancesMap::const_iterator itr = binds.begin(); itr != binds.end(); ++itr)
  15407. + if (InstanceSave* save = itr->second.save)
  15408. + ch.PSendSysMessage("map: %d inst: %d perm: %s diff: %d canReset: %s", itr->first, save->GetInstanceId(), itr->second.perm ? "yes" : "no", save->GetDifficulty(), save->CanReset() ? "yes" : "no");
  15409. + }
  15410. +
  15411. + ch.PSendSysMessage("Listing player instance binds:");
  15412. +
  15413. + for (uint8 i = 0; i != MAX_DIFFICULTY; ++i)
  15414. + {
  15415. + Player::BoundInstancesMap &binds = _botBoundInstances[Difficulty(i)];
  15416. + for (Player::BoundInstancesMap::const_iterator itr = binds.begin(); itr != binds.end(); ++itr)
  15417. + if (InstanceSave* save = itr->second.save)
  15418. + ch.PSendSysMessage("map: %d inst: %d perm: %s diff: %d canReset: %s", itr->first, save->GetInstanceId(), itr->second.perm ? "yes" : "no", save->GetDifficulty(), save->CanReset() ? "yes" : "no");
  15419. + }
  15420. +}
  15421. +
  15422. +inline void PlayerbotAI::_doTimers(uint32 diff)
  15423. +{
  15424. + if (_waitTimer > diff) _waitTimer -= diff;
  15425. + if (_followTimer > diff) _followTimer -= diff;
  15426. + if (_mountTimer > diff) _mountTimer -= diff;
  15427. + if (_cureTimer > diff) _cureTimer -= diff;
  15428. + if (_selfResTimer > diff) _selfResTimer -= diff;
  15429. +}
  15430. diff --git a/src/server/game/AI/PlayerBots/bp_ai.h b/src/server/game/AI/PlayerBots/bp_ai.h
  15431. new file mode 100644
  15432. index 0000000..b960918
  15433. --- /dev/null
  15434. +++ b/src/server/game/AI/PlayerBots/bp_ai.h
  15435. @@ -0,0 +1,611 @@
  15436. +#ifndef _PLAYERBOTAI_H
  15437. +#define _PLAYERBOTAI_H
  15438. +
  15439. +#include "Common.h"
  15440. +#include "DBCEnums.h"
  15441. +
  15442. +//class Group;
  15443. +class Item;
  15444. +class Player;
  15445. +class PlayerbotClassAI;
  15446. +class PlayerbotMgr;
  15447. +class Quest;
  15448. +class Spell;
  15449. +class SpellInfo;
  15450. +class Unit;
  15451. +class WorldObject;
  15452. +class WorldPacket;
  15453. +
  15454. +struct Position;
  15455. +
  15456. +enum BotSpecial
  15457. +{
  15458. + SPELL_ROOT = 42716 // Netherspite SPELL_BANISH_ROOT "Self Root Forever (No Visual)"
  15459. +};
  15460. +
  15461. +enum BotStates
  15462. +{
  15463. + //BOTSTATE_NORMAL = 0x000, //nothing
  15464. + BOTSTATE_FOLLOW = 0x001, //following (deprecated?)
  15465. + BOTSTATE_STAY = 0x002, //stay (not same as MOVEMENT_FLAG_HOLD_GROUND!)
  15466. + BOTSTATE_COMBAT = 0x004, //do combat moves
  15467. + BOTSTATE_LOOTING = 0x008, //need to perform loot sequence
  15468. + BOTSTATE_TAME = 0x010, //has target to tame (deprecated?)
  15469. + BOTSTATE_DELAYED = 0x020, //delayed operations queued (wtf?)
  15470. + BOTSTATE_RESET = 0x040 //need to reset these flags
  15471. +};
  15472. +
  15473. +enum BotCombatStates
  15474. +{
  15475. + COMBAT_STATE_RANGED = 0x001, //do ranged combat
  15476. + COMBAT_STATE_STAY = 0x002, //stay in place
  15477. + COMBAT_STATE_PVP = 0x004, //for PvP behaviour
  15478. + COMBAT_STATE_RESET = 0x008 //free to change movement (used to chase current target)
  15479. +};
  15480. +
  15481. +enum BotMovementFlags
  15482. +{
  15483. + MOVEMENT_FLAG_NONE = 0x000, //following normally
  15484. + MOVEMENT_FLAG_TARGET_CHANGED = 0x001, //should be present to follow others
  15485. + MOVEMENT_FLAG_HOLD_GROUND = 0x002, //rotate only
  15486. + MOVEMENT_FLAG_RANDOM_MOVEMENT = 0x004 //move randomly while doing nothing
  15487. +};
  15488. +
  15489. +enum RacialTraits
  15490. +{
  15491. + ARCANE_TORRENT_MANA_CLASSES = 28730,
  15492. + ARCANE_TORRENT_DEATH_KNIGHT = 50613,
  15493. + ARCANE_TORRENT_ROGUE = 25046,
  15494. + BERSERKING_ALL = 26297,
  15495. + BLOOD_FURY_MELEE_CLASSES = 20572,
  15496. + BLOOD_FURY_WARLOCK = 33702,
  15497. + BLOOD_FURY_SHAMAN = 33697,
  15498. + ESCAPE_ARTIST_ALL = 20589,
  15499. + EVERY_MAN_FOR_HIMSELF_ALL = 59752,
  15500. + GIFT_OF_THE_NAARU_DEATH_KNIGHT = 59545,
  15501. + GIFT_OF_THE_NAARU_HUNTER = 59543,
  15502. + GIFT_OF_THE_NAARU_MAGE = 59548,
  15503. + GIFT_OF_THE_NAARU_PALADIN = 59542,
  15504. + GIFT_OF_THE_NAARU_PRIEST = 59544,
  15505. + GIFT_OF_THE_NAARU_SHAMAN = 59547,
  15506. + GIFT_OF_THE_NAARU_WARRIOR = 28880,
  15507. + SHADOWMELD_ALL = 58984,
  15508. + STONEFORM_ALL = 20594,
  15509. + WAR_STOMP_ALL = 20549,
  15510. + WILL_OF_THE_FORSAKEN_ALL = 7744
  15511. +};
  15512. +
  15513. +struct AfterCast
  15514. +{
  15515. + AfterCast();
  15516. +
  15517. + //WorldLocation* GetTravelLocation() const { return _travelDest; }
  15518. + //void SetTravelLocation(WorldLocation* loc) { _travelDest = loc; }
  15519. + inline Unit* GetTarget() const;
  15520. + inline void SetTarget(Unit* target);
  15521. + inline void SetAfterCastCommand(void (*newAfterCast)(Unit*));
  15522. + inline void LaunchAfterCastCommand();
  15523. +
  15524. + private:
  15525. + //WorldLocation* _travelDest;
  15526. + Unit* _afterCastTarget;
  15527. + void (*afterCast)(Unit*);
  15528. +};
  15529. +
  15530. +class PlayerbotAI
  15531. +{
  15532. + public:
  15533. + PlayerbotAI(PlayerbotMgr* const mgr, Player* const bot);
  15534. + virtual ~PlayerbotAI();
  15535. +
  15536. + Player* GetMaster() const;
  15537. + //inline BotState GetBotState() const { return m_botState; }
  15538. + //inline void SetBotState(BotState state) { m_botState = state; }
  15539. +
  15540. + //on world update tick
  15541. + void UpdateAI(uint32 diff);
  15542. + void ReloadClassAI();
  15543. +
  15544. + //actions
  15545. + void UpdateDeadActions(uint32 diff);
  15546. + void UpdateIncombatActions();
  15547. + void UpdateFollowActions(uint32 diff);
  15548. +
  15549. + //states
  15550. + void UpdateMountedState(uint32 diff);
  15551. + void UpdateStandState();
  15552. +
  15553. + inline uint8 GetBotState() const { return _botStates; }
  15554. + inline bool HasBotState(BotStates state) const { return (_botStates & state); }
  15555. + inline void AddBotState(BotStates state) { if (!(_botStates & state)) _botStates |= state; }
  15556. + inline void ClearBotState(BotStates state) { if (_botStates & state) _botStates &= ~state; }
  15557. +
  15558. + //casts
  15559. + // units
  15560. + bool CastSpell(Unit* victim, uint32 spellId) const;
  15561. + bool CastSpell(Unit* victim, SpellInfo const* spellInfo) const;
  15562. + // items
  15563. + bool CastSpell(Item* item, uint32 spellId) const;
  15564. + bool CastSpell(Item* item, SpellInfo const* spellInfo) const;
  15565. + // cures
  15566. + void CureGroup(Player* player, uint32 cureSpell, const uint32 diff);
  15567. + private:
  15568. + inline bool _CureTarget(Unit* target, SpellInfo const* spellInfo) const;
  15569. + bool _CanCureTarget(Unit* target, SpellInfo const* spellInfo) const;
  15570. + public:
  15571. +
  15572. + //spells
  15573. + static uint32 InitSpell(Player* caster, uint32 baseSpell);
  15574. +
  15575. + inline bool IsMoving() const;
  15576. +
  15577. + //currspell
  15578. + inline Spell* GetCurrentSpell() const;
  15579. + inline uint32 GetCurrentSpellId() const;
  15580. +
  15581. + //movement
  15582. + void SetMovement(WorldObject* followTarget);
  15583. + inline uint8 GetMovementFlags() const { return _movementFlags; }
  15584. + inline bool HasMovementFlag(BotMovementFlags flag) const { return (_movementFlags & flag); }
  15585. + inline void AddMovementFlag(BotMovementFlags flag) { if (!(_movementFlags & flag)) _movementFlags |= flag; }
  15586. + inline void ClearMovementFlag(BotMovementFlags flag) { if (_movementFlags & flag) _movementFlags &= ~flag; }
  15587. + inline void RemoveMovementFlag(BotMovementFlags flag) { ClearMovementFlag(flag); }
  15588. + inline bool CanMove() const { return (!(_movementFlags & MOVEMENT_FLAG_HOLD_GROUND)); }
  15589. + inline bool CanFollowOthers() const { return (_movementFlags & MOVEMENT_FLAG_TARGET_CHANGED); }
  15590. +
  15591. + //combat
  15592. + inline uint8 GetCombatStates() const { return _combatStates; }
  15593. + inline bool HasCombatState(BotCombatStates state) const { return (_combatStates & state); }
  15594. + inline void AddCombatState(BotCombatStates state) { if (!(_combatStates & state)) _combatStates |= state; }
  15595. + inline void ClearCombatState(BotCombatStates state) { if (_combatStates & state) _combatStates &= ~state; }
  15596. + inline bool ShouldRanged() const { return (_combatStates & COMBAT_STATE_RANGED); }
  15597. + inline bool ShouldStay() const { return (_combatStates & COMBAT_STATE_STAY); }
  15598. +
  15599. + bool HasAttackTarget(uint8 myclass, uint32 myspec);
  15600. + Unit* GetAttackTarget(bool ranged, bool byspell, bool &reset) const;
  15601. + void GetInPosition(bool force, bool ranged, Unit* target = NULL, Position* pos = NULL);
  15602. +
  15603. + //combat utils
  15604. + static bool CanPlayerbotAttack(Player* bot, Unit* target, int8 byspell = 0);
  15605. + static bool IsUnitInDuel(Unit* target, Player* master);
  15606. + static bool IsUnitInPlayersParty(Unit* unit, Player* master);
  15607. +
  15608. + //packets
  15609. + void OnBotOutgoingPacket(WorldPacket const& packet);
  15610. + void HandleTeleportAck();
  15611. +
  15612. + //commands
  15613. + void HandleCommand(std::string const& /*msg*/, Player& /*player*/) {}
  15614. +
  15615. + //interraction
  15616. + inline void Tell(std::string const& text, uint64 targetGUID = 0);
  15617. +
  15618. + //utilities
  15619. + static uint32 GetLostHP(Unit* unit);
  15620. + static uint8 GetHealthPCT(Unit* unit);
  15621. + static uint8 GetManaPCT(Unit* unit);
  15622. +
  15623. + //temp / debug
  15624. + //TODO: init range for classes
  15625. + uint32 GetMySpec() const { return _mySpec; }
  15626. + inline float GetMinAttackRange(Unit* target) const;
  15627. + void InviteToMastersGroup() { _InviteToMastersGroup(); }
  15628. + void SendDebugInfo() { _SendDebugInfo(); }
  15629. + inline void GiveLevel(uint8 /*level*/) {}
  15630. + inline void AcceptQuest(Quest const* /*quest*/, Player* /*divider*/) {}
  15631. +
  15632. + protected:
  15633. + static inline bool CCed(Unit* unit, bool root = false);
  15634. +
  15635. + private:
  15636. + inline void _doTimers(uint32 diff);
  15637. +
  15638. + void _UnbindInstance(uint32 mapId, Difficulty diff);
  15639. + void _InviteToMastersGroup();
  15640. + void _SendDebugInfo();
  15641. +
  15642. + Unit* _opponent;
  15643. +
  15644. + PlayerbotMgr* const _mgr;
  15645. + Player* const me;
  15646. + PlayerbotClassAI* _classAI;
  15647. +
  15648. + AfterCast* _afterCast;
  15649. +
  15650. + uint32 _mySpec;
  15651. +
  15652. + uint8 _botStates;
  15653. + uint8 _combatStates;
  15654. + uint8 _movementFlags;
  15655. +
  15656. + uint32 _followTimer;
  15657. + uint64 _followTargetGUID;
  15658. +
  15659. + uint32 _waitTimer;
  15660. + uint32 _mountTimer;
  15661. + uint32 _cureTimer;
  15662. + uint32 _selfResTimer;
  15663. +
  15664. + bool _canSelfRes;
  15665. +
  15666. + //// This is called from ChatHandler.cpp when there is an incoming message to the bot
  15667. + //// from a whisper or from the party channel
  15668. + //void HandleCommand(std::string const& text, Player& fromPlayer);
  15669. +
  15670. + ////Packets
  15671. + //void HandleBotOutgoingPacket(const WorldPacket& packet);
  15672. +
  15673. + //// This is called by WorldSession.cpp
  15674. + //// when it detects that a bot is being teleported. It acknowledges to the server to complete the
  15675. + //// teleportation
  15676. + //void HandleTeleportAck();
  15677. +
  15678. + //// Returns what kind of situation we are in so the ai can react accordingly
  15679. + //ScenarioType GetScenarioType() const { return m_ScenarioType; }
  15680. + //CombatStyle GetCombatStyle() const { return m_combatStyle; }
  15681. + //void SetCombatStyle(CombatStyle cs) { m_combatStyle = cs; }
  15682. +
  15683. + //PlayerbotClassAI* GetClassAI() const { return _classAI; }
  15684. + //PlayerbotMgr* GetManager() const { return _mgr; }
  15685. +
  15686. + //// finds spell ID for matching substring args
  15687. + //// in priority of full text match, spells not taking reagents, and highest rank
  15688. + //uint32 getSpellId(const char* args, bool master = false) const;
  15689. + //uint32 getPetSpellId(const char* args) const;
  15690. + //// Initialize spell using rank 1 spell id
  15691. + //uint32 initPetSpell(uint32 spellIconId);
  15692. +
  15693. + //// extract mail ids from links
  15694. + //void extractMailIds(std::string const& text, std::list<uint32>& mailIds) const;
  15695. +
  15696. + //// extract quest ids from links
  15697. + //void extractQuestIds(std::string const& text, std::list<uint32>& questIds) const;
  15698. +
  15699. + //// extract auction ids from links
  15700. + //void extractAuctionIds(std::string const& text, std::list<uint32>& auctionIds) const;
  15701. +
  15702. + //// extracts talent ids to list
  15703. + //void extractTalentIds(std::string const& text, std::list<talentPair>& talentIds) const;
  15704. +
  15705. + //// extracts item ids from links
  15706. + //void extractItemIds(std::string const& text, std::list<uint32>& itemIds) const;
  15707. +
  15708. + //// extract spellid from links
  15709. + //void extractSpellId(std::string const& text, uint32 &spellId) const;
  15710. +
  15711. + //// extract spellids from links to list
  15712. + //void extractSpellIdList(std::string const& text, BotEntryList& m_spellsToLearn) const;
  15713. +
  15714. + //// extracts currency from a string as #g#s#c and returns the total in copper
  15715. + //uint32 extractMoney(std::string const& text) const;
  15716. +
  15717. + //// extracts gameobject info from link
  15718. + //void extractGOinfo(std::string const& text, BotObjectList& m_lootTargets) const;
  15719. +
  15720. + //// finds items in bots equipment and adds them to foundItemList, removes found items from itemIdSearchList
  15721. + //void findItemsInEquip(std::list<uint32>& itemIdSearchList, std::list<Item*>& foundItemList) const;
  15722. + //// finds items in bots inventory and adds them to foundItemList, removes found items from itemIdSearchList
  15723. + //void findItemsInInv(std::list<uint32>& itemIdSearchList, std::list<Item*>& foundItemList) const;
  15724. + //// finds nearby game objects that are specified in m_collectObjects then adds them to the m_lootTargets list
  15725. + //void findNearbyGO();
  15726. + //// finds nearby creatures, whose UNIT_NPC_FLAGS match the flags specified in item list m_itemIds
  15727. + //void findNearbyCreature();
  15728. + //// finds nearby corpse that is lootable
  15729. + //void findNearbyCorpse();
  15730. +
  15731. + //void GiveLevel(uint32 level);
  15732. +
  15733. + //// Error check the TS DB. Should only be used when admins want to verify their new TS input
  15734. + //uint32 TalentSpecDBContainsError();
  15735. +
  15736. + //// Get talent specs or counts thereof
  15737. + //uint32 GetTalentSpecsAmount();
  15738. + //uint32 GetTalentSpecsAmount(uint32 specClass);
  15739. + //std::list<TalentSpec> GetTalentSpecs(uint32 specClass);
  15740. + //TalentSpec GetTalentSpec(uint32 specClass, uint32 choice);
  15741. + //TalentSpec GetActiveTalentSpec() const { return m_activeTalentSpec; }
  15742. + //void ClearActiveTalentSpec() { m_activeTalentSpec.specName = ""; m_activeTalentSpec.specClass = 0; m_activeTalentSpec.specPurpose = TSP_NONE; for (int i = 0; i < 71; i++) m_activeTalentSpec.talentId[i] = 0; for (int i = 0; i < 3; i++) { m_activeTalentSpec.glyphIdMajor[i] = 0; m_activeTalentSpec.glyphIdMinor[i] = 0; } }
  15743. + //void SetActiveTalentSpec(TalentSpec ts) { m_activeTalentSpec = ts; }
  15744. + //bool ApplyActiveTalentSpec();
  15745. +
  15746. + //void MakeSpellLink(SpellInfo const* sInfo, std::ostringstream& out);
  15747. + //void MakeWeaponSkillLink(SpellInfo const* sInfo, std::ostringstream& out, uint32 skillid);
  15748. +
  15749. + //// currently bots only obey commands from the master
  15750. + //bool canObeyCommandFrom(Player const& player) const;
  15751. +
  15752. + //// get current casting spell (will return NULL if no spell!)
  15753. +
  15754. + //bool HasAura(uint32 spellId, const Unit& player) const;
  15755. + //bool HasAura(const char* spellName, const Unit& player) const;
  15756. + //bool HasAura(const char* spellName) const;
  15757. +
  15758. + //bool CanReceiveSpecificSpell(uint8 spec, Unit* target) const;
  15759. +
  15760. + //bool HasTool(uint32 TC) const;
  15761. + //bool HasSpellReagents(uint32 spellId) const;
  15762. + //void ItemCountInInv(uint32 itemid, uint32& count);
  15763. + //uint32 GetSpellCharges(uint32 spellId);
  15764. +
  15765. + //Item* FindFood() const;
  15766. + //Item* FindDrink() const;
  15767. + //Item* FindBandage() const;
  15768. + //Item* FindPoison() const;
  15769. + //Item* FindMount(uint32 matchingRidingSkill) const;
  15770. + //Item* FindItem(uint32 ItemId, bool Equipped_too = false);
  15771. + //Item* FindItemInBank(uint32 ItemId);
  15772. + //Item* FindKeyForLockValue(uint32 reqSkillValue);
  15773. + //Item* FindBombForLockValue(uint32 reqSkillValue);
  15774. + //Item* FindConsumable(uint32 displayId) const;
  15775. + //uint8 _findItemSlot(Item* target);
  15776. + //bool CanStore() const;
  15777. +
  15778. + //// ******* Actions ****************************************
  15779. + //// Your handlers can call these actions to make the bot do things.
  15780. + //void TellMaster(std::string const& text) const;
  15781. + //void TellMaster(const char* fmt, ...) const;
  15782. + //void SendWhisper(std::string const& text, Player& player) const;
  15783. + //bool CastSpell(const char* args);
  15784. + //bool CastSpell(uint32 spellId);
  15785. + //bool CastSpell(uint32 spellId, Unit& target);
  15786. + //bool CheckBotCast(SpellInfo const* sInfo);
  15787. + //bool CastPetSpell(uint32 spellId, Unit* target = NULL);
  15788. + //bool Buff(uint32 spellId, Unit* target, void (*beforeCast)(Player *) = NULL);
  15789. + //bool SelfBuff(uint32 spellId);
  15790. + //bool IsInRange(Unit* Target, uint32 spellId);
  15791. +
  15792. + //void UseItem(Item* item, uint32 targetFlag, uint64 targetGUID);
  15793. + //void UseItem(Item* item, uint8 targetInventorySlot);
  15794. + //void UseItem(Item* item, Unit* target);
  15795. + //void UseItem(Item* item);
  15796. +
  15797. + //void PlaySound(uint32 soundid);
  15798. + //void Announce(AnnounceFlags msg);
  15799. +
  15800. + //void EquipItem(Item* src_Item);
  15801. + ////void Stay();
  15802. + ////bool Follow(Player& player);
  15803. + //void SendNotEquipList(Player& player);
  15804. +
  15805. + //uint8 m_DelayAttack;
  15806. + //Unit* gPrimtarget;
  15807. + //Unit* gSectarget;
  15808. + //uint32 gQuestFetch;
  15809. +
  15810. + //bool m_AutoEquipToggle; //switch for autoequip
  15811. + //uint32 SellWhite; //switch for white item auto sell
  15812. + //uint8 DistOverRide;
  15813. + //float gDist[2]; //gDist, gTemp vars are used for variable follow distance
  15814. + //float gTempDist;
  15815. + //float gTempDist2;
  15816. + //uint8 m_FollowAutoGo;
  15817. + //uint8 IsUpOrDown; //tracks variable follow distance
  15818. + //void BotDataRestore();
  15819. + //void CombatOrderRestore();
  15820. + //void AutoUpgradeEquipment();
  15821. + //void AutoEquipComparison(Item* pItem, Item* pItem2);
  15822. + //bool ItemStatComparison(ItemTemplate const* pProto, ItemTemplate const* pProto2);
  15823. + //void Feast();
  15824. + //void InterruptCurrentCastingSpell();
  15825. + //void Attack(Unit* forcedTarget = NULL);
  15826. + //void GetCombatTarget(Unit* forcedTarget = 0);
  15827. + //void GetDuelTarget(Unit* forcedTarget);
  15828. + //Unit* GetCurrentTarget() const { return m_targetCombat; }
  15829. + //void DoNextCombatManeuver();
  15830. + //void DoCombatMovement();
  15831. + ////void SetIgnoreUpdateTime(uint8 t = 0) { m_ignoreAIUpdatesUntilTime = time(NULL) + t; };
  15832. + //void SetState(BotState state);
  15833. + //void SetQuestNeedItems();
  15834. + //void SetQuestNeedCreatures();
  15835. + //void SendQuestNeedList();
  15836. + //bool IsInQuestItemList(uint32 itemid) const { return m_needItemList.find(itemid) != m_needItemList.end(); };
  15837. + //bool IsInQuestCreatureList(uint32 id) const { return m_needCreatureOrGOList.find(id) != m_needCreatureOrGOList.end(); };
  15838. + //bool IsItemUseful(uint32 itemid);
  15839. + //void SendOrders(Player& player);
  15840. + //bool DoTeleport(WorldObject& obj);
  15841. + //void DoLoot();
  15842. + //void DoFlight();
  15843. + //void GetTaxi(uint64 guid, BotTaxiNode& nodes);
  15844. + //void BeingRolledOn(uint64 target) { m_being_rolled_on.push_back(target); };
  15845. +
  15846. + //bool HasCollectFlag(uint16 flag) { return m_collectionFlags & flag; }
  15847. + //void SetCollectFlag(uint16 flag)
  15848. + //{
  15849. + // if (HasCollectFlag(flag)) m_collectionFlags &= ~flag;
  15850. + // else m_collectionFlags |= flag;
  15851. + //}
  15852. +
  15853. + //uint32 EstRepairAll();
  15854. + //uint32 EstRepair(uint16 pos);
  15855. +
  15856. + //void AcceptQuest(Quest const *qInfo, Player *pGiver);
  15857. + //void TurnInQuests(WorldObject *questgiver);
  15858. + //void ListQuests(WorldObject* questgiver);
  15859. + //bool AddQuest(uint32 const entry, WorldObject* questgiver);
  15860. + //void MakeQuestLink(Quest const* quest, std::ostringstream &out);
  15861. +
  15862. + //bool IsInCombat();
  15863. + //bool IsGroupInCombat();
  15864. + //Player* GetGroupTank(); // TODO: didn't want to pollute non-playerbot code but this should really go in group.cpp
  15865. + //void SetGroupCombatOrder(CombatOrderType co);
  15866. + //void ClearGroupCombatOrder(CombatOrderType co);
  15867. + //void SetGroupIgnoreUpdateTime(uint8 t);
  15868. + //bool GroupHoTOnTank() const;
  15869. + //bool CanPull(Player& fromPlayer);
  15870. + //bool CastPull();
  15871. + //bool GroupTankHoldsAggro();
  15872. + //void UpdateAttackerInfo();
  15873. + //Unit* FindAttacker(ATTACKERINFOTYPE ait = AIT_NONE, Unit* victim = NULL);
  15874. + //uint32 GetAttackerCount() const { return m_attackerInfo.size(); };
  15875. + //void SetCombatOrderByStr(std::string str, Unit* target = NULL);
  15876. + //void SetCombatOrder(CombatOrderType co, Unit* target = NULL);
  15877. + //void ClearCombatOrder(CombatOrderType co);
  15878. + //CombatOrderType GetCombatOrder() const { return this->m_combatOrder; }
  15879. + //bool IsTank() const { return (m_combatOrder & ORDERS_TANK) ? true : false; }
  15880. + //bool IsHealer() const { return (m_combatOrder & ORDERS_HEAL) ? true : false; }
  15881. + //bool IsDPS() const { return (m_combatOrder & ORDERS_ASSIST) ? true : false; }
  15882. + //void SetMovementOrder(MovementOrderType mo, Unit* followTarget = NULL);
  15883. + //MovementOrderType GetMovementOrder() const { return this->m_movementOrder; }
  15884. + //void MovementReset();
  15885. + //void MovementClear();
  15886. +
  15887. + // void SetInFront(Unit const* obj);
  15888. +
  15889. + // void ItemLocalization(std::string& itemName, uint32 const itemID) const;
  15890. + // void QuestLocalization(std::string& questTitle, uint32 const questID) const;
  15891. + // void CreatureLocalization(std::string& creatureName, uint32 const entry) const;
  15892. + // void GameObjectLocalization(std::string& gameobjectName, uint32 const entry) const;
  15893. +
  15894. + // uint32 GetFreeBagSpace() const;
  15895. + // bool DropGarbage(bool bVerbose);
  15896. + // void SellGarbage(Player& player, bool listNonTrash = true, bool bDetailTrashSold = false, bool verbose = true);
  15897. + // void Sell(uint32 const itemid);
  15898. + // void Buy(Creature* vendor, uint32 const itemid);
  15899. + // std::string DropItem(uint32 const itemid);
  15900. + // void AddAuction(uint32 const itemid, Creature* aCreature);
  15901. + // void ListAuctions();
  15902. + // bool RemoveAuction(uint32 const auctionid);
  15903. + // void Repair(uint32 const itemid, Creature* rCreature);
  15904. + // bool Talent(Creature* tCreature);
  15905. + // void InspectUpdate();
  15906. + // bool Withdraw(uint32 const itemid);
  15907. + // bool Deposit(uint32 const itemid);
  15908. + // void BankBalance();
  15909. + // std::string Cash(uint32 copper);
  15910. + // std::string AuctionResult(std::string subject, std::string body);
  15911. +
  15912. + //protected:
  15913. + // bool ValidateTalent(uint16 talent, uint32 charClass);
  15914. + // bool ValidateGlyph(uint16 glyph, uint32 charClass);
  15915. + // bool ValidateMajorGlyph(uint16 glyph, uint32 charClass);
  15916. + // bool ValidateMinorGlyph(uint16 glyph, uint32 charClass);
  15917. +
  15918. + //private:
  15919. + // bool ExtractCommand(std::string const sLookingFor, std::string& text, bool bUseShort = false);
  15920. + // // outsource commands for code clarity
  15921. + // void _HandleCommandReset(std::string& text, Player& fromPlayer);
  15922. + // void _HandleCommandOrders(std::string& text, Player& fromPlayer);
  15923. + // void _HandleCommandFollow(std::string& text, Player& fromPlayer);
  15924. + // void _HandleCommandStay(std::string& text, Player& fromPlayer);
  15925. + // void _HandleCommandAttack(std::string& text, Player& fromPlayer);
  15926. + // void _HandleCommandPull(std::string& text, Player& fromPlayer);
  15927. + // void _HandleCommandCast(std::string& text, Player& fromPlayer);
  15928. + // void _HandleCommandSell(std::string& text, Player& fromPlayer);
  15929. + // void _HandleCommandBuy(std::string& text, Player& fromPlayer);
  15930. + // void _HandleCommandDrop(std::string& text, Player& fromPlayer);
  15931. + // void _HandleCommandRepair(std::string& text, Player& fromPlayer);
  15932. + // void _HandleCommandAuction(std::string& text, Player& fromPlayer);
  15933. + // void _HandleCommandMail(std::string& text, Player& fromPlayer);
  15934. + // void _HandleCommandBank(std::string& text, Player& fromPlayer);
  15935. + // void _HandleCommandTalent(std::string& text, Player& fromPlayer);
  15936. + // void _HandleCommandUse(std::string& text, Player& fromPlayer);
  15937. + // void _HandleCommandEquip(std::string& text, Player& fromPlayer);
  15938. + // void _HandleCommandFind(std::string& text, Player& fromPlayer);
  15939. + // void _HandleCommandGet(std::string& text, Player& fromPlayer);
  15940. + // void _HandleCommandCollect(std::string& text, Player& fromPlayer);
  15941. + // void _HandleCommandQuest(std::string& text, Player& fromPlayer);
  15942. + // void _HandleCommandCraft(std::string& text, Player& fromPlayer);
  15943. + // void _HandleCommandEnchant(std::string& text, Player& fromPlayer);
  15944. + // void _HandleCommandProcess(std::string& text, Player& fromPlayer);
  15945. + // void _HandleCommandPet(std::string& text, Player& fromPlayer);
  15946. + // void _HandleCommandSpells(std::string& text, Player& fromPlayer);
  15947. + // void _HandleCommandSurvey(std::string& text, Player& fromPlayer);
  15948. + // void _HandleCommandSkill(std::string& text, Player& fromPlayer);
  15949. + // bool _HandleCommandSkillLearnHelper(TrainerSpell const* tSpell, uint32 spellId, uint32 cost);
  15950. + // void _HandleCommandStats(std::string& text, Player& fromPlayer);
  15951. + // void _HandleCommandHelp(std::string& text, Player& fromPlayer);
  15952. + // void _HandleCommandHelp(const char* szText, Player& fromPlayer) { std::string text = szText; _HandleCommandHelp(text, fromPlayer); }
  15953. + // void _HandleCommandGM(std::string& text, Player& fromPlayer);
  15954. + // std::string _HandleCommandHelpHelper(std::string sCommand, std::string sExplain, HELPERLINKABLES reqLink = HL_NONE, bool bReqLinkMultiples = false, bool bCommandShort = false);
  15955. +
  15956. + //// ****** Closed Actions ********************************
  15957. + //// These actions may only be called at special times.
  15958. + //// Trade methods are only applicable when the trade window is open
  15959. + //// and are only called from within HandleCommand.
  15960. + //bool TradeItem(Item const& item, int8 slot = -1);
  15961. + //bool TradeCopper(uint32 copper);
  15962. +
  15963. + //// Helper routines not needed by class AIs.
  15964. + //void UpdateAttackersForTarget(Unit* victim);
  15965. +
  15966. + //void _doSellItem(Item const* item, std::ostringstream& report, std::ostringstream& canSell, uint32& TotalCost, uint32& TotalSold);
  15967. + //void MakeItemLink(Item const* item, std::ostringstream& out, bool IncludeQuantity = true);
  15968. + //void MakeItemText(Item const* item, std::ostringstream& out, bool IncludeQuantity = true);
  15969. + //void MakeItemLink(ItemTemplate const* item, std::ostringstream& out);
  15970. +
  15971. + // it is safe to keep these back reference pointers because m_bot
  15972. + // owns the "this" object and m_master owns m_bot. The owner always cleans up.
  15973. +
  15974. + // ignores AI updates until time specified
  15975. + // no need to waste CPU cycles during casting etc
  15976. + //time_t m_ignoreAIUpdatesUntilTime;
  15977. +
  15978. + //CombatStyle m_combatStyle;
  15979. + //CombatOrderType m_combatOrder;
  15980. + //MovementOrderType m_movementOrder;
  15981. +
  15982. + //TalentSpec m_activeTalentSpec;
  15983. +
  15984. + //ScenarioType m_ScenarioType;
  15985. +
  15986. + // defines the state of behaviour of the bot
  15987. +
  15988. + //// list of items, creatures or gameobjects needed to fullfill quests
  15989. + //BotNeedItem m_needItemList;
  15990. + //BotNeedItem m_needCreatureOrGOList;
  15991. +
  15992. + //// list of creatures we recently attacked and want to loot
  15993. + //BotNPCList m_findNPC; // list of NPCs
  15994. + //BotTaskList m_tasks; // list of tasks
  15995. + //BotObjectList m_lootTargets; // list of targets
  15996. + //BotEntryList m_spellsToLearn; // list of spells
  15997. + //uint64 m_lootCurrent; // current remains of interest
  15998. + //uint64 m_lootPrev; // previous loot
  15999. + //BotEntryList m_collectObjects; // object entries searched for in findNearbyGO
  16000. + //BotTaxiNode m_taxiNodes; // flight node chain
  16001. + //BotEntryList m_noToolList; // list of required tools
  16002. + //BotObjectList m_being_rolled_on; // list of targets currently involved in item rolls
  16003. +
  16004. + //uint16 m_collectionFlags; // what the bot should look for to loot
  16005. + //uint32 m_collectDist; // distance to collect objects
  16006. + //bool m_inventory_full;
  16007. + //uint32 m_itemTarget;
  16008. + //bool m_dropWhite;
  16009. +
  16010. + ////time_t m_TimeDoneEating;
  16011. + ////time_t m_TimeDoneDrinking;
  16012. + ////uint32 m_CurrentlyCastingSpellId;
  16013. + //uint32 m_CraftSpellId;
  16014. + ////bool m_IsFollowingMaster;
  16015. +
  16016. + //// if master commands bot to do something, store here until updateAI
  16017. + //// can do it
  16018. + //uint32 m_spellIdCommand;
  16019. + //uint64 m_targetGuidCommand;
  16020. + //uint64 m_taxiMaster;
  16021. +
  16022. + //BotObjectSet m_ignorePlayersChat; // list of players that the bot will not respond to
  16023. +
  16024. + //AttackerInfoList m_attackerInfo;
  16025. +
  16026. + //bool m_targetChanged;
  16027. + //CombatTargetType m_targetType;
  16028. +
  16029. + //Unit* m_targetCombat; // current combat target
  16030. + //Unit* m_targetAssist; // get new target by checking attacker list of assisted player
  16031. + //Unit* m_targetProtect; // check
  16032. +
  16033. + //Unit* m_followTarget; // whom to follow in non combat situation?
  16034. +
  16035. + //uint32 FISHING,
  16036. + // HERB_GATHERING,
  16037. + // MINING,
  16038. + // SKINNING,
  16039. + // ASPECT_OF_THE_MONKEY;
  16040. +
  16041. + //float m_destX, m_destY, m_destZ; // latest coordinates for chase and point movement types
  16042. +
  16043. + //bool m_bDebugCommandChat;
  16044. +};
  16045. +
  16046. +#endif
  16047. \ No newline at end of file
  16048. diff --git a/src/server/game/AI/PlayerBots/bp_cai.cpp b/src/server/game/AI/PlayerBots/bp_cai.cpp
  16049. new file mode 100644
  16050. index 0000000..3b76ed6
  16051. --- /dev/null
  16052. +++ b/src/server/game/AI/PlayerBots/bp_cai.cpp
  16053. @@ -0,0 +1,14 @@
  16054. +#include "bp_cai.h"
  16055. +//#include "Common.h"
  16056. +//#include "World.h"
  16057. +//#include "SpellMgr.h"
  16058. +#include "Player.h"
  16059. +#include "Group.h"
  16060. +//#include "ObjectMgr.h"
  16061. +//#include "WorldPacket.h"
  16062. +//#include "Unit.h"
  16063. +#include "bp_ai.h"
  16064. +
  16065. +PlayerbotClassAI::PlayerbotClassAI(Player* const master, Player* const bot, PlayerbotAI* const ai) :
  16066. +m_master(master), me(bot), m_ai(ai) {}
  16067. +PlayerbotClassAI::~PlayerbotClassAI() {}
  16068. diff --git a/src/server/game/AI/PlayerBots/bp_cai.h b/src/server/game/AI/PlayerBots/bp_cai.h
  16069. new file mode 100644
  16070. index 0000000..6b21da4
  16071. --- /dev/null
  16072. +++ b/src/server/game/AI/PlayerBots/bp_cai.h
  16073. @@ -0,0 +1,118 @@
  16074. +#ifndef _PLAYERBOTCLASSAI_H
  16075. +#define _PLAYERBOTCLASSAI_H
  16076. +
  16077. +#include "Common.h"
  16078. +
  16079. +class PlayerbotAI;
  16080. +class Player;
  16081. +class Unit;
  16082. +
  16083. +enum CombatManeuverReturns
  16084. +{
  16085. + // TODO: RETURN_NO_ACTION_UNKNOWN is not part of ANY_OK or ANY_ERROR. It's also bad form and should be eliminated ASAP.
  16086. + RETURN_NO_ACTION_OK = 0x01, // No action taken during this combat maneuver, as intended (just wait, etc...)
  16087. + RETURN_NO_ACTION_UNKNOWN = 0x02, // No action taken during this combat maneuver, unknown reason
  16088. + RETURN_NO_ACTION_ERROR = 0x04, // No action taken due to error
  16089. + RETURN_NO_ACTION_INVALIDTARGET = 0x08, // No action taken - invalid target
  16090. + RETURN_FINISHED_FIRST_MOVES = 0x10, // Last action of first-combat-maneuver finished, continue onto next-combat-maneuver
  16091. + RETURN_CONTINUE = 0x20, // Continue first moves; normal return value for next-combat-maneuver
  16092. + RETURN_NO_ACTION_INSUFFICIENT_POWER = 0x40, // No action taken due to insufficient power (rage, focus, mana, runes)
  16093. + RETURN_ANY_OK = 0x31, // All the OK values bitwise OR'ed
  16094. + RETURN_ANY_ACTION = 0x30, // All returns that result in action (which should also be 'OK')
  16095. + RETURN_ANY_ERROR = 0x4C // All the ERROR values bitwise OR'ed
  16096. +};
  16097. +
  16098. +enum MainSpec
  16099. +{
  16100. + MAGE_SPEC_FIRE = 41,
  16101. + MAGE_SPEC_FROST = 61,
  16102. + MAGE_SPEC_ARCANE = 81,
  16103. + WARRIOR_SPEC_ARMS = 161,
  16104. + WARRIOR_SPEC_PROTECTION = 163,
  16105. + WARRIOR_SPEC_FURY = 164,
  16106. + ROGUE_SPEC_COMBAT = 181,
  16107. + ROGUE_SPEC_ASSASSINATION = 182,
  16108. + ROGUE_SPEC_SUBTELTY = 183,
  16109. + PRIEST_SPEC_DISCIPLINE = 201,
  16110. + PRIEST_SPEC_HOLY = 202,
  16111. + PRIEST_SPEC_SHADOW = 203,
  16112. + SHAMAN_SPEC_ELEMENTAL = 261,
  16113. + SHAMAN_SPEC_RESTORATION = 262,
  16114. + SHAMAN_SPEC_ENHANCEMENT = 263,
  16115. + DRUID_SPEC_FERAL = 281,
  16116. + DRUID_SPEC_RESTORATION = 282,
  16117. + DRUID_SPEC_BALANCE = 283,
  16118. + WARLOCK_SPEC_DESTRUCTION = 301,
  16119. + WARLOCK_SPEC_AFFLICTION = 302,
  16120. + WARLOCK_SPEC_DEMONOLOGY = 303,
  16121. + HUNTER_SPEC_BEASTMASTERY = 361,
  16122. + HUNTER_SPEC_SURVIVAL = 362,
  16123. + HUNTER_SPEC_MARKSMANSHIP = 363,
  16124. + PALADIN_SPEC_RETRIBUTION = 381,
  16125. + PALADIN_SPEC_HOLY = 382,
  16126. + PALADIN_SPEC_PROTECTION = 383,
  16127. + DEATHKNIGHT_SPEC_BLOOD = 398,
  16128. + DEATHKNIGHT_SPEC_FROST = 399,
  16129. + DEATHKNIGHT_SPEC_UNHOLY = 400
  16130. +};
  16131. +
  16132. +class PlayerbotClassAI
  16133. +{
  16134. + public:
  16135. + PlayerbotClassAI(Player* const master, Player* const bot, PlayerbotAI* const ai);
  16136. + virtual ~PlayerbotClassAI();
  16137. +
  16138. + //// all combat actions go here
  16139. + //CombatManeuverReturns DoFirstCombatManeuver(Unit*);
  16140. + //CombatManeuverReturns DoNextCombatManeuver(Unit*);
  16141. + //bool Pull() { /*DEBUG_LOG("[PlayerbotAI]: Warning: Using PlayerbotClassAI::Pull() rather than class specific function");*/ return false; }
  16142. +
  16143. + //// all non combat actions go here, ex buffs, heals, rezzes
  16144. + //virtual void DoNonCombatActions();
  16145. + //bool EatDrinkBandage(bool bMana = true, uint8 foodPercent = 50, uint8 drinkPercent = 50, uint8 bandagePercent = 70);
  16146. +
  16147. + // Utilities
  16148. + Player* GetMaster() const { return m_master; }
  16149. + Player* GetPlayerBot() const { return me; }
  16150. + PlayerbotAI* GetAI() const { return m_ai; }
  16151. + virtual void DoCombatActions() {}
  16152. + virtual void UpdateGroupActions(uint32 const /*diff*/) {}
  16153. + virtual void SendClassDebugInfo() {}
  16154. + //bool CanPull() const;
  16155. + //bool CastHoTOnTank();
  16156. + //time_t GetWaitUntil() const { return m_WaitUntil; }
  16157. + //void SetWait(uint8 t);
  16158. + //void ClearWait() { m_WaitUntil = 0; }
  16159. + ////void SetWaitUntil(time_t t) { m_WaitUntil = t; }
  16160. +
  16161. + protected:
  16162. + //CombatManeuverReturns DoFirstCombatManeuverPVE(Unit*);
  16163. + //CombatManeuverReturns DoNextCombatManeuverPVE(Unit*);
  16164. + //CombatManeuverReturns DoFirstCombatManeuverPVP(Unit*);
  16165. + //CombatManeuverReturns DoNextCombatManeuverPVP(Unit*);
  16166. +
  16167. + CombatManeuverReturns CastSpellNoRanged(uint32 /*nextAction*/, Unit* /*pTarget*/) { return RETURN_ANY_ERROR; }
  16168. + CombatManeuverReturns CastSpellWand(uint32 /*nextAction*/, Unit* /*pTarget*/, uint32 /*SHOOT*/) { return RETURN_ANY_ERROR; }
  16169. + //virtual CombatManeuverReturns HealPlayer(Player* target);
  16170. + //CombatManeuverReturns Buff(bool (*BuffHelper)(PlayerbotAI*, uint32, Unit*), uint32 spellId, uint32 type = JOB_ALL, bool bMustBeOOC = true);
  16171. + Player* GetHealTarget() { return NULL; }
  16172. + //Player* GetResurrectionTarget(JOB_TYPE type = JOB_ALL, bool bMustBeOOC = true);
  16173. + //JOB_TYPE GetTargetJob(Player* target);
  16174. +
  16175. + //// These values are used in GetHealTarget and can be overridden per class (to accomodate healing spell health checks)
  16176. + //uint8 m_MinHealthPercentTank;
  16177. + //uint8 m_MinHealthPercentHealer;
  16178. + //uint8 m_MinHealthPercentDPS;
  16179. + //uint8 m_MinHealthPercentMaster;
  16180. +
  16181. + //time_t m_WaitUntil;
  16182. +
  16183. + Player* const m_master;
  16184. + Player* const me;
  16185. + PlayerbotAI* const m_ai;
  16186. +
  16187. + //// first aid
  16188. + //uint32 RECENTLY_BANDAGED;
  16189. +};
  16190. +
  16191. +#endif
  16192. diff --git a/src/server/game/AI/PlayerBots/bp_dk_ai.cpp b/src/server/game/AI/PlayerBots/bp_dk_ai.cpp
  16193. new file mode 100644
  16194. index 0000000..501ac70
  16195. --- /dev/null
  16196. +++ b/src/server/game/AI/PlayerBots/bp_dk_ai.cpp
  16197. @@ -0,0 +1,549 @@
  16198. +// a simple DK class by rrtn :)
  16199. +#include "bp_dk_ai.h"
  16200. +#include "bp_ai.h"
  16201. +#include "Player.h"
  16202. +#include "Pet.h"
  16203. +
  16204. +PlayerbotDeathKnightAI::PlayerbotDeathKnightAI(Player* const master, Player* const bot, PlayerbotAI* const ai) : PlayerbotClassAI(master, bot, ai)
  16205. +{
  16206. +
  16207. + PLAGUE_STRIKE = PlayerbotAI::InitSpell(me, PLAGUE_STRIKE_1); // Unholy
  16208. + DEATH_GRIP = PlayerbotAI::InitSpell(me, DEATH_GRIP_1);
  16209. + DEATH_COIL = PlayerbotAI::InitSpell(me, DEATH_COIL_DEATH_KNIGHT_1);
  16210. + DEATH_STRIKE = PlayerbotAI::InitSpell(me, DEATH_STRIKE_1);
  16211. + UNHOLY_BLIGHT = 0; // Passive
  16212. + SCOURGE_STRIKE = PlayerbotAI::InitSpell(me, SCOURGE_STRIKE_1);
  16213. + DEATH_AND_DECAY = PlayerbotAI::InitSpell(me, DEATH_AND_DECAY_1);
  16214. + CORPSE_EXPLOSION = PlayerbotAI::InitSpell(me, CORPSE_EXPLOSION_1);
  16215. + BONE_SHIELD = PlayerbotAI::InitSpell(me, BONE_SHIELD_1); // buffs
  16216. + ANTI_MAGIC_SHELL = PlayerbotAI::InitSpell(me, ANTI_MAGIC_SHELL_1);
  16217. + ANTI_MAGIC_ZONE = PlayerbotAI::InitSpell(me, ANTI_MAGIC_ZONE_1);
  16218. + GHOUL_FRENZY = PlayerbotAI::InitSpell(me, GHOUL_FRENZY_1);
  16219. + RAISE_DEAD = PlayerbotAI::InitSpell(me, RAISE_DEAD_1); // pets
  16220. + SUMMON_GARGOYLE = PlayerbotAI::InitSpell(me, SUMMON_GARGOYLE_1);
  16221. + ARMY_OF_THE_DEAD = PlayerbotAI::InitSpell(me, ARMY_OF_THE_DEAD_1);
  16222. + ICY_TOUCH = PlayerbotAI::InitSpell(me, ICY_TOUCH_1); // Frost
  16223. + OBLITERATE = PlayerbotAI::InitSpell(me, OBLITERATE_1);
  16224. + HOWLING_BLAST = PlayerbotAI::InitSpell(me, HOWLING_BLAST_1);
  16225. + FROST_STRIKE = PlayerbotAI::InitSpell(me, FROST_STRIKE_1);
  16226. + CHAINS_OF_ICE = PlayerbotAI::InitSpell(me, CHAINS_OF_ICE_1);
  16227. + RUNE_STRIKE = PlayerbotAI::InitSpell(me, RUNE_STRIKE_1);
  16228. + ICY_CLUTCH = 0; // No such spell
  16229. + MIND_FREEZE = PlayerbotAI::InitSpell(me, MIND_FREEZE_1);
  16230. + HUNGERING_COLD = PlayerbotAI::InitSpell(me, HUNGERING_COLD_1);
  16231. + KILLING_MACHINE = 0; // Passive
  16232. + DEATHCHILL = PlayerbotAI::InitSpell(me, DEATHCHILL_1);
  16233. + HORN_OF_WINTER = PlayerbotAI::InitSpell(me, HORN_OF_WINTER_1);
  16234. + ICEBOUND_FORTITUDE = PlayerbotAI::InitSpell(me, ICEBOUND_FORTITUDE_1);
  16235. + EMPOWER_WEAPON = PlayerbotAI::InitSpell(me, EMPOWER_RUNE_WEAPON_1);
  16236. + UNBREAKABLE_ARMOR = PlayerbotAI::InitSpell(me, UNBREAKABLE_ARMOR_1);
  16237. + BLOOD_STRIKE = PlayerbotAI::InitSpell(me, BLOOD_STRIKE_1); // Blood
  16238. + PESTILENCE = PlayerbotAI::InitSpell(me, PESTILENCE_1);
  16239. + STRANGULATE = PlayerbotAI::InitSpell(me, STRANGULATE_1);
  16240. + BLOOD_BOIL = PlayerbotAI::InitSpell(me, BLOOD_BOIL_1);
  16241. + HEART_STRIKE = PlayerbotAI::InitSpell(me, HEART_STRIKE_1);
  16242. + DANCING_WEAPON = PlayerbotAI::InitSpell(me, DANCING_RUNE_WEAPON_1);
  16243. + DARK_COMMAND = PlayerbotAI::InitSpell(me, DARK_COMMAND_1);
  16244. + MARK_OF_BLOOD = PlayerbotAI::InitSpell(me, MARK_OF_BLOOD_1); // buffs
  16245. + RUNE_TAP = PlayerbotAI::InitSpell(me, RUNE_TAP_1);
  16246. + VAMPIRIC_BLOOD = PlayerbotAI::InitSpell(me, VAMPIRIC_BLOOD_1);
  16247. + DEATH_PACT = PlayerbotAI::InitSpell(me, DEATH_PACT_1);
  16248. + HYSTERIA = PlayerbotAI::InitSpell(me, HYSTERIA_1);
  16249. + UNHOLY_PRESENCE = PlayerbotAI::InitSpell(me, UNHOLY_PRESENCE_1); // presence (TODO: better spell == presence)
  16250. + FROST_PRESENCE = PlayerbotAI::InitSpell(me, FROST_PRESENCE_1);
  16251. + BLOOD_PRESENCE = PlayerbotAI::InitSpell(me, BLOOD_PRESENCE_1);
  16252. +
  16253. + //RECENTLY_BANDAGED = 11196; // first aid check
  16254. +
  16255. + // racial
  16256. + ARCANE_TORRENT = PlayerbotAI::InitSpell(me, ARCANE_TORRENT_DEATH_KNIGHT); // blood elf
  16257. + GIFT_OF_THE_NAARU = PlayerbotAI::InitSpell(me, GIFT_OF_THE_NAARU_DEATH_KNIGHT); // draenei
  16258. + STONEFORM = PlayerbotAI::InitSpell(me, STONEFORM_ALL); // dwarf
  16259. + ESCAPE_ARTIST = PlayerbotAI::InitSpell(me, ESCAPE_ARTIST_ALL); // gnome
  16260. + EVERY_MAN_FOR_HIMSELF = PlayerbotAI::InitSpell(me, EVERY_MAN_FOR_HIMSELF_ALL); // human
  16261. + BLOOD_FURY = PlayerbotAI::InitSpell(me, BLOOD_FURY_MELEE_CLASSES); // orc
  16262. + WAR_STOMP = PlayerbotAI::InitSpell(me, WAR_STOMP_ALL); // tauren
  16263. + BERSERKING = PlayerbotAI::InitSpell(me, BERSERKING_ALL); // troll
  16264. + WILL_OF_THE_FORSAKEN = PlayerbotAI::InitSpell(me, WILL_OF_THE_FORSAKEN_ALL); // undead
  16265. +}
  16266. +
  16267. +PlayerbotDeathKnightAI::~PlayerbotDeathKnightAI() {}
  16268. +
  16269. +CombatManeuverReturns PlayerbotDeathKnightAI::DoFirstCombatManeuver(Unit* pTarget)
  16270. +{
  16271. + //// There are NPCs in BGs and Open World PvP, so don't filter this on PvP scenarios (of course if PvP targets anyone but tank, all bets are off anyway)
  16272. + //// Wait until the tank says so, until any non-tank gains aggro or X seconds - whichever is shortest
  16273. + //if (m_ai->GetCombatOrder() & PlayerbotAI::ORDERS_TEMP_WAIT_TANKAGGRO)
  16274. + //{
  16275. + // if (m_WaitUntil > m_ai->CurrentTime() && m_ai->GroupTankHoldsAggro())
  16276. + // {
  16277. + // if (PlayerbotAI::ORDERS_TANK & m_ai->GetCombatOrder())
  16278. + // {
  16279. + // if (pTarget->GetCombatReach() <= ATTACK_DISTANCE)
  16280. + // {
  16281. + // // Set everyone's UpdateAI() waiting to 2 seconds
  16282. + // m_ai->SetGroupIgnoreUpdateTime(2);
  16283. + // // Clear their TEMP_WAIT_TANKAGGRO flag
  16284. + // m_ai->ClearGroupCombatOrder(PlayerbotAI::ORDERS_TEMP_WAIT_TANKAGGRO);
  16285. + // // Start attacking, force target on current target
  16286. + // m_ai->Attack(m_ai->GetCurrentTarget());
  16287. +
  16288. + // // While everyone else is waiting 2 second, we need to build up aggro, so don't return
  16289. + // }
  16290. + // else
  16291. + // {
  16292. + // // TODO: add check if target is ranged
  16293. + // return RETURN_NO_ACTION_OK; // wait for target to get nearer
  16294. + // }
  16295. + // }
  16296. + // else
  16297. + // return RETURN_NO_ACTION_OK; // wait it out
  16298. + // }
  16299. + // else
  16300. + // {
  16301. + // m_ai->ClearGroupCombatOrder(PlayerbotAI::ORDERS_TEMP_WAIT_TANKAGGRO);
  16302. + // }
  16303. + //}
  16304. +
  16305. + //if (m_ai->GetCombatOrder() & PlayerbotAI::ORDERS_TEMP_WAIT_OOC)
  16306. + //{
  16307. + // if (m_WaitUntil > m_ai->CurrentTime() && !m_ai->IsGroupInCombat())
  16308. + // return RETURN_NO_ACTION_OK; // wait it out
  16309. + // else
  16310. + // m_ai->ClearGroupCombatOrder(PlayerbotAI::ORDERS_TEMP_WAIT_OOC);
  16311. + //}
  16312. +
  16313. + //switch (m_ai->GetScenarioType())
  16314. + //{
  16315. + // case PlayerbotAI::SCENARIO_PVP_DUEL:
  16316. + // case PlayerbotAI::SCENARIO_PVP_BG:
  16317. + // case PlayerbotAI::SCENARIO_PVP_ARENA:
  16318. + // case PlayerbotAI::SCENARIO_PVP_OPENWORLD:
  16319. + // return DoFirstCombatManeuverPVP(pTarget);
  16320. + // case PlayerbotAI::SCENARIO_PVE:
  16321. + // case PlayerbotAI::SCENARIO_PVE_ELITE:
  16322. + // case PlayerbotAI::SCENARIO_PVE_RAID:
  16323. + // default:
  16324. + // return DoFirstCombatManeuverPVE(pTarget);
  16325. + // break;
  16326. + //}
  16327. +
  16328. + return RETURN_NO_ACTION_ERROR;
  16329. +}
  16330. +
  16331. +CombatManeuverReturns PlayerbotDeathKnightAI::DoFirstCombatManeuverPVE(Unit* /*pTarget*/)
  16332. +{
  16333. + return RETURN_NO_ACTION_OK;
  16334. +}
  16335. +
  16336. +CombatManeuverReturns PlayerbotDeathKnightAI::DoFirstCombatManeuverPVP(Unit* /*pTarget*/)
  16337. +{
  16338. + return RETURN_NO_ACTION_OK;
  16339. +}
  16340. +
  16341. +CombatManeuverReturns PlayerbotDeathKnightAI::DoNextCombatManeuver(Unit *pTarget)
  16342. +{
  16343. + //switch (m_ai->GetScenarioType())
  16344. + //{
  16345. + // case PlayerbotAI::SCENARIO_PVP_DUEL:
  16346. + // case PlayerbotAI::SCENARIO_PVP_BG:
  16347. + // case PlayerbotAI::SCENARIO_PVP_ARENA:
  16348. + // case PlayerbotAI::SCENARIO_PVP_OPENWORLD:
  16349. + // return DoNextCombatManeuverPVP(pTarget);
  16350. + // case PlayerbotAI::SCENARIO_PVE:
  16351. + // case PlayerbotAI::SCENARIO_PVE_ELITE:
  16352. + // case PlayerbotAI::SCENARIO_PVE_RAID:
  16353. + // default:
  16354. + // return DoNextCombatManeuverPVE(pTarget);
  16355. + // break;
  16356. + //}
  16357. +
  16358. + return RETURN_NO_ACTION_ERROR;
  16359. +}
  16360. +
  16361. +CombatManeuverReturns PlayerbotDeathKnightAI::DoNextCombatManeuverPVE(Unit *pTarget)
  16362. +{
  16363. + //if (!m_ai) return RETURN_NO_ACTION_ERROR;
  16364. + //if (!m_bot) return RETURN_NO_ACTION_ERROR;
  16365. +
  16366. + //// DK Attacks: Unholy, Frost & Blood
  16367. +
  16368. + //// damage spells
  16369. + //Unit* pVictim = pTarget->getVictim();
  16370. + //Pet *pet = m_bot->GetPet();
  16371. + //float dist = pTarget->GetCombatReach();
  16372. + //std::ostringstream out;
  16373. +
  16374. + //switch (SpellSequence)
  16375. + //{
  16376. + // case SPELL_DK_UNHOLY:
  16377. + // if (UNHOLY_PRESENCE > 0 && !m_bot->HasAura(UNHOLY_PRESENCE, EFFECT_0) && !m_bot->HasAura(BLOOD_PRESENCE, EFFECT_0) && !m_bot->HasAura(FROST_PRESENCE, EFFECT_0) && m_ai->CastSpell(UNHOLY_PRESENCE, *m_bot))
  16378. + // return RETURN_CONTINUE;
  16379. +
  16380. + // // check for BONE_SHIELD in combat
  16381. + // if (BONE_SHIELD > 0 && !m_bot->HasAura(BONE_SHIELD, EFFECT_0) && !m_bot->HasAura(ARMY_OF_THE_DEAD, EFFECT_0) && m_ai->CastSpell(BONE_SHIELD, *m_bot))
  16382. + // return RETURN_CONTINUE;
  16383. +
  16384. + // if (ARMY_OF_THE_DEAD > 0 && m_ai->GetAttackerCount() >= 5 && LastSpellUnholyDK < 1 && m_ai->CastSpell(ARMY_OF_THE_DEAD) && m_bot->HasAura(ARMY_OF_THE_DEAD, EFFECT_0))
  16385. + // {
  16386. + // out << " summoning Army of the Dead!";
  16387. + // //m_ai->SetIgnoreUpdateTime(7);
  16388. + // SpellSequence = SPELL_DK_FROST;
  16389. + // LastSpellUnholyDK = LastSpellUnholyDK + 1;
  16390. + // return RETURN_CONTINUE;
  16391. + // }
  16392. + // if (PLAGUE_STRIKE > 0 && !pTarget->HasAura(PLAGUE_STRIKE, EFFECT_0) && LastSpellUnholyDK < 2 && m_ai->CastSpell(PLAGUE_STRIKE, *pTarget))
  16393. + // {
  16394. + // out << " Plague Strike";
  16395. + // SpellSequence = SPELL_DK_FROST;
  16396. + // LastSpellUnholyDK = LastSpellUnholyDK + 1;
  16397. + // return RETURN_CONTINUE;
  16398. + // }
  16399. + // if (DEATH_GRIP > 0 && !pTarget->HasAura(DEATH_GRIP, EFFECT_0) && LastSpellUnholyDK < 3 && m_ai->CastSpell(DEATH_GRIP, *pTarget))
  16400. + // {
  16401. + // out << " Death Grip";
  16402. + // SpellSequence = SPELL_DK_FROST;
  16403. + // LastSpellUnholyDK = LastSpellUnholyDK + 1;
  16404. + // return RETURN_CONTINUE;
  16405. + // }
  16406. + // if (DEATH_COIL > 0 && LastSpellUnholyDK < 4 && m_ai->CastSpell(DEATH_COIL, *pTarget))
  16407. + // {
  16408. + // out << " Death Coil";
  16409. + // SpellSequence = SPELL_DK_FROST;
  16410. + // LastSpellUnholyDK = LastSpellUnholyDK + 1;
  16411. + // return RETURN_CONTINUE;
  16412. + // }
  16413. + // if (DEATH_STRIKE > 0 && !pTarget->HasAura(DEATH_STRIKE, EFFECT_0) && LastSpellUnholyDK < 5 && m_ai->CastSpell(DEATH_STRIKE, *pTarget))
  16414. + // {
  16415. + // out << " Death Strike";
  16416. + // SpellSequence = SPELL_DK_FROST;
  16417. + // LastSpellUnholyDK = LastSpellUnholyDK + 1;
  16418. + // return RETURN_CONTINUE;
  16419. + // }
  16420. + // if (UNHOLY_BLIGHT > 0 && !pTarget->HasAura(UNHOLY_BLIGHT, EFFECT_0) && LastSpellUnholyDK < 6 && m_ai->CastSpell(UNHOLY_BLIGHT))
  16421. + // {
  16422. + // out << " Unholy Blight";
  16423. + // SpellSequence = SPELL_DK_FROST;
  16424. + // LastSpellUnholyDK = LastSpellUnholyDK + 1;
  16425. + // return RETURN_CONTINUE;
  16426. + // }
  16427. + // if (SCOURGE_STRIKE > 0 && LastSpellUnholyDK < 7 && m_ai->CastSpell(SCOURGE_STRIKE, *pTarget))
  16428. + // {
  16429. + // out << " Scourge Strike";
  16430. + // SpellSequence = SPELL_DK_FROST;
  16431. + // LastSpellUnholyDK = LastSpellUnholyDK + 1;
  16432. + // return RETURN_CONTINUE;
  16433. + // }
  16434. + // if (DEATH_AND_DECAY > 0 && m_ai->GetAttackerCount() >= 3 && dist <= ATTACK_DISTANCE && !pTarget->HasAura(DEATH_AND_DECAY, EFFECT_0) && LastSpellUnholyDK < 8 && m_ai->CastSpell(DEATH_AND_DECAY))
  16435. + // {
  16436. + // out << " Death and Decay";
  16437. + // //m_ai->SetIgnoreUpdateTime(1);
  16438. + // SpellSequence = SPELL_DK_FROST;
  16439. + // LastSpellUnholyDK = LastSpellUnholyDK + 1;
  16440. + // return RETURN_CONTINUE;
  16441. + // }
  16442. + // if (SUMMON_GARGOYLE > 0 && !m_bot->HasAura(ARMY_OF_THE_DEAD, EFFECT_0) && !pTarget->HasAura(SUMMON_GARGOYLE, EFFECT_0) && LastSpellUnholyDK < 9 && m_ai->CastSpell(SUMMON_GARGOYLE, *pTarget))
  16443. + // {
  16444. + // out << " summoning Gargoyle";
  16445. + // //m_ai->SetIgnoreUpdateTime(2);
  16446. + // SpellSequence = SPELL_DK_FROST;
  16447. + // LastSpellUnholyDK = LastSpellUnholyDK + 1;
  16448. + // return RETURN_CONTINUE;
  16449. + // }
  16450. + // if (CORPSE_EXPLOSION > 0 && dist <= ATTACK_DISTANCE && LastSpellUnholyDK < 10 && m_ai->CastSpell(CORPSE_EXPLOSION, *pTarget))
  16451. + // {
  16452. + // out << " Corpse Explosion";
  16453. + // SpellSequence = SPELL_DK_FROST;
  16454. + // LastSpellUnholyDK = LastSpellUnholyDK + 1;
  16455. + // return RETURN_CONTINUE;
  16456. + // }
  16457. + // if (ANTI_MAGIC_SHELL > 0 && pTarget->IsNonMeleeSpellCasted(true) && !m_bot->HasAura(ANTI_MAGIC_SHELL, EFFECT_0) && LastSpellUnholyDK < 11 && m_ai->CastSpell(ANTI_MAGIC_SHELL, *m_bot))
  16458. + // {
  16459. + // out << " Anti-Magic Shell";
  16460. + // SpellSequence = SPELL_DK_FROST;
  16461. + // LastSpellUnholyDK = LastSpellUnholyDK + 1;
  16462. + // return RETURN_CONTINUE;
  16463. + // }
  16464. + // if (ANTI_MAGIC_ZONE > 0 && pTarget->IsNonMeleeSpellCasted(true) && !m_bot->HasAura(ANTI_MAGIC_SHELL, EFFECT_0) && LastSpellUnholyDK < 12 && m_ai->CastSpell(ANTI_MAGIC_ZONE, *m_bot))
  16465. + // {
  16466. + // out << " Anti-Magic Zone";
  16467. + // SpellSequence = SPELL_DK_FROST;
  16468. + // LastSpellUnholyDK = LastSpellUnholyDK + 1;
  16469. + // return RETURN_CONTINUE;
  16470. + // }
  16471. + // if (!pet && RAISE_DEAD > 0 && !m_bot->HasAura(ARMY_OF_THE_DEAD, EFFECT_0) && LastSpellUnholyDK < 13 && m_ai->CastSpell(RAISE_DEAD))
  16472. + // {
  16473. + // out << " summoning Ghoul";
  16474. + // SpellSequence = SPELL_DK_FROST;
  16475. + // LastSpellUnholyDK = LastSpellUnholyDK + 1;
  16476. + // return RETURN_CONTINUE;
  16477. + // }
  16478. + // if (pet && GHOUL_FRENZY > 0 && pVictim == pet && !pet->HasAura(GHOUL_FRENZY, EFFECT_0) && LastSpellUnholyDK < 14 && m_ai->CastSpell(GHOUL_FRENZY, *pet))
  16479. + // {
  16480. + // out << " casting Ghoul Frenzy on pet";
  16481. + // SpellSequence = SPELL_DK_FROST;
  16482. + // LastSpellUnholyDK = LastSpellUnholyDK + 1;
  16483. + // return RETURN_CONTINUE;
  16484. + // }
  16485. + // if (LastSpellUnholyDK > 15)
  16486. + // {
  16487. + // LastSpellUnholyDK = 0;
  16488. + // SpellSequence = SPELL_DK_FROST;
  16489. + // return RETURN_NO_ACTION_OK; // Not really OK but that's just how the DK rotation works right now
  16490. + // }
  16491. +
  16492. + // LastSpellUnholyDK = 0;
  16493. +
  16494. + // case SPELL_DK_FROST:
  16495. + // if (FROST_PRESENCE > 0 && !m_bot->HasAura(FROST_PRESENCE, EFFECT_0) && !m_bot->HasAura(BLOOD_PRESENCE, EFFECT_0) && !m_bot->HasAura(UNHOLY_PRESENCE, EFFECT_0) && m_ai->CastSpell (FROST_PRESENCE, *m_bot))
  16496. + // return RETURN_CONTINUE;
  16497. +
  16498. + // if (DEATHCHILL > 0)
  16499. + // {
  16500. + // if (!m_bot->HasAura(DEATHCHILL, EFFECT_0) && !m_bot->HasAura(KILLING_MACHINE, EFFECT_0) && m_ai->CastSpell (DEATHCHILL, *m_bot))
  16501. + // return RETURN_CONTINUE;
  16502. + // }
  16503. + // else if (KILLING_MACHINE > 0)
  16504. + // {
  16505. + // if (!m_bot->HasAura(KILLING_MACHINE, EFFECT_0) && !m_bot->HasAura(DEATHCHILL, EFFECT_0) && m_ai->CastSpell (KILLING_MACHINE, *m_bot))
  16506. + // return RETURN_CONTINUE;
  16507. + // }
  16508. +
  16509. + // if (ICY_TOUCH > 0 && !pTarget->HasAura(ICY_TOUCH, EFFECT_0) && LastSpellFrostDK < 1 && m_ai->CastSpell(ICY_TOUCH, *pTarget))
  16510. + // {
  16511. + // out << " Icy Touch";
  16512. + // SpellSequence = SPELL_DK_BLOOD;
  16513. + // LastSpellFrostDK = LastSpellFrostDK + 1;
  16514. + // return RETURN_CONTINUE;
  16515. + // }
  16516. + // if (OBLITERATE > 0 && LastSpellFrostDK < 2 && m_ai->CastSpell(OBLITERATE, *pTarget))
  16517. + // {
  16518. + // out << " Obliterate";
  16519. + // SpellSequence = SPELL_DK_BLOOD;
  16520. + // LastSpellFrostDK = LastSpellFrostDK + 1;
  16521. + // return RETURN_CONTINUE;
  16522. + // }
  16523. + // if (FROST_STRIKE > 0 && LastSpellFrostDK < 3 && m_ai->CastSpell(FROST_STRIKE, *pTarget))
  16524. + // {
  16525. + // out << " Frost Strike";
  16526. + // SpellSequence = SPELL_DK_BLOOD;
  16527. + // LastSpellFrostDK = LastSpellFrostDK + 1;
  16528. + // return RETURN_CONTINUE;
  16529. + // }
  16530. + // if (HOWLING_BLAST > 0 && m_ai->GetAttackerCount() >= 3 && LastSpellFrostDK < 4 && m_ai->CastSpell(HOWLING_BLAST, *pTarget))
  16531. + // {
  16532. + // out << " Howling Blast";
  16533. + // SpellSequence = SPELL_DK_BLOOD;
  16534. + // LastSpellFrostDK = LastSpellFrostDK + 1;
  16535. + // return RETURN_CONTINUE;
  16536. + // }
  16537. + // if (CHAINS_OF_ICE > 0 && !pTarget->HasAura(CHAINS_OF_ICE, EFFECT_0) && LastSpellFrostDK < 5 && m_ai->CastSpell(CHAINS_OF_ICE, *pTarget))
  16538. + // {
  16539. + // out << " Chains of Ice";
  16540. + // SpellSequence = SPELL_DK_BLOOD;
  16541. + // LastSpellFrostDK = LastSpellFrostDK + 1;
  16542. + // return RETURN_CONTINUE;
  16543. + // }
  16544. + // if (RUNE_STRIKE > 0 && LastSpellFrostDK < 6 && m_ai->CastSpell(RUNE_STRIKE, *pTarget))
  16545. + // {
  16546. + // out << " Rune Strike";
  16547. + // SpellSequence = SPELL_DK_BLOOD;
  16548. + // LastSpellFrostDK = LastSpellFrostDK + 1;
  16549. + // return RETURN_CONTINUE;
  16550. + // }
  16551. + // if (ICY_CLUTCH > 0 && !pTarget->HasAura(ICY_CLUTCH, EFFECT_0) && LastSpellFrostDK < 7 && m_ai->CastSpell(ICY_CLUTCH, *pTarget))
  16552. + // {
  16553. + // out << " Icy Clutch";
  16554. + // SpellSequence = SPELL_DK_BLOOD;
  16555. + // LastSpellFrostDK = LastSpellFrostDK + 1;
  16556. + // return RETURN_CONTINUE;
  16557. + // }
  16558. + // if (ICEBOUND_FORTITUDE > 0 && m_bot->GetHealthPct() < 50 && pVictim == m_bot && !m_bot->HasAura(ICEBOUND_FORTITUDE, EFFECT_0) && LastSpellFrostDK < 8 && m_ai->CastSpell(ICEBOUND_FORTITUDE, *m_bot))
  16559. + // {
  16560. + // out << " Icebound Fortitude";
  16561. + // SpellSequence = SPELL_DK_BLOOD;
  16562. + // LastSpellFrostDK = LastSpellFrostDK + 1;
  16563. + // return RETURN_CONTINUE;
  16564. + // }
  16565. + // if (MIND_FREEZE > 0 && pTarget->IsNonMeleeSpellCasted(true) && dist <= ATTACK_DISTANCE && LastSpellFrostDK < 9 && m_ai->CastSpell(MIND_FREEZE, *pTarget))
  16566. + // {
  16567. + // out << " Mind Freeze";
  16568. + // SpellSequence = SPELL_DK_BLOOD;
  16569. + // LastSpellFrostDK = LastSpellFrostDK + 1;
  16570. + // return RETURN_CONTINUE;
  16571. + // }
  16572. + // if (HUNGERING_COLD > 0 && m_ai->GetAttackerCount() >= 3 && dist <= ATTACK_DISTANCE && LastSpellFrostDK < 10 && m_ai->CastSpell(HUNGERING_COLD, *pTarget))
  16573. + // {
  16574. + // out << " Hungering Cold";
  16575. + // SpellSequence = SPELL_DK_BLOOD;
  16576. + // LastSpellFrostDK = LastSpellFrostDK + 1;
  16577. + // return RETURN_CONTINUE;
  16578. + // }
  16579. + // if (EMPOWER_WEAPON > 0 && LastSpellFrostDK < 11 && m_ai->CastSpell(EMPOWER_WEAPON, *m_bot))
  16580. + // {
  16581. + // out << " Empower Rune Weapon";
  16582. + // SpellSequence = SPELL_DK_BLOOD;
  16583. + // LastSpellFrostDK = LastSpellFrostDK + 1;
  16584. + // return RETURN_CONTINUE;
  16585. + // }
  16586. + // if (UNBREAKABLE_ARMOR > 0 && !m_bot->HasAura(UNBREAKABLE_ARMOR, EFFECT_0) && m_bot->GetHealthPct() < 70 && pVictim == m_bot && LastSpellFrostDK < 12 && m_ai->CastSpell(UNBREAKABLE_ARMOR, *m_bot))
  16587. + // {
  16588. + // out << " Unbreakable Armor";
  16589. + // SpellSequence = SPELL_DK_BLOOD;
  16590. + // LastSpellFrostDK = LastSpellFrostDK + 1;
  16591. + // return RETURN_CONTINUE;
  16592. + // }
  16593. + // if (LastSpellFrostDK > 13)
  16594. + // {
  16595. + // LastSpellFrostDK = 0;
  16596. + // SpellSequence = SPELL_DK_BLOOD;
  16597. + // return RETURN_NO_ACTION_OK; // Not really OK, but that's just how the DK rotation works right now
  16598. + // }
  16599. +
  16600. + // LastSpellFrostDK = 0;
  16601. +
  16602. + // case SPELL_DK_BLOOD:
  16603. + // if (BLOOD_PRESENCE > 0 && !m_bot->HasAura(BLOOD_PRESENCE, EFFECT_0) && !m_bot->HasAura(UNHOLY_PRESENCE, EFFECT_0) && !m_bot->HasAura(FROST_PRESENCE, EFFECT_0) && m_ai->CastSpell (BLOOD_PRESENCE, *m_bot))
  16604. + // return RETURN_CONTINUE;
  16605. +
  16606. + // if (MARK_OF_BLOOD > 0 && !pTarget->HasAura(MARK_OF_BLOOD, EFFECT_0) && LastSpellBloodDK < 1 && m_ai->CastSpell(MARK_OF_BLOOD, *pTarget))
  16607. + // {
  16608. + // out << " Mark of Blood";
  16609. + // SpellSequence = SPELL_DK_UNHOLY;
  16610. + // LastSpellBloodDK = LastSpellBloodDK + 1;
  16611. + // return RETURN_CONTINUE;
  16612. + // }
  16613. + // if (BLOOD_STRIKE > 0 && LastSpellBloodDK < 2 && m_ai->CastSpell(BLOOD_STRIKE, *pTarget))
  16614. + // {
  16615. + // out << " Blood Strike";
  16616. + // SpellSequence = SPELL_DK_UNHOLY;
  16617. + // LastSpellBloodDK = LastSpellBloodDK + 1;
  16618. + // return RETURN_CONTINUE;
  16619. + // }
  16620. + // if (PESTILENCE > 0 && m_ai->GetAttackerCount() >= 3 && LastSpellBloodDK < 3 && m_ai->CastSpell(PESTILENCE, *pTarget))
  16621. + // {
  16622. + // out << " Pestilence";
  16623. + // SpellSequence = SPELL_DK_UNHOLY;
  16624. + // LastSpellBloodDK = LastSpellBloodDK + 1;
  16625. + // return RETURN_CONTINUE;
  16626. + // }
  16627. + // if (STRANGULATE > 0 && !pTarget->HasAura(STRANGULATE, EFFECT_0) && LastSpellBloodDK < 4 && m_ai->CastSpell(STRANGULATE, *pTarget))
  16628. + // {
  16629. + // out << " Strangulate";
  16630. + // SpellSequence = SPELL_DK_UNHOLY;
  16631. + // LastSpellBloodDK = LastSpellBloodDK + 1;
  16632. + // return RETURN_CONTINUE;
  16633. + // }
  16634. + // if (BLOOD_BOIL > 0 && m_ai->GetAttackerCount() >= 5 && dist <= ATTACK_DISTANCE && LastSpellBloodDK < 5 && m_ai->CastSpell(BLOOD_BOIL, *pTarget))
  16635. + // {
  16636. + // out << " Blood Boil";
  16637. + // SpellSequence = SPELL_DK_UNHOLY;
  16638. + // LastSpellBloodDK = LastSpellBloodDK + 1;
  16639. + // return RETURN_CONTINUE;
  16640. + // }
  16641. + // if (HEART_STRIKE > 0 && LastSpellBloodDK < 6 && m_ai->CastSpell(HEART_STRIKE, *pTarget))
  16642. + // {
  16643. + // out << " Heart Strike";
  16644. + // SpellSequence = SPELL_DK_UNHOLY;
  16645. + // LastSpellBloodDK = LastSpellBloodDK + 1;
  16646. + // return RETURN_CONTINUE;
  16647. + // }
  16648. + // if (VAMPIRIC_BLOOD > 0 && m_bot->GetHealthPct() < 70 && !m_bot->HasAura(VAMPIRIC_BLOOD, EFFECT_0) && LastSpellBloodDK < 7 && m_ai->CastSpell(VAMPIRIC_BLOOD, *m_bot))
  16649. + // {
  16650. + // out << " Vampiric Blood";
  16651. + // SpellSequence = SPELL_DK_UNHOLY;
  16652. + // LastSpellBloodDK = LastSpellBloodDK + 1;
  16653. + // return RETURN_CONTINUE;
  16654. + // }
  16655. + // if (RUNE_TAP > 0 && m_bot->GetHealthPct() < 70 && !m_bot->HasAura(VAMPIRIC_BLOOD, EFFECT_0) && LastSpellBloodDK < 8 && m_ai->CastSpell(RUNE_TAP, *m_bot))
  16656. + // {
  16657. + // out << " Rune Tap";
  16658. + // SpellSequence = SPELL_DK_UNHOLY;
  16659. + // LastSpellBloodDK = LastSpellBloodDK + 1;
  16660. + // return RETURN_CONTINUE;
  16661. + // }
  16662. + // if (HYSTERIA > 0 && m_bot->GetHealthPct() > 25 && !m_bot->HasAura(HYSTERIA, EFFECT_0) && LastSpellBloodDK < 9 && m_ai->CastSpell(HYSTERIA, *m_bot))
  16663. + // {
  16664. + // out << " Hysteria";
  16665. + // SpellSequence = SPELL_DK_UNHOLY;
  16666. + // LastSpellBloodDK = LastSpellBloodDK + 1;
  16667. + // return RETURN_CONTINUE;
  16668. + // }
  16669. + // if (DANCING_WEAPON > 0 && !m_bot->HasAura(DANCING_WEAPON, EFFECT_0) && LastSpellBloodDK < 10 && m_ai->CastSpell(DANCING_WEAPON, *pTarget))
  16670. + // {
  16671. + // out << " summoning Dancing Rune Weapon";
  16672. + // SpellSequence = SPELL_DK_UNHOLY;
  16673. + // LastSpellBloodDK = LastSpellBloodDK + 1;
  16674. + // return RETURN_CONTINUE;
  16675. + // }
  16676. + // if (DARK_COMMAND > 0 && m_bot->GetHealthPct() > 50 && pVictim != m_bot && !pTarget->HasAura(DARK_COMMAND, EFFECT_0) && LastSpellBloodDK < 11 && m_ai->CastSpell(DARK_COMMAND, *pTarget))
  16677. + // {
  16678. + // out << " Dark Command";
  16679. + // SpellSequence = SPELL_DK_UNHOLY;
  16680. + // LastSpellBloodDK = LastSpellBloodDK + 1;
  16681. + // return RETURN_CONTINUE;
  16682. + // }
  16683. + // if (pet && DEATH_PACT > 0 && m_bot->GetHealthPct() < 50 && LastSpellBloodDK < 12 && m_ai->CastSpell(DEATH_PACT, *pet))
  16684. + // {
  16685. + // out << " Death Pact (sacrifice pet)";
  16686. + // SpellSequence = SPELL_DK_UNHOLY;
  16687. + // LastSpellBloodDK = LastSpellBloodDK + 1;
  16688. + // return RETURN_CONTINUE;
  16689. + // }
  16690. + // if (LastSpellBloodDK > 13)
  16691. + // {
  16692. + // LastSpellBloodDK = 0;
  16693. + // SpellSequence = SPELL_DK_UNHOLY;
  16694. + // return RETURN_NO_ACTION_OK; // Not really OK but that's just how DK rotation works right now
  16695. + // }
  16696. + //}
  16697. + //if (m_ai->GetManager()->m_confDebugWhisper)
  16698. + // m_ai->TellMaster(out.str().c_str());
  16699. +
  16700. + return RETURN_NO_ACTION_UNKNOWN;
  16701. +} // end DoNextCombatManeuver
  16702. +
  16703. +CombatManeuverReturns PlayerbotDeathKnightAI::DoNextCombatManeuverPVP(Unit* pTarget)
  16704. +{
  16705. + //if (m_ai->CastSpell(PLAGUE_STRIKE))
  16706. + // return RETURN_CONTINUE;
  16707. +
  16708. + return DoNextCombatManeuverPVE(pTarget); // TODO: bad idea perhaps, but better than the alternative
  16709. +}
  16710. +
  16711. +void PlayerbotDeathKnightAI::DoNonCombatActions()
  16712. +{
  16713. + //if (!m_ai) return;
  16714. + //if (!m_bot) return;
  16715. +
  16716. + //SpellSequence = SPELL_DK_UNHOLY;
  16717. +
  16718. + //// buff master with HORN_OF_WINTER
  16719. + //if (HORN_OF_WINTER > 0)
  16720. + // (!GetMaster()->HasAura(HORN_OF_WINTER, EFFECT_0) && m_ai->CastSpell (HORN_OF_WINTER, *GetMaster()));
  16721. +
  16722. + //// hp check
  16723. + //if (m_bot->getStandState() != UNIT_STAND_STATE_STAND)
  16724. + // m_bot->SetStandState(UNIT_STAND_STATE_STAND);
  16725. +
  16726. + //if (EatDrinkBandage(false))
  16727. + // return;
  16728. +} // end DoNonCombatActions
  16729. +
  16730. +// Match up with "Pull()" below
  16731. +bool PlayerbotDeathKnightAI::CanPull()
  16732. +{
  16733. + if (DEATH_GRIP && !me->HasSpellCooldown(DEATH_GRIP))
  16734. + return true;
  16735. +
  16736. + return false;
  16737. +}
  16738. +
  16739. +// Match up with "CanPull()" above
  16740. +bool PlayerbotDeathKnightAI::Pull()
  16741. +{
  16742. + //if (DEATH_GRIP && m_ai->CastSpell(DEATH_GRIP))
  16743. + // return true;
  16744. +
  16745. + return false;
  16746. +}
  16747. diff --git a/src/server/game/AI/PlayerBots/bp_dk_ai.h b/src/server/game/AI/PlayerBots/bp_dk_ai.h
  16748. new file mode 100644
  16749. index 0000000..ba7f042
  16750. --- /dev/null
  16751. +++ b/src/server/game/AI/PlayerBots/bp_dk_ai.h
  16752. @@ -0,0 +1,159 @@
  16753. +#ifndef _PLAYERDEATHKNIGHTAI_H
  16754. +#define _PLAYERDEATHKNIGHTAI_H
  16755. +
  16756. +#include "bp_cai.h"
  16757. +
  16758. +enum
  16759. +{
  16760. + SPELL_DK_UNHOLY,
  16761. + SPELL_DK_FROST,
  16762. + SPELL_DK_BLOOD
  16763. +};
  16764. +
  16765. +enum DeathKnightSpells
  16766. +{
  16767. + ANTI_MAGIC_SHELL_1 = 48707,
  16768. + ANTI_MAGIC_ZONE_1 = 51052,
  16769. + ARMY_OF_THE_DEAD_1 = 42650,
  16770. + BLOOD_BOIL_1 = 48721,
  16771. + BLOOD_PRESENCE_1 = 48266,
  16772. + BLOOD_STRIKE_1 = 45902,
  16773. + BLOOD_TAP_1 = 45529,
  16774. + BONE_SHIELD_1 = 49222,
  16775. + CHAINS_OF_ICE_1 = 45524,
  16776. + CORPSE_EXPLOSION_1 = 49158,
  16777. + DANCING_RUNE_WEAPON_1 = 49028,
  16778. + DARK_COMMAND_1 = 56222,
  16779. + DEATH_AND_DECAY_1 = 43265,
  16780. + DEATH_COIL_DEATH_KNIGHT_1 = 47541,
  16781. + DEATH_GRIP_1 = 49576,
  16782. + DEATH_PACT_1 = 48743,
  16783. + DEATH_STRIKE_1 = 49998,
  16784. + DEATHCHILL_1 = 49796,
  16785. + EMPOWER_RUNE_WEAPON_1 = 47568,
  16786. + FROST_PRESENCE_1 = 48263,
  16787. + FROST_STRIKE_1 = 49143,
  16788. + GHOUL_FRENZY_1 = 63560,
  16789. + HEART_STRIKE_1 = 55050,
  16790. + HORN_OF_WINTER_1 = 57330,
  16791. + HOWLING_BLAST_1 = 49184,
  16792. + HUNGERING_COLD_1 = 49203,
  16793. + HYSTERIA_1 = 49016,
  16794. + ICEBOUND_FORTITUDE_1 = 48792,
  16795. + ICY_TOUCH_1 = 45477,
  16796. + LICHBORNE_1 = 49039,
  16797. + MARK_OF_BLOOD_1 = 49005,
  16798. + MIND_FREEZE_1 = 47528,
  16799. + OBLITERATE_1 = 49020,
  16800. + PATH_OF_FROST_1 = 3714,
  16801. + PESTILENCE_1 = 50842,
  16802. + PLAGUE_STRIKE_1 = 45462,
  16803. + RAISE_ALLY_1 = 61999,
  16804. + RAISE_DEAD_1 = 46584,
  16805. + RUNE_STRIKE_1 = 56815,
  16806. + RUNE_TAP_1 = 48982,
  16807. + SCOURGE_STRIKE_1 = 55090,
  16808. + STRANGULATE_1 = 47476,
  16809. + SUMMON_GARGOYLE_1 = 49206,
  16810. + UNBREAKABLE_ARMOR_1 = 51271,
  16811. + UNHOLY_PRESENCE_1 = 48265,
  16812. + VAMPIRIC_BLOOD_1 = 55233,
  16813. + IMPROVED_ICY_TALONS_1 = 55610
  16814. +};
  16815. +//class Player;
  16816. +
  16817. +class PlayerbotDeathKnightAI : PlayerbotClassAI
  16818. +{
  16819. +public:
  16820. + PlayerbotDeathKnightAI(Player * const master, Player * const bot, PlayerbotAI * const ai);
  16821. + virtual ~PlayerbotDeathKnightAI();
  16822. +
  16823. + // all combat actions go here
  16824. + CombatManeuverReturns DoFirstCombatManeuver(Unit* pTarget);
  16825. + CombatManeuverReturns DoNextCombatManeuver(Unit* pTarget);
  16826. + bool Pull();
  16827. +
  16828. + // all non combat actions go here, ex buffs, heals, rezzes
  16829. + void DoNonCombatActions();
  16830. +
  16831. + // buff a specific player, usually a real PC who is not in group
  16832. + //void BuffPlayer(Player *target);
  16833. +
  16834. + // Utility Functions
  16835. + bool CanPull();
  16836. +
  16837. +private:
  16838. + CombatManeuverReturns DoFirstCombatManeuverPVE(Unit* pTarget);
  16839. + CombatManeuverReturns DoNextCombatManeuverPVE(Unit* pTarget);
  16840. + CombatManeuverReturns DoFirstCombatManeuverPVP(Unit* pTarget);
  16841. + CombatManeuverReturns DoNextCombatManeuverPVP(Unit* pTarget);
  16842. +
  16843. + // Unholy
  16844. + uint32 BONE_SHIELD,
  16845. + PLAGUE_STRIKE,
  16846. + DEATH_GRIP,
  16847. + DEATH_COIL,
  16848. + DEATH_STRIKE,
  16849. + UNHOLY_BLIGHT,
  16850. + SCOURGE_STRIKE,
  16851. + DEATH_AND_DECAY,
  16852. + UNHOLY_PRESENCE,
  16853. + RAISE_DEAD,
  16854. + ARMY_OF_THE_DEAD,
  16855. + SUMMON_GARGOYLE,
  16856. + ANTI_MAGIC_SHELL,
  16857. + ANTI_MAGIC_ZONE,
  16858. + GHOUL_FRENZY,
  16859. + CORPSE_EXPLOSION;
  16860. +
  16861. + // Frost
  16862. + uint32 ICY_TOUCH,
  16863. + OBLITERATE,
  16864. + HOWLING_BLAST,
  16865. + FROST_STRIKE,
  16866. + CHAINS_OF_ICE,
  16867. + RUNE_STRIKE,
  16868. + ICY_CLUTCH,
  16869. + HORN_OF_WINTER,
  16870. + KILLING_MACHINE,
  16871. + FROST_PRESENCE,
  16872. + DEATHCHILL,
  16873. + ICEBOUND_FORTITUDE,
  16874. + MIND_FREEZE,
  16875. + EMPOWER_WEAPON,
  16876. + HUNGERING_COLD,
  16877. + UNBREAKABLE_ARMOR,
  16878. + IMPROVED_ICY_TALONS;
  16879. +
  16880. + // Blood
  16881. + uint32 BLOOD_STRIKE,
  16882. + PESTILENCE,
  16883. + STRANGULATE,
  16884. + BLOOD_BOIL,
  16885. + HEART_STRIKE,
  16886. + MARK_OF_BLOOD,
  16887. + BLOOD_PRESENCE,
  16888. + RUNE_TAP,
  16889. + VAMPIRIC_BLOOD,
  16890. + DEATH_PACT,
  16891. + DEATH_RUNE_MASTERY,
  16892. + HYSTERIA,
  16893. + DANCING_WEAPON,
  16894. + DARK_COMMAND;
  16895. +
  16896. + // racial
  16897. + uint32 ARCANE_TORRENT,
  16898. + GIFT_OF_THE_NAARU,
  16899. + STONEFORM,
  16900. + ESCAPE_ARTIST,
  16901. + EVERY_MAN_FOR_HIMSELF,
  16902. + SHADOWMELD,
  16903. + BLOOD_FURY,
  16904. + WAR_STOMP,
  16905. + BERSERKING,
  16906. + WILL_OF_THE_FORSAKEN;
  16907. +
  16908. + uint32 SpellSequence, LastSpellUnholyDK, LastSpellFrostDK, LastSpellBloodDK;
  16909. +};
  16910. +
  16911. +#endif
  16912. diff --git a/src/server/game/AI/PlayerBots/bp_dru_ai.cpp b/src/server/game/AI/PlayerBots/bp_dru_ai.cpp
  16913. new file mode 100644
  16914. index 0000000..f4e43db
  16915. --- /dev/null
  16916. +++ b/src/server/game/AI/PlayerBots/bp_dru_ai.cpp
  16917. @@ -0,0 +1,787 @@
  16918. +/*
  16919. + Name : PlayerbotDruidAI.cpp
  16920. + Complete: maybe around 33%
  16921. + Authors : rrtn, Natsukawa
  16922. + Version : 0.42
  16923. + */
  16924. +#include "bp_dru_ai.h"
  16925. +#include "SpellAuras.h"
  16926. +#include "bp_ai.h"
  16927. +#include "Player.h"
  16928. +#include "Pet.h"
  16929. +
  16930. +PlayerbotDruidAI::PlayerbotDruidAI(Player* const master, Player* const bot, PlayerbotAI* const ai) : PlayerbotClassAI(master, bot, ai)
  16931. +{
  16932. + MOONFIRE = PlayerbotAI::InitSpell(me, MOONFIRE_1); // attacks
  16933. + STARFIRE = PlayerbotAI::InitSpell(me, STARFIRE_1);
  16934. + STARFALL = PlayerbotAI::InitSpell(me, STARFALL_1);
  16935. + WRATH = PlayerbotAI::InitSpell(me, WRATH_1);
  16936. + ROOTS = PlayerbotAI::InitSpell(me, ENTANGLING_ROOTS_1);
  16937. + INSECT_SWARM = PlayerbotAI::InitSpell(me, INSECT_SWARM_1);
  16938. + FORCE_OF_NATURE = PlayerbotAI::InitSpell(me, FORCE_OF_NATURE_1);
  16939. + HURRICANE = PlayerbotAI::InitSpell(me, HURRICANE_1);
  16940. + MARK_OF_THE_WILD = PlayerbotAI::InitSpell(me, MARK_OF_THE_WILD_1); // buffs
  16941. + GIFT_OF_THE_WILD = PlayerbotAI::InitSpell(me, GIFT_OF_THE_WILD_1);
  16942. + THORNS = PlayerbotAI::InitSpell(me, THORNS_1);
  16943. + BARKSKIN = PlayerbotAI::InitSpell(me, BARKSKIN_1);
  16944. + INNERVATE = PlayerbotAI::InitSpell(me, INNERVATE_1);
  16945. + FAERIE_FIRE = PlayerbotAI::InitSpell(me, FAERIE_FIRE_1); // debuffs
  16946. + FAERIE_FIRE_FERAL = PlayerbotAI::InitSpell(me, FAERIE_FIRE_FERAL_1);
  16947. + REJUVENATION = PlayerbotAI::InitSpell(me, REJUVENATION_1); // heals
  16948. + REGROWTH = PlayerbotAI::InitSpell(me, REGROWTH_1);
  16949. + WILD_GROWTH = PlayerbotAI::InitSpell(me, WILD_GROWTH_1);
  16950. + LIFEBLOOM = PlayerbotAI::InitSpell(me, LIFEBLOOM_1);
  16951. + NOURISH = PlayerbotAI::InitSpell(me, NOURISH_1);
  16952. + HEALING_TOUCH = PlayerbotAI::InitSpell(me, HEALING_TOUCH_1);
  16953. + SWIFTMEND = PlayerbotAI::InitSpell(me, SWIFTMEND_1);
  16954. + TRANQUILITY = PlayerbotAI::InitSpell(me, TRANQUILITY_1);
  16955. + REVIVE = PlayerbotAI::InitSpell(me, REVIVE_1);
  16956. + REBIRTH = PlayerbotAI::InitSpell(me, REBIRTH_1);
  16957. + REMOVE_CURSE = PlayerbotAI::InitSpell(me, REMOVE_CURSE_DRUID_1);
  16958. + ABOLISH_POISON = PlayerbotAI::InitSpell(me, ABOLISH_POISON_1);
  16959. + // Druid Forms
  16960. + MOONKIN_FORM = PlayerbotAI::InitSpell(me, MOONKIN_FORM_1);
  16961. + DIRE_BEAR_FORM = PlayerbotAI::InitSpell(me, DIRE_BEAR_FORM_1);
  16962. + BEAR_FORM = PlayerbotAI::InitSpell(me, BEAR_FORM_1);
  16963. + CAT_FORM = PlayerbotAI::InitSpell(me, CAT_FORM_1);
  16964. + TREE_OF_LIFE = PlayerbotAI::InitSpell(me, TREE_OF_LIFE_1);
  16965. + TRAVEL_FORM = PlayerbotAI::InitSpell(me, TRAVEL_FORM_1);
  16966. + // Cat Attack type's
  16967. + RAKE = PlayerbotAI::InitSpell(me, RAKE_1);
  16968. + CLAW = PlayerbotAI::InitSpell(me, CLAW_1); // 45
  16969. + COWER = PlayerbotAI::InitSpell(me, COWER_1); // 20
  16970. + MANGLE = PlayerbotAI::InitSpell(me, MANGLE_1); // 45
  16971. + TIGERS_FURY = PlayerbotAI::InitSpell(me, TIGERS_FURY_1);
  16972. + MANGLE_CAT = PlayerbotAI::InitSpell(me, MANGLE_CAT_1); //40
  16973. + // Cat Finishing Move's
  16974. + RIP = PlayerbotAI::InitSpell(me, RIP_1); // 30
  16975. + FEROCIOUS_BITE = PlayerbotAI::InitSpell(me, FEROCIOUS_BITE_1); // 35
  16976. + MAIM = PlayerbotAI::InitSpell(me, MAIM_1); // 35
  16977. + SAVAGE_ROAR = PlayerbotAI::InitSpell(me, SAVAGE_ROAR_1); //25
  16978. + // Bear/Dire Bear Attacks & Buffs
  16979. + BASH = PlayerbotAI::InitSpell(me, BASH_1);
  16980. + MAUL = PlayerbotAI::InitSpell(me, MAUL_1); // 15
  16981. + SWIPE = PlayerbotAI::InitSpell(me, SWIPE_BEAR_1); // 20
  16982. + DEMORALIZING_ROAR = PlayerbotAI::InitSpell(me, DEMORALIZING_ROAR_1); // 10
  16983. + CHALLENGING_ROAR = PlayerbotAI::InitSpell(me, CHALLENGING_ROAR_1);
  16984. + ENRAGE = PlayerbotAI::InitSpell(me, ENRAGE_1);
  16985. + GROWL = PlayerbotAI::InitSpell(me, GROWL_1);
  16986. + MANGLE_BEAR = PlayerbotAI::InitSpell(me, MANGLE_BEAR_1);
  16987. + LACERATE = PlayerbotAI::InitSpell(me, LACERATE_1);
  16988. +
  16989. + //RECENTLY_BANDAGED = 11196; // first aid check
  16990. +
  16991. + // racial
  16992. + SHADOWMELD = PlayerbotAI::InitSpell(me, SHADOWMELD_ALL);
  16993. + WAR_STOMP = PlayerbotAI::InitSpell(me, WAR_STOMP_ALL); // tauren
  16994. +
  16995. + //Procs
  16996. + ECLIPSE = PlayerbotAI::InitSpell(me, ECLIPSE_1);
  16997. + ECLIPSE_SOLAR = PlayerbotAI::InitSpell(me, ECLIPSE_SOLAR_1);
  16998. + ECLIPSE_LUNAR = PlayerbotAI::InitSpell(me, ECLIPSE_LUNAR_1);
  16999. +}
  17000. +
  17001. +PlayerbotDruidAI::~PlayerbotDruidAI() {}
  17002. +
  17003. +CombatManeuverReturns PlayerbotDruidAI::DoFirstCombatManeuver(Unit* pTarget)
  17004. +{
  17005. + //// There are NPCs in BGs and Open World PvP, so don't filter this on PvP scenarios (of course if PvP targets anyone but tank, all bets are off anyway)
  17006. + //// Wait until the tank says so, until any non-tank gains aggro or X seconds - whichever is shortest
  17007. + //if (m_ai->GetCombatOrder() & PlayerbotAI::ORDERS_TEMP_WAIT_TANKAGGRO)
  17008. + //{
  17009. + // if (m_WaitUntil > m_ai->CurrentTime() && m_ai->GroupTankHoldsAggro())
  17010. + // {
  17011. + // if (PlayerbotAI::ORDERS_TANK & m_ai->GetCombatOrder())
  17012. + // {
  17013. + // if (pTarget->GetCombatReach() <= ATTACK_DISTANCE)
  17014. + // {
  17015. + // // Set everyone's UpdateAI() waiting to 2 seconds
  17016. + // m_ai->SetGroupIgnoreUpdateTime(2);
  17017. + // // Clear their TEMP_WAIT_TANKAGGRO flag
  17018. + // m_ai->ClearGroupCombatOrder(PlayerbotAI::ORDERS_TEMP_WAIT_TANKAGGRO);
  17019. + // // Start attacking, force target on current target
  17020. + // m_ai->Attack(m_ai->GetCurrentTarget());
  17021. +
  17022. + // // While everyone else is waiting 2 second, we need to build up aggro, so don't return
  17023. + // }
  17024. + // else
  17025. + // {
  17026. + // // TODO: add check if target is ranged
  17027. + // return RETURN_NO_ACTION_OK; // wait for target to get nearer
  17028. + // }
  17029. + // }
  17030. + // else if (PlayerbotAI::ORDERS_HEAL & m_ai->GetCombatOrder())
  17031. + // return _DoNextPVECombatManeuverHeal();
  17032. + // else
  17033. + // return RETURN_NO_ACTION_OK; // wait it out
  17034. + // }
  17035. + // else
  17036. + // {
  17037. + // m_ai->ClearGroupCombatOrder(PlayerbotAI::ORDERS_TEMP_WAIT_TANKAGGRO);
  17038. + // }
  17039. + //}
  17040. +
  17041. + //if (m_ai->GetCombatOrder() & PlayerbotAI::ORDERS_TEMP_WAIT_OOC)
  17042. + //{
  17043. + // if (m_WaitUntil > m_ai->CurrentTime() && !m_ai->IsGroupInCombat())
  17044. + // return RETURN_NO_ACTION_OK; // wait it out
  17045. + // else
  17046. + // m_ai->ClearGroupCombatOrder(PlayerbotAI::ORDERS_TEMP_WAIT_OOC);
  17047. + //}
  17048. +
  17049. + //switch (m_ai->GetScenarioType())
  17050. + //{
  17051. + // case PlayerbotAI::SCENARIO_PVP_DUEL:
  17052. + // case PlayerbotAI::SCENARIO_PVP_BG:
  17053. + // case PlayerbotAI::SCENARIO_PVP_ARENA:
  17054. + // case PlayerbotAI::SCENARIO_PVP_OPENWORLD:
  17055. + // return DoFirstCombatManeuverPVP(pTarget);
  17056. +
  17057. + // case PlayerbotAI::SCENARIO_PVE:
  17058. + // case PlayerbotAI::SCENARIO_PVE_ELITE:
  17059. + // case PlayerbotAI::SCENARIO_PVE_RAID:
  17060. + // default:
  17061. + // return DoFirstCombatManeuverPVE(pTarget);
  17062. + //}
  17063. +
  17064. + return RETURN_NO_ACTION_ERROR;
  17065. +}
  17066. +
  17067. +CombatManeuverReturns PlayerbotDruidAI::DoFirstCombatManeuverPVE(Unit* /*pTarget*/)
  17068. +{
  17069. + return RETURN_NO_ACTION_OK;
  17070. +}
  17071. +
  17072. +CombatManeuverReturns PlayerbotDruidAI::DoFirstCombatManeuverPVP(Unit* /*pTarget*/)
  17073. +{
  17074. + return RETURN_NO_ACTION_OK;
  17075. +}
  17076. +
  17077. +CombatManeuverReturns PlayerbotDruidAI::DoNextCombatManeuver(Unit* pTarget)
  17078. +{
  17079. + //switch (m_ai->GetScenarioType())
  17080. + //{
  17081. + // case PlayerbotAI::SCENARIO_PVP_DUEL:
  17082. + // case PlayerbotAI::SCENARIO_PVP_BG:
  17083. + // case PlayerbotAI::SCENARIO_PVP_ARENA:
  17084. + // case PlayerbotAI::SCENARIO_PVP_OPENWORLD:
  17085. + // return DoNextCombatManeuverPVP(pTarget);
  17086. +
  17087. + // case PlayerbotAI::SCENARIO_PVE:
  17088. + // case PlayerbotAI::SCENARIO_PVE_ELITE:
  17089. + // case PlayerbotAI::SCENARIO_PVE_RAID:
  17090. + // default:
  17091. + // return DoNextCombatManeuverPVE(pTarget);
  17092. + //}
  17093. +
  17094. + return RETURN_NO_ACTION_ERROR;
  17095. +}
  17096. +
  17097. +CombatManeuverReturns PlayerbotDruidAI::DoNextCombatManeuverPVE(Unit* pTarget)
  17098. +{
  17099. + //if (!m_ai) return RETURN_NO_ACTION_ERROR;
  17100. + //if (!m_bot) return RETURN_NO_ACTION_ERROR;
  17101. +
  17102. + ////uint32 masterHP = GetMaster()->GetHealth() * 100 / GetMaster()->GetMaxHealth();
  17103. +
  17104. + //uint32 spec = m_bot->GetSpec();
  17105. + //if (spec == 0) // default to spellcasting or healing for healer
  17106. + // spec = (PlayerbotAI::ORDERS_HEAL & m_ai->GetCombatOrder() ? DRUID_SPEC_RESTORATION : DRUID_SPEC_BALANCE);
  17107. +
  17108. + //// Make sure healer stays put, don't even melee (aggro) if in range.
  17109. + //if (m_ai->IsHealer() && m_ai->GetCombatStyle() != PlayerbotAI::COMBAT_RANGED)
  17110. + // m_ai->SetCombatStyle(PlayerbotAI::COMBAT_RANGED);
  17111. + //else if (!m_ai->IsHealer() && m_ai->GetCombatStyle() != PlayerbotAI::COMBAT_MELEE)
  17112. + // m_ai->SetCombatStyle(PlayerbotAI::COMBAT_MELEE);
  17113. +
  17114. + ////Unit* pVictim = pTarget->getVictim();
  17115. + //uint32 BEAR = (DIRE_BEAR_FORM > 0 ? DIRE_BEAR_FORM : BEAR_FORM);
  17116. +
  17117. + //// TODO: do something to allow emergency heals for non-healers?
  17118. + //switch (CheckForms())
  17119. + //{
  17120. + // case RETURN_OK_SHIFTING:
  17121. + // return RETURN_CONTINUE;
  17122. +
  17123. + // case RETURN_FAIL:
  17124. + // case RETURN_OK_CANNOTSHIFT:
  17125. + // if (spec == DRUID_SPEC_FERAL)
  17126. + // spec = DRUID_SPEC_BALANCE; // Can't shift, force spellcasting
  17127. + // break; // rest functions without form
  17128. +
  17129. + // //case RETURN_OK_NOCHANGE: // great!
  17130. + // //case RETURN_FAIL_WAITINGONSELFBUFF: // This is war dammit! No time for silly buffs during combat...
  17131. + // default:
  17132. + // break;
  17133. + //}
  17134. +
  17135. + ////Used to determine if this bot is highest on threat
  17136. + //Unit *newTarget = m_ai->FindAttacker((PlayerbotAI::ATTACKERINFOTYPE) (PlayerbotAI::AIT_VICTIMSELF | PlayerbotAI::AIT_HIGHESTTHREAT), m_bot);
  17137. + //if (newTarget) // TODO: && party has a tank
  17138. + //{
  17139. + // if (HealPlayer(m_bot) == RETURN_CONTINUE)
  17140. + // return RETURN_CONTINUE;
  17141. +
  17142. + // // TODO: Heal tank
  17143. +
  17144. + // // We have aggro, don't need to heal self or tank, wait for aggro to subside
  17145. + // //if (m_ai->IsHealer()) // Commented out: not necessary because of below. Leave code here in case below ever changes.
  17146. + // // return RETURN_NO_ACTION_OK;
  17147. +
  17148. + // // We have no shoot spell; Assume auto-attack is on
  17149. + // return RETURN_NO_ACTION_OK;
  17150. + //}
  17151. +
  17152. + //if (m_ai->IsHealer())
  17153. + // return _DoNextPVECombatManeuverHeal();
  17154. +
  17155. + //switch (spec)
  17156. + //{
  17157. + // case DRUID_SPEC_FERAL:
  17158. + // if (BEAR > 0 && m_bot->HasAura(BEAR))
  17159. + // return _DoNextPVECombatManeuverBear(pTarget);
  17160. + // if (CAT_FORM > 0 && m_bot->HasAura(CAT_FORM))
  17161. + // return _DoNextPVECombatManeuverCat(pTarget);
  17162. + // // NO break - failover to DRUID_SPEC_BALANCE
  17163. +
  17164. + // case DRUID_SPEC_RESTORATION: // There is no Resto DAMAGE rotation. If you insist, go Balance...
  17165. + // case DRUID_SPEC_BALANCE:
  17166. + // if (m_bot->HasAura(BEAR) || m_bot->HasAura(CAT_FORM) || m_bot->HasAura(TREE_OF_LIFE))
  17167. + // return RETURN_NO_ACTION_UNKNOWN; // Didn't shift out of inappropriate form
  17168. +
  17169. + // return _DoNextPVECombatManeuverSpellDPS(pTarget);
  17170. +
  17171. + // /*if (BASH > 0 && !pTarget->HasAura(BASH, EFFECT_0) && DruidSpellCombat < 5 && CastSpell(BASH, pTarget))
  17172. + // return RETURN_CONTINUE;
  17173. + // if (CHALLENGING_ROAR > 0 && pVictim != m_bot && !pTarget->HasAura(CHALLENGING_ROAR, EFFECT_0) && !pTarget->HasAura(GROWL, EFFECT_0) && CastSpell(CHALLENGING_ROAR, pTarget))
  17174. + // return RETURN_CONTINUE;
  17175. + // if (ROOTS > 0 && !pTarget->HasAura(ROOTS, EFFECT_0) && CastSpell(ROOTS, pTarget))
  17176. + // return RETURN_CONTINUE;
  17177. + // if (HURRICANE > 0 && m_ai->GetAttackerCount() >= 5 && CastSpell(HURRICANE, pTarget))
  17178. + // {
  17179. + // //m_ai->SetIgnoreUpdateTime(10);
  17180. + // return RETURN_CONTINUE;
  17181. + // }
  17182. + // if (STARFALL > 0 && !m_bot->HasAura(STARFALL, EFFECT_0) && m_ai->GetAttackerCount() >= 3 && CastSpell(STARFALL, pTarget))
  17183. + // return RETURN_CONTINUE;
  17184. + // if (BARKSKIN > 0 && pVictim == m_bot && m_bot->GetHealthPct() < 75 && !m_bot->HasAura(BARKSKIN, EFFECT_0) && CastSpell(BARKSKIN, m_bot))
  17185. + // return RETURN_CONTINUE;
  17186. + // if (INNERVATE > 0 && !m_bot->HasAura(INNERVATE, EFFECT_0) && CastSpell(INNERVATE, m_bot))
  17187. + // return RETURN_CONTINUE;
  17188. + // */
  17189. + //}
  17190. +
  17191. + return RETURN_NO_ACTION_UNKNOWN;
  17192. +} // end DoNextCombatManeuver
  17193. +
  17194. +CombatManeuverReturns PlayerbotDruidAI::DoNextCombatManeuverPVP(Unit* pTarget)
  17195. +{
  17196. + //if (m_ai->CastSpell(MOONFIRE))
  17197. + // return RETURN_CONTINUE;
  17198. +
  17199. + return DoNextCombatManeuverPVE(pTarget); // TODO: bad idea perhaps, but better than the alternative
  17200. +}
  17201. +
  17202. +CombatManeuverReturns PlayerbotDruidAI::_DoNextPVECombatManeuverBear(Unit* pTarget)
  17203. +{
  17204. + //if (!m_ai) return RETURN_NO_ACTION_ERROR;
  17205. + //if (!m_bot) return RETURN_NO_ACTION_ERROR;
  17206. +
  17207. + //if (!m_bot->HasAura( (DIRE_BEAR_FORM > 0 ? DIRE_BEAR_FORM : BEAR_FORM) )) return RETURN_NO_ACTION_ERROR;
  17208. +
  17209. + //// Used to determine if this bot is highest on threat
  17210. + //Unit* newTarget = m_ai->FindAttacker((PlayerbotAI::ATTACKERINFOTYPE) (PlayerbotAI::AIT_VICTIMSELF | PlayerbotAI::AIT_HIGHESTTHREAT), m_bot);
  17211. + //Unit* pVictim = pTarget->getVictim();
  17212. +
  17213. + //// Face enemy, make sure you're attacking
  17214. + //if (!m_bot->HasInArc(M_PI, pTarget))
  17215. + //{
  17216. + // m_bot->SetFacingTo(m_bot->GetAngle(pTarget));
  17217. + // if (pVictim)
  17218. + // pVictim->Attack(pTarget, true);
  17219. + //}
  17220. +
  17221. + //if (PlayerbotAI::ORDERS_TANK & m_ai->GetCombatOrder() && !newTarget && GROWL > 0 && !m_bot->HasSpellCooldown(GROWL))
  17222. + // if (CastSpell(GROWL, pTarget))
  17223. + // return RETURN_CONTINUE;
  17224. +
  17225. + //if (FAERIE_FIRE_FERAL > 0 && !pTarget->HasAura(FAERIE_FIRE_FERAL, EFFECT_0))
  17226. + // if (CastSpell(FAERIE_FIRE_FERAL, pTarget))
  17227. + // return RETURN_CONTINUE;
  17228. +
  17229. + //if (SWIPE > 0 && m_ai->GetAttackerCount() >= 2 && CastSpell(SWIPE, pTarget))
  17230. + // return RETURN_CONTINUE;
  17231. +
  17232. + //if (ENRAGE > 0 && !m_bot->HasSpellCooldown(ENRAGE) && CastSpell(ENRAGE, m_bot))
  17233. + // return RETURN_CONTINUE;
  17234. +
  17235. + //if (DEMORALIZING_ROAR > 0 && !pTarget->HasAura(DEMORALIZING_ROAR, EFFECT_0) && CastSpell(DEMORALIZING_ROAR, pTarget))
  17236. + // return RETURN_CONTINUE;
  17237. +
  17238. + //if (MANGLE_BEAR > 0 && !pTarget->HasAura(MANGLE_BEAR) && CastSpell(MANGLE_BEAR, pTarget))
  17239. + // return RETURN_CONTINUE;
  17240. +
  17241. + //if (LACERATE > 0 && !pTarget->HasAura(LACERATE, EFFECT_0) && CastSpell(LACERATE, pTarget))
  17242. + // return RETURN_CONTINUE;
  17243. +
  17244. + //if (MAUL > 0 && CastSpell(MAUL, pTarget))
  17245. + // return RETURN_CONTINUE;
  17246. +
  17247. + return RETURN_NO_ACTION_UNKNOWN;
  17248. +}
  17249. +
  17250. +CombatManeuverReturns PlayerbotDruidAI::_DoNextPVECombatManeuverCat(Unit* pTarget)
  17251. +{
  17252. + //if (!m_ai) return RETURN_NO_ACTION_ERROR;
  17253. + //if (!m_bot) return RETURN_NO_ACTION_ERROR;
  17254. +
  17255. + //if (!m_bot->HasAura(CAT_FORM)) return RETURN_NO_ACTION_UNKNOWN;
  17256. +
  17257. + ////Used to determine if this bot is highest on threat
  17258. + //Unit *newTarget = m_ai->FindAttacker((PlayerbotAI::ATTACKERINFOTYPE) (PlayerbotAI::AIT_VICTIMSELF | PlayerbotAI::AIT_HIGHESTTHREAT), m_bot);
  17259. + //Unit* pVictim = pTarget->getVictim();
  17260. +
  17261. + //// Face enemy, make sure you're attacking
  17262. + //if (!m_bot->HasInArc(M_PI, pTarget))
  17263. + //{
  17264. + // m_bot->SetFacingTo(m_bot->GetAngle(pTarget));
  17265. + // if (pVictim)
  17266. + // pVictim->Attack(pTarget, true);
  17267. + //}
  17268. +
  17269. + //// Attempt to do a finishing move
  17270. + //if (m_bot->GetComboPoints() >= 5)
  17271. + //{
  17272. + // // 25 Energy
  17273. + // if (SAVAGE_ROAR > 0 && !m_bot->HasAura(SAVAGE_ROAR))
  17274. + // {
  17275. + // if (CastSpell(SAVAGE_ROAR, pTarget))
  17276. + // return RETURN_CONTINUE;
  17277. + // }
  17278. + // // 30 Energy
  17279. + // else if (RIP > 0 && !pTarget->HasAura(RIP, EFFECT_0))
  17280. + // {
  17281. + // if (CastSpell(RIP, pTarget))
  17282. + // return RETURN_CONTINUE;
  17283. + // }
  17284. + // // 35 Energy
  17285. + // else if (FEROCIOUS_BITE > 0)
  17286. + // {
  17287. + // if (CastSpell(FEROCIOUS_BITE, pTarget))
  17288. + // return RETURN_CONTINUE;
  17289. + // }
  17290. + //} // End 5 ComboPoints
  17291. +
  17292. + //if (newTarget && COWER > 0 && !m_bot->HasSpellCooldown(COWER) && CastSpell(COWER, pTarget))
  17293. + // return RETURN_CONTINUE;
  17294. +
  17295. + //if (FAERIE_FIRE_FERAL > 0 && !pTarget->HasAura(FAERIE_FIRE_FERAL, EFFECT_0) && CastSpell(FAERIE_FIRE_FERAL, pTarget))
  17296. + // return RETURN_CONTINUE;
  17297. +
  17298. + //if (TIGERS_FURY > 0 && !m_bot->HasSpellCooldown(TIGERS_FURY) && CastSpell(TIGERS_FURY))
  17299. + // return RETURN_CONTINUE;
  17300. +
  17301. + //if (MANGLE_CAT > 0 && !pTarget->HasAura(MANGLE_CAT) && CastSpell(MANGLE_CAT))
  17302. + // return RETURN_CONTINUE;
  17303. +
  17304. + //if (RAKE > 0 && !pTarget->HasAura(RAKE) && CastSpell(RAKE, pTarget))
  17305. + // return RETURN_CONTINUE;
  17306. +
  17307. + //if (CLAW > 0 && CastSpell(CLAW, pTarget))
  17308. + // return RETURN_CONTINUE;
  17309. +
  17310. + return RETURN_NO_ACTION_UNKNOWN;
  17311. +}
  17312. +
  17313. +CombatManeuverReturns PlayerbotDruidAI::_DoNextPVECombatManeuverSpellDPS(Unit* pTarget)
  17314. +{
  17315. + //if (!m_ai) return RETURN_NO_ACTION_ERROR;
  17316. + //if (!m_bot) return RETURN_NO_ACTION_ERROR;
  17317. +
  17318. + //uint32 NATURE = (STARFIRE > 0 ? STARFIRE : WRATH);
  17319. +
  17320. + //if (FAERIE_FIRE > 0 && !pTarget->HasAura(FAERIE_FIRE, EFFECT_0) && CastSpell(FAERIE_FIRE, pTarget))
  17321. + // return RETURN_CONTINUE;
  17322. +
  17323. + //if (MOONFIRE > 0 && !pTarget->HasAura(MOONFIRE, EFFECT_0) && CastSpell(MOONFIRE, pTarget))
  17324. + // return RETURN_CONTINUE;
  17325. +
  17326. + //if (INSECT_SWARM > 0 && !pTarget->HasAura(INSECT_SWARM, EFFECT_0) && CastSpell(INSECT_SWARM, pTarget))
  17327. + // return RETURN_CONTINUE;
  17328. +
  17329. + //// TODO: Doesn't work, I can't seem to nail the aura/effect index that would make this work properly
  17330. + //if (ECLIPSE_SOLAR > 0 && WRATH > 0 && m_bot->HasAura(ECLIPSE_SOLAR) && CastSpell(WRATH, pTarget))
  17331. + // return RETURN_CONTINUE;
  17332. +
  17333. + //// TODO: Doesn't work, I can't seem to nail the aura/effect index that would make this work properly
  17334. + //if (ECLIPSE_LUNAR > 0 && STARFIRE > 0 && m_bot->HasAura(ECLIPSE_LUNAR) && CastSpell(STARFIRE, pTarget))
  17335. + // return RETURN_CONTINUE;
  17336. +
  17337. + //if (FORCE_OF_NATURE > 0 && CastSpell(FORCE_OF_NATURE))
  17338. + // return RETURN_CONTINUE;
  17339. +
  17340. + //if (NATURE > 0 && CastSpell(NATURE, pTarget))
  17341. + // return RETURN_CONTINUE;
  17342. +
  17343. + //// Face enemy, make sure you're attacking
  17344. + //if (!m_bot->HasInArc(M_PI, pTarget))
  17345. + //{
  17346. + // m_bot->SetFacingTo(m_bot->GetAngle(pTarget));
  17347. + // if (m_ai->GetCombatStyle() == PlayerbotAI::COMBAT_MELEE)
  17348. + // m_bot->Attack(pTarget, true);
  17349. + // else
  17350. + // m_bot->AttackStop();
  17351. + //}
  17352. +
  17353. + return RETURN_NO_ACTION_UNKNOWN;
  17354. +}
  17355. +
  17356. +CombatManeuverReturns PlayerbotDruidAI::_DoNextPVECombatManeuverHeal()
  17357. +{
  17358. + //if (!m_ai) return RETURN_NO_ACTION_ERROR;
  17359. + //if (!m_bot) return RETURN_NO_ACTION_ERROR;
  17360. +
  17361. + //// (un)Shapeshifting is considered one step closer so will return true (and have the bot wait a bit for the GCD)
  17362. + //if (TREE_OF_LIFE > 0 && !m_bot->HasAura(TREE_OF_LIFE, EFFECT_0))
  17363. + // if (CastSpell(TREE_OF_LIFE, m_bot))
  17364. + // return RETURN_CONTINUE;
  17365. +
  17366. + //if (m_bot->HasAura(CAT_FORM, EFFECT_0))
  17367. + //{
  17368. + // m_bot->RemoveAurasDueToSpell(CAT_FORM_1);
  17369. + // //m_ai->TellMaster("FormClearCat");
  17370. + // return RETURN_CONTINUE;
  17371. + //}
  17372. + //if (m_bot->HasAura(BEAR_FORM, EFFECT_0))
  17373. + //{
  17374. + // m_bot->RemoveAurasDueToSpell(BEAR_FORM_1);
  17375. + // //m_ai->TellMaster("FormClearBear");
  17376. + // return RETURN_CONTINUE;
  17377. + //}
  17378. + //if (m_bot->HasAura(DIRE_BEAR_FORM, EFFECT_0))
  17379. + //{
  17380. + // m_bot->RemoveAurasDueToSpell(DIRE_BEAR_FORM_1);
  17381. + // //m_ai->TellMaster("FormClearDireBear");
  17382. + // return RETURN_CONTINUE;
  17383. + //}
  17384. + //// spellcasting form, but disables healing spells so it's got to go
  17385. + //if (m_bot->HasAura(MOONKIN_FORM, EFFECT_0))
  17386. + //{
  17387. + // m_bot->RemoveAurasDueToSpell(MOONKIN_FORM_1);
  17388. + // //m_ai->TellMaster("FormClearMoonkin");
  17389. + // return RETURN_CONTINUE;
  17390. + //}
  17391. +
  17392. + //if (HealPlayer(GetHealTarget()) & (RETURN_NO_ACTION_OK | RETURN_CONTINUE))
  17393. + // return RETURN_CONTINUE;
  17394. +
  17395. + return RETURN_NO_ACTION_UNKNOWN;
  17396. +}
  17397. +
  17398. +CombatManeuverReturns PlayerbotDruidAI::HealPlayer(Player* target)
  17399. +{
  17400. + //CombatManeuverReturns r = PlayerbotClassAI::HealPlayer(target);
  17401. + //if (r != RETURN_NO_ACTION_OK)
  17402. + // return r;
  17403. +
  17404. + //if (!target->isAlive())
  17405. + //{
  17406. + // if (m_bot->isInCombat())
  17407. + // {
  17408. + // // TODO: Add check for cooldown
  17409. + // if (REBIRTH && m_ai->CastSpell(REBIRTH, *target))
  17410. + // {
  17411. + // std::string msg = "Resurrecting ";
  17412. + // msg += target->GetName();
  17413. + // m_bot->Say(msg, LANG_UNIVERSAL);
  17414. + // return RETURN_CONTINUE;
  17415. + // }
  17416. + // }
  17417. + // else
  17418. + // {
  17419. + // if (REVIVE && m_ai->CastSpell(REVIVE, *target))
  17420. + // {
  17421. + // std::string msg = "Resurrecting ";
  17422. + // msg += target->GetName();
  17423. + // m_bot->Say(msg, LANG_UNIVERSAL);
  17424. + // return RETURN_CONTINUE;
  17425. + // }
  17426. + // }
  17427. + // return RETURN_NO_ACTION_ERROR; // not error per se - possibly just OOM
  17428. + //}
  17429. +
  17430. + ////If spell exists and orders say we should be dispelling
  17431. + //if ((REMOVE_CURSE > 0 || ABOLISH_POISON > 0) && (m_ai->GetCombatOrder() & PlayerbotAI::ORDERS_NODISPEL) == 0)
  17432. + //{
  17433. + // //This does something important(lol)
  17434. + // uint32 dispelMask = SpellInfo::GetDispelMask(DISPEL_CURSE);
  17435. + // uint32 dispelMask2 = SpellInfo::GetDispelMask(DISPEL_POISON);
  17436. + // //Get a list of all the targets auras(spells affecting target)
  17437. + // Unit::AuraMap const& auras = target->GetOwnedAuras();
  17438. + // //Iterate through the auras
  17439. + // for (Unit::AuraMap::const_iterator itr = auras.begin(); itr != auras.end(); itr++)
  17440. + // {
  17441. + // Aura *holder = itr->second;
  17442. + // //I dont know what this does but it doesn't work without it
  17443. + // if ((1 << holder->GetSpellInfo()->Dispel) & dispelMask)
  17444. + // {
  17445. + // //If the spell is dispellable and we can dispel it, do so
  17446. + // if ((holder->GetSpellInfo()->Dispel == DISPEL_CURSE) & (REMOVE_CURSE > 0))
  17447. + // {
  17448. + // if (CastSpell(REMOVE_CURSE, target))
  17449. + // return RETURN_CONTINUE;
  17450. + // return RETURN_NO_ACTION_ERROR;
  17451. + // }
  17452. + // }
  17453. + // else if ((1 << holder->GetSpellInfo()->Dispel) & dispelMask2)
  17454. + // {
  17455. + // if ((holder->GetSpellInfo()->Dispel == DISPEL_POISON) & (ABOLISH_POISON > 0))
  17456. + // {
  17457. + // if (CastSpell(ABOLISH_POISON, target))
  17458. + // return RETURN_CONTINUE;
  17459. + // return RETURN_NO_ACTION_ERROR;
  17460. + // }
  17461. + // }
  17462. + // }
  17463. + //}
  17464. +
  17465. + //uint8 hp = target->GetHealthPct();
  17466. +
  17467. + //// Everyone is healthy enough, return OK. MUST correlate to highest value below (should be last HP check)
  17468. + //if (hp >= 90)
  17469. + // return RETURN_NO_ACTION_OK;
  17470. +
  17471. + //// Reset form if needed
  17472. + //if (!m_bot->HasAura(TREE_OF_LIFE) || TREE_OF_LIFE == 0)
  17473. + // GoBuffForm(GetPlayerBot());
  17474. +
  17475. + //// Start heals. Do lowest HP checks at the top
  17476. + //if (hp < 30)
  17477. + //{
  17478. + // // TODO: Use in conjunction with Nature's Swiftness
  17479. + // if (HEALING_TOUCH > 0 && (NOURISH == 0 /*|| CastSpell(NATURES_SWIFTNESS)*/ ) && CastSpell(HEALING_TOUCH, target))
  17480. + // return RETURN_CONTINUE;
  17481. +
  17482. + // if (NOURISH > 0 && CastSpell(NOURISH, target))
  17483. + // return RETURN_CONTINUE;
  17484. + //}
  17485. +
  17486. + //if (hp < 45 && WILD_GROWTH > 0 && !target->HasAura(WILD_GROWTH) && CastSpell(WILD_GROWTH, target))
  17487. + // return RETURN_CONTINUE;
  17488. +
  17489. + //if (hp < 50 && SWIFTMEND > 0 && (target->HasAura(REJUVENATION) || target->HasAura(REGROWTH)) && CastSpell(SWIFTMEND, target))
  17490. + // return RETURN_CONTINUE;
  17491. +
  17492. + //if (hp < 60 && REGROWTH > 0 && !target->HasAura(REGROWTH) && CastSpell(REGROWTH, target))
  17493. + // return RETURN_CONTINUE;
  17494. +
  17495. + //if (hp < 65 && LIFEBLOOM > 0 && !target->HasAura(LIFEBLOOM) && CastSpell(LIFEBLOOM, target))
  17496. + // return RETURN_CONTINUE;
  17497. +
  17498. + //if (hp < 90 && REJUVENATION > 0 && !target->HasAura(REJUVENATION) && CastSpell(REJUVENATION, target))
  17499. + // return RETURN_CONTINUE;
  17500. +
  17501. + return RETURN_NO_ACTION_UNKNOWN;
  17502. +} // end HealTarget
  17503. +
  17504. +/**
  17505. +* CheckForms()
  17506. +*
  17507. +* Returns bool - Value indicates success - shape was shifted, already shifted, no need to shift.
  17508. +*/
  17509. +uint8 PlayerbotDruidAI::CheckForms()
  17510. +{
  17511. + //if (!m_ai) return RETURN_FAIL;
  17512. + //if (!m_bot) return RETURN_FAIL;
  17513. +
  17514. + //uint32 spec = m_bot->GetSpec();
  17515. + //uint32 BEAR = (DIRE_BEAR_FORM > 0 ? DIRE_BEAR_FORM : BEAR_FORM);
  17516. +
  17517. + //if (spec == DRUID_SPEC_BALANCE)
  17518. + //{
  17519. + // if (m_bot->HasAura(MOONKIN_FORM))
  17520. + // return RETURN_OK_NOCHANGE;
  17521. +
  17522. + // if (!MOONKIN_FORM)
  17523. + // return RETURN_OK_CANNOTSHIFT;
  17524. +
  17525. + // if (CastSpell(MOONKIN_FORM))
  17526. + // return RETURN_OK_SHIFTING;
  17527. + // else
  17528. + // return RETURN_FAIL;
  17529. + //}
  17530. +
  17531. + //if (spec == DRUID_SPEC_FERAL)
  17532. + //{
  17533. + // // Use Bear form only if we are told we're a tank and have thorns up
  17534. + // if (m_ai->GetCombatOrder() & PlayerbotAI::ORDERS_TANK)
  17535. + // {
  17536. + // if (m_bot->HasAura(BEAR))
  17537. + // return RETURN_OK_NOCHANGE;
  17538. +
  17539. + // if (!BEAR)
  17540. + // return RETURN_OK_CANNOTSHIFT;
  17541. +
  17542. + // if (!m_bot->HasAura(THORNS))
  17543. + // return RETURN_FAIL_WAITINGONSELFBUFF;
  17544. +
  17545. + // if (CastSpell(BEAR))
  17546. + // return RETURN_OK_SHIFTING;
  17547. + // else
  17548. + // return RETURN_FAIL;
  17549. + // }
  17550. + // else // No tank orders - try to go kitty or at least bear
  17551. + // {
  17552. + // if (CAT_FORM > 0)
  17553. + // {
  17554. + // if (m_bot->HasAura(CAT_FORM))
  17555. + // return RETURN_OK_NOCHANGE;
  17556. +
  17557. + // if (CastSpell(CAT_FORM))
  17558. + // return RETURN_OK_SHIFTING;
  17559. + // else
  17560. + // return RETURN_FAIL;
  17561. + // }
  17562. +
  17563. + // if (BEAR > 0)
  17564. + // {
  17565. + // if (m_bot->HasAura(BEAR))
  17566. + // return RETURN_OK_NOCHANGE;
  17567. +
  17568. + // if (CastSpell(BEAR))
  17569. + // return RETURN_OK_SHIFTING;
  17570. + // else
  17571. + // return RETURN_FAIL;
  17572. + // }
  17573. +
  17574. + // return RETURN_OK_CANNOTSHIFT;
  17575. + // }
  17576. + //}
  17577. +
  17578. + //if (spec == DRUID_SPEC_RESTORATION)
  17579. + //{
  17580. + // if (m_bot->HasAura(TREE_OF_LIFE))
  17581. + // return RETURN_OK_NOCHANGE;
  17582. +
  17583. + // if (!TREE_OF_LIFE)
  17584. + // return RETURN_OK_CANNOTSHIFT;
  17585. +
  17586. + // if (CastSpell(TREE_OF_LIFE))
  17587. + // return RETURN_OK_SHIFTING;
  17588. + // else
  17589. + // return RETURN_FAIL;
  17590. + //}
  17591. +
  17592. + // Unknown Spec
  17593. + return RETURN_FAIL;
  17594. +}
  17595. +
  17596. +void PlayerbotDruidAI::DoNonCombatActions()
  17597. +{
  17598. + //if (!m_ai) return;
  17599. + //if (!m_bot) return;
  17600. +
  17601. + //if (!m_bot->isAlive() || m_bot->IsInDuel()) return;
  17602. +
  17603. + //// Revive
  17604. + //if (HealPlayer(GetResurrectionTarget()) & RETURN_CONTINUE)
  17605. + // return;
  17606. +
  17607. + //// Heal
  17608. + //if (m_ai->IsHealer())
  17609. + //{
  17610. + // if (HealPlayer(GetHealTarget()) & RETURN_CONTINUE)
  17611. + // return;// RETURN_CONTINUE;
  17612. + //}
  17613. + //else
  17614. + //{
  17615. + // // Is this desirable? Debatable.
  17616. + // // TODO: In a group/raid with a healer you'd want this bot to focus on DPS (it's not specced/geared for healing either)
  17617. + // if (HealPlayer(m_bot) & RETURN_CONTINUE)
  17618. + // return;// RETURN_CONTINUE;
  17619. + //}
  17620. +
  17621. + //// Buff
  17622. + //if (m_bot->GetGroup() && GIFT_OF_THE_WILD && m_ai->HasSpellReagents(GIFT_OF_THE_WILD) && m_ai->Buff(GIFT_OF_THE_WILD, m_bot))
  17623. + // return;
  17624. + //if (Buff(&PlayerbotDruidAI::BuffHelper, MARK_OF_THE_WILD))
  17625. + // return;
  17626. + //if (Buff(&PlayerbotDruidAI::BuffHelper, THORNS, (m_bot->GetGroup() ? JOB_TANK : JOB_ALL)))
  17627. + // return;
  17628. +
  17629. + //// Return to fighting form AFTER reviving, healing, buffing
  17630. + //CheckForms();
  17631. +
  17632. + //// hp/mana check
  17633. + //if (m_bot->getStandState() != UNIT_STAND_STATE_STAND)
  17634. + // m_bot->SetStandState(UNIT_STAND_STATE_STAND);
  17635. +
  17636. + //if (EatDrinkBandage())
  17637. + // return;
  17638. +
  17639. + //if (INNERVATE && !m_bot->HasAura(INNERVATE) && (m_bot->GetPower(POWER_MANA)*100/m_bot->GetMaxPower(POWER_MANA)) <= 20 && CastSpell(INNERVATE, m_bot))
  17640. + // return;
  17641. +} // end DoNonCombatActions
  17642. +
  17643. +bool PlayerbotDruidAI::BuffHelper(PlayerbotAI* ai, uint32 spellId, Unit* target)
  17644. +{
  17645. + //if (!ai) return false;
  17646. + //if (spellId == 0) return false;
  17647. + //if (!target) return false;
  17648. +
  17649. + //Pet * pet = target->GetTypeId() == TYPEID_PLAYER ? target->ToPlayer()->GetPet() : NULL;
  17650. + //if (pet && !pet->HasAuraType(SPELL_AURA_MOD_UNATTACKABLE) && ai->Buff(spellId, pet, &(PlayerbotDruidAI::GoBuffForm)))
  17651. + // return true;
  17652. +
  17653. + //if (ai->Buff(spellId, target, &(PlayerbotDruidAI::GoBuffForm)))
  17654. + // return true;
  17655. +
  17656. + return false;
  17657. +}
  17658. +
  17659. +void PlayerbotDruidAI::GoBuffForm(Player* self)
  17660. +{
  17661. + // RANK_1 spell ids used because this is a static method which does not have access to instance.
  17662. + // There is only one rank for these spells anyway.
  17663. + if (self->HasAura(CAT_FORM_1))
  17664. + self->RemoveAurasDueToSpell(CAT_FORM_1);
  17665. + if (self->HasAura(BEAR_FORM_1))
  17666. + self->RemoveAurasDueToSpell(BEAR_FORM_1);
  17667. + if (self->HasAura(DIRE_BEAR_FORM_1))
  17668. + self->RemoveAurasDueToSpell(DIRE_BEAR_FORM_1);
  17669. + if (self->HasAura(MOONKIN_FORM_1))
  17670. + self->RemoveAurasDueToSpell(MOONKIN_FORM_1);
  17671. + if (self->HasAura(TRAVEL_FORM_1))
  17672. + self->RemoveAurasDueToSpell(TRAVEL_FORM_1);
  17673. +}
  17674. +
  17675. +// Match up with "Pull()" below
  17676. +bool PlayerbotDruidAI::CanPull()
  17677. +{
  17678. + if (BEAR_FORM && FAERIE_FIRE_FERAL)
  17679. + return true;
  17680. +
  17681. + return false;
  17682. +}
  17683. +
  17684. +// Match up with "CanPull()" above
  17685. +bool PlayerbotDruidAI::Pull()
  17686. +{
  17687. + //if (BEAR_FORM && (CastSpell(FAERIE_FIRE_FERAL) & RETURN_CONTINUE))
  17688. + // return true;
  17689. +
  17690. + return false;
  17691. +}
  17692. +
  17693. +bool PlayerbotDruidAI::CastHoTOnTank()
  17694. +{
  17695. + //if (!m_ai) return false;
  17696. +
  17697. + //if ((PlayerbotAI::ORDERS_HEAL & m_ai->GetCombatOrder()) == 0) return false;
  17698. +
  17699. + //// Druid HoTs: Rejuvenation, Regrowth, Tranquility (channeled, AoE), Lifebloom, and Wild Growth
  17700. + //if (REJUVENATION)
  17701. + // return (RETURN_CONTINUE & CastSpell(REJUVENATION, m_ai->GetGroupTank()));
  17702. +
  17703. + return false;
  17704. +}
  17705. diff --git a/src/server/game/AI/PlayerBots/bp_dru_ai.h b/src/server/game/AI/PlayerBots/bp_dru_ai.h
  17706. new file mode 100644
  17707. index 0000000..acac4e1
  17708. --- /dev/null
  17709. +++ b/src/server/game/AI/PlayerBots/bp_dru_ai.h
  17710. @@ -0,0 +1,223 @@
  17711. +#ifndef _PLAYERBOTDRUIDAI_H
  17712. +#define _PLAYERBOTDRUIDAI_H
  17713. +
  17714. +#include "bp_cai.h"
  17715. +
  17716. +enum DruidSpells
  17717. +{
  17718. + ABOLISH_POISON_1 = 2893,
  17719. + AQUATIC_FORM_1 = 1066,
  17720. + BARKSKIN_1 = 22812,
  17721. + BASH_1 = 5211,
  17722. + BEAR_FORM_1 = 5487,
  17723. + BERSERK_1 = 50334,
  17724. + CAT_FORM_1 = 768,
  17725. + CHALLENGING_ROAR_1 = 5209,
  17726. + CLAW_1 = 1082,
  17727. + COWER_1 = 8998,
  17728. + CURE_POISON_1 = 8946,
  17729. + CYCLONE_1 = 33786,
  17730. + DASH_1 = 1850,
  17731. + DEMORALIZING_ROAR_1 = 99,
  17732. + DIRE_BEAR_FORM_1 = 9634,
  17733. + ENRAGE_1 = 5229,
  17734. + ENTANGLING_ROOTS_1 = 339,
  17735. + FAERIE_FIRE_1 = 770,
  17736. + FAERIE_FIRE_FERAL_1 = 16857,
  17737. + FERAL_CHARGE_1 = 49377,
  17738. + FERAL_CHARGE_BEAR_1 = 16979,
  17739. + FERAL_CHARGE_CAT_1 = 49376,
  17740. + FEROCIOUS_BITE_1 = 22568,
  17741. + FLIGHT_FORM_1 = 33943,
  17742. + FORCE_OF_NATURE_1 = 33831,
  17743. + FRENZIED_REGENERATION_1 = 22842,
  17744. + GIFT_OF_THE_WILD_1 = 21849,
  17745. + GROWL_1 = 6795,
  17746. + HEALING_TOUCH_1 = 5185,
  17747. + HIBERNATE_1 = 2637,
  17748. + HURRICANE_1 = 16914,
  17749. + INNERVATE_1 = 29166,
  17750. + INSECT_SWARM_1 = 5570,
  17751. + LACERATE_1 = 33745,
  17752. + LIFEBLOOM_1 = 33763,
  17753. + MAIM_1 = 22570,
  17754. + MANGLE_1 = 33917,
  17755. + MANGLE_BEAR_1 = 33878,
  17756. + MANGLE_CAT_1 = 33876,
  17757. + MARK_OF_THE_WILD_1 = 1126,
  17758. + MAUL_1 = 6807,
  17759. + MOONFIRE_1 = 8921,
  17760. + MOONKIN_FORM_1 = 24858,
  17761. + NATURES_GRASP_1 = 16689,
  17762. + NATURES_SWIFTNESS_DRUID_1 = 17116,
  17763. + NOURISH_1 = 50464,
  17764. + POUNCE_1 = 9005,
  17765. + PROWL_1 = 5215,
  17766. + RAKE_1 = 1822,
  17767. + RAVAGE_1 = 6785,
  17768. + REBIRTH_1 = 20484,
  17769. + REGROWTH_1 = 8936,
  17770. + REJUVENATION_1 = 774,
  17771. + REMOVE_CURSE_DRUID_1 = 2782,
  17772. + REVIVE_1 = 50769,
  17773. + RIP_1 = 1079,
  17774. + SAVAGE_ROAR_1 = 52610,
  17775. + SHRED_1 = 5221,
  17776. + SOOTHE_ANIMAL_1 = 2908,
  17777. + STARFALL_1 = 48505,
  17778. + STARFIRE_1 = 2912,
  17779. + SURVIVAL_INSTINCTS_1 = 61336,
  17780. + SWIFTMEND_1 = 18562,
  17781. + SWIFT_FLIGHT_FORM_1 = 40120,
  17782. + SWIPE_BEAR_1 = 779,
  17783. + SWIPE_CAT_1 = 62078,
  17784. + THORNS_1 = 467,
  17785. + TIGERS_FURY_1 = 5217,
  17786. + TRANQUILITY_1 = 740,
  17787. + TRAVEL_FORM_1 = 783,
  17788. + TREE_OF_LIFE_1 = 33891,
  17789. + TYPHOON_1 = 50516,
  17790. + WILD_GROWTH_1 = 48438,
  17791. + WRATH_1 = 5176,
  17792. + ECLIPSE_1 = 48525,
  17793. +
  17794. + //Procs
  17795. + ECLIPSE_SOLAR_1 = 48517,
  17796. + ECLIPSE_LUNAR_1 = 48518
  17797. +};
  17798. +
  17799. +//class Player;
  17800. +
  17801. +class PlayerbotDruidAI : PlayerbotClassAI
  17802. +{
  17803. +public:
  17804. + PlayerbotDruidAI(Player * const master, Player * const bot, PlayerbotAI * const ai);
  17805. + virtual ~PlayerbotDruidAI();
  17806. +
  17807. + // all combat actions go here
  17808. + CombatManeuverReturns DoFirstCombatManeuver(Unit* pTarget);
  17809. + CombatManeuverReturns DoNextCombatManeuver(Unit* pTarget);
  17810. + bool Pull();
  17811. +
  17812. + // all non combat actions go here, ex buffs, heals, rezzes
  17813. + void DoNonCombatActions();
  17814. +
  17815. + // Utility Functions
  17816. + bool CanPull();
  17817. + bool CastHoTOnTank();
  17818. +
  17819. +private:
  17820. + CombatManeuverReturns DoFirstCombatManeuverPVE(Unit* pTarget);
  17821. + CombatManeuverReturns DoNextCombatManeuverPVE(Unit* pTarget);
  17822. + CombatManeuverReturns DoFirstCombatManeuverPVP(Unit* pTarget);
  17823. + CombatManeuverReturns DoNextCombatManeuverPVP(Unit* pTarget);
  17824. +
  17825. + CombatManeuverReturns CastSpell(uint32 nextAction, Unit *pTarget = NULL) { return CastSpellNoRanged(nextAction, pTarget); }
  17826. +
  17827. + // Combat Maneuver helper functions
  17828. + CombatManeuverReturns _DoNextPVECombatManeuverBear(Unit* pTarget);
  17829. + CombatManeuverReturns _DoNextPVECombatManeuverCat(Unit* pTarget);
  17830. + CombatManeuverReturns _DoNextPVECombatManeuverSpellDPS(Unit* pTarget);
  17831. + CombatManeuverReturns _DoNextPVECombatManeuverHeal();
  17832. +
  17833. + // Heals the target based off its hps
  17834. + CombatManeuverReturns HealPlayer (Player* target);
  17835. + Player* GetHealTarget() { return PlayerbotClassAI::GetHealTarget(); }
  17836. +
  17837. + static bool BuffHelper(PlayerbotAI* ai, uint32 spellId, Unit *target);
  17838. + // Callback method to reset shapeshift forms blocking buffs and heals
  17839. + static void GoBuffForm(Player *self);
  17840. +
  17841. + //Assumes form based on spec
  17842. + uint8 CheckForms();
  17843. + enum CheckForms_ReturnValues {
  17844. + RETURN_FAIL = 0,
  17845. + RETURN_FAIL_WAITINGONSELFBUFF,
  17846. + RETURN_OK_NOCHANGE,
  17847. + RETURN_OK_SHIFTING,
  17848. + RETURN_OK_CANNOTSHIFT
  17849. + };
  17850. +
  17851. + // druid cat/bear/dire bear/moonkin/tree of life forms
  17852. + uint32 CAT_FORM,
  17853. + BEAR_FORM,
  17854. + DIRE_BEAR_FORM,
  17855. + MOONKIN_FORM,
  17856. + TREE_OF_LIFE,
  17857. + TRAVEL_FORM;
  17858. +
  17859. + // druid cat attacks
  17860. + uint32 CLAW,
  17861. + COWER,
  17862. + TIGERS_FURY,
  17863. + RAKE,
  17864. + RIP,
  17865. + FEROCIOUS_BITE,
  17866. + MAIM,
  17867. + MANGLE,
  17868. + MANGLE_CAT,
  17869. + SAVAGE_ROAR;
  17870. +
  17871. + // druid bear/dire bear attacks & buffs
  17872. + uint32 BASH,
  17873. + MAUL,
  17874. + SWIPE,
  17875. + DEMORALIZING_ROAR,
  17876. + CHALLENGING_ROAR,
  17877. + GROWL,
  17878. + ENRAGE,
  17879. + FAERIE_FIRE_FERAL,
  17880. + MANGLE_BEAR,
  17881. + LACERATE;
  17882. +
  17883. + // druid caster DPS attacks & debuffs
  17884. + uint32 MOONFIRE,
  17885. + ROOTS,
  17886. + WRATH,
  17887. + STARFALL,
  17888. + STARFIRE,
  17889. + INSECT_SWARM,
  17890. + FAERIE_FIRE,
  17891. + FORCE_OF_NATURE,
  17892. + HURRICANE,
  17893. + ECLIPSE_SOLAR,
  17894. + ECLIPSE_LUNAR,
  17895. + ECLIPSE;
  17896. +
  17897. + // druid buffs
  17898. + uint32 MARK_OF_THE_WILD,
  17899. + GIFT_OF_THE_WILD,
  17900. + THORNS,
  17901. + INNERVATE,
  17902. + BARKSKIN;
  17903. +
  17904. + // druid heals
  17905. + uint32 LIFEBLOOM,
  17906. + REJUVENATION,
  17907. + REGROWTH,
  17908. + NOURISH,
  17909. + HEALING_TOUCH,
  17910. + WILD_GROWTH,
  17911. + SWIFTMEND,
  17912. + TRANQUILITY,
  17913. + REVIVE,
  17914. + REBIRTH,
  17915. + REMOVE_CURSE,
  17916. + ABOLISH_POISON;
  17917. +
  17918. + // racial
  17919. + uint32 ARCANE_TORRENT,
  17920. + GIFT_OF_THE_NAARU,
  17921. + STONEFORM,
  17922. + ESCAPE_ARTIST,
  17923. + EVERY_MAN_FOR_HIMSELF,
  17924. + SHADOWMELD,
  17925. + BLOOD_FURY,
  17926. + WAR_STOMP,
  17927. + BERSERKING,
  17928. + WILL_OF_THE_FORSAKEN;
  17929. +
  17930. + uint32 SpellSequence, DruidSpellCombat;
  17931. +};
  17932. +
  17933. +#endif
  17934. diff --git a/src/server/game/AI/PlayerBots/bp_hun_ai.cpp b/src/server/game/AI/PlayerBots/bp_hun_ai.cpp
  17935. new file mode 100644
  17936. index 0000000..87d32b0
  17937. --- /dev/null
  17938. +++ b/src/server/game/AI/PlayerBots/bp_hun_ai.cpp
  17939. @@ -0,0 +1,407 @@
  17940. +// an improved Hunter by rrtn & Runsttren :)
  17941. +#include "bp_hun_ai.h"
  17942. +#include "bp_ai.h"
  17943. +#include "Player.h"
  17944. +#include "Pet.h"
  17945. +
  17946. +PlayerbotHunterAI::PlayerbotHunterAI(Player* const master, Player* const bot, PlayerbotAI* const ai) : PlayerbotClassAI(master, bot, ai)
  17947. +{
  17948. + // PET CTRL
  17949. + PET_SUMMON = PlayerbotAI::InitSpell(me, CALL_PET_1);
  17950. + PET_DISMISS = PlayerbotAI::InitSpell(me, DISMISS_PET_1);
  17951. + PET_REVIVE = PlayerbotAI::InitSpell(me, REVIVE_PET_1);
  17952. + PET_MEND = PlayerbotAI::InitSpell(me, MEND_PET_1);
  17953. + PET_FEED = 1539;
  17954. +
  17955. + INTIMIDATION = PlayerbotAI::InitSpell(me, INTIMIDATION_1); // (generic)
  17956. +
  17957. + // PET SKILLS must be initialized by pets
  17958. + SONIC_BLAST = 0; // bat
  17959. + DEMORALIZING_SCREECH = 0;
  17960. + BAD_ATTITUDE = 0; // crocolisk
  17961. + NETHER_SHOCK = 0;
  17962. +
  17963. + // RANGED COMBAT
  17964. + AUTO_SHOT = PlayerbotAI::InitSpell(me, AUTO_SHOT_1);
  17965. + HUNTERS_MARK = PlayerbotAI::InitSpell(me, HUNTERS_MARK_1);
  17966. + ARCANE_SHOT = PlayerbotAI::InitSpell(me, ARCANE_SHOT_1);
  17967. + CONCUSSIVE_SHOT = PlayerbotAI::InitSpell(me, CONCUSSIVE_SHOT_1);
  17968. + DISTRACTING_SHOT = PlayerbotAI::InitSpell(me, DISTRACTING_SHOT_1);
  17969. + MULTI_SHOT = PlayerbotAI::InitSpell(me, MULTISHOT_1);
  17970. + EXPLOSIVE_SHOT = PlayerbotAI::InitSpell(me, EXPLOSIVE_SHOT_1);
  17971. + SERPENT_STING = PlayerbotAI::InitSpell(me, SERPENT_STING_1);
  17972. + SCORPID_STING = PlayerbotAI::InitSpell(me, SCORPID_STING_1);
  17973. + WYVERN_STING = PlayerbotAI::InitSpell(me, WYVERN_STING_1);
  17974. + VIPER_STING = PlayerbotAI::InitSpell(me, VIPER_STING_1);
  17975. + AIMED_SHOT = PlayerbotAI::InitSpell(me, AIMED_SHOT_1);
  17976. + STEADY_SHOT = PlayerbotAI::InitSpell(me, STEADY_SHOT_1);
  17977. + CHIMERA_SHOT = PlayerbotAI::InitSpell(me, CHIMERA_SHOT_1);
  17978. + VOLLEY = PlayerbotAI::InitSpell(me, VOLLEY_1);
  17979. + BLACK_ARROW = PlayerbotAI::InitSpell(me, BLACK_ARROW_1);
  17980. + KILL_SHOT = PlayerbotAI::InitSpell(me, KILL_SHOT_1);
  17981. +
  17982. + // MELEE
  17983. + RAPTOR_STRIKE = PlayerbotAI::InitSpell(me, RAPTOR_STRIKE_1);
  17984. + WING_CLIP = PlayerbotAI::InitSpell(me, WING_CLIP_1);
  17985. + MONGOOSE_BITE = PlayerbotAI::InitSpell(me, MONGOOSE_BITE_1);
  17986. + DISENGAGE = PlayerbotAI::InitSpell(me, DISENGAGE_1);
  17987. + MISDIRECTION = PlayerbotAI::InitSpell(me, MISDIRECTION_1);
  17988. + DETERRENCE = PlayerbotAI::InitSpell(me, DETERRENCE_1);
  17989. +
  17990. + // TRAPS
  17991. + BEAR_TRAP = 0; // non-player spell
  17992. + FREEZING_TRAP = PlayerbotAI::InitSpell(me, FREEZING_TRAP_1);
  17993. + IMMOLATION_TRAP = PlayerbotAI::InitSpell(me, IMMOLATION_TRAP_1);
  17994. + FROST_TRAP = PlayerbotAI::InitSpell(me, FROST_TRAP_1);
  17995. + EXPLOSIVE_TRAP = PlayerbotAI::InitSpell(me, EXPLOSIVE_TRAP_1);
  17996. + ARCANE_TRAP = 0; // non-player spell
  17997. + SNAKE_TRAP = PlayerbotAI::InitSpell(me, SNAKE_TRAP_1);
  17998. +
  17999. + // BUFFS
  18000. + ASPECT_OF_THE_HAWK = PlayerbotAI::InitSpell(me, ASPECT_OF_THE_HAWK_1);
  18001. + ASPECT_OF_THE_MONKEY = PlayerbotAI::InitSpell(me, ASPECT_OF_THE_MONKEY_1);
  18002. + RAPID_FIRE = PlayerbotAI::InitSpell(me, RAPID_FIRE_1);
  18003. + TRUESHOT_AURA = PlayerbotAI::InitSpell(me, TRUESHOT_AURA_1);
  18004. +
  18005. + //RECENTLY_BANDAGED = 11196; // first aid check
  18006. +
  18007. + // racial
  18008. + ARCANE_TORRENT = PlayerbotAI::InitSpell(me, ARCANE_TORRENT_MANA_CLASSES);
  18009. + GIFT_OF_THE_NAARU = PlayerbotAI::InitSpell(me, GIFT_OF_THE_NAARU_HUNTER); // draenei
  18010. + STONEFORM = PlayerbotAI::InitSpell(me, STONEFORM_ALL); // dwarf
  18011. + SHADOWMELD = PlayerbotAI::InitSpell(me, SHADOWMELD_ALL);
  18012. + BLOOD_FURY = PlayerbotAI::InitSpell(me, BLOOD_FURY_MELEE_CLASSES); // orc
  18013. + WAR_STOMP = PlayerbotAI::InitSpell(me, WAR_STOMP_ALL); // tauren
  18014. + BERSERKING = PlayerbotAI::InitSpell(me, BERSERKING_ALL); // troll
  18015. +
  18016. + m_petSummonFailed = false;
  18017. +}
  18018. +
  18019. +PlayerbotHunterAI::~PlayerbotHunterAI() {}
  18020. +
  18021. +CombatManeuverReturns PlayerbotHunterAI::DoFirstCombatManeuver(Unit* pTarget)
  18022. +{
  18023. + //// There are NPCs in BGs and Open World PvP, so don't filter this on PvP scenarios (of course if PvP targets anyone but tank, all bets are off anyway)
  18024. + //// Wait until the tank says so, until any non-tank gains aggro or X seconds - whichever is shortest
  18025. + //if (m_ai->GetCombatOrder() & PlayerbotAI::ORDERS_TEMP_WAIT_TANKAGGRO)
  18026. + //{
  18027. + // if (m_WaitUntil > m_ai->CurrentTime() && m_ai->GroupTankHoldsAggro())
  18028. + // {
  18029. + // return RETURN_NO_ACTION_OK; // wait it out
  18030. + // }
  18031. + // else
  18032. + // {
  18033. + // m_ai->ClearGroupCombatOrder(PlayerbotAI::ORDERS_TEMP_WAIT_TANKAGGRO);
  18034. + // }
  18035. + //}
  18036. +
  18037. + //if (m_ai->GetCombatOrder() & PlayerbotAI::ORDERS_TEMP_WAIT_OOC)
  18038. + //{
  18039. + // if (m_WaitUntil > m_ai->CurrentTime() && !m_ai->IsGroupInCombat())
  18040. + // return RETURN_NO_ACTION_OK; // wait it out
  18041. + // else
  18042. + // m_ai->ClearGroupCombatOrder(PlayerbotAI::ORDERS_TEMP_WAIT_OOC);
  18043. + //}
  18044. +
  18045. + //switch (m_ai->GetScenarioType())
  18046. + //{
  18047. + // case PlayerbotAI::SCENARIO_PVP_DUEL:
  18048. + // case PlayerbotAI::SCENARIO_PVP_BG:
  18049. + // case PlayerbotAI::SCENARIO_PVP_ARENA:
  18050. + // case PlayerbotAI::SCENARIO_PVP_OPENWORLD:
  18051. + // return DoFirstCombatManeuverPVP(pTarget);
  18052. + // case PlayerbotAI::SCENARIO_PVE:
  18053. + // case PlayerbotAI::SCENARIO_PVE_ELITE:
  18054. + // case PlayerbotAI::SCENARIO_PVE_RAID:
  18055. + // default:
  18056. + // return DoFirstCombatManeuverPVE(pTarget);
  18057. + // break;
  18058. + //}
  18059. +
  18060. + return RETURN_NO_ACTION_ERROR;
  18061. +}
  18062. +
  18063. +CombatManeuverReturns PlayerbotHunterAI::DoFirstCombatManeuverPVE(Unit* /*pTarget*/)
  18064. +{
  18065. + return RETURN_NO_ACTION_OK;
  18066. +}
  18067. +
  18068. +CombatManeuverReturns PlayerbotHunterAI::DoFirstCombatManeuverPVP(Unit* /*pTarget*/)
  18069. +{
  18070. + return RETURN_NO_ACTION_OK;
  18071. +}
  18072. +
  18073. +CombatManeuverReturns PlayerbotHunterAI::DoNextCombatManeuver(Unit *pTarget)
  18074. +{
  18075. + //switch (m_ai->GetScenarioType())
  18076. + //{
  18077. + // case PlayerbotAI::SCENARIO_PVP_DUEL:
  18078. + // case PlayerbotAI::SCENARIO_PVP_BG:
  18079. + // case PlayerbotAI::SCENARIO_PVP_ARENA:
  18080. + // case PlayerbotAI::SCENARIO_PVP_OPENWORLD:
  18081. + // return DoNextCombatManeuverPVP(pTarget);
  18082. + // case PlayerbotAI::SCENARIO_PVE:
  18083. + // case PlayerbotAI::SCENARIO_PVE_ELITE:
  18084. + // case PlayerbotAI::SCENARIO_PVE_RAID:
  18085. + // default:
  18086. + // return DoNextCombatManeuverPVE(pTarget);
  18087. + // break;
  18088. + //}
  18089. +
  18090. + return RETURN_NO_ACTION_ERROR;
  18091. +}
  18092. +
  18093. +CombatManeuverReturns PlayerbotHunterAI::DoNextCombatManeuverPVE(Unit *pTarget)
  18094. +{
  18095. + //if (!m_ai) return RETURN_NO_ACTION_ERROR;
  18096. + //if (!m_bot) return RETURN_NO_ACTION_ERROR;
  18097. + //if (!pTarget) return RETURN_NO_ACTION_ERROR;
  18098. +
  18099. + //Unit* pVictim = pTarget->getVictim();
  18100. +
  18101. + //// check for pet and heal if neccessary
  18102. + //Pet *pet = m_bot->GetPet();
  18103. + //// TODO: clarify/simplify: !pet->getDeathState() != ALIVE
  18104. + //if (pet && PET_MEND > 0 && pet->isAlive() && pet->GetHealthPct() < 50 && pVictim != m_bot && !pet->HasAura(PET_MEND, EFFECT_0) && m_ai->CastSpell(PET_MEND, *m_bot))
  18105. + //{
  18106. + // m_ai->TellMaster("healing pet.");
  18107. + // return RETURN_CONTINUE;
  18108. + //}
  18109. + //else if (pet && INTIMIDATION > 0 && pVictim == pet && !pet->HasAura(INTIMIDATION, EFFECT_0) && m_ai->CastSpell(INTIMIDATION, *m_bot))
  18110. + // return RETURN_CONTINUE;
  18111. +
  18112. + //// racial traits
  18113. + //if (m_bot->getRace() == RACE_ORC && !m_bot->HasAura(BLOOD_FURY, EFFECT_0))
  18114. + // m_ai->CastSpell(BLOOD_FURY, *m_bot);
  18115. + //else if (m_bot->getRace() == RACE_TROLL && !m_bot->HasAura(BERSERKING, EFFECT_0))
  18116. + // m_ai->CastSpell(BERSERKING, *m_bot);
  18117. +
  18118. + //// check if ranged combat is possible
  18119. + //float dist = pTarget->GetCombatReach();
  18120. + //if ((dist <= ATTACK_DISTANCE || !m_bot->GetUInt32Value(PLAYER_AMMO_ID)) && m_ai->GetCombatStyle() == PlayerbotAI::COMBAT_RANGED)
  18121. + //{
  18122. + // // switch to melee combat (target in melee range, out of ammo)
  18123. + // m_ai->SetCombatStyle(PlayerbotAI::COMBAT_MELEE);
  18124. + // if (!m_bot->GetUInt32Value(PLAYER_AMMO_ID))
  18125. + // m_ai->TellMaster("Out of ammo!");
  18126. + //}
  18127. + //else if (dist > ATTACK_DISTANCE && m_ai->GetCombatStyle() == PlayerbotAI::COMBAT_MELEE)
  18128. + // m_ai->SetCombatStyle(PlayerbotAI::COMBAT_RANGED);
  18129. +
  18130. + //// Set appropriate aspect
  18131. + //if (m_ai->GetCombatStyle() == PlayerbotAI::COMBAT_RANGED)
  18132. + //{
  18133. + // if (ASPECT_OF_THE_HAWK && !m_bot->HasAura(ASPECT_OF_THE_HAWK, EFFECT_0))
  18134. + // m_ai->CastSpell(ASPECT_OF_THE_HAWK, *m_bot);
  18135. + //}
  18136. + //else
  18137. + //{
  18138. + // if (ASPECT_OF_THE_MONKEY && !m_bot->HasAura(ASPECT_OF_THE_MONKEY, EFFECT_0))
  18139. + // m_ai->CastSpell(ASPECT_OF_THE_MONKEY, *m_bot);
  18140. + //}
  18141. +
  18142. + //// activate auto shot: Reworked to account for AUTO_SHOT being a triggered spell
  18143. + //if (AUTO_SHOT > 0 && m_ai->GetCombatStyle() == PlayerbotAI::COMBAT_RANGED && m_ai->GetCurrentSpellId() != AUTO_SHOT)
  18144. + // m_bot->CastSpell(pTarget, AUTO_SHOT, true);
  18145. +
  18146. + //// damage spells
  18147. + //if (m_ai->GetCombatStyle() == PlayerbotAI::COMBAT_RANGED)
  18148. + //{
  18149. + // if (HUNTERS_MARK > 0 && !pTarget->HasAura(HUNTERS_MARK, EFFECT_0) && m_ai->CastSpell(HUNTERS_MARK, *pTarget))
  18150. + // return RETURN_CONTINUE;
  18151. + // else if (RAPID_FIRE > 0 && !m_bot->HasAura(RAPID_FIRE, EFFECT_0) && m_ai->CastSpell(RAPID_FIRE, *m_bot))
  18152. + // return RETURN_CONTINUE;
  18153. + // else if (MULTI_SHOT > 0 && m_ai->GetAttackerCount() >= 3 && m_ai->CastSpell(MULTI_SHOT, *pTarget))
  18154. + // return RETURN_CONTINUE;
  18155. + // else if (ARCANE_SHOT > 0 && m_ai->CastSpell(ARCANE_SHOT, *pTarget))
  18156. + // return RETURN_CONTINUE;
  18157. + // else if (CONCUSSIVE_SHOT > 0 && !pTarget->HasAura(CONCUSSIVE_SHOT, EFFECT_0) && m_ai->CastSpell(CONCUSSIVE_SHOT, *pTarget))
  18158. + // return RETURN_CONTINUE;
  18159. + // else if (EXPLOSIVE_SHOT > 0 && !pTarget->HasAura(EXPLOSIVE_SHOT, EFFECT_0) && m_ai->CastSpell(EXPLOSIVE_SHOT, *pTarget))
  18160. + // return RETURN_CONTINUE;
  18161. + // else if (VIPER_STING > 0 && pTarget->GetPower(POWER_MANA) > 0 && (m_bot->GetPower(POWER_MANA)*100/m_bot->GetMaxPower(POWER_MANA)) < 70 && !pTarget->HasAura(VIPER_STING, EFFECT_0) && m_ai->CastSpell(VIPER_STING, *pTarget))
  18162. + // return RETURN_CONTINUE;
  18163. + // else if (SERPENT_STING > 0 && !pTarget->HasAura(SERPENT_STING, EFFECT_0) && !pTarget->HasAura(SCORPID_STING, EFFECT_0) && !pTarget->HasAura(VIPER_STING, EFFECT_0) && m_ai->CastSpell(SERPENT_STING, *pTarget))
  18164. + // return RETURN_CONTINUE;
  18165. + // else if (SCORPID_STING > 0 && !pTarget->HasAura(WYVERN_STING, EFFECT_0) && !pTarget->HasAura(SCORPID_STING, EFFECT_0) && !pTarget->HasAura(SERPENT_STING, EFFECT_0) && !pTarget->HasAura(VIPER_STING, EFFECT_0) && m_ai->CastSpell(SCORPID_STING, *pTarget))
  18166. + // return RETURN_CONTINUE;
  18167. + // else if (CHIMERA_SHOT > 0 && m_ai->CastSpell(CHIMERA_SHOT, *pTarget))
  18168. + // return RETURN_CONTINUE;
  18169. + // else if (VOLLEY > 0 && m_ai->GetAttackerCount() >= 3 && m_ai->CastSpell(VOLLEY, *pTarget))
  18170. + // return RETURN_CONTINUE;
  18171. + // else if (BLACK_ARROW > 0 && !pTarget->HasAura(BLACK_ARROW, EFFECT_0) && m_ai->CastSpell(BLACK_ARROW, *pTarget))
  18172. + // return RETURN_CONTINUE;
  18173. + // else if (AIMED_SHOT > 0 && m_ai->CastSpell(AIMED_SHOT, *pTarget))
  18174. + // return RETURN_CONTINUE;
  18175. + // else if (STEADY_SHOT > 0 && m_ai->CastSpell(STEADY_SHOT, *pTarget))
  18176. + // return RETURN_CONTINUE;
  18177. + // else if (KILL_SHOT > 0 && pTarget->GetHealthPct() < 20 && m_ai->CastSpell(KILL_SHOT, *pTarget))
  18178. + // return RETURN_CONTINUE;
  18179. + // else
  18180. + // return RETURN_NO_ACTION_OK;
  18181. + //}
  18182. + //else
  18183. + //{
  18184. + // if (RAPTOR_STRIKE > 0 && m_ai->CastSpell(RAPTOR_STRIKE, *pTarget))
  18185. + // return RETURN_CONTINUE;
  18186. + // else if (EXPLOSIVE_TRAP > 0 && !pTarget->HasAura(EXPLOSIVE_TRAP, EFFECT_0) && !pTarget->HasAura(ARCANE_TRAP, EFFECT_0) && !pTarget->HasAura(IMMOLATION_TRAP, EFFECT_0) && !pTarget->HasAura(FROST_TRAP, EFFECT_0) && !pTarget->HasAura(BEAR_TRAP, EFFECT_0) && m_ai->CastSpell(EXPLOSIVE_TRAP, *pTarget))
  18187. + // return RETURN_CONTINUE;
  18188. + // else if (WING_CLIP > 0 && !pTarget->HasAura(WING_CLIP, EFFECT_0) && m_ai->CastSpell(WING_CLIP, *pTarget))
  18189. + // return RETURN_CONTINUE;
  18190. + // else if (IMMOLATION_TRAP > 0 && !pTarget->HasAura(IMMOLATION_TRAP, EFFECT_0) && !pTarget->HasAura(ARCANE_TRAP, EFFECT_0) && !pTarget->HasAura(EXPLOSIVE_TRAP, EFFECT_0) && !pTarget->HasAura(FROST_TRAP, EFFECT_0) && !pTarget->HasAura(BEAR_TRAP, EFFECT_0) && m_ai->CastSpell(IMMOLATION_TRAP, *pTarget))
  18191. + // return RETURN_CONTINUE;
  18192. + // else if (MONGOOSE_BITE > 0 && m_ai->CastSpell(MONGOOSE_BITE, *pTarget))
  18193. + // return RETURN_CONTINUE;
  18194. + // else if (FROST_TRAP > 0 && !pTarget->HasAura(FROST_TRAP, EFFECT_0) && !pTarget->HasAura(ARCANE_TRAP, EFFECT_0) && !pTarget->HasAura(IMMOLATION_TRAP, EFFECT_0) && !pTarget->HasAura(EXPLOSIVE_TRAP, EFFECT_0) && !pTarget->HasAura(BEAR_TRAP, EFFECT_0) && m_ai->CastSpell(FROST_TRAP, *pTarget))
  18195. + // return RETURN_CONTINUE;
  18196. + // else if (ARCANE_TRAP > 0 && !pTarget->HasAura(ARCANE_TRAP, EFFECT_0) && !pTarget->HasAura(BEAR_TRAP, EFFECT_0) && !pTarget->HasAura(EXPLOSIVE_TRAP, EFFECT_0) && !pTarget->HasAura(IMMOLATION_TRAP, EFFECT_0) && !pTarget->HasAura(FROST_TRAP, EFFECT_0) && m_ai->CastSpell(ARCANE_TRAP, *pTarget))
  18197. + // return RETURN_CONTINUE;
  18198. + // else if (DETERRENCE > 0 && pVictim == m_bot && m_bot->GetHealthPct() < 50 && !m_bot->HasAura(DETERRENCE, EFFECT_0) && m_ai->CastSpell(DETERRENCE, *m_bot))
  18199. + // return RETURN_CONTINUE;
  18200. + // else if (m_bot->getRace() == RACE_TAUREN && !pTarget->HasAura(WAR_STOMP, EFFECT_0) && m_ai->CastSpell(WAR_STOMP, *pTarget))
  18201. + // return RETURN_CONTINUE;
  18202. + // else if (m_bot->getRace() == RACE_BLOODELF && !pTarget->HasAura(ARCANE_TORRENT, EFFECT_0) && m_ai->CastSpell(ARCANE_TORRENT, *pTarget))
  18203. + // return RETURN_CONTINUE;
  18204. + // else if (m_bot->getRace() == RACE_DWARF && m_bot->HasAuraState(AURA_STATE_DEADLY_POISON) && m_ai->CastSpell(STONEFORM, *m_bot))
  18205. + // return RETURN_CONTINUE;
  18206. + // else if (m_bot->getRace() == RACE_NIGHTELF && pVictim == m_bot && m_bot->GetHealthPct() < 25 && !m_bot->HasAura(SHADOWMELD, EFFECT_0) && m_ai->CastSpell(SHADOWMELD, *m_bot))
  18207. + // return RETURN_CONTINUE;
  18208. + // else if (m_bot->getRace() == RACE_DRAENEI && m_bot->GetHealthPct() < 25 && !m_bot->HasAura(GIFT_OF_THE_NAARU, EFFECT_0) && m_ai->CastSpell(GIFT_OF_THE_NAARU, *m_bot))
  18209. + // return RETURN_CONTINUE;
  18210. + // else if (pet && pet->isAlive() && MISDIRECTION > 0 && pVictim == m_bot && !m_bot->HasAura(MISDIRECTION, EFFECT_0) && m_ai->CastSpell(MISDIRECTION, *pet))
  18211. + // return RETURN_CONTINUE;
  18212. + // /*else if(FREEZING_TRAP > 0 && !pTarget->HasAura(FREEZING_TRAP, EFFECT_0) && !pTarget->HasAura(ARCANE_TRAP, EFFECT_0) && !pTarget->HasAura(EXPLOSIVE_TRAP, EFFECT_0) && !pTarget->HasAura(BEAR_TRAP, EFFECT_0) && !pTarget->HasAura(IMMOLATION_TRAP, EFFECT_0) && !pTarget->HasAura(FROST_TRAP, EFFECT_0) && m_ai->CastSpell(FREEZING_TRAP,*pTarget) )
  18213. + // out << " > Freezing Trap"; // this can trap your bots too
  18214. + // else if(BEAR_TRAP > 0 && !pTarget->HasAura(BEAR_TRAP, EFFECT_0) && !pTarget->HasAura(ARCANE_TRAP, EFFECT_0) && !pTarget->HasAura(EXPLOSIVE_TRAP, EFFECT_0) && !pTarget->HasAura(IMMOLATION_TRAP, EFFECT_0) && !pTarget->HasAura(FROST_TRAP, EFFECT_0) && m_ai->CastSpell(BEAR_TRAP,*pTarget) )
  18215. + // out << " > Bear Trap"; // this was just too annoying :)
  18216. + // else if(DISENGAGE > 0 && pVictim && m_ai->CastSpell(DISENGAGE,*pTarget) )
  18217. + // out << " > Disengage!"; // attempt to return to ranged combat*/
  18218. + // else RETURN_NO_ACTION_OK;
  18219. + //}
  18220. +
  18221. + return RETURN_NO_ACTION_OK;
  18222. +} // end DoNextCombatManeuver
  18223. +
  18224. +CombatManeuverReturns PlayerbotHunterAI::DoNextCombatManeuverPVP(Unit* pTarget)
  18225. +{
  18226. + //if (m_ai->CastSpell(RAPTOR_STRIKE))
  18227. + // return RETURN_CONTINUE;
  18228. +
  18229. + return DoNextCombatManeuverPVE(pTarget); // TODO: bad idea perhaps, but better than the alternative
  18230. +}
  18231. +
  18232. +void PlayerbotHunterAI::DoNonCombatActions()
  18233. +{
  18234. + //if (!m_ai) return;
  18235. + //if (!m_bot) return;
  18236. +
  18237. + //// buff group
  18238. + //if (TRUESHOT_AURA > 0 && !m_bot->HasAura(TRUESHOT_AURA, EFFECT_0))
  18239. + // m_ai->CastSpell(TRUESHOT_AURA, *m_bot);
  18240. +
  18241. + //// buff myself
  18242. + //if (ASPECT_OF_THE_HAWK > 0 && !m_bot->HasAura(ASPECT_OF_THE_HAWK, EFFECT_0))
  18243. + // m_ai->CastSpell(ASPECT_OF_THE_HAWK, *m_bot);
  18244. +
  18245. + //// hp/mana check
  18246. + //if (m_bot->getStandState() != UNIT_STAND_STATE_STAND)
  18247. + // m_bot->SetStandState(UNIT_STAND_STATE_STAND);
  18248. +
  18249. + //if (EatDrinkBandage())
  18250. + // return;
  18251. +
  18252. + //if (m_bot->getRace() == RACE_DRAENEI && !m_bot->HasAura(GIFT_OF_THE_NAARU, EFFECT_0) && m_bot->GetHealthPct() < 70)
  18253. + //{
  18254. + // m_ai->TellMaster("I'm casting gift of the naaru.");
  18255. + // m_ai->CastSpell(GIFT_OF_THE_NAARU, *m_bot);
  18256. + // return;
  18257. + //}
  18258. +
  18259. + //// check for pet
  18260. + //if (PET_SUMMON > 0 && !m_petSummonFailed && m_bot->GetPetGUID())
  18261. + //{
  18262. + // // we can summon pet, and no critical summon errors before
  18263. + // Pet *pet = m_bot->GetPet();
  18264. + // if (!pet)
  18265. + // {
  18266. + // // summon pet
  18267. + // if (PET_SUMMON > 0 && m_ai->CastSpell(PET_SUMMON, *m_bot))
  18268. + // m_ai->TellMaster("summoning pet.");
  18269. + // else
  18270. + // {
  18271. + // m_petSummonFailed = true;
  18272. + // m_ai->TellMaster("summon pet failed!");
  18273. + // }
  18274. + // }
  18275. + // else if (!(pet->isAlive()))
  18276. + // {
  18277. + // if (PET_REVIVE > 0 && m_ai->CastSpell(PET_REVIVE, *m_bot))
  18278. + // m_ai->TellMaster("reviving pet.");
  18279. + // }
  18280. + // else if (pet->GetHealthPct() < 50)
  18281. + // {
  18282. + // if (PET_MEND > 0 && pet->isAlive() && !pet->HasAura(PET_MEND, EFFECT_0) && m_ai->CastSpell(PET_MEND, *m_bot))
  18283. + // m_ai->TellMaster("healing pet.");
  18284. + // }
  18285. + // else if (pet->GetHappinessState() != HAPPY) // if pet is hungry
  18286. + // {
  18287. + // Unit *caster = (Unit *) m_bot;
  18288. + // // list out items in main backpack
  18289. + // for (uint8 slot = INVENTORY_SLOT_ITEM_START; slot < INVENTORY_SLOT_ITEM_END; slot++)
  18290. + // {
  18291. + // Item* const pItem = m_bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot);
  18292. + // if (pItem)
  18293. + // {
  18294. + // const ItemTemplate* const pItemProto = pItem->GetTemplate();
  18295. + // if (!pItemProto)
  18296. + // continue;
  18297. +
  18298. + // if (pet->HaveInDiet(pItemProto)) // is pItem in pets diet
  18299. + // {
  18300. + // // DEBUG_LOG ("[PlayerbotHunterAI]: DoNonCombatActions - Food for pet: %s",pItemProto->Name1);
  18301. + // caster->CastSpell(caster, 51284, true); // pet feed visual
  18302. + // uint32 count = 1; // number of items used
  18303. + // int32 benefit = pet->GetCurrentFoodBenefitLevel(pItemProto->ItemLevel); // nutritional value of food
  18304. + // m_bot->DestroyItemCount(pItem, count, true); // remove item from inventory
  18305. + // m_bot->CastCustomSpell(m_bot, PET_FEED, &benefit, NULL, NULL, true); // feed pet
  18306. + // m_ai->TellMaster("feeding pet.");
  18307. + // //m_ai->SetIgnoreUpdateTime(10);
  18308. + // return;
  18309. + // }
  18310. + // }
  18311. + // }
  18312. + // // list out items in other removable backpacks
  18313. + // for (uint8 bag = INVENTORY_SLOT_BAG_START; bag < INVENTORY_SLOT_BAG_END; ++bag)
  18314. + // {
  18315. + // const Bag* const pBag = (Bag *) m_bot->GetItemByPos(INVENTORY_SLOT_BAG_0, bag);
  18316. + // if (pBag)
  18317. + // for (uint8 slot = 0; slot < pBag->GetBagSize(); ++slot)
  18318. + // {
  18319. + // Item* const pItem = m_bot->GetItemByPos(bag, slot);
  18320. + // if (pItem)
  18321. + // {
  18322. + // const ItemTemplate* const pItemProto = pItem->GetTemplate();
  18323. + // if (!pItemProto)
  18324. + // continue;
  18325. +
  18326. + // if (pet->HaveInDiet(pItemProto)) // is pItem in pets diet
  18327. + // {
  18328. + // // DEBUG_LOG ("[PlayerbotHunterAI]: DoNonCombatActions - Food for pet: %s",pItemProto->Name1);
  18329. + // caster->CastSpell(caster, 51284, true); // pet feed visual
  18330. + // uint32 count = 1; // number of items used
  18331. + // int32 benefit = pet->GetCurrentFoodBenefitLevel(pItemProto->ItemLevel); // nutritional value of food
  18332. + // m_bot->DestroyItemCount(pItem, count, true); // remove item from inventory
  18333. + // m_bot->CastCustomSpell(m_bot, PET_FEED, &benefit, NULL, NULL, true); // feed pet
  18334. + // m_ai->TellMaster("feeding pet.");
  18335. + // //m_ai->SetIgnoreUpdateTime(10);
  18336. + // return;
  18337. + // }
  18338. + // }
  18339. + // }
  18340. + // }
  18341. + // if (pet->HasAura(PET_MEND, EFFECT_0) && !pet->HasAura(PET_FEED, EFFECT_0))
  18342. + // m_ai->TellMaster("..no pet food!");
  18343. + // //m_ai->SetIgnoreUpdateTime(7);
  18344. + // }
  18345. + //}
  18346. +} // end DoNonCombatActions
  18347. diff --git a/src/server/game/AI/PlayerBots/bp_hun_ai.h b/src/server/game/AI/PlayerBots/bp_hun_ai.h
  18348. new file mode 100644
  18349. index 0000000..4881df9
  18350. --- /dev/null
  18351. +++ b/src/server/game/AI/PlayerBots/bp_hun_ai.h
  18352. @@ -0,0 +1,122 @@
  18353. +#ifndef _PLAYERHUNTERAI_H
  18354. +#define _PLAYERHUNTERAI_H
  18355. +
  18356. +#include "bp_cai.h"
  18357. +
  18358. +enum
  18359. +{
  18360. + SPELL_HUNTER
  18361. +};
  18362. +
  18363. +enum HunterSpells
  18364. +{
  18365. + ARCANE_SHOT_1 = 3044,
  18366. + ASPECT_OF_THE_BEAST_1 = 13161,
  18367. + ASPECT_OF_THE_CHEETAH_1 = 5118,
  18368. + ASPECT_OF_THE_DRAGONHAWK_1 = 61846,
  18369. + ASPECT_OF_THE_HAWK_1 = 13165,
  18370. + ASPECT_OF_THE_MONKEY_1 = 13163,
  18371. + ASPECT_OF_THE_PACK_1 = 13159,
  18372. + ASPECT_OF_THE_VIPER_1 = 34074,
  18373. + ASPECT_OF_THE_WILD_1 = 20043,
  18374. + AUTO_SHOT_1 = 75,
  18375. + BEAST_LORE_1 = 1462,
  18376. + CALL_PET_1 = 883,
  18377. + CALL_STABLED_PET_1 = 62757,
  18378. + CONCUSSIVE_SHOT_1 = 5116,
  18379. + DETERRENCE_1 = 19263,
  18380. + DISENGAGE_1 = 781,
  18381. + DISMISS_PET_1 = 2641,
  18382. + DISTRACTING_SHOT_1 = 20736,
  18383. + EAGLE_EYE_1 = 6197,
  18384. + EXPLOSIVE_TRAP_1 = 13813,
  18385. + EYES_OF_THE_BEAST_1 = 1002,
  18386. + FEED_PET_1 = 6991,
  18387. + FEIGN_DEATH_1 = 5384,
  18388. + FLARE_1 = 1543,
  18389. + FREEZING_ARROW_1 = 60192,
  18390. + FREEZING_TRAP_1 = 1499,
  18391. + FROST_TRAP_1 = 13809,
  18392. + HUNTERS_MARK_1 = 1130,
  18393. + IMMOLATION_TRAP_1 = 13795,
  18394. + KILL_COMMAND_1 = 34026,
  18395. + KILL_SHOT_1 = 53351,
  18396. + MASTERS_CALL_1 = 53271,
  18397. + MEND_PET_1 = 136,
  18398. + MISDIRECTION_1 = 34477,
  18399. + MONGOOSE_BITE_1 = 1495,
  18400. + MULTISHOT_1 = 2643,
  18401. + RAPID_FIRE_1 = 3045,
  18402. + RAPTOR_STRIKE_1 = 2973,
  18403. + REVIVE_PET_1 = 982,
  18404. + SCARE_BEAST_1 = 1513,
  18405. + SCORPID_STING_1 = 3043,
  18406. + SERPENT_STING_1 = 1978,
  18407. + SNAKE_TRAP_1 = 34600,
  18408. + STEADY_SHOT_1 = 56641,
  18409. + TAME_BEAST_1 = 1515,
  18410. + TRACK_BEASTS_1 = 1494,
  18411. + TRACK_DEMONS_1 = 19878,
  18412. + TRACK_DRAGONKIN_1 = 19879,
  18413. + TRACK_ELEMENTALS_1 = 19880,
  18414. + TRACK_GIANTS_1 = 19882,
  18415. + TRACK_HIDDEN_1 = 19885,
  18416. + TRACK_HUMANOIDS_1 = 19883,
  18417. + TRACK_UNDEAD_1 = 19884,
  18418. + TRANQUILIZING_SHOT_1 = 19801,
  18419. + VIPER_STING_1 = 3034,
  18420. + VOLLEY_1 = 1510,
  18421. + WING_CLIP_1 = 2974,
  18422. + AIMED_SHOT_1 = 19434,
  18423. + BESTIAL_WRATH_1 = 19574,
  18424. + BLACK_ARROW_1 = 3674,
  18425. + CHIMERA_SHOT_1 = 53209,
  18426. + COUNTERATTACK_1 = 19306,
  18427. + EXPLOSIVE_SHOT_1 = 53301,
  18428. + INTIMIDATION_1 = 19577,
  18429. + READINESS_1 = 23989,
  18430. + SCATTER_SHOT_1 = 19503,
  18431. + SILENCING_SHOT_1 = 34490,
  18432. + TRUESHOT_AURA_1 = 19506,
  18433. + WYVERN_STING_1 = 19386
  18434. +};
  18435. +
  18436. +//class Player;
  18437. +
  18438. +class PlayerbotHunterAI : PlayerbotClassAI
  18439. +{
  18440. +public:
  18441. + PlayerbotHunterAI(Player * const master, Player * const bot, PlayerbotAI * const ai);
  18442. + virtual ~PlayerbotHunterAI();
  18443. + bool HasPet(Player* bot);
  18444. +
  18445. + // all combat actions go here
  18446. + CombatManeuverReturns DoFirstCombatManeuver(Unit* pTarget);
  18447. + CombatManeuverReturns DoNextCombatManeuver(Unit* pTarget);
  18448. +
  18449. + // all non combat actions go here, ex buffs, heals, rezzes
  18450. + void DoNonCombatActions();
  18451. +
  18452. + // buff a specific player, usually a real PC who is not in group
  18453. + //void BuffPlayer(Player *target);
  18454. +
  18455. +private:
  18456. + CombatManeuverReturns DoFirstCombatManeuverPVE(Unit* pTarget);
  18457. + CombatManeuverReturns DoNextCombatManeuverPVE(Unit* pTarget);
  18458. + CombatManeuverReturns DoFirstCombatManeuverPVP(Unit* pTarget);
  18459. + CombatManeuverReturns DoNextCombatManeuverPVP(Unit* pTarget);
  18460. +
  18461. + // Hunter
  18462. + bool m_petSummonFailed;
  18463. +
  18464. + uint32 PET_SUMMON, PET_DISMISS, PET_REVIVE, PET_MEND, PET_FEED, BAD_ATTITUDE, SONIC_BLAST, NETHER_SHOCK, DEMORALIZING_SCREECH, INTIMIDATION;
  18465. + uint32 AUTO_SHOT, HUNTERS_MARK, ARCANE_SHOT, CONCUSSIVE_SHOT, DISTRACTING_SHOT, MULTI_SHOT, EXPLOSIVE_SHOT, SERPENT_STING, SCORPID_STING, VIPER_STING, WYVERN_STING, AIMED_SHOT, STEADY_SHOT, CHIMERA_SHOT, VOLLEY, BLACK_ARROW, KILL_SHOT;
  18466. + uint32 RAPTOR_STRIKE, WING_CLIP, MONGOOSE_BITE, DISENGAGE, DETERRENCE;
  18467. + uint32 BEAR_TRAP, FREEZING_TRAP, IMMOLATION_TRAP, FROST_TRAP, EXPLOSIVE_TRAP, ARCANE_TRAP, SNAKE_TRAP;
  18468. + uint32 ASPECT_OF_THE_HAWK, ASPECT_OF_THE_MONKEY, RAPID_FIRE, TRUESHOT_AURA, MISDIRECTION;
  18469. +
  18470. + // racial
  18471. + uint32 ARCANE_TORRENT, GIFT_OF_THE_NAARU, STONEFORM, ESCAPE_ARTIST, EVERY_MAN_FOR_HIMSELF, SHADOWMELD, BLOOD_FURY, WAR_STOMP, BERSERKING, WILL_OF_THE_FORSAKEN;
  18472. +};
  18473. +
  18474. +#endif
  18475. diff --git a/src/server/game/AI/PlayerBots/bp_mag_ai.cpp b/src/server/game/AI/PlayerBots/bp_mag_ai.cpp
  18476. new file mode 100644
  18477. index 0000000..eab1752
  18478. --- /dev/null
  18479. +++ b/src/server/game/AI/PlayerBots/bp_mag_ai.cpp
  18480. @@ -0,0 +1,356 @@
  18481. +#include "bp_mag_ai.h"
  18482. +#include "bp_ai.h"
  18483. +#include "Player.h"
  18484. +#include "Pet.h"
  18485. +
  18486. +PlayerbotMageAI::PlayerbotMageAI(Player* const master, Player* const bot, PlayerbotAI* const ai) : PlayerbotClassAI(master, bot, ai)
  18487. +{
  18488. + ARCANE_MISSILES = PlayerbotAI::InitSpell(me, ARCANE_MISSILES_1);
  18489. + ARCANE_EXPLOSION = PlayerbotAI::InitSpell(me, ARCANE_EXPLOSION_1);
  18490. + COUNTERSPELL = PlayerbotAI::InitSpell(me, COUNTERSPELL_1);
  18491. + SLOW = PlayerbotAI::InitSpell(me, SLOW_1);
  18492. + ARCANE_BARRAGE = PlayerbotAI::InitSpell(me, ARCANE_BARRAGE_1);
  18493. + ARCANE_BLAST = PlayerbotAI::InitSpell(me, ARCANE_BLAST_1);
  18494. + ARCANE_POWER = PlayerbotAI::InitSpell(me, ARCANE_POWER_1);
  18495. + DAMPEN_MAGIC = PlayerbotAI::InitSpell(me, DAMPEN_MAGIC_1);
  18496. + AMPLIFY_MAGIC = PlayerbotAI::InitSpell(me, AMPLIFY_MAGIC_1);
  18497. + MAGE_ARMOR = PlayerbotAI::InitSpell(me, MAGE_ARMOR_1);
  18498. + MIRROR_IMAGE = PlayerbotAI::InitSpell(me, MIRROR_IMAGE_1);
  18499. + ARCANE_INTELLECT = PlayerbotAI::InitSpell(me, ARCANE_INTELLECT_1);
  18500. + ARCANE_BRILLIANCE = PlayerbotAI::InitSpell(me, ARCANE_BRILLIANCE_1);
  18501. + DALARAN_INTELLECT = PlayerbotAI::InitSpell(me, DALARAN_INTELLECT_1);
  18502. + DALARAN_BRILLIANCE = PlayerbotAI::InitSpell(me, DALARAN_BRILLIANCE_1);
  18503. + MANA_SHIELD = PlayerbotAI::InitSpell(me, MANA_SHIELD_1);
  18504. + CONJURE_WATER = PlayerbotAI::InitSpell(me, CONJURE_WATER_1);
  18505. + CONJURE_FOOD = PlayerbotAI::InitSpell(me, CONJURE_FOOD_1);
  18506. + FIREBALL = PlayerbotAI::InitSpell(me, FIREBALL_1);
  18507. + FIRE_BLAST = PlayerbotAI::InitSpell(me, FIRE_BLAST_1);
  18508. + FLAMESTRIKE = PlayerbotAI::InitSpell(me, FLAMESTRIKE_1);
  18509. + SCORCH = PlayerbotAI::InitSpell(me, SCORCH_1);
  18510. + PYROBLAST = PlayerbotAI::InitSpell(me, PYROBLAST_1);
  18511. + BLAST_WAVE = PlayerbotAI::InitSpell(me, BLAST_WAVE_1);
  18512. + COMBUSTION = PlayerbotAI::InitSpell(me, COMBUSTION_1);
  18513. + DRAGONS_BREATH = PlayerbotAI::InitSpell(me, DRAGONS_BREATH_1);
  18514. + LIVING_BOMB = PlayerbotAI::InitSpell(me, LIVING_BOMB_1);
  18515. + FROSTFIRE_BOLT = PlayerbotAI::InitSpell(me, FROSTFIRE_BOLT_1);
  18516. + FIRE_WARD = PlayerbotAI::InitSpell(me, FIRE_WARD_1);
  18517. + MOLTEN_ARMOR = PlayerbotAI::InitSpell(me, MOLTEN_ARMOR_1);
  18518. + ICY_VEINS = PlayerbotAI::InitSpell(me, ICY_VEINS_1);
  18519. + DEEP_FREEZE = PlayerbotAI::InitSpell(me, DEEP_FREEZE_1);
  18520. + FROSTBOLT = PlayerbotAI::InitSpell(me, FROSTBOLT_1);
  18521. + FROST_NOVA = PlayerbotAI::InitSpell(me, FROST_NOVA_1);
  18522. + BLIZZARD = PlayerbotAI::InitSpell(me, BLIZZARD_1);
  18523. + CONE_OF_COLD = PlayerbotAI::InitSpell(me, CONE_OF_COLD_1);
  18524. + ICE_BARRIER = PlayerbotAI::InitSpell(me, ICE_BARRIER_1);
  18525. + SUMMON_WATER_ELEMENTAL = PlayerbotAI::InitSpell(me, SUMMON_WATER_ELEMENTAL_1);
  18526. + FROST_WARD = PlayerbotAI::InitSpell(me, FROST_WARD_1);
  18527. + ICE_LANCE = PlayerbotAI::InitSpell(me, ICE_LANCE_1);
  18528. + FROST_ARMOR = PlayerbotAI::InitSpell(me, FROST_ARMOR_1);
  18529. + ICE_ARMOR = PlayerbotAI::InitSpell(me, ICE_ARMOR_1);
  18530. + ICE_BLOCK = PlayerbotAI::InitSpell(me, ICE_BLOCK_1);
  18531. + COLD_SNAP = PlayerbotAI::InitSpell(me, COLD_SNAP_1);
  18532. +
  18533. + // RANGED COMBAT
  18534. + SHOOT = PlayerbotAI::InitSpell(me, SHOOT_2);
  18535. +
  18536. + //RECENTLY_BANDAGED = 11196; // first aid check
  18537. +
  18538. + // racial
  18539. + ARCANE_TORRENT = PlayerbotAI::InitSpell(me, ARCANE_TORRENT_MANA_CLASSES); // blood elf
  18540. + GIFT_OF_THE_NAARU = PlayerbotAI::InitSpell(me, GIFT_OF_THE_NAARU_MAGE); // draenei
  18541. + ESCAPE_ARTIST = PlayerbotAI::InitSpell(me, ESCAPE_ARTIST_ALL); // gnome
  18542. + EVERY_MAN_FOR_HIMSELF = PlayerbotAI::InitSpell(me, EVERY_MAN_FOR_HIMSELF_ALL); // human
  18543. + BERSERKING = PlayerbotAI::InitSpell(me, BERSERKING_ALL); // troll
  18544. + WILL_OF_THE_FORSAKEN = PlayerbotAI::InitSpell(me, WILL_OF_THE_FORSAKEN_ALL); // undead
  18545. +}
  18546. +
  18547. +PlayerbotMageAI::~PlayerbotMageAI() {}
  18548. +
  18549. +CombatManeuverReturns PlayerbotMageAI::DoFirstCombatManeuver(Unit* pTarget)
  18550. +{
  18551. + //// There are NPCs in BGs and Open World PvP, so don't filter this on PvP scenarios (of course if PvP targets anyone but tank, all bets are off anyway)
  18552. + //// Wait until the tank says so, until any non-tank gains aggro or X seconds - whichever is shortest
  18553. + //if (m_ai->GetCombatOrder() & PlayerbotAI::ORDERS_TEMP_WAIT_TANKAGGRO)
  18554. + //{
  18555. + // if (m_WaitUntil > m_ai->CurrentTime() && m_ai->GroupTankHoldsAggro())
  18556. + // {
  18557. + // return RETURN_NO_ACTION_OK; // wait it out
  18558. + // }
  18559. + // else
  18560. + // {
  18561. + // m_ai->ClearGroupCombatOrder(PlayerbotAI::ORDERS_TEMP_WAIT_TANKAGGRO);
  18562. + // }
  18563. + //}
  18564. +
  18565. + //if (m_ai->GetCombatOrder() & PlayerbotAI::ORDERS_TEMP_WAIT_OOC)
  18566. + //{
  18567. + // if (m_WaitUntil > m_ai->CurrentTime() && !m_ai->IsGroupInCombat())
  18568. + // return RETURN_NO_ACTION_OK; // wait it out
  18569. + // else
  18570. + // m_ai->ClearGroupCombatOrder(PlayerbotAI::ORDERS_TEMP_WAIT_OOC);
  18571. + //}
  18572. +
  18573. + //switch (m_ai->GetScenarioType())
  18574. + //{
  18575. + // case PlayerbotAI::SCENARIO_PVP_DUEL:
  18576. + // case PlayerbotAI::SCENARIO_PVP_BG:
  18577. + // case PlayerbotAI::SCENARIO_PVP_ARENA:
  18578. + // case PlayerbotAI::SCENARIO_PVP_OPENWORLD:
  18579. + // return DoFirstCombatManeuverPVP(pTarget);
  18580. + // case PlayerbotAI::SCENARIO_PVE:
  18581. + // case PlayerbotAI::SCENARIO_PVE_ELITE:
  18582. + // case PlayerbotAI::SCENARIO_PVE_RAID:
  18583. + // default:
  18584. + // return DoFirstCombatManeuverPVE(pTarget);
  18585. + // break;
  18586. + //}
  18587. +
  18588. + return RETURN_NO_ACTION_ERROR;
  18589. +}
  18590. +
  18591. +CombatManeuverReturns PlayerbotMageAI::DoFirstCombatManeuverPVE(Unit* /*pTarget*/)
  18592. +{
  18593. + return RETURN_NO_ACTION_OK;
  18594. +}
  18595. +
  18596. +CombatManeuverReturns PlayerbotMageAI::DoFirstCombatManeuverPVP(Unit* /*pTarget*/)
  18597. +{
  18598. + return RETURN_NO_ACTION_OK;
  18599. +}
  18600. +
  18601. +CombatManeuverReturns PlayerbotMageAI::DoNextCombatManeuver(Unit *pTarget)
  18602. +{
  18603. + //switch (m_ai->GetScenarioType())
  18604. + //{
  18605. + // case PlayerbotAI::SCENARIO_PVP_DUEL:
  18606. + // case PlayerbotAI::SCENARIO_PVP_BG:
  18607. + // case PlayerbotAI::SCENARIO_PVP_ARENA:
  18608. + // case PlayerbotAI::SCENARIO_PVP_OPENWORLD:
  18609. + // return DoNextCombatManeuverPVP(pTarget);
  18610. + // case PlayerbotAI::SCENARIO_PVE:
  18611. + // case PlayerbotAI::SCENARIO_PVE_ELITE:
  18612. + // case PlayerbotAI::SCENARIO_PVE_RAID:
  18613. + // default:
  18614. + // return DoNextCombatManeuverPVE(pTarget);
  18615. + // break;
  18616. + //}
  18617. +
  18618. + return RETURN_NO_ACTION_ERROR;
  18619. +}
  18620. +
  18621. +CombatManeuverReturns PlayerbotMageAI::DoNextCombatManeuverPVE(Unit *pTarget)
  18622. +{
  18623. + //if (!m_ai) return RETURN_NO_ACTION_ERROR;
  18624. + //if (!m_bot) return RETURN_NO_ACTION_ERROR;
  18625. +
  18626. + //Unit* pVictim = pTarget->getVictim();
  18627. + //float dist = pTarget->GetCombatReach();
  18628. + //uint32 spec = m_bot->GetSpec();
  18629. +
  18630. + //if (m_ai->GetCombatStyle() != PlayerbotAI::COMBAT_RANGED && dist > ATTACK_DISTANCE)
  18631. + // m_ai->SetCombatStyle(PlayerbotAI::COMBAT_RANGED);
  18632. + //// if can't shoot OR have no ranged (wand) equipped
  18633. + //else if(m_ai->GetCombatStyle() != PlayerbotAI::COMBAT_MELEE && (SHOOT == 0 || !m_bot->GetWeaponForAttack(RANGED_ATTACK, true)))
  18634. + // m_ai->SetCombatStyle(PlayerbotAI::COMBAT_MELEE);
  18635. +
  18636. + ////Used to determine if this bot is highest on threat
  18637. + //Unit *newTarget = m_ai->FindAttacker((PlayerbotAI::ATTACKERINFOTYPE) (PlayerbotAI::AIT_VICTIMSELF | PlayerbotAI::AIT_HIGHESTTHREAT), m_bot);
  18638. + //if (newTarget) // TODO: && party has a tank
  18639. + //{
  18640. + // // Insert instant threat reducing spell (if a mage has one)
  18641. +
  18642. + // // Have threat, can't quickly lower it. 3 options remain: Stop attacking, lowlevel damage (wand), keep on keeping on.
  18643. + // if (newTarget->GetHealthPct() > 25)
  18644. + // {
  18645. + // // If elite, do nothing and pray tank gets aggro off you
  18646. + // // TODO: Is there an IsElite function? If so, find it and insert.
  18647. + // //if (newTarget->IsElite())
  18648. + // // return;
  18649. +
  18650. + // // Not an elite. You could insert FEAR here but in any PvE situation that's 90-95% likely
  18651. + // // to worsen the situation for the group. ... So please don't.
  18652. + // CastSpell(SHOOT, pTarget);
  18653. + // return RETURN_CONTINUE;
  18654. + // }
  18655. + //}
  18656. +
  18657. + //switch (spec)
  18658. + //{
  18659. + // case MAGE_SPEC_FROST:
  18660. + // if (ICY_VEINS > 0 && !m_bot->HasAura(ICY_VEINS, EFFECT_0) && CastSpell(ICY_VEINS, m_bot))
  18661. + // return RETURN_CONTINUE;
  18662. + // if (ICE_BLOCK > 0 && pVictim == m_bot && !m_bot->HasAura(ICE_BLOCK, EFFECT_0) && CastSpell(ICE_BLOCK, m_bot))
  18663. + // return RETURN_CONTINUE;
  18664. + // if (ICE_BARRIER > 0 && pVictim == m_bot && !m_bot->HasAura(ICE_BARRIER, EFFECT_0) && m_bot->GetHealthPct() < 50 && CastSpell(ICE_BARRIER, m_bot))
  18665. + // return RETURN_CONTINUE;
  18666. + // if (DEEP_FREEZE > 0 && pTarget->HasAura(AURA_STATE_FROZEN, EFFECT_0) && !pTarget->HasAura(DEEP_FREEZE, EFFECT_0) && CastSpell(DEEP_FREEZE, pTarget))
  18667. + // return RETURN_CONTINUE;
  18668. + // if (BLIZZARD > 0 && m_ai->GetAttackerCount() >= 5 && CastSpell(BLIZZARD, pTarget))
  18669. + // {
  18670. + // //m_ai->SetIgnoreUpdateTime(8);
  18671. + // return RETURN_CONTINUE;
  18672. + // }
  18673. + // if (CONE_OF_COLD > 0 && dist <= ATTACK_DISTANCE && !pTarget->HasAura(CONE_OF_COLD, EFFECT_0) && CastSpell(CONE_OF_COLD, pTarget))
  18674. + // return RETURN_CONTINUE;
  18675. + // if (FROSTBOLT > 0 && !pTarget->HasAura(FROSTBOLT, EFFECT_0) && CastSpell(FROSTBOLT, pTarget))
  18676. + // return RETURN_CONTINUE;
  18677. + // if (FROST_WARD > 0 && !m_bot->HasAura(FROST_WARD, EFFECT_0) && CastSpell(FROST_WARD, m_bot))
  18678. + // return RETURN_CONTINUE;
  18679. + // if (FROST_NOVA > 0 && dist <= ATTACK_DISTANCE && !pTarget->HasAura(FROST_NOVA, EFFECT_0) && CastSpell(FROST_NOVA, pTarget))
  18680. + // return RETURN_CONTINUE;
  18681. + // if (ICE_LANCE > 0 && CastSpell(ICE_LANCE, pTarget))
  18682. + // return RETURN_CONTINUE;
  18683. + // if (SUMMON_WATER_ELEMENTAL > 0 && CastSpell(SUMMON_WATER_ELEMENTAL))
  18684. + // return RETURN_CONTINUE;
  18685. + // if (COLD_SNAP > 0 && CastSpell(COLD_SNAP, m_bot))
  18686. + // return RETURN_CONTINUE;
  18687. +
  18688. + // if (FROSTBOLT > 0)
  18689. + // return CastSpell(FROSTBOLT, pTarget);
  18690. + // break;
  18691. +
  18692. + // case MAGE_SPEC_FIRE:
  18693. + // if (FIRE_WARD > 0 && !m_bot->HasAura(FIRE_WARD, EFFECT_0) && CastSpell(FIRE_WARD, m_bot))
  18694. + // return RETURN_CONTINUE;
  18695. + // if (COMBUSTION > 0 && !m_bot->HasAura(COMBUSTION, EFFECT_0) && CastSpell(COMBUSTION, m_bot))
  18696. + // return RETURN_CONTINUE;
  18697. + // if (FIREBALL > 0 && CastSpell(FIREBALL, pTarget))
  18698. + // return RETURN_CONTINUE;
  18699. + // if (FIRE_BLAST > 0 && CastSpell(FIRE_BLAST, pTarget))
  18700. + // return RETURN_CONTINUE;
  18701. + // if (FLAMESTRIKE > 0 && CastSpell(FLAMESTRIKE, pTarget))
  18702. + // return RETURN_CONTINUE;
  18703. + // if (SCORCH > 0 && CastSpell(SCORCH, pTarget))
  18704. + // return RETURN_CONTINUE;
  18705. + // if (PYROBLAST > 0 && !pTarget->HasAura(PYROBLAST, EFFECT_0) && CastSpell(PYROBLAST, pTarget))
  18706. + // return RETURN_CONTINUE;
  18707. + // if (BLAST_WAVE > 0 && m_ai->GetAttackerCount() >= 3 && dist <= ATTACK_DISTANCE && CastSpell(BLAST_WAVE, pTarget))
  18708. + // return RETURN_CONTINUE;
  18709. + // if (DRAGONS_BREATH > 0 && dist <= ATTACK_DISTANCE && CastSpell(DRAGONS_BREATH, pTarget))
  18710. + // return RETURN_CONTINUE;
  18711. + // if (LIVING_BOMB > 0 && !pTarget->HasAura(LIVING_BOMB, EFFECT_0) && CastSpell(LIVING_BOMB, pTarget))
  18712. + // return RETURN_CONTINUE;
  18713. + // if (FROSTFIRE_BOLT > 0 && !pTarget->HasAura(FROSTFIRE_BOLT, EFFECT_0) && CastSpell(FROSTFIRE_BOLT, pTarget))
  18714. + // return RETURN_CONTINUE;
  18715. +
  18716. + // if (FIREBALL > 0)
  18717. + // return CastSpell(FIREBALL, pTarget);
  18718. + // break;
  18719. +
  18720. + // case MAGE_SPEC_ARCANE:
  18721. + // if (ARCANE_POWER > 0 && CastSpell(ARCANE_POWER, pTarget))
  18722. + // return RETURN_CONTINUE;
  18723. + // if (ARCANE_MISSILES > 0 && CastSpell(ARCANE_MISSILES, pTarget))
  18724. + // {
  18725. + // //m_ai->SetIgnoreUpdateTime(3);
  18726. + // return RETURN_CONTINUE;
  18727. + // }
  18728. + // if (ARCANE_EXPLOSION > 0 && m_ai->GetAttackerCount() >= 3 && dist <= ATTACK_DISTANCE && CastSpell(ARCANE_EXPLOSION, pTarget))
  18729. + // return RETURN_CONTINUE;
  18730. + // if (COUNTERSPELL > 0 && pTarget->IsNonMeleeSpellCasted(true) && CastSpell(COUNTERSPELL, pTarget))
  18731. + // return RETURN_CONTINUE;
  18732. + // if (SLOW > 0 && !pTarget->HasAura(SLOW, EFFECT_0) && CastSpell(SLOW, pTarget))
  18733. + // return RETURN_CONTINUE;
  18734. + // if (ARCANE_BARRAGE > 0 && CastSpell(ARCANE_BARRAGE, pTarget))
  18735. + // return RETURN_CONTINUE;
  18736. + // if (ARCANE_BLAST > 0 && CastSpell(ARCANE_BLAST, pTarget))
  18737. + // return RETURN_CONTINUE;
  18738. + // if (MIRROR_IMAGE > 0 && CastSpell(MIRROR_IMAGE))
  18739. + // return RETURN_CONTINUE;
  18740. + // if (MANA_SHIELD > 0 && m_bot->GetHealthPct() < 70 && pVictim == m_bot && !m_bot->HasAura(MANA_SHIELD, EFFECT_0) && CastSpell(MANA_SHIELD, m_bot))
  18741. + // return RETURN_CONTINUE;
  18742. +
  18743. + // if (FIREBALL > 0)
  18744. + // return CastSpell(FIREBALL, pTarget);
  18745. + // break;
  18746. + //}
  18747. +
  18748. + //// No spec due to low level OR no spell found yet
  18749. + //if (FROSTBOLT > 0 && !pTarget->HasAura(FROSTBOLT, EFFECT_0))
  18750. + // return CastSpell(FROSTBOLT, pTarget);
  18751. + //if (FIREBALL > 0) // Very low levels
  18752. + // return CastSpell(FIREBALL, pTarget);
  18753. +
  18754. + return RETURN_NO_ACTION_ERROR; // What? Not even Fireball is available?
  18755. +} // end DoNextCombatManeuver
  18756. +
  18757. +CombatManeuverReturns PlayerbotMageAI::DoNextCombatManeuverPVP(Unit* pTarget)
  18758. +{
  18759. + //if (m_ai->CastSpell(FIREBALL))
  18760. + // return RETURN_CONTINUE;
  18761. +
  18762. + return DoNextCombatManeuverPVE(pTarget); // TODO: bad idea perhaps, but better than the alternative
  18763. +}
  18764. +
  18765. +void PlayerbotMageAI::DoNonCombatActions()
  18766. +{
  18767. + //Player* master = GetMaster();
  18768. +
  18769. + //if (!m_bot || !master)
  18770. + // return;
  18771. +
  18772. + //// Buff armor
  18773. + //if (MOLTEN_ARMOR)
  18774. + //{
  18775. + // if (m_ai->SelfBuff(MOLTEN_ARMOR))
  18776. + // return;
  18777. + //}
  18778. + //else if (MAGE_ARMOR)
  18779. + //{
  18780. + // if (m_ai->SelfBuff(MAGE_ARMOR))
  18781. + // return;
  18782. + //}
  18783. + //else if (ICE_ARMOR)
  18784. + //{
  18785. + // if (m_ai->SelfBuff(ICE_ARMOR))
  18786. + // return;
  18787. + //}
  18788. + //else if (FROST_ARMOR)
  18789. + // if (m_ai->SelfBuff(FROST_ARMOR))
  18790. + // return;
  18791. +
  18792. + //// buff group
  18793. + //if (m_bot->GetGroup() && ARCANE_BRILLIANCE && m_ai->HasSpellReagents(ARCANE_BRILLIANCE) && m_ai->Buff(ARCANE_BRILLIANCE, m_bot))
  18794. + // return;
  18795. + //if (Buff(&PlayerbotMageAI::BuffHelper, ARCANE_INTELLECT, (JOB_ALL | JOB_MANAONLY)))
  18796. + // return;
  18797. +
  18798. + //// conjure food & water + hp/mana check
  18799. + //if (m_bot->getStandState() != UNIT_STAND_STATE_STAND)
  18800. + // m_bot->SetStandState(UNIT_STAND_STATE_STAND);
  18801. +
  18802. + //// TODO: The beauty of a mage is not only its ability to supply itself with water, but to share its water
  18803. + //// So, conjure at *least* 1.25 stacks, ready to trade a stack and still have some left for self
  18804. + //if (m_ai->FindDrink() == NULL && CONJURE_WATER && m_ai->CastSpell(CONJURE_WATER, *m_bot))
  18805. + //{
  18806. + // m_ai->TellMaster("I'm conjuring some water.");
  18807. + // //m_ai->SetIgnoreUpdateTime(3);
  18808. + // return;
  18809. + //}
  18810. + //if (m_ai->FindFood() == NULL && CONJURE_FOOD && m_ai->CastSpell(CONJURE_FOOD, *m_bot))
  18811. + //{
  18812. + // m_ai->TellMaster("I'm conjuring some food.");
  18813. + // //m_ai->SetIgnoreUpdateTime(3);
  18814. + // return;
  18815. + //}
  18816. +
  18817. + //if (EatDrinkBandage())
  18818. + // return;
  18819. +} // end DoNonCombatActions
  18820. +
  18821. +// TODO: this and priest's BuffHelper are identical and thus could probably go in PlayerbotClassAI.cpp somewhere
  18822. +bool PlayerbotMageAI::BuffHelper(PlayerbotAI* ai, uint32 spellId, Unit *target)
  18823. +{
  18824. + //if (!ai) return false;
  18825. + //if (spellId == 0) return false;
  18826. + //if (!target) return false;
  18827. +
  18828. + //Pet* pet = target->GetTypeId() == TYPEID_PLAYER ? target->ToPlayer()->GetPet() : NULL;
  18829. + //if (pet && !pet->HasAuraType(SPELL_AURA_MOD_UNATTACKABLE) && ai->Buff(spellId, pet))
  18830. + // return true;
  18831. +
  18832. + //if (ai->Buff(spellId, target))
  18833. + // return true;
  18834. +
  18835. + return false;
  18836. +}
  18837. diff --git a/src/server/game/AI/PlayerBots/bp_mag_ai.h b/src/server/game/AI/PlayerBots/bp_mag_ai.h
  18838. new file mode 100644
  18839. index 0000000..7a2a17d
  18840. --- /dev/null
  18841. +++ b/src/server/game/AI/PlayerBots/bp_mag_ai.h
  18842. @@ -0,0 +1,165 @@
  18843. +#ifndef _PlayerbotMageAI_H
  18844. +#define _PlayerbotMageAI_H
  18845. +
  18846. +#include "bp_cai.h"
  18847. +
  18848. +enum
  18849. +{
  18850. + SPELL_FROST,
  18851. + SPELL_FIRE,
  18852. + SPELL_ARCANE
  18853. +};
  18854. +
  18855. +enum MageSpells
  18856. +{
  18857. + AMPLIFY_MAGIC_1 = 1008,
  18858. + ARCANE_BARRAGE_1 = 44425,
  18859. + ARCANE_BLAST_1 = 30451,
  18860. + ARCANE_BRILLIANCE_1 = 23028,
  18861. + ARCANE_EXPLOSION_1 = 1449,
  18862. + ARCANE_INTELLECT_1 = 1459,
  18863. + ARCANE_MISSILES_1 = 5143,
  18864. + ARCANE_POWER_1 = 12042,
  18865. + BLAST_WAVE_1 = 11113,
  18866. + BLINK_1 = 1953,
  18867. + BLIZZARD_1 = 10,
  18868. + COLD_SNAP_1 = 11958,
  18869. + COMBUSTION_1 = 11129,
  18870. + CONE_OF_COLD_1 = 120,
  18871. + CONJURE_FOOD_1 = 587,
  18872. + CONJURE_MANA_GEM_1 = 759,
  18873. + CONJURE_REFRESHMENT_1 = 42955,
  18874. + CONJURE_WATER_1 = 5504,
  18875. + COUNTERSPELL_1 = 2139,
  18876. + DALARAN_BRILLIANCE_1 = 61316,
  18877. + DALARAN_INTELLECT_1 = 61024,
  18878. + DAMPEN_MAGIC_1 = 604,
  18879. + DEEP_FREEZE_1 = 44572,
  18880. + DRAGONS_BREATH_1 = 31661,
  18881. + EVOCATION_1 = 12051,
  18882. + FIRE_BLAST_1 = 2136,
  18883. + FIRE_WARD_1 = 543,
  18884. + FIREBALL_1 = 133,
  18885. + FLAMESTRIKE_1 = 2120,
  18886. + FOCUS_MAGIC_1 = 54646,
  18887. + FROST_ARMOR_1 = 168,
  18888. + FROST_NOVA_1 = 122,
  18889. + FROST_WARD_1 = 6143,
  18890. + FROSTBOLT_1 = 116,
  18891. + FROSTFIRE_BOLT_1 = 44614,
  18892. + ICE_ARMOR_1 = 7302,
  18893. + ICE_BARRIER_1 = 11426,
  18894. + ICE_BLOCK_1 = 45438,
  18895. + ICE_LANCE_1 = 30455,
  18896. + ICY_VEINS_1 = 12472,
  18897. + INVISIBILITY_1 = 66,
  18898. + LIVING_BOMB_1 = 44457,
  18899. + MAGE_ARMOR_1 = 6117,
  18900. + MANA_SHIELD_1 = 1463,
  18901. + MIRROR_IMAGE_1 = 55342,
  18902. + MOLTEN_ARMOR_1 = 30482,
  18903. + PRESENCE_OF_MIND_1 = 12043,
  18904. + PYROBLAST_1 = 11366,
  18905. + REMOVE_CURSE_MAGE_1 = 475,
  18906. + RITUAL_OF_REFRESHMENT_1 = 43987,
  18907. + SCORCH_1 = 2948,
  18908. + SHOOT_2 = 5019,
  18909. + SLOW_1 = 31589,
  18910. + SLOW_FALL_1 = 130,
  18911. + SPELLSTEAL_1 = 30449,
  18912. + SUMMON_WATER_ELEMENTAL_1 = 31687
  18913. +};
  18914. +//class Player;
  18915. +
  18916. +class PlayerbotMageAI : PlayerbotClassAI
  18917. +{
  18918. +public:
  18919. + PlayerbotMageAI(Player * const master, Player * const bot, PlayerbotAI * const ai);
  18920. + virtual ~PlayerbotMageAI();
  18921. +
  18922. + // all combat actions go here
  18923. + CombatManeuverReturns DoFirstCombatManeuver(Unit* pTarget);
  18924. + CombatManeuverReturns DoNextCombatManeuver(Unit* pTarget);
  18925. +
  18926. + // all non combat actions go here, ex buffs, heals, rezzes
  18927. + void DoNonCombatActions();
  18928. +
  18929. +private:
  18930. + CombatManeuverReturns DoFirstCombatManeuverPVE(Unit* pTarget);
  18931. + CombatManeuverReturns DoNextCombatManeuverPVE(Unit* pTarget);
  18932. + CombatManeuverReturns DoFirstCombatManeuverPVP(Unit* pTarget);
  18933. + CombatManeuverReturns DoNextCombatManeuverPVP(Unit* pTarget);
  18934. +
  18935. + CombatManeuverReturns CastSpell(uint32 nextAction, Unit *pTarget = NULL) { return CastSpellWand(nextAction, pTarget, SHOOT); }
  18936. +
  18937. + static bool BuffHelper(PlayerbotAI* ai, uint32 spellId, Unit *target);
  18938. +
  18939. + // ARCANE
  18940. + uint32 ARCANE_MISSILES,
  18941. + ARCANE_EXPLOSION,
  18942. + COUNTERSPELL,
  18943. + SLOW,
  18944. + ARCANE_BARRAGE,
  18945. + ARCANE_BLAST,
  18946. + MIRROR_IMAGE,
  18947. + ARCANE_POWER;
  18948. + // ranged
  18949. + uint32 SHOOT;
  18950. +
  18951. + // FIRE
  18952. + uint32 FIREBALL,
  18953. + FIRE_BLAST,
  18954. + FLAMESTRIKE,
  18955. + SCORCH,
  18956. + PYROBLAST,
  18957. + BLAST_WAVE,
  18958. + COMBUSTION,
  18959. + DRAGONS_BREATH,
  18960. + LIVING_BOMB,
  18961. + FROSTFIRE_BOLT,
  18962. + FIRE_WARD;
  18963. +
  18964. + // FROST
  18965. + uint32 DEEP_FREEZE,
  18966. + FROSTBOLT,
  18967. + FROST_NOVA,
  18968. + BLIZZARD,
  18969. + ICY_VEINS,
  18970. + CONE_OF_COLD,
  18971. + ICE_BARRIER,
  18972. + SUMMON_WATER_ELEMENTAL,
  18973. + ICE_LANCE,
  18974. + FROST_WARD,
  18975. + ICE_BLOCK,
  18976. + COLD_SNAP;
  18977. +
  18978. + // buffs
  18979. + uint32 FROST_ARMOR,
  18980. + ICE_ARMOR,
  18981. + MAGE_ARMOR,
  18982. + MOLTEN_ARMOR,
  18983. + ARCANE_INTELLECT,
  18984. + ARCANE_BRILLIANCE,
  18985. + DALARAN_INTELLECT,
  18986. + DALARAN_BRILLIANCE,
  18987. + MANA_SHIELD,
  18988. + DAMPEN_MAGIC,
  18989. + AMPLIFY_MAGIC;
  18990. +
  18991. + // racial
  18992. + uint32 ARCANE_TORRENT,
  18993. + GIFT_OF_THE_NAARU,
  18994. + STONEFORM,
  18995. + ESCAPE_ARTIST,
  18996. + EVERY_MAN_FOR_HIMSELF,
  18997. + SHADOWMELD,
  18998. + BLOOD_FURY,
  18999. + WAR_STOMP,
  19000. + BERSERKING,
  19001. + WILL_OF_THE_FORSAKEN;
  19002. +
  19003. + uint32 CONJURE_WATER,
  19004. + CONJURE_FOOD;
  19005. +};
  19006. +
  19007. +#endif
  19008. diff --git a/src/server/game/AI/PlayerBots/bp_mgr.cpp b/src/server/game/AI/PlayerBots/bp_mgr.cpp
  19009. new file mode 100644
  19010. index 0000000..b0e4d7c
  19011. --- /dev/null
  19012. +++ b/src/server/game/AI/PlayerBots/bp_mgr.cpp
  19013. @@ -0,0 +1,1136 @@
  19014. +//#include "Config/Config.h"
  19015. +#include "Common.h"
  19016. +#include "Config.h"
  19017. +#include "Player.h"
  19018. +#include "bp_ai.h"
  19019. +#include "bp_cai.h"
  19020. +#include "bp_mgr.h"
  19021. +//#include "WorldPacket.h"
  19022. +#include "WorldSession.h"
  19023. +//#include "Chat.h"
  19024. +//#include "ObjectMgr.h"
  19025. +#include "GossipDef.h"
  19026. +//#include "Language.h"
  19027. +//#include "WaypointMovementGenerator.h"
  19028. +//#include "Guild.h"
  19029. +#include "Group.h"
  19030. +#include "SpellInfo.h"
  19031. +#include "Opcodes.h"
  19032. +
  19033. +PlayerbotMgr::PlayerbotMgr(Player* const master) : m_master(master)
  19034. +{
  19035. + // load config variables
  19036. + m_confMaxNumBots = ConfigMgr::GetIntDefault("Bot.MaxPlayerbots", 9);
  19037. + m_confDebugWhisper = ConfigMgr::GetBoolDefault("Bot.DebugWhisper", false);
  19038. + m_confFollowDistance[0] = ConfigMgr::GetFloatDefault("Bot.FollowDistanceMin", 0.5f);
  19039. + m_confFollowDistance[1] = ConfigMgr::GetFloatDefault("Bot.FollowDistanceMax", 1.0f);
  19040. + m_confCollectCombat = ConfigMgr::GetBoolDefault("Bot.Collect.Combat", true);
  19041. + m_confCollectQuest = ConfigMgr::GetBoolDefault("Bot.Collect.Quest", true);
  19042. + m_confCollectProfession = ConfigMgr::GetBoolDefault("Bot.Collect.Profession", true);
  19043. + m_confCollectLoot = ConfigMgr::GetBoolDefault("Bot.Collect.Loot", true);
  19044. + m_confCollectSkin = ConfigMgr::GetBoolDefault("Bot.Collect.Skin", true);
  19045. + m_confCollectObjects = ConfigMgr::GetBoolDefault("Bot.Collect.Objects", true);
  19046. + m_confCollectDistanceMax = ConfigMgr::GetIntDefault("Bot.Collect.DistanceMax", 50);
  19047. + gConfigSellLevelDiff = ConfigMgr::GetIntDefault("Bot.SellAll.LevelDiff", 10);
  19048. + if (m_confCollectDistanceMax > 100)
  19049. + {
  19050. + //sLog->outError("Playerbot: Bot.Collect.DistanceMax higher than allowed. Using 100");
  19051. + m_confCollectDistanceMax = 100;
  19052. + }
  19053. + m_confCollectDistance = ConfigMgr::GetIntDefault("Bot.Collect.Distance", 25);
  19054. + if (m_confCollectDistance > m_confCollectDistanceMax)
  19055. + {
  19056. + //sLog.outError("Playerbot: PlayerbotAI.Collect.Distance higher than PlayerbotAI.Collect.DistanceMax. Using DistanceMax value");
  19057. + m_confCollectDistance = m_confCollectDistanceMax;
  19058. + }
  19059. +}
  19060. +
  19061. +PlayerbotMgr::~PlayerbotMgr()
  19062. +{
  19063. + if (!m_playerBots.empty())
  19064. + LogoutAllBots();
  19065. +}
  19066. +
  19067. +//void PlayerbotMgr::Update(uint32 const /*p_time*/) {}
  19068. +
  19069. +void PlayerbotMgr::HandleMasterIncomingPacket(WorldPacket const& packet)
  19070. +{
  19071. + switch (packet.GetOpcode())
  19072. + {
  19073. + case CMSG_TEXT_EMOTE:
  19074. + {
  19075. + WorldPacket p(packet);
  19076. + p.rpos(0); // reset reader
  19077. + uint32 emoteNum;
  19078. + p >> emoteNum;
  19079. +
  19080. + switch (emoteNum)
  19081. + {
  19082. + case TEXT_EMOTE_BONK:
  19083. + {
  19084. + uint64 sel = m_master->GetSelection();
  19085. + if (!sel || !IS_PLAYER_GUID(sel))
  19086. + return;
  19087. + Player* bot = GetPlayerBot(sel);
  19088. + if (!bot)
  19089. + return;
  19090. + PlayerbotAI* botAI = bot->GetPlayerbotAI();
  19091. + if (!botAI)
  19092. + return;
  19093. +
  19094. + botAI->SendDebugInfo();
  19095. + return;
  19096. + }
  19097. + }
  19098. + }
  19099. + }
  19100. +}
  19101. +//void PlayerbotMgr::HandleMasterIncomingPacket(WorldPacket const& packet)
  19102. +//{
  19103. +// switch (packet.GetOpcode())
  19104. +// {
  19105. +// //case CMSG_OFFER_PETITION:
  19106. +// //{
  19107. +// // WorldPacket p(packet);
  19108. +// // p.rpos(0); // reset reader
  19109. +// // uint64 petitionGuid;
  19110. +// // uint64 playerGuid;
  19111. +// // uint32 junk;
  19112. +//
  19113. +// // p >> junk; // this is not petition type!
  19114. +// // p >> petitionGuid; // petition guid
  19115. +// // p >> playerGuid; // player guid
  19116. +//
  19117. +// // Player* player = ObjectAccessor::FindPlayer(playerGuid);
  19118. +// // if (!player)
  19119. +// // return;
  19120. +//
  19121. +// // uint32 petitionLowGuid = GUID_LOPART(petitionGuid);
  19122. +//
  19123. +// // QueryResult result = CharacterDatabase.PQuery("SELECT * FROM petition_sign WHERE playerguid = '%u' AND petitionguid = '%u'", player->GetGUIDLow(), petitionLowGuid);
  19124. +//
  19125. +// // if (result)
  19126. +// // {
  19127. +// // ChatHandler(m_master).PSendSysMessage("%s has already signed the petition", player->GetName().c_str());
  19128. +// // delete result;
  19129. +// // return;
  19130. +// // }
  19131. +//
  19132. +// // CharacterDatabase.PExecute("INSERT INTO petition_sign (ownerguid,petitionguid, playerguid, player_account) VALUES ('%u', '%u', '%u','%u')",
  19133. +// // m_master->GetGUIDLow(), petitionLowGuid, player->GetGUIDLow(), m_master->GetSession()->GetAccountId());
  19134. +//
  19135. +// // p.Initialize(SMSG_PETITION_SIGN_RESULTS, (8+8+4));
  19136. +// // p << uint64(petitionGuid);
  19137. +// // p << uint64(playerGuid);
  19138. +// // p << uint32(PETITION_SIGN_OK);
  19139. +//
  19140. +// // // close at signer side
  19141. +// // m_master->GetSession()->SendPacket(&p);
  19142. +//
  19143. +// // return;
  19144. +// //}
  19145. +//
  19146. +// //case CMSG_ACTIVATETAXI:
  19147. +// // {
  19148. +// // WorldPacket p(packet);
  19149. +// // p.rpos(0); // reset reader
  19150. +//
  19151. +// // uint64 guid;
  19152. +// // std::vector<uint32> nodes;
  19153. +// // nodes.resize(2);
  19154. +// // //uint8 delay = 9;
  19155. +// // p >> guid >> nodes[0] >> nodes[1];
  19156. +//
  19157. +// // for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it)
  19158. +// // {
  19159. +//
  19160. +// // //delay = delay + 3;
  19161. +// // Player* bot = it->second;
  19162. +// // if (!bot)
  19163. +// // continue;
  19164. +//
  19165. +// // Group* group = bot->GetGroup();
  19166. +// // if (!group)
  19167. +// // continue;
  19168. +//
  19169. +// // Unit* target = sObjectAccessor->GetUnit(*bot, guid);
  19170. +//
  19171. +// // //bot->GetPlayerbotAI()->SetIgnoreUpdateTime(delay);
  19172. +//
  19173. +// // bot->GetMotionMaster()->Clear(true);
  19174. +// // bot->GetMotionMaster()->MoveFollow(target, INTERACTION_DISTANCE, bot->GetOrientation());
  19175. +// // bot->GetPlayerbotAI()->GetTaxi(guid, nodes);
  19176. +// // }
  19177. +// // return;
  19178. +// // }
  19179. +//
  19180. +// //case CMSG_ACTIVATETAXIEXPRESS:
  19181. +// // {
  19182. +// // WorldPacket p(packet);
  19183. +// // p.rpos(0); // reset reader
  19184. +//
  19185. +// // uint64 guid;
  19186. +// // uint32 node_count;
  19187. +// // //uint8 delay = 9;
  19188. +// // p >> guid;
  19189. +// // p.read_skip<uint32>();
  19190. +// // p >> node_count;
  19191. +//
  19192. +// // std::vector<uint32> nodes;
  19193. +// // for (uint32 i = 0; i < node_count; ++i)
  19194. +// // {
  19195. +// // uint32 node;
  19196. +// // p >> node;
  19197. +// // nodes.push_back(node);
  19198. +// // }
  19199. +//
  19200. +// // if (nodes.empty())
  19201. +// // return;
  19202. +//
  19203. +//
  19204. +// // for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it)
  19205. +// // {
  19206. +//
  19207. +// // //delay = delay + 3;
  19208. +// // Player* bot = it->second;
  19209. +// // if (!bot)
  19210. +// // return;
  19211. +// // Group* group = bot->GetGroup();
  19212. +// // if (!group)
  19213. +// // continue;
  19214. +// // Unit* target = sObjectAccessor->GetUnit(*bot, guid);
  19215. +//
  19216. +// // //bot->GetPlayerbotAI()->SetIgnoreUpdateTime(delay);
  19217. +//
  19218. +// // bot->GetMotionMaster()->Clear(true);
  19219. +// // bot->GetMotionMaster()->MoveFollow(target, INTERACTION_DISTANCE, bot->GetOrientation());
  19220. +// // bot->GetPlayerbotAI()->GetTaxi(guid, nodes);
  19221. +// // }
  19222. +// // return;
  19223. +// // }
  19224. +//
  19225. +// //case CMSG_MOVE_SPLINE_DONE:
  19226. +// // {
  19227. +// // // DEBUG_LOG ("[PlayerbotMgr]: HandleMasterIncomingPacket - Received CMSG_MOVE_SPLINE_DONE");
  19228. +//
  19229. +// // WorldPacket p(packet);
  19230. +// // p.rpos(0); // reset reader
  19231. +//
  19232. +// // ObjectGuid guid; // used only for proper packet read
  19233. +// // MovementInfo movementInfo; // used only for proper packet read
  19234. +//
  19235. +// // p >> guid.ReadAsPacked();
  19236. +// // p >> movementInfo;
  19237. +// // p >> Unused<uint32>(); // unk
  19238. +//
  19239. +// // for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it)
  19240. +// // {
  19241. +//
  19242. +// // Player* const bot = it->second;
  19243. +// // if (!bot)
  19244. +// // return;
  19245. +//
  19246. +// // // in taxi flight packet received in 2 case:
  19247. +// // // 1) end taxi path in far (multi-node) flight
  19248. +// // // 2) switch from one map to other in case multi-map taxi path
  19249. +// // // we need process only (1)
  19250. +// // uint32 curDest = bot->m_taxi.GetTaxiDestination();
  19251. +// // if (!curDest)
  19252. +// // return;
  19253. +//
  19254. +// // TaxiNodesEntry const* curDestNode = sTaxiNodesStore.LookupEntry(curDest);
  19255. +//
  19256. +// // // far teleport case
  19257. +// // if (curDestNode && curDestNode->map_id != bot->GetMapId())
  19258. +// // {
  19259. +// // if (bot->GetMotionMaster()->GetCurrentMovementGeneratorType() == FLIGHT_MOTION_TYPE)
  19260. +// // {
  19261. +// // // short preparations to continue flight
  19262. +// // FlightPathMovementGenerator* flight = (FlightPathMovementGenerator *) (bot->GetMotionMaster()->top());
  19263. +//
  19264. +// // flight->Interrupt(*bot); // will reset at map landing
  19265. +//
  19266. +// // flight->SetCurrentNodeAfterTeleport();
  19267. +// // TaxiPathNodeEntry const& node = flight->GetPath()[flight->GetCurrentNode()];
  19268. +// // flight->SkipCurrentNode();
  19269. +//
  19270. +// // bot->TeleportTo(curDestNode->map_id, node.x, node.y, node.z, bot->GetOrientation());
  19271. +// // }
  19272. +// // return;
  19273. +// // }
  19274. +//
  19275. +// // uint32 destinationnode = bot->m_taxi.NextTaxiDestination();
  19276. +// // if (destinationnode > 0) // if more destinations to go
  19277. +// // {
  19278. +// // // current source node for next destination
  19279. +// // uint32 sourcenode = bot->m_taxi.GetTaxiSource();
  19280. +//
  19281. +// // // Add to taximask middle hubs in taxicheat mode (to prevent having player with disabled taxicheat and not having back flight path)
  19282. +// // if (bot->isTaxiCheater())
  19283. +// // if (bot->m_taxi.SetTaximaskNode(sourcenode))
  19284. +// // {
  19285. +// // WorldPacket data(SMSG_NEW_TAXI_PATH, 0);
  19286. +// // bot->GetSession()->SendPacket(&data);
  19287. +// // }
  19288. +//
  19289. +// // // DEBUG_LOG ("[PlayerbotMgr]: HandleMasterIncomingPacket - Received CMSG_MOVE_SPLINE_DONE Taxi has to go from %u to %u", sourcenode, destinationnode);
  19290. +//
  19291. +// // uint32 mountDisplayId = sObjectMgr->GetTaxiMountDisplayId(sourcenode, bot->GetTeam());
  19292. +//
  19293. +// // uint32 path, cost;
  19294. +// // sObjectMgr->GetTaxiPath(sourcenode, destinationnode, path, cost);
  19295. +//
  19296. +// // if (path && mountDisplayId)
  19297. +// // bot->GetSession()->SendDoFlight(mountDisplayId, path, 1); // skip start fly node
  19298. +// // else
  19299. +// // bot->m_taxi.ClearTaxiDestinations(); // clear problematic path and next
  19300. +// // }
  19301. +// // else
  19302. +// // /* std::ostringstream out;
  19303. +// // out << "Destination reached" << bot->GetName();
  19304. +// // ChatHandler ch(m_master);
  19305. +// // ch.SendSysMessage(out.str().c_str()); */
  19306. +// // bot->m_taxi.ClearTaxiDestinations(); // Destination, clear source node
  19307. +// // }
  19308. +// // return;
  19309. +// // }
  19310. +//
  19311. +// // if master is logging out, log out all bots
  19312. +// case CMSG_LOGOUT_REQUEST:
  19313. +// {
  19314. +// LogoutAllBots();
  19315. +// return;
  19316. +// }
  19317. +//
  19318. +// // If master inspects one of his bots, give the master useful info in chat window
  19319. +// // such as inventory that can be equipped
  19320. +// //case CMSG_INSPECT:
  19321. +// //{
  19322. +// // WorldPacket p(packet);
  19323. +// // p.rpos(0); // reset reader
  19324. +// // uint64 guid;
  19325. +// // p >> guid;
  19326. +// // Player* bot = GetPlayerBot(guid);
  19327. +// // if (bot)
  19328. +// // bot->GetPlayerbotAI()->SendNotEquipList(*bot);
  19329. +// // return;
  19330. +// //}
  19331. +//
  19332. +// // handle emotes from the master
  19333. +// //case CMSG_EMOTE:
  19334. +// case CMSG_TEXT_EMOTE:
  19335. +// {
  19336. +// WorldPacket p(packet);
  19337. +// p.rpos(0); // reset reader
  19338. +// uint32 emoteNum;
  19339. +// p >> emoteNum;
  19340. +//
  19341. +// /* std::ostringstream out;
  19342. +// out << "emote is: " << emoteNum;
  19343. +// ChatHandler ch(m_master);
  19344. +// ch.SendSysMessage(out.str().c_str()); */
  19345. +//
  19346. +// switch (emoteNum)
  19347. +// {
  19348. +// case TEXT_EMOTE_BOW:
  19349. +// {
  19350. +// // Buff anyone who bows before me. Useful for players not in bot's group
  19351. +// // How do I get correct target???
  19352. +// //Player* const pPlayer = GetPlayerBot(m_master->GetSelection());
  19353. +// //if (pPlayer->GetPlayerbotAI()->GetClassAI())
  19354. +// // pPlayer->GetPlayerbotAI()->GetClassAI()->BuffPlayer(pPlayer);
  19355. +// return;
  19356. +// }
  19357. +// /*
  19358. +// case TEXT_EMOTE_BONK:
  19359. +// {
  19360. +// Player* const pPlayer = GetPlayerBot(m_master->GetSelection());
  19361. +// if (!pPlayer || !pPlayer->GetPlayerbotAI())
  19362. +// return;
  19363. +// PlayerbotAI* const pBot = pPlayer->GetPlayerbotAI();
  19364. +//
  19365. +// ChatHandler ch(m_master);
  19366. +// {
  19367. +// std::ostringstream out;
  19368. +// out << "CurrentTime: " << CurrentTime()
  19369. +// << " m_ignoreAIUpdatesUntilTime: " << pBot->m_ignoreAIUpdatesUntilTime;
  19370. +// ch.SendSysMessage(out.str().c_str());
  19371. +// }
  19372. +// {
  19373. +// std::ostringstream out;
  19374. +// out << "m_TimeDoneEating: " << pBot->m_TimeDoneEating
  19375. +// << " m_TimeDoneDrinking: " << pBot->m_TimeDoneDrinking;
  19376. +// ch.SendSysMessage(out.str().c_str());
  19377. +// }
  19378. +// {
  19379. +// std::ostringstream out;
  19380. +// out << "m_CurrentlyCastingSpellId: " << pBot->m_CurrentlyCastingSpellId;
  19381. +// ch.SendSysMessage(out.str().c_str());
  19382. +// }
  19383. +// {
  19384. +// std::ostringstream out;
  19385. +// out << "IsBeingTeleported() " << pBot->GetPlayer()->IsBeingTeleported();
  19386. +// ch.SendSysMessage(out.str().c_str());
  19387. +// }
  19388. +// {
  19389. +// std::ostringstream out;
  19390. +// bool tradeActive = (pBot->GetPlayer()->GetTrader()) ? true : false;
  19391. +// out << "tradeActive: " << tradeActive;
  19392. +// ch.SendSysMessage(out.str().c_str());
  19393. +// }
  19394. +// {
  19395. +// std::ostringstream out;
  19396. +// out << "IsCharmed() " << pBot->getPlayer()->isCharmed();
  19397. +// ch.SendSysMessage(out.str().c_str());
  19398. +// }
  19399. +// return;
  19400. +// }
  19401. +// */
  19402. +//
  19403. +// case TEXT_EMOTE_EAT:
  19404. +// case TEXT_EMOTE_DRINK:
  19405. +// {
  19406. +// for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it)
  19407. +// {
  19408. +// Player* bot = it->second;
  19409. +// //bot->GetPlayerbotAI()->Feast();
  19410. +// }
  19411. +// return;
  19412. +// }
  19413. +//
  19414. +// // emote to attack selected target
  19415. +// //case TEXT_EMOTE_POINT:
  19416. +// //{
  19417. +// // uint64 attackOnGuid = m_master->GetSelection();
  19418. +// // if (!attackOnGuid)
  19419. +// // return;
  19420. +//
  19421. +// // Unit* thingToAttack = sObjectAccessor->GetUnit(*m_master, attackOnGuid);
  19422. +// // if (!thingToAttack) return;
  19423. +//
  19424. +// // Player* bot = 0;
  19425. +// // for (PlayerBotMap::iterator itr = m_playerBots.begin(); itr != m_playerBots.end(); ++itr)
  19426. +// // {
  19427. +// // bot = itr->second;
  19428. +// // if (!bot->IsFriendlyTo(thingToAttack))
  19429. +// // {
  19430. +// // if (!bot->IsWithinLOSInMap(thingToAttack))
  19431. +// // bot->GetPlayerbotAI()->DoTeleport(*m_master);
  19432. +// // if (bot->IsWithinLOSInMap(thingToAttack))
  19433. +// // bot->GetPlayerbotAI()->Attack(thingToAttack);
  19434. +// // }
  19435. +// // }
  19436. +// // return;
  19437. +// //}
  19438. +//
  19439. +// // emote to stay
  19440. +// case TEXT_EMOTE_STAND:
  19441. +// {
  19442. +// uint64 sel = m_master->GetSelection();
  19443. +// if (!sel)
  19444. +// {
  19445. +// for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it)
  19446. +// it->second->GetPlayerbotAI()->SetMovementOrder(PlayerbotAI::MOVEMENT_STAY);
  19447. +// }
  19448. +// else if (Player* bot = GetPlayerBot(sel))
  19449. +// bot->GetPlayerbotAI()->SetMovementOrder(PlayerbotAI::MOVEMENT_STAY);
  19450. +// return;
  19451. +// }
  19452. +//
  19453. +// // 324 is the followme emote (not defined in enum)
  19454. +// // if master has bot selected then only bot follows, else all bots follow
  19455. +// case TEXT_EMOTE_FOLLOW:
  19456. +// case TEXT_EMOTE_WAVE:
  19457. +// {
  19458. +// uint64 sel = m_master->GetSelection();
  19459. +// if (!sel)
  19460. +// {
  19461. +// for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it)
  19462. +// it->second->GetPlayerbotAI()->SetMovementOrder(PlayerbotAI::MOVEMENT_FOLLOW);
  19463. +// }
  19464. +// else if (Player* bot = GetPlayerBot(sel))
  19465. +// bot->GetPlayerbotAI()->SetMovementOrder(PlayerbotAI::MOVEMENT_FOLLOW);
  19466. +// return;
  19467. +// }
  19468. +// }
  19469. +// return;
  19470. +// } /* EMOTE ends here */
  19471. +//
  19472. +// case CMSG_GAMEOBJ_USE: // not sure if we still need this one
  19473. +// case CMSG_GAMEOBJ_REPORT_USE:
  19474. +// {
  19475. +// WorldPacket p(packet);
  19476. +// p.rpos(0); // reset reader
  19477. +// uint64 objGUID;
  19478. +// p >> objGUID;
  19479. +// for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it)
  19480. +// {
  19481. +// Player* bot = it->second;
  19482. +// GameObject* obj = m_master->GetMap()->GetGameObject(objGUID);
  19483. +// if (!obj)
  19484. +// return;
  19485. +//
  19486. +// // add other go types here, i.e.:
  19487. +// // GAMEOBJECT_TYPE_CHEST - loot quest items of chest
  19488. +// if (obj->GetGoType() == GAMEOBJECT_TYPE_QUESTGIVER)
  19489. +// {
  19490. +// bot->GetPlayerbotAI()->TurnInQuests(obj);
  19491. +//
  19492. +// // auto accept every available quest this NPC has
  19493. +// bot->PrepareQuestMenu(objGUID);
  19494. +// QuestMenu& questMenu = bot->PlayerTalkClass->GetQuestMenu();
  19495. +// for (uint32 iI = 0; iI < questMenu.GetMenuItemCount(); ++iI)
  19496. +// {
  19497. +// QuestMenuItem const& qItem = questMenu.GetItem(iI);
  19498. +// uint32 questID = qItem.QuestId;
  19499. +// if (!bot->GetPlayerbotAI()->AddQuest(questID, obj))
  19500. +// bot->GetPlayerbotAI()->TellMaster("Couldn't take quest");
  19501. +// }
  19502. +// }
  19503. +// }
  19504. +// }
  19505. +// break;
  19506. +//
  19507. +// case CMSG_QUESTGIVER_HELLO:
  19508. +// {
  19509. +// WorldPacket p(packet);
  19510. +// p.rpos(0); // reset reader
  19511. +// uint64 npcGUID;
  19512. +// p >> npcGUID;
  19513. +// WorldObject* pNpc = sObjectAccessor->GetWorldObject(*m_master, npcGUID);
  19514. +// if (!pNpc)
  19515. +// return;
  19516. +//
  19517. +// // for all master's bots
  19518. +// for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it)
  19519. +// {
  19520. +// Player* bot = it->second;
  19521. +// bot->GetPlayerbotAI()->TurnInQuests(pNpc);
  19522. +// }
  19523. +//
  19524. +// return;
  19525. +// }
  19526. +//
  19527. +// // if master accepts a quest, bots should also try to accept quest
  19528. +// case CMSG_QUESTGIVER_ACCEPT_QUEST:
  19529. +// {
  19530. +// WorldPacket p(packet);
  19531. +// p.rpos(0); // reset reader
  19532. +// uint64 guid;
  19533. +// uint32 quest;
  19534. +// uint32 unk1;
  19535. +// p >> guid >> quest >> unk1;
  19536. +//
  19537. +// Quest const* qInfo = sObjectMgr->GetQuestTemplate(quest);
  19538. +// if (qInfo)
  19539. +// for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it)
  19540. +// {
  19541. +// Player* bot = it->second;
  19542. +// if (bot->GetQuestStatus(quest) == QUEST_STATUS_COMPLETE)
  19543. +// bot->GetPlayerbotAI()->TellMaster("I already completed that quest.");
  19544. +// else if (!bot->CanTakeQuest(qInfo, false))
  19545. +// {
  19546. +// if (!bot->SatisfyQuestStatus(qInfo, false))
  19547. +// bot->GetPlayerbotAI()->TellMaster("I already have that quest.");
  19548. +// else
  19549. +// bot->GetPlayerbotAI()->TellMaster("I can't take that quest.");
  19550. +// }
  19551. +// else if (!bot->SatisfyQuestLog(false))
  19552. +// bot->GetPlayerbotAI()->TellMaster("My quest log is full.");
  19553. +// else if (!bot->CanAddQuest(qInfo, false))
  19554. +// bot->GetPlayerbotAI()->TellMaster("I can't take that quest because it requires that I take items, but my bags are full!");
  19555. +// else
  19556. +// {
  19557. +// p.rpos(0); // reset reader
  19558. +// bot->GetSession()->HandleQuestgiverAcceptQuestOpcode(p);
  19559. +// bot->GetPlayerbotAI()->TellMaster("Got the quest.");
  19560. +//
  19561. +// // build needed items if quest contains any
  19562. +// for (uint8 i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; i++)
  19563. +// {
  19564. +// if (qInfo->RequiredItemCount[i] > 0)
  19565. +// {
  19566. +// bot->GetPlayerbotAI()->SetQuestNeedItems();
  19567. +// break;
  19568. +// }
  19569. +//
  19570. +// // build needed creatures if quest contains any
  19571. +// for (uint8 i = 0; i < QUEST_OBJECTIVES_COUNT; i++)
  19572. +// {
  19573. +// if (qInfo->RequiredNpcOrGoCount[i] > 0)
  19574. +// {
  19575. +// bot->GetPlayerbotAI()->SetQuestNeedCreatures();
  19576. +// break;
  19577. +// }
  19578. +// }
  19579. +// }
  19580. +// }
  19581. +// }
  19582. +// return;
  19583. +// }
  19584. +//
  19585. +// case CMSG_AREATRIGGER:
  19586. +// {
  19587. +// WorldPacket p(packet);
  19588. +//
  19589. +// for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it)
  19590. +// {
  19591. +// Player* bot = it->second;
  19592. +// if (!bot)
  19593. +// continue;
  19594. +//
  19595. +// if (bot->IsWithinDistInMap(m_master, 50))
  19596. +// {
  19597. +// p.rpos(0); // reset reader
  19598. +// bot->GetSession()->HandleAreaTriggerOpcode(p);
  19599. +// }
  19600. +// }
  19601. +// return;
  19602. +// }
  19603. +//
  19604. +// case CMSG_QUESTGIVER_COMPLETE_QUEST:
  19605. +// {
  19606. +// WorldPacket p(packet);
  19607. +// p.rpos(0); // reset reader
  19608. +// uint32 quest;
  19609. +// uint64 npcGUID;
  19610. +// p >> npcGUID >> quest;
  19611. +//
  19612. +// // DEBUG_LOG ("[PlayerbotMgr]: HandleMasterIncomingPacket - Received CMSG_QUESTGIVER_COMPLETE_QUEST npc = %s, quest = %u", npcGUID.GetString().c_str(), quest);
  19613. +//
  19614. +// WorldObject* pNpc = sObjectAccessor->GetWorldObject(*m_master, npcGUID);
  19615. +// if (!pNpc)
  19616. +// return;
  19617. +//
  19618. +// // for all master's bots
  19619. +// for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it)
  19620. +// {
  19621. +// Player* bot = it->second;
  19622. +// bot->GetPlayerbotAI()->TurnInQuests(pNpc);
  19623. +// }
  19624. +// return;
  19625. +// }
  19626. +//
  19627. +// case CMSG_LOOT_ROLL:
  19628. +// {
  19629. +// WorldPacket p(packet); //WorldPacket packet for CMSG_LOOT_ROLL, (8+4+1)
  19630. +// uint64 Guid;
  19631. +// uint32 itemSlot;
  19632. +// uint8 rollType;
  19633. +// Loot* loot = NULL;
  19634. +//
  19635. +// p.rpos(0); //reset packet pointer
  19636. +// p >> Guid; //guid of the lootable target
  19637. +// p >> itemSlot; //loot index
  19638. +// p >> rollType; //need,greed or pass on roll
  19639. +//
  19640. +// if (IS_CREATURE_GUID(Guid))
  19641. +// {
  19642. +// if (Creature* c = m_master->GetMap()->GetCreature(Guid))
  19643. +// loot = &c->loot;
  19644. +// }
  19645. +// else if (IS_GAMEOBJECT_GUID(Guid))
  19646. +// {
  19647. +// if (GameObject* go = m_master->GetMap()->GetGameObject(Guid))
  19648. +// loot = &go->loot;
  19649. +// }
  19650. +//
  19651. +// if (!loot)
  19652. +// return;
  19653. +//
  19654. +// LootItem& lootItem = loot->items[itemSlot];
  19655. +// ItemTemplate const* pProto = sObjectMgr->GetItemTemplate(lootItem.itemid);
  19656. +// if (!pProto)
  19657. +// return;
  19658. +//
  19659. +// for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it)
  19660. +// {
  19661. +// uint32 choice = 0;
  19662. +//
  19663. +// Player* bot = it->second;
  19664. +// if (!bot)
  19665. +// continue;
  19666. +//
  19667. +// Group* group = bot->GetGroup();
  19668. +// if (!group || group != m_master->GetGroup())
  19669. +// continue;
  19670. +//
  19671. +// if (bot->GetPlayerbotAI()->CanStore())
  19672. +// {
  19673. +// if (bot->CanUseItem(pProto) == EQUIP_ERR_OK && bot->GetPlayerbotAI()->IsItemUseful(lootItem.itemid))
  19674. +// choice = 1; // Need
  19675. +// else if (pProto->Quality < ITEM_QUALITY_LEGENDARY && bot->HasSkill(SKILL_ENCHANTING))
  19676. +// choice = 3; // Disenchant
  19677. +// else
  19678. +// choice = 2; // Greed
  19679. +// }
  19680. +// else
  19681. +// choice = 0; // Pass
  19682. +//
  19683. +// bot->GetPlayerbotAI()->BeingRolledOn(Guid);
  19684. +//
  19685. +// group->CountRollVote(bot->GetGUID(), Guid, RollVote(choice));
  19686. +//
  19687. +// switch (choice)
  19688. +// {
  19689. +// case ROLL_NEED:
  19690. +// bot->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED, 1);
  19691. +// break;
  19692. +// case ROLL_GREED:
  19693. +// case ROLL_DISENCHANT:
  19694. +// bot->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED, 1);
  19695. +// break;
  19696. +// }
  19697. +// }
  19698. +// return;
  19699. +// }
  19700. +//
  19701. +// // Handle GOSSIP activate actions, prior to GOSSIP select menu actions
  19702. +// case CMSG_GOSSIP_HELLO:
  19703. +// {
  19704. +// // DEBUG_LOG ("[PlayerbotMgr]: HandleMasterIncomingPacket - Received CMSG_GOSSIP_HELLO");
  19705. +//
  19706. +// WorldPacket p(packet); //WorldPacket packet for CMSG_GOSSIP_HELLO, (8)
  19707. +// uint64 guid;
  19708. +// p.rpos(0); //reset packet pointer
  19709. +// p >> guid;
  19710. +// for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it)
  19711. +// {
  19712. +// Player* bot = it->second;
  19713. +// if (!bot)
  19714. +// continue;
  19715. +// Creature* pCreature = bot->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_NONE);
  19716. +// if (!pCreature)
  19717. +// {
  19718. +// //DEBUG_LOG ("[PlayerbotMgr]: HandleMasterIncomingPacket - Received CMSG_GOSSIP_HELLO %s not found or you can't interact with him.", guid.GetString().c_str());
  19719. +// continue;
  19720. +// }
  19721. +//
  19722. +// GossipMenuItemsMapBounds pMenuItemBounds = sObjectMgr->GetGossipMenuItemsMapBounds(pCreature->GetCreatureTemplate()->GossipMenuId);
  19723. +// for (GossipMenuItemsContainer::const_iterator itr = pMenuItemBounds.first; itr != pMenuItemBounds.second; ++itr)
  19724. +// {
  19725. +// uint32 npcflags = pCreature->GetUInt32Value(UNIT_NPC_FLAGS);
  19726. +//
  19727. +// if (!(itr->second.OptionNpcflag & npcflags))
  19728. +// continue;
  19729. +//
  19730. +// switch (itr->second.OptionType)
  19731. +// {
  19732. +// case GOSSIP_OPTION_TAXIVENDOR:
  19733. +// {
  19734. +// // bot->GetPlayerbotAI()->TellMaster("PlayerbotMgr:GOSSIP_OPTION_TAXIVENDOR");
  19735. +// bot->GetSession()->SendLearnNewTaxiNode(pCreature);
  19736. +// break;
  19737. +// }
  19738. +// case GOSSIP_OPTION_QUESTGIVER:
  19739. +// {
  19740. +// // bot->GetPlayerbotAI()->TellMaster("PlayerbotMgr:GOSSIP_OPTION_QUESTGIVER");
  19741. +// bot->GetPlayerbotAI()->TurnInQuests(pCreature);
  19742. +// break;
  19743. +// }
  19744. +// case GOSSIP_OPTION_VENDOR:
  19745. +// {
  19746. +// // bot->GetPlayerbotAI()->TellMaster("PlayerbotMgr:GOSSIP_OPTION_VENDOR");
  19747. +// if (!ConfigMgr::GetBoolDefault("PlayerbotAI.SellGarbage", true))
  19748. +// continue;
  19749. +//
  19750. +// // changed the SellGarbage() function to support ch.SendSysMessaage()
  19751. +// bot->GetPlayerbotAI()->SellGarbage(*bot);
  19752. +// break;
  19753. +// }
  19754. +// case GOSSIP_OPTION_STABLEPET:
  19755. +// {
  19756. +// // bot->GetPlayerbotAI()->TellMaster("PlayerbotMgr:GOSSIP_OPTION_STABLEPET");
  19757. +// break;
  19758. +// }
  19759. +// case GOSSIP_OPTION_AUCTIONEER:
  19760. +// {
  19761. +// // bot->GetPlayerbotAI()->TellMaster("PlayerbotMgr:GOSSIP_OPTION_AUCTIONEER");
  19762. +// break;
  19763. +// }
  19764. +// case GOSSIP_OPTION_BANKER:
  19765. +// {
  19766. +// // bot->GetPlayerbotAI()->TellMaster("PlayerbotMgr:GOSSIP_OPTION_BANKER");
  19767. +// break;
  19768. +// }
  19769. +// case GOSSIP_OPTION_INNKEEPER:
  19770. +// {
  19771. +// // bot->GetPlayerbotAI()->TellMaster("PlayerbotMgr:GOSSIP_OPTION_INNKEEPER");
  19772. +// break;
  19773. +// }
  19774. +// }
  19775. +// }
  19776. +// }
  19777. +// return;
  19778. +// }
  19779. +//
  19780. +// //case CMSG_SPIRIT_HEALER_ACTIVATE:
  19781. +// //{
  19782. +// // for (PlayerBotMap::iterator itr = m_playerBots.begin(); itr != m_playerBots.end(); ++itr)
  19783. +// // {
  19784. +// // if (Group* grp = itr->second->GetGroup())
  19785. +// // grp->RemoveMember(itr->first, 1);
  19786. +// // }
  19787. +// // return;
  19788. +// //}
  19789. +//
  19790. +// case CMSG_LIST_INVENTORY:
  19791. +// {
  19792. +// if (!ConfigMgr::GetBoolDefault("PlayerbotAI.SellGarbage", true))
  19793. +// return;
  19794. +//
  19795. +// WorldPacket p(packet);
  19796. +// p.rpos(0); // reset reader
  19797. +// uint64 npcGUID;
  19798. +// p >> npcGUID;
  19799. +// WorldObject* const pNpc = (WorldObject*)sObjectAccessor->GetObjectByTypeMask(*m_master, npcGUID, TYPEMASK_UNIT | TYPEMASK_GAMEOBJECT);
  19800. +// if (!pNpc)
  19801. +// return;
  19802. +//
  19803. +// // for all master's bots
  19804. +// for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it)
  19805. +// {
  19806. +// Player* bot = it->second;
  19807. +// if (!bot->IsInMap(pNpc))
  19808. +// {
  19809. +// bot->GetPlayerbotAI()->TellMaster("I'm too far away to sell items!");
  19810. +// continue;
  19811. +// }
  19812. +// else
  19813. +// {
  19814. +// // changed the SellGarbage() function to support ch.SendSysMessaage()
  19815. +// bot->GetPlayerbotAI()->SellGarbage(*bot);
  19816. +// }
  19817. +// }
  19818. +// return;
  19819. +// }
  19820. +//
  19821. +// /*
  19822. +// case CMSG_NAME_QUERY:
  19823. +// case MSG_MOVE_START_FORWARD:
  19824. +// case MSG_MOVE_STOP:
  19825. +// case MSG_MOVE_SET_FACING:
  19826. +// case MSG_MOVE_START_STRAFE_LEFT:
  19827. +// case MSG_MOVE_START_STRAFE_RIGHT:
  19828. +// case MSG_MOVE_STOP_STRAFE:
  19829. +// case MSG_MOVE_START_BACKWARD:
  19830. +// case MSG_MOVE_HEARTBEAT:
  19831. +// case CMSG_STANDSTATECHANGE:
  19832. +// case CMSG_QUERY_TIME:
  19833. +// case CMSG_CREATURE_QUERY:
  19834. +// case CMSG_GAMEOBJECT_QUERY:
  19835. +// case MSG_MOVE_JUMP:
  19836. +// case MSG_MOVE_FALL_LAND:
  19837. +// return;
  19838. +//
  19839. +// default:
  19840. +// {
  19841. +// const char* oc = LookupOpcodeName(packet.GetOpcode());
  19842. +// // ChatHandler ch(m_master);
  19843. +// // ch.SendSysMessage(oc);
  19844. +//
  19845. +// std::ostringstream out;
  19846. +// out << "masterin: " << oc;
  19847. +// sLog.outError(out.str().c_str());
  19848. +// }
  19849. +// */
  19850. +// }
  19851. +//}
  19852. +//
  19853. +void PlayerbotMgr::HandleMasterOutgoingPacket(WorldPacket const& /*packet*/)
  19854. +{
  19855. + /*
  19856. + switch (packet.GetOpcode())
  19857. + {
  19858. + // maybe our bots should only start looting after the master loots?
  19859. + //case SMSG_LOOT_RELEASE_RESPONSE: {}
  19860. + case SMSG_NAME_QUERY_RESPONSE:
  19861. + case SMSG_MONSTER_MOVE:
  19862. + case SMSG_COMPRESSED_UPDATE_OBJECT:
  19863. + case SMSG_DESTROY_OBJECT:
  19864. + case SMSG_UPDATE_OBJECT:
  19865. + case SMSG_STANDSTATE_UPDATE:
  19866. + case MSG_MOVE_HEARTBEAT:
  19867. + case SMSG_QUERY_TIME_RESPONSE:
  19868. + case SMSG_AURA_UPDATE_ALL:
  19869. + case SMSG_CREATURE_QUERY_RESPONSE:
  19870. + case SMSG_GAMEOBJECT_QUERY_RESPONSE:
  19871. + return;
  19872. + default:
  19873. + {
  19874. + const char* oc = LookupOpcodeName(packet.GetOpcode());
  19875. +
  19876. + std::ostringstream out;
  19877. + out << "masterout: " << oc;
  19878. + sLog.outError(out.str().c_str());
  19879. + }
  19880. + }
  19881. + */
  19882. +}
  19883. +
  19884. +void PlayerbotMgr::LogoutAllBots()
  19885. +{
  19886. + RemoveAllBotsFromGroup();
  19887. + while (true)
  19888. + {
  19889. + PlayerBotMap::const_iterator itr = GetPlayerBotsBegin();
  19890. + if (itr == GetPlayerBotsEnd()) break;
  19891. + Player* bot = itr->second;
  19892. + LogoutPlayerBot(bot->GetGUID());
  19893. + }
  19894. +}
  19895. +
  19896. +void PlayerbotMgr::Stay()
  19897. +{
  19898. + for (PlayerBotMap::const_iterator itr = GetPlayerBotsBegin(); itr != GetPlayerBotsEnd(); ++itr)
  19899. + {
  19900. + itr->second->GetMotionMaster()->Clear();
  19901. + }
  19902. +}
  19903. +
  19904. +// Playerbot mod: logs out a Playerbot.
  19905. +void PlayerbotMgr::LogoutPlayerBot(uint64 guid, bool uninvite)
  19906. +{
  19907. + if (Player* bot = GetPlayerBot(guid))
  19908. + {
  19909. + if (uninvite)
  19910. + if (Group* group = bot->GetGroup())
  19911. + group->RemoveMember(guid);
  19912. +
  19913. + WorldSession* botWorldSession = bot->GetSession();
  19914. + botWorldSession->m_master = NULL;
  19915. + m_playerBots.erase(guid); //deletes bot player ptr inside this WorldSession PlayerBotMap
  19916. + botWorldSession->LogoutPlayer(true); //this will delete the bot Player object and PlayerbotAI (along with class AI) object
  19917. + delete botWorldSession; //finally delete the bot's WorldSession
  19918. + botWorldSession = NULL;
  19919. + return;
  19920. + }
  19921. + std::string name;
  19922. + sObjectMgr->GetPlayerNameByGUID(guid, name);
  19923. + sLog->outFatal(LOG_FILTER_PLAYER, "PlayerbotMgr:: Trying to logout playerbot (%s, guid %u) which does not exist!!!", name.c_str(), GUID_LOPART(guid));
  19924. +}
  19925. +
  19926. +// Playerbot mod: Gets a player bot Player object for this WorldSession master
  19927. +Player* PlayerbotMgr::GetPlayerBot(uint64 playerGuid) const
  19928. +{
  19929. + PlayerBotMap::const_iterator it = m_playerBots.find(playerGuid);
  19930. + return (it == m_playerBots.end()) ? NULL : it->second;
  19931. +}
  19932. +
  19933. +void PlayerbotMgr::OnBotLogin(Player* const bot)
  19934. +{
  19935. + if (!bot)
  19936. + return;
  19937. +
  19938. + // tell the world session that they now manage this new bot
  19939. + m_playerBots[bot->GetGUID()] = bot;
  19940. +
  19941. + // if bot is in a group and master is not in group then
  19942. + // have bot leave their group
  19943. + if (bot->GetGroup() && (!m_master->GetGroup() || !m_master->GetGroup()->IsMember(bot->GetGUID())))
  19944. + bot->RemoveFromGroup();
  19945. +
  19946. + // sometimes master can lose leadership, pass leadership to master check
  19947. + uint64 masterGuid = m_master->GetGUID();
  19948. + if (m_master->GetGroup() && !m_master->GetGroup()->IsLeader(masterGuid))
  19949. + {
  19950. + // But only do so if one of the master's bots is leader
  19951. + for (PlayerBotMap::const_iterator itr = GetPlayerBotsBegin(); itr != GetPlayerBotsEnd(); itr++)
  19952. + {
  19953. + Player* bot = itr->second;
  19954. + if (m_master->GetGroup()->IsLeader(bot->GetGUID()))
  19955. + {
  19956. + m_master->GetGroup()->ChangeLeader(masterGuid);
  19957. + break;
  19958. + }
  19959. + }
  19960. + }
  19961. + // Finally, give the bot some AI, object is owned by the player class
  19962. + PlayerbotAI* ai = new PlayerbotAI(this, bot);
  19963. + bot->SetPlayerbotAI(ai);
  19964. +}
  19965. +
  19966. +void PlayerbotMgr::RemoveAllBotsFromGroup(bool force)
  19967. +{
  19968. + Group* gr = m_master->GetGroup();
  19969. + if (gr || force)
  19970. + for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); m_master->GetGroup() && it != GetPlayerBotsEnd(); ++it)
  19971. + if (force || gr == it->second->GetGroup())
  19972. + gr->RemoveMember(it->first);
  19973. +}
  19974. +
  19975. +void Player::GetSkills(std::list<uint32>& m_spellsToLearn)
  19976. +{
  19977. + for (SkillStatusMap::const_iterator itr = mSkillStatus.begin(); itr != mSkillStatus.end(); ++itr)
  19978. + {
  19979. + if (itr->second.uState == SKILL_DELETED)
  19980. + continue;
  19981. +
  19982. + m_spellsToLearn.push_back(itr->first);
  19983. + }
  19984. +}
  19985. +
  19986. +void Player::MakeTalentGlyphLink(std::ostringstream &out)
  19987. +{
  19988. + // |cff4e96f7|Htalent:1396:4|h[Unleashed Fury]|h|r
  19989. + // |cff66bbff|Hglyph:23:460|h[Glyph of Fortitude]|h|r
  19990. +
  19991. + if (m_specsCount)
  19992. + // loop through all specs (only 1 for now)
  19993. + for (uint32 specIdx = 0; specIdx < m_specsCount; ++specIdx)
  19994. + {
  19995. + // find class talent tabs (all players have 3 talent tabs)
  19996. + uint32 const* talentTabIds = GetTalentTabPages(getClass());
  19997. +
  19998. + out << "\n" << "Active Talents ";
  19999. +
  20000. + for (uint32 i = 0; i < 3; ++i)
  20001. + {
  20002. + uint32 talentTabId = talentTabIds[i];
  20003. + for (PlayerTalentMap::iterator iter = m_talents[specIdx]->begin(); iter != m_talents[specIdx]->end(); ++iter)
  20004. + {
  20005. + PlayerTalent *talent = (*iter).second;
  20006. +
  20007. + if (talent->state == PLAYERSPELL_REMOVED)
  20008. + continue;
  20009. +
  20010. + TalentSpellPos const* talentPos = GetTalentSpellPos((*iter).first);
  20011. + if (!talentPos)
  20012. + continue;
  20013. + TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentPos->talent_id);
  20014. + if (!talentInfo)
  20015. + continue;
  20016. + // skip another tab talents
  20017. + if (talentInfo->TalentTab != talentTabId)
  20018. + continue;
  20019. +
  20020. + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo((*iter).first);
  20021. + if (!spellInfo)
  20022. + continue;
  20023. +
  20024. + out << "|cff4e96f7|Htalent:" << talentInfo->TalentID << ":" << talentInfo->RankID[spellInfo->GetRank()]
  20025. + << " |h[" << spellInfo->SpellName[GetSession()->GetSessionDbcLocale()] << "]|h|r";
  20026. + }
  20027. + }
  20028. +
  20029. + uint32 freepoints = 0;
  20030. +
  20031. + out << " Unspent points : ";
  20032. +
  20033. + if ((freepoints = GetFreeTalentPoints()) > 0)
  20034. + out << "|h|cff00ff00" << freepoints << "|h|r";
  20035. + else
  20036. + out << "|h|cffff0000" << freepoints << "|h|r";
  20037. +
  20038. + out << "\n" << "Active Glyphs ";
  20039. + // GlyphProperties.dbc
  20040. + for (uint8 i = 0; i < MAX_GLYPH_SLOT_INDEX; ++i)
  20041. + {
  20042. + GlyphPropertiesEntry const* glyph = sGlyphPropertiesStore.LookupEntry(m_Glyphs[specIdx][i]);
  20043. + if (!glyph)
  20044. + continue;
  20045. +
  20046. + SpellEntry const* spell_entry = sSpellStore.LookupEntry(glyph->SpellId);
  20047. +
  20048. + out << "|cff66bbff|Hglyph:" << GetGlyphSlot(i) << ":" << m_Glyphs[specIdx][i]
  20049. + << " |h[" << spell_entry->SpellName[GetSession()->GetSessionDbcLocale()] << "]|h|r";
  20050. +
  20051. + }
  20052. + }
  20053. +}
  20054. +
  20055. +void Player::chompAndTrim(std::string& str)
  20056. +{
  20057. + while (str.length() > 0)
  20058. + {
  20059. + char lc = str[str.length() - 1];
  20060. + if (lc == '\r' || lc == '\n' || lc == ' ' || lc == '"' || lc == '\'')
  20061. + str = str.substr(0, str.length() - 1);
  20062. + else
  20063. + break;
  20064. + }
  20065. +
  20066. + while (str.length() > 0)
  20067. + {
  20068. + char lc = str[0];
  20069. + if (lc == ' ' || lc == '"' || lc == '\'')
  20070. + str = str.substr(1, str.length() - 1);
  20071. + else
  20072. + break;
  20073. + }
  20074. +}
  20075. +
  20076. +bool Player::getNextQuestId(std::string const& pString, uint32& pStartPos, uint32& pId)
  20077. +{
  20078. + bool result = false;
  20079. + uint32 i;
  20080. + for (i = pStartPos; i < pString.size(); ++i)
  20081. + {
  20082. + if (pString[i] == ',')
  20083. + break;
  20084. + }
  20085. + if (i > pStartPos)
  20086. + {
  20087. + std::string idString = pString.substr(pStartPos, i - pStartPos);
  20088. + pStartPos = i + 1;
  20089. + chompAndTrim(idString);
  20090. + pId = atoi(idString.c_str());
  20091. + result = true;
  20092. + }
  20093. + return result;
  20094. +}
  20095. +
  20096. +void Player::UpdateMail()
  20097. +{
  20098. + // save money,items and mail to prevent cheating
  20099. + SQLTransaction trans = CharacterDatabase.BeginTransaction();
  20100. + SaveGoldToDB(trans);
  20101. + SaveInventoryAndGoldToDB(trans);
  20102. + _SaveMail(trans);
  20103. + CharacterDatabase.CommitTransaction(trans);
  20104. +}
  20105. +
  20106. +uint32 PlayerbotMgr::GetSpec(Player* player)
  20107. +{
  20108. + uint32 row = 0;
  20109. + uint32 spec = 0;
  20110. + uint8 rank = 0;
  20111. + uint32 m_activeSpec = player->GetActiveSpec();
  20112. + PlayerTalentMap* m_talents = player->GetTalents(m_activeSpec);
  20113. +
  20114. + for (PlayerTalentMap::iterator iter = m_talents->begin(); iter != m_talents->end(); ++iter)
  20115. + {
  20116. + PlayerTalent* talent = (*iter).second;
  20117. + if (talent->state == PLAYERSPELL_REMOVED)
  20118. + continue;
  20119. + TalentSpellPos const* talentPos = GetTalentSpellPos((*iter).first);
  20120. + if (!talentPos)
  20121. + continue;
  20122. + TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentPos->talent_id);
  20123. + if (!talentInfo)
  20124. + continue;
  20125. +
  20126. + //spec is set with 3 rules
  20127. + //a) there is no spec set yet
  20128. + //b) found talent deeper in talent tree
  20129. + //c) found talent with same depth, but with higher rank
  20130. + if (spec == 0 || talentInfo->Row > row || (talentInfo->Row == row && talentPos->rank > rank))
  20131. + {
  20132. + row = talentInfo->Row;
  20133. + spec = talentInfo->TalentTab;
  20134. + }
  20135. + }
  20136. +
  20137. + //Return the tree with the deepest talent
  20138. + return spec;
  20139. +}
  20140. +
  20141. +bool Player::HavePBot() const
  20142. +{
  20143. + return m_playerbotMgr != NULL && !m_playerbotMgr->getPlayerbots().empty();
  20144. +}
  20145. +
  20146. +uint16 PlayerbotMgr::Rand() const
  20147. +{
  20148. + return urand(0, 100 + m_playerBots.empty() ? 0 : (m_playerBots.size() - 1) * 10);
  20149. +}
  20150. diff --git a/src/server/game/AI/PlayerBots/bp_mgr.h b/src/server/game/AI/PlayerBots/bp_mgr.h
  20151. new file mode 100644
  20152. index 0000000..8fb5c6c
  20153. --- /dev/null
  20154. +++ b/src/server/game/AI/PlayerBots/bp_mgr.h
  20155. @@ -0,0 +1,64 @@
  20156. +#ifndef _PLAYERBOTMGR_H
  20157. +#define _PLAYERBOTMGR_H
  20158. +
  20159. +#include "Common.h"
  20160. +
  20161. +class Player;
  20162. +class WorldPacket;
  20163. +
  20164. +typedef UNORDERED_MAP<uint64, Player*> PlayerBotMap;
  20165. +
  20166. +class PlayerbotMgr
  20167. +{
  20168. + public:
  20169. + PlayerbotMgr(Player* const master);
  20170. + virtual ~PlayerbotMgr();
  20171. +
  20172. + //void Update(uint32 const p_time);
  20173. +
  20174. + //Packets
  20175. + void HandleMasterIncomingPacket(WorldPacket const& packet);
  20176. + void HandleMasterOutgoingPacket(WorldPacket const& packet);
  20177. +
  20178. + void AddPlayerBot(uint64 guid);
  20179. + void LogoutPlayerBot(uint64 guid, bool uninvite = true);
  20180. + Player* GetPlayerBot(uint64 guid) const;
  20181. + Player* GetMaster() const { return m_master; }
  20182. +
  20183. + PlayerBotMap::const_iterator GetPlayerBotsBegin() const { return m_playerBots.begin(); }
  20184. + PlayerBotMap::const_iterator GetPlayerBotsEnd() const { return m_playerBots.end(); }
  20185. + PlayerBotMap const& getPlayerbots() const { return m_playerBots; }
  20186. + uint8 GetPlayerBotsCount() const { return uint8(m_playerBots.size()); }
  20187. +
  20188. + void LogoutAllBots();
  20189. + void RemoveAllBotsFromGroup(bool force = false);
  20190. + void OnBotLogin(Player* const bot);
  20191. + void Stay();
  20192. +
  20193. + uint16 Rand() const;
  20194. + static uint32 GetSpec(Player* player);
  20195. +
  20196. + public:
  20197. + //config variables (needs review)
  20198. + uint32 m_confRestrictBotLevel;
  20199. + uint32 m_confDisableBotsInRealm;
  20200. + uint32 m_confMaxNumBots;
  20201. + bool m_confDisableBots;
  20202. + bool m_confDebugWhisper;
  20203. + float m_confFollowDistance[2];
  20204. + uint32 gConfigSellLevelDiff;
  20205. + bool m_confCollectCombat;
  20206. + bool m_confCollectQuest;
  20207. + bool m_confCollectProfession;
  20208. + bool m_confCollectLoot;
  20209. + bool m_confCollectSkin;
  20210. + bool m_confCollectObjects;
  20211. + uint32 m_confCollectDistance;
  20212. + uint32 m_confCollectDistanceMax;
  20213. +
  20214. + private:
  20215. + Player* const m_master;
  20216. + PlayerBotMap m_playerBots;
  20217. +};
  20218. +
  20219. +#endif
  20220. diff --git a/src/server/game/AI/PlayerBots/bp_pal_ai.cpp b/src/server/game/AI/PlayerBots/bp_pal_ai.cpp
  20221. new file mode 100644
  20222. index 0000000..180c5be
  20223. --- /dev/null
  20224. +++ b/src/server/game/AI/PlayerBots/bp_pal_ai.cpp
  20225. @@ -0,0 +1,673 @@
  20226. +/*
  20227. + Name : PlayerbotPaladinAI.cpp
  20228. + Complete: maybe around 27% :D
  20229. + Author : Natsukawa
  20230. + Version : 0.35
  20231. + */
  20232. +#include "bp_ai.h"
  20233. +#include "bp_pal_ai.h"
  20234. +#include "SpellAuras.h"
  20235. +#include "Player.h"
  20236. +#include "Pet.h"
  20237. +
  20238. +PlayerbotPaladinAI::PlayerbotPaladinAI(Player* const master, Player* const bot, PlayerbotAI* const ai) : PlayerbotClassAI(master, bot, ai)
  20239. +{
  20240. + RETRIBUTION_AURA = PlayerbotAI::InitSpell(me, RETRIBUTION_AURA_1);
  20241. + CRUSADER_AURA = PlayerbotAI::InitSpell(me, CRUSADER_AURA_1);
  20242. + CRUSADER_STRIKE = PlayerbotAI::InitSpell(me, CRUSADER_STRIKE_1);
  20243. + SEAL_OF_COMMAND = PlayerbotAI::InitSpell(me, SEAL_OF_COMMAND_1);
  20244. + SEAL_OF_RIGHTEOUSNESS = PlayerbotAI::InitSpell(me, SEAL_OF_RIGHTEOUSNESS_1);
  20245. + SEAL_OF_CORRUPTION = PlayerbotAI::InitSpell(me, SEAL_OF_CORRUPTION_1);
  20246. + SEAL_OF_JUSTICE = PlayerbotAI::InitSpell(me, SEAL_OF_JUSTICE_1);
  20247. + SEAL_OF_LIGHT = PlayerbotAI::InitSpell(me, SEAL_OF_LIGHT_1);
  20248. + SEAL_OF_VENGEANCE = PlayerbotAI::InitSpell(me, SEAL_OF_VENGEANCE_1);
  20249. + SEAL_OF_WISDOM = PlayerbotAI::InitSpell(me, SEAL_OF_WISDOM_1);
  20250. + JUDGEMENT_OF_LIGHT = PlayerbotAI::InitSpell(me, JUDGEMENT_OF_LIGHT_1);
  20251. + JUDGEMENT_OF_WISDOM = PlayerbotAI::InitSpell(me, JUDGEMENT_OF_WISDOM_1);
  20252. + JUDGEMENT_OF_JUSTICE = PlayerbotAI::InitSpell(me, JUDGEMENT_OF_JUSTICE_1);
  20253. + DIVINE_STORM = PlayerbotAI::InitSpell(me, DIVINE_STORM_1);
  20254. + BLESSING_OF_MIGHT = PlayerbotAI::InitSpell(me, BLESSING_OF_MIGHT_1);
  20255. + GREATER_BLESSING_OF_MIGHT = PlayerbotAI::InitSpell(me, GREATER_BLESSING_OF_MIGHT_1);
  20256. + HAMMER_OF_WRATH = PlayerbotAI::InitSpell(me, HAMMER_OF_WRATH_1);
  20257. + FLASH_OF_LIGHT = PlayerbotAI::InitSpell(me, FLASH_OF_LIGHT_1); // Holy
  20258. + HOLY_LIGHT = PlayerbotAI::InitSpell(me, HOLY_LIGHT_1);
  20259. + HOLY_SHOCK = PlayerbotAI::InitSpell(me, HOLY_SHOCK_1);
  20260. + HOLY_WRATH = PlayerbotAI::InitSpell(me, HOLY_WRATH_1);
  20261. + DIVINE_FAVOR = PlayerbotAI::InitSpell(me, DIVINE_FAVOR_1);
  20262. + CONCENTRATION_AURA = PlayerbotAI::InitSpell(me, CONCENTRATION_AURA_1);
  20263. + BLESSING_OF_WISDOM = PlayerbotAI::InitSpell(me, BLESSING_OF_WISDOM_1);
  20264. + GREATER_BLESSING_OF_WISDOM = PlayerbotAI::InitSpell(me, GREATER_BLESSING_OF_WISDOM_1);
  20265. + CONSECRATION = PlayerbotAI::InitSpell(me, CONSECRATION_1);
  20266. + AVENGING_WRATH = PlayerbotAI::InitSpell(me, AVENGING_WRATH_1);
  20267. + LAY_ON_HANDS = PlayerbotAI::InitSpell(me, LAY_ON_HANDS_1);
  20268. + EXORCISM = PlayerbotAI::InitSpell(me, EXORCISM_1);
  20269. + SACRED_SHIELD = PlayerbotAI::InitSpell(me, SACRED_SHIELD_1);
  20270. + DIVINE_PLEA = PlayerbotAI::InitSpell(me, DIVINE_PLEA_1);
  20271. + BLESSING_OF_KINGS = PlayerbotAI::InitSpell(me, BLESSING_OF_KINGS_1);
  20272. + GREATER_BLESSING_OF_KINGS = PlayerbotAI::InitSpell(me, GREATER_BLESSING_OF_KINGS_1);
  20273. + BLESSING_OF_SANCTUARY = PlayerbotAI::InitSpell(me, BLESSING_OF_SANCTUARY_1);
  20274. + GREATER_BLESSING_OF_SANCTUARY = PlayerbotAI::InitSpell(me, GREATER_BLESSING_OF_SANCTUARY_1);
  20275. + HAMMER_OF_JUSTICE = PlayerbotAI::InitSpell(me, HAMMER_OF_JUSTICE_1);
  20276. + RIGHTEOUS_FURY = PlayerbotAI::InitSpell(me, RIGHTEOUS_FURY_1);
  20277. + RIGHTEOUS_DEFENSE = PlayerbotAI::InitSpell(me, RIGHTEOUS_DEFENSE_1);
  20278. + SHADOW_RESISTANCE_AURA = PlayerbotAI::InitSpell(me, SHADOW_RESISTANCE_AURA_1);
  20279. + DEVOTION_AURA = PlayerbotAI::InitSpell(me, DEVOTION_AURA_1);
  20280. + FIRE_RESISTANCE_AURA = PlayerbotAI::InitSpell(me, FIRE_RESISTANCE_AURA_1);
  20281. + FROST_RESISTANCE_AURA = PlayerbotAI::InitSpell(me, FROST_RESISTANCE_AURA_1);
  20282. + HAND_OF_PROTECTION = PlayerbotAI::InitSpell(me, HAND_OF_PROTECTION_1);
  20283. + DIVINE_PROTECTION = PlayerbotAI::InitSpell(me, DIVINE_PROTECTION_1);
  20284. + DIVINE_INTERVENTION = PlayerbotAI::InitSpell(me, DIVINE_INTERVENTION_1);
  20285. + DIVINE_SACRIFICE = PlayerbotAI::InitSpell(me, DIVINE_SACRIFICE_1);
  20286. + DIVINE_SHIELD = PlayerbotAI::InitSpell(me, DIVINE_SHIELD_1);
  20287. + HOLY_SHIELD = PlayerbotAI::InitSpell(me, HOLY_SHIELD_1);
  20288. + AVENGERS_SHIELD = PlayerbotAI::InitSpell(me, AVENGERS_SHIELD_1);
  20289. + HAND_OF_SACRIFICE = PlayerbotAI::InitSpell(me, HAND_OF_SACRIFICE_1);
  20290. + SHIELD_OF_RIGHTEOUSNESS = PlayerbotAI::InitSpell(me, SHIELD_OF_RIGHTEOUSNESS_1);
  20291. + REDEMPTION = PlayerbotAI::InitSpell(me, REDEMPTION_1);
  20292. + PURIFY = PlayerbotAI::InitSpell(me, PURIFY_1);
  20293. + CLEANSE = PlayerbotAI::InitSpell(me, CLEANSE_1);
  20294. + HAND_OF_RECKONING = PlayerbotAI::InitSpell(me, HAND_OF_RECKONING_1);
  20295. + ART_OF_WAR = PlayerbotAI::InitSpell(me, ART_OF_WAR_1);
  20296. + HAMMER_OF_THE_RIGHTEOUS = PlayerbotAI::InitSpell(me, HAMMER_OF_THE_RIGHTEOUS_1);
  20297. +
  20298. + // Warrior auras
  20299. + DEFENSIVE_STANCE = 71; //Def Stance
  20300. + BERSERKER_STANCE = 2458; //Ber Stance
  20301. + BATTLE_STANCE = 2457; //Bat Stance
  20302. +
  20303. + FORBEARANCE = 25771; // cannot be protected
  20304. +
  20305. + //RECENTLY_BANDAGED = 11196; // first aid check
  20306. +
  20307. + // racial
  20308. + ARCANE_TORRENT = PlayerbotAI::InitSpell(me, ARCANE_TORRENT_MANA_CLASSES);
  20309. + GIFT_OF_THE_NAARU = PlayerbotAI::InitSpell(me, GIFT_OF_THE_NAARU_PALADIN); // draenei
  20310. + STONEFORM = PlayerbotAI::InitSpell(me, STONEFORM_ALL); // dwarf
  20311. + EVERY_MAN_FOR_HIMSELF = PlayerbotAI::InitSpell(me, EVERY_MAN_FOR_HIMSELF_ALL); // human
  20312. +
  20313. + //The check doesn't work for now
  20314. + //PRAYER_OF_SHADOW_PROTECTION = PlayerbotAI::InitSpell(me, PriestSpells::PRAYER_OF_SHADOW_PROTECTION_1);
  20315. +}
  20316. +
  20317. +PlayerbotPaladinAI::~PlayerbotPaladinAI() {}
  20318. +
  20319. +CombatManeuverReturns PlayerbotPaladinAI::DoFirstCombatManeuver(Unit* pTarget)
  20320. +{
  20321. + //// There are NPCs in BGs and Open World PvP, so don't filter this on PvP scenarios (of course if PvP targets anyone but tank, all bets are off anyway)
  20322. + //// Wait until the tank says so, until any non-tank gains aggro or X seconds - whichever is shortest
  20323. + //if (m_ai->GetCombatOrder() & PlayerbotAI::ORDERS_TEMP_WAIT_TANKAGGRO)
  20324. + //{
  20325. + // if (m_WaitUntil > m_ai->CurrentTime() && m_ai->GroupTankHoldsAggro())
  20326. + // {
  20327. + // if (PlayerbotAI::ORDERS_TANK & m_ai->GetCombatOrder())
  20328. + // {
  20329. + // if (pTarget->GetCombatReach() <= ATTACK_DISTANCE)
  20330. + // {
  20331. + // // Set everyone's UpdateAI() waiting to 2 seconds
  20332. + // m_ai->SetGroupIgnoreUpdateTime(2);
  20333. + // // Clear their TEMP_WAIT_TANKAGGRO flag
  20334. + // m_ai->ClearGroupCombatOrder(PlayerbotAI::ORDERS_TEMP_WAIT_TANKAGGRO);
  20335. + // // Start attacking, force target on current target
  20336. + // m_ai->Attack(m_ai->GetCurrentTarget());
  20337. +
  20338. + // // While everyone else is waiting 2 second, we need to build up aggro, so don't return
  20339. + // }
  20340. + // else
  20341. + // {
  20342. + // // TODO: add check if target is ranged
  20343. + // return RETURN_NO_ACTION_OK; // wait for target to get nearer
  20344. + // }
  20345. + // }
  20346. + // else if (PlayerbotAI::ORDERS_HEAL & m_ai->GetCombatOrder())
  20347. + // return HealPlayer(GetHealTarget());
  20348. + // else
  20349. + // return RETURN_NO_ACTION_OK; // wait it out
  20350. + // }
  20351. + // else
  20352. + // {
  20353. + // m_ai->ClearGroupCombatOrder(PlayerbotAI::ORDERS_TEMP_WAIT_TANKAGGRO);
  20354. + // }
  20355. + //}
  20356. +
  20357. + //if (m_ai->GetCombatOrder() & PlayerbotAI::ORDERS_TEMP_WAIT_OOC)
  20358. + //{
  20359. + // if (m_WaitUntil > m_ai->CurrentTime() && !m_ai->IsGroupInCombat())
  20360. + // return RETURN_NO_ACTION_OK; // wait it out
  20361. + // else
  20362. + // m_ai->ClearGroupCombatOrder(PlayerbotAI::ORDERS_TEMP_WAIT_OOC);
  20363. + //}
  20364. +
  20365. + //switch (m_ai->GetScenarioType())
  20366. + //{
  20367. + // case PlayerbotAI::SCENARIO_PVP_DUEL:
  20368. + // case PlayerbotAI::SCENARIO_PVP_BG:
  20369. + // case PlayerbotAI::SCENARIO_PVP_ARENA:
  20370. + // case PlayerbotAI::SCENARIO_PVP_OPENWORLD:
  20371. + // return DoFirstCombatManeuverPVP(pTarget);
  20372. + // case PlayerbotAI::SCENARIO_PVE:
  20373. + // case PlayerbotAI::SCENARIO_PVE_ELITE:
  20374. + // case PlayerbotAI::SCENARIO_PVE_RAID:
  20375. + // default:
  20376. + // return DoFirstCombatManeuverPVE(pTarget);
  20377. + // break;
  20378. + //}
  20379. +
  20380. + return RETURN_NO_ACTION_ERROR;
  20381. +}
  20382. +
  20383. +CombatManeuverReturns PlayerbotPaladinAI::DoFirstCombatManeuverPVE(Unit* /*pTarget*/)
  20384. +{
  20385. + return RETURN_NO_ACTION_OK;
  20386. +}
  20387. +
  20388. +CombatManeuverReturns PlayerbotPaladinAI::DoFirstCombatManeuverPVP(Unit* /*pTarget*/)
  20389. +{
  20390. + return RETURN_NO_ACTION_OK;
  20391. +}
  20392. +
  20393. +CombatManeuverReturns PlayerbotPaladinAI::DoNextCombatManeuver(Unit *pTarget)
  20394. +{
  20395. + //switch (m_ai->GetScenarioType())
  20396. + //{
  20397. + // case PlayerbotAI::SCENARIO_PVP_DUEL:
  20398. + // case PlayerbotAI::SCENARIO_PVP_BG:
  20399. + // case PlayerbotAI::SCENARIO_PVP_ARENA:
  20400. + // case PlayerbotAI::SCENARIO_PVP_OPENWORLD:
  20401. + // return DoNextCombatManeuverPVP(pTarget);
  20402. + // case PlayerbotAI::SCENARIO_PVE:
  20403. + // case PlayerbotAI::SCENARIO_PVE_ELITE:
  20404. + // case PlayerbotAI::SCENARIO_PVE_RAID:
  20405. + // default:
  20406. + // return DoNextCombatManeuverPVE(pTarget);
  20407. + // break;
  20408. + //}
  20409. +
  20410. + return RETURN_NO_ACTION_ERROR;
  20411. +}
  20412. +
  20413. +CombatManeuverReturns PlayerbotPaladinAI::DoNextCombatManeuverPVE(Unit *pTarget)
  20414. +{
  20415. + //if (!m_ai) return RETURN_NO_ACTION_ERROR;
  20416. + //if (!m_bot) return RETURN_NO_ACTION_ERROR;
  20417. + //if (!pTarget) return RETURN_NO_ACTION_INVALIDTARGET;
  20418. +
  20419. + //// damage spells
  20420. + //uint32 spec = m_bot->GetSpec();
  20421. + //std::ostringstream out;
  20422. +
  20423. + //// Make sure healer stays put, don't even melee (aggro) if in range.
  20424. + //if (m_ai->IsHealer() && m_ai->GetCombatStyle() != PlayerbotAI::COMBAT_RANGED)
  20425. + // m_ai->SetCombatStyle(PlayerbotAI::COMBAT_RANGED);
  20426. + //else if (!m_ai->IsHealer() && m_ai->GetCombatStyle() != PlayerbotAI::COMBAT_MELEE)
  20427. + // m_ai->SetCombatStyle(PlayerbotAI::COMBAT_MELEE);
  20428. +
  20429. + //// Heal
  20430. + //if (m_ai->IsHealer())
  20431. + //{
  20432. + // if (HealPlayer(GetHealTarget()) & (RETURN_NO_ACTION_OK | RETURN_CONTINUE))
  20433. + // return RETURN_CONTINUE;
  20434. + //}
  20435. + //else
  20436. + //{
  20437. + // // Is this desirable? Debatable.
  20438. + // // TODO: In a group/raid with a healer you'd want this bot to focus on DPS (it's not specced/geared for healing either)
  20439. + // if (HealPlayer(m_bot) & (RETURN_NO_ACTION_OK | RETURN_CONTINUE))
  20440. + // return RETURN_CONTINUE;
  20441. + //}
  20442. +
  20443. + ////Used to determine if this bot has highest threat
  20444. + //Unit *newTarget = m_ai->FindAttacker((PlayerbotAI::ATTACKERINFOTYPE) (PlayerbotAI::AIT_VICTIMSELF | PlayerbotAI::AIT_HIGHESTTHREAT), m_bot);
  20445. + //switch (spec)
  20446. + //{
  20447. + // case PALADIN_SPEC_HOLY:
  20448. + // if (m_ai->IsHealer())
  20449. + // return RETURN_NO_ACTION_OK;
  20450. + // // else: DPS (retribution, NEVER protection)
  20451. +
  20452. + // case PALADIN_SPEC_RETRIBUTION:
  20453. + // if (HAMMER_OF_WRATH > 0 && pTarget->GetHealth() < pTarget->GetMaxHealth() * 0.20 && m_ai->CastSpell (HAMMER_OF_WRATH, *pTarget))
  20454. + // return RETURN_CONTINUE;
  20455. + // if (ART_OF_WAR > 0 && EXORCISM > 0 && !m_bot->HasSpellCooldown(EXORCISM) && m_bot->HasAura(ART_OF_WAR, EFFECT_0) && m_ai->CastSpell (EXORCISM, *pTarget))
  20456. + // return RETURN_CONTINUE;
  20457. + // if (CRUSADER_STRIKE > 0 && !m_bot->HasSpellCooldown(CRUSADER_STRIKE) && m_ai->CastSpell (CRUSADER_STRIKE, *pTarget))
  20458. + // return RETURN_CONTINUE;
  20459. + // if (DIVINE_STORM > 0 && /*m_ai->GetAttackerCount() >= 3 && dist <= ATTACK_DISTANCE*/ !m_bot->HasSpellCooldown(DIVINE_STORM) && m_ai->CastSpell (DIVINE_STORM, *pTarget))
  20460. + // return RETURN_CONTINUE;
  20461. + // if (JUDGEMENT_OF_LIGHT > 0 && m_ai->CastSpell (JUDGEMENT_OF_LIGHT, *pTarget))
  20462. + // return RETURN_CONTINUE;
  20463. + // if (AVENGING_WRATH > 0 && m_ai->CastSpell (AVENGING_WRATH, *m_bot))
  20464. + // return RETURN_CONTINUE;
  20465. + // /*if (HAMMER_OF_JUSTICE > 0 && !pTarget->HasAura(HAMMER_OF_JUSTICE, EFFECT_0) && m_ai->CastSpell (HAMMER_OF_JUSTICE, *pTarget))
  20466. + // return RETURN_CONTINUE;*/
  20467. + // /*if (SACRED_SHIELD > 0 && pVictim == m_bot && m_bot->GetHealthPct() < 70 && !m_bot->HasAura(SACRED_SHIELD, EFFECT_0) && m_ai->CastSpell (SACRED_SHIELD, *m_bot))
  20468. + // return RETURN_CONTINUE;*/
  20469. + // /*if (HOLY_WRATH > 0 && m_ai->GetAttackerCount() >= 3 && dist <= ATTACK_DISTANCE && m_ai->CastSpell (HOLY_WRATH, *pTarget))
  20470. + // return RETURN_CONTINUE;*/
  20471. + // /*if (HAND_OF_SACRIFICE > 0 && pVictim == GetMaster() && !GetMaster()->HasAura(HAND_OF_SACRIFICE, EFFECT_0) && m_ai->CastSpell (HAND_OF_SACRIFICE, *GetMaster()))
  20472. + // return RETURN_CONTINUE;*/
  20473. + // /*if (DIVINE_PROTECTION > 0 && pVictim == m_bot && !m_bot->HasAura(FORBEARANCE, EFFECT_0) && m_bot->GetHealthPct() < 30 && m_ai->CastSpell (DIVINE_PROTECTION, *m_bot))
  20474. + // return RETURN_CONTINUE;*/
  20475. + // /*if (RIGHTEOUS_DEFENSE > 0 && pVictim != m_bot && m_bot->GetHealthPct() > 70 && m_ai->CastSpell (RIGHTEOUS_DEFENSE, *pTarget))
  20476. + // return RETURN_CONTINUE;*/
  20477. + // /*if (DIVINE_PLEA > 0 && !m_bot->HasAura(DIVINE_PLEA, EFFECT_0) && m_ai->CastSpell (DIVINE_PLEA, *m_bot))
  20478. + // return RETURN_CONTINUE;*/
  20479. + // /*if (DIVINE_FAVOR > 0 && !m_bot->HasAura(DIVINE_FAVOR, EFFECT_0) && m_ai->CastSpell (DIVINE_FAVOR, *m_bot))
  20480. + // return RETURN_CONTINUE;*/
  20481. + // return RETURN_NO_ACTION_OK;
  20482. +
  20483. + // case PALADIN_SPEC_PROTECTION:
  20484. + // //Taunt if orders specify
  20485. + // if (m_ai->GetCombatOrder() & PlayerbotAI::ORDERS_TANK && !newTarget && HAND_OF_RECKONING > 0 && !m_bot->HasSpellCooldown(HAND_OF_RECKONING) && m_ai->CastSpell(HAND_OF_RECKONING, *pTarget))
  20486. + // return RETURN_CONTINUE;
  20487. + // if (CONSECRATION > 0 && !m_bot->HasSpellCooldown(CONSECRATION) && m_ai->CastSpell(CONSECRATION, *pTarget))
  20488. + // return RETURN_CONTINUE;
  20489. + // if (HOLY_SHIELD > 0 && !m_bot->HasAura(HOLY_SHIELD) && m_ai->CastSpell(HOLY_SHIELD, *m_bot))
  20490. + // return RETURN_CONTINUE;
  20491. + // if (AVENGERS_SHIELD > 0 && !m_bot->HasSpellCooldown(AVENGERS_SHIELD) && m_ai->CastSpell(AVENGERS_SHIELD, *pTarget))
  20492. + // return RETURN_CONTINUE;
  20493. + // if (HAMMER_OF_THE_RIGHTEOUS > 0 && !m_bot->HasSpellCooldown(HAMMER_OF_THE_RIGHTEOUS) && m_ai->CastSpell(HAMMER_OF_THE_RIGHTEOUS, *pTarget))
  20494. + // return RETURN_CONTINUE;
  20495. + // if (SHIELD_OF_RIGHTEOUSNESS > 0 && !m_bot->HasSpellCooldown(SHIELD_OF_RIGHTEOUSNESS) && m_ai->CastSpell(SHIELD_OF_RIGHTEOUSNESS, *pTarget))
  20496. + // return RETURN_CONTINUE;
  20497. + // if (JUDGEMENT_OF_LIGHT > 0 && m_ai->CastSpell (JUDGEMENT_OF_LIGHT, *pTarget))
  20498. + // return RETURN_CONTINUE;
  20499. + // return RETURN_NO_ACTION_OK;
  20500. + //}
  20501. +
  20502. + //if (DIVINE_SHIELD > 0 && m_bot->GetHealthPct() < 30 && pVictim == m_bot && !m_bot->HasAura(FORBEARANCE, EFFECT_0) && !m_bot->HasAura(DIVINE_SHIELD, EFFECT_0))
  20503. + // m_ai->CastSpell(DIVINE_SHIELD, *m_bot);
  20504. +
  20505. + //if (DIVINE_SACRIFICE > 0 && m_bot->GetHealthPct() > 50 && pVictim != m_bot && !m_bot->HasAura(DIVINE_SACRIFICE, EFFECT_0))
  20506. + // m_ai->CastSpell(DIVINE_SACRIFICE, *m_bot);
  20507. +
  20508. + return RETURN_NO_ACTION_OK;
  20509. +}
  20510. +
  20511. +CombatManeuverReturns PlayerbotPaladinAI::DoNextCombatManeuverPVP(Unit* pTarget)
  20512. +{
  20513. + //if (m_ai->CastSpell(HAMMER_OF_JUSTICE))
  20514. + // return RETURN_CONTINUE;
  20515. +
  20516. + return DoNextCombatManeuverPVE(pTarget); // TODO: bad idea perhaps, but better than the alternative
  20517. +}
  20518. +
  20519. +CombatManeuverReturns PlayerbotPaladinAI::HealPlayer(Player* target)
  20520. +{
  20521. + //CombatManeuverReturns r = PlayerbotClassAI::HealPlayer(target);
  20522. + //if (r != RETURN_NO_ACTION_OK)
  20523. + // return r;
  20524. +
  20525. + //if (!target->isAlive())
  20526. + //{
  20527. + // if (REDEMPTION && m_ai->CastSpell(REDEMPTION, *target))
  20528. + // {
  20529. + // std::string msg = "Resurrecting ";
  20530. + // msg += target->GetName();
  20531. + // m_bot->Say(msg, LANG_UNIVERSAL);
  20532. + // return RETURN_CONTINUE;
  20533. + // }
  20534. + // return RETURN_NO_ACTION_ERROR; // not error per se - possibly just OOM
  20535. + //}
  20536. +
  20537. + //if (PURIFY > 0 && (m_ai->GetCombatOrder() & PlayerbotAI::ORDERS_NODISPEL) == 0)
  20538. + //{
  20539. + // uint32 DISPEL = CLEANSE > 0 ? CLEANSE : PURIFY;
  20540. + // uint32 dispelMask = SpellInfo::GetDispelMask(DISPEL_DISEASE);
  20541. + // uint32 dispelMask2 = SpellInfo::GetDispelMask(DISPEL_POISON);
  20542. + // uint32 dispelMask3 = SpellInfo::GetDispelMask(DISPEL_MAGIC);
  20543. + // Unit::AuraMap const& auras = target->GetOwnedAuras();
  20544. + // for (Unit::AuraMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr)
  20545. + // {
  20546. + // Aura *holder = itr->second;
  20547. + // if ((1 << holder->GetSpellInfo()->Dispel) & dispelMask)
  20548. + // {
  20549. + // if (holder->GetSpellInfo()->Dispel == DISPEL_DISEASE)
  20550. + // {
  20551. + // if (m_ai->CastSpell(DISPEL, *target))
  20552. + // return RETURN_CONTINUE;
  20553. + // return RETURN_NO_ACTION_ERROR;
  20554. + // }
  20555. + // }
  20556. + // else if ((1 << holder->GetSpellInfo()->Dispel) & dispelMask2)
  20557. + // {
  20558. + // if (holder->GetSpellInfo()->Dispel == DISPEL_POISON)
  20559. + // {
  20560. + // if (m_ai->CastSpell(DISPEL, *target))
  20561. + // return RETURN_CONTINUE;
  20562. + // return RETURN_NO_ACTION_ERROR;
  20563. + // }
  20564. + // }
  20565. + // else if ((1 << holder->GetSpellInfo()->Dispel) & dispelMask3 & (DISPEL == CLEANSE))
  20566. + // {
  20567. + // if (holder->GetSpellInfo()->Dispel == DISPEL_MAGIC)
  20568. + // {
  20569. + // if (m_ai->CastSpell(DISPEL, *target))
  20570. + // return RETURN_CONTINUE;
  20571. + // return RETURN_NO_ACTION_ERROR;
  20572. + // }
  20573. + // }
  20574. + // }
  20575. + //}
  20576. +
  20577. + //uint8 hp = target->GetHealthPct();
  20578. +
  20579. + //// Everyone is healthy enough, return OK. MUST correlate to highest value below (should be last HP check)
  20580. + //if (hp >= 90)
  20581. + // return RETURN_NO_ACTION_OK;
  20582. +
  20583. + //if (hp < 25 && m_ai->CastSpell(LAY_ON_HANDS, *target))
  20584. + // return RETURN_CONTINUE;
  20585. +
  20586. + //// You probably want to save this for tank/healer trouble
  20587. + //if (hp < 30 && HAND_OF_PROTECTION > 0 && !target->HasAura(FORBEARANCE, EFFECT_0)
  20588. + // && !target->HasAura(HAND_OF_PROTECTION, EFFECT_0) && !target->HasAura(DIVINE_PROTECTION, EFFECT_0)
  20589. + // && !target->HasAura(DIVINE_SHIELD, EFFECT_0) && (GetTargetJob(target) & (JOB_HEAL | JOB_TANK))
  20590. + // && m_ai->CastSpell(HAND_OF_PROTECTION, *target))
  20591. + // return RETURN_CONTINUE;
  20592. +
  20593. + //// Isn't this more of a group heal spell?
  20594. + //if (hp < 40 && m_ai->CastSpell(FLASH_OF_LIGHT, *target))
  20595. + // return RETURN_CONTINUE;
  20596. +
  20597. + //if (hp < 60 && m_ai->CastSpell(HOLY_SHOCK, *target))
  20598. + // return RETURN_CONTINUE;
  20599. +
  20600. + //if (hp < 90 && m_ai->CastSpell(HOLY_LIGHT, *target))
  20601. + // return RETURN_CONTINUE;
  20602. +
  20603. + return RETURN_NO_ACTION_UNKNOWN;
  20604. +} // end HealTarget
  20605. +
  20606. +void PlayerbotPaladinAI::CheckAuras()
  20607. +{
  20608. + //if (!m_ai) return;
  20609. + //if (!m_bot) return;
  20610. +
  20611. + //uint32 spec = m_bot->GetSpec();
  20612. +
  20613. + //// If we have resist orders, adjust accordingly
  20614. + //if (m_ai->GetCombatOrder() & PlayerbotAI::ORDERS_RESIST_FROST)
  20615. + //{
  20616. + // if (!m_bot->HasAura(FROST_RESISTANCE_AURA) && FROST_RESISTANCE_AURA > 0 && !m_bot->HasAura(FROST_RESISTANCE_AURA))
  20617. + // m_ai->CastSpell(FROST_RESISTANCE_AURA);
  20618. + // return;
  20619. + //}
  20620. + //else if (m_ai->GetCombatOrder() & PlayerbotAI::ORDERS_RESIST_FIRE)
  20621. + //{
  20622. + // if (!m_bot->HasAura(FIRE_RESISTANCE_AURA) && FIRE_RESISTANCE_AURA > 0 && !m_bot->HasAura(FIRE_RESISTANCE_AURA))
  20623. + // m_ai->CastSpell(FIRE_RESISTANCE_AURA);
  20624. + // return;
  20625. + //}
  20626. + //else if (m_ai->GetCombatOrder() & PlayerbotAI::ORDERS_RESIST_SHADOW)
  20627. + //{
  20628. + // // Shadow protection check is broken, they stack!
  20629. + // if (!m_bot->HasAura(SHADOW_RESISTANCE_AURA) && SHADOW_RESISTANCE_AURA > 0 && !m_bot->HasAura(SHADOW_RESISTANCE_AURA)) // /*&& !m_bot->HasAura(PRAYER_OF_SHADOW_PROTECTION)*/ /*&& !m_bot->HasAura(PRAYER_OF_SHADOW_PROTECTION)*/
  20630. + // m_ai->CastSpell(SHADOW_RESISTANCE_AURA);
  20631. + // return;
  20632. + //}
  20633. +
  20634. + //// If we have no resist orders, adjust aura based on spec
  20635. + //if (spec == PALADIN_SPEC_HOLY)
  20636. + //{
  20637. + // if (CONCENTRATION_AURA > 0 && !m_bot->HasAura(CONCENTRATION_AURA))
  20638. + // m_ai->CastSpell(CONCENTRATION_AURA);
  20639. + // return;
  20640. + //}
  20641. + //else if (spec == PALADIN_SPEC_PROTECTION)
  20642. + //{
  20643. + // if (DEVOTION_AURA > 0 && !m_bot->HasAura(DEVOTION_AURA))
  20644. + // m_ai->CastSpell(DEVOTION_AURA);
  20645. + // return;
  20646. + //}
  20647. + //else if (spec == PALADIN_SPEC_RETRIBUTION)
  20648. + //{
  20649. + // if (RETRIBUTION_AURA > 0 && !m_bot->HasAura(RETRIBUTION_AURA))
  20650. + // m_ai->CastSpell(RETRIBUTION_AURA);
  20651. + // return;
  20652. + //}
  20653. +}
  20654. +
  20655. +void PlayerbotPaladinAI::CheckSeals()
  20656. +{
  20657. + //if (!m_ai) return;
  20658. + //if (!m_bot) return;
  20659. +
  20660. + //uint32 spec = m_bot->GetSpec();
  20661. + //uint32 RACIAL = (SEAL_OF_CORRUPTION > 0) ? SEAL_OF_CORRUPTION : SEAL_OF_VENGEANCE;
  20662. +
  20663. + //switch (spec)
  20664. + //{
  20665. + // case PALADIN_SPEC_HOLY:
  20666. + // //I'm not even sure if holy uses seals?
  20667. + // if (SEAL_OF_WISDOM > 0 && !m_bot->HasAura(SEAL_OF_WISDOM, EFFECT_0))
  20668. + // m_ai->CastSpell(SEAL_OF_WISDOM, *m_bot);
  20669. + // break;
  20670. +
  20671. + // case PALADIN_SPEC_PROTECTION:
  20672. + // if (RACIAL > 0 && !m_bot->HasAura(RACIAL, EFFECT_0))
  20673. + // m_ai->CastSpell(RACIAL, *m_bot);
  20674. + // else if (SEAL_OF_RIGHTEOUSNESS > 0 && !m_bot->HasAura(SEAL_OF_RIGHTEOUSNESS, EFFECT_0) && !m_bot->HasAura(RACIAL, EFFECT_0))
  20675. + // m_ai->CastSpell(SEAL_OF_RIGHTEOUSNESS, *m_bot);
  20676. + // break;
  20677. +
  20678. + // case PALADIN_SPEC_RETRIBUTION:
  20679. + // if (RACIAL > 0 && !m_bot->HasAura(RACIAL, EFFECT_0))
  20680. + // m_ai->CastSpell(RACIAL, *m_bot);
  20681. + // else if (SEAL_OF_COMMAND > 0 && !m_bot->HasAura(SEAL_OF_COMMAND, EFFECT_0) && !m_bot->HasAura(RACIAL, EFFECT_0))
  20682. + // m_ai->CastSpell(SEAL_OF_COMMAND, *m_bot);
  20683. + // else if (SEAL_OF_RIGHTEOUSNESS > 0 && !m_bot->HasAura(SEAL_OF_RIGHTEOUSNESS, EFFECT_0) && !m_bot->HasAura(SEAL_OF_COMMAND, EFFECT_0) && !m_bot->HasAura(RACIAL, EFFECT_0))
  20684. + // m_ai->CastSpell(SEAL_OF_RIGHTEOUSNESS, *m_bot);
  20685. + // break;
  20686. + //}
  20687. +}
  20688. +
  20689. +void PlayerbotPaladinAI::DoNonCombatActions()
  20690. +{
  20691. + //if (!m_ai) return;
  20692. + //if (!m_bot) return;
  20693. +
  20694. + //if (!m_bot->isAlive() || m_bot->IsInDuel()) return;
  20695. +
  20696. + //CheckAuras();
  20697. + //CheckSeals();
  20698. +
  20699. + ////Put up RF if tank
  20700. + //if (m_ai->GetCombatOrder() & PlayerbotAI::ORDERS_TANK)
  20701. + // m_ai->SelfBuff(RIGHTEOUS_FURY);
  20702. + ////Disable RF if not tank
  20703. + //else if (m_bot->HasAura(RIGHTEOUS_FURY))
  20704. + // m_bot->RemoveAurasDueToSpell(RIGHTEOUS_FURY);
  20705. +
  20706. + //// Revive
  20707. + //if (HealPlayer(GetResurrectionTarget()) & RETURN_CONTINUE)
  20708. + // return;
  20709. +
  20710. + //// Heal
  20711. + //if (m_ai->IsHealer())
  20712. + //{
  20713. + // if (HealPlayer(GetHealTarget()) & RETURN_CONTINUE)
  20714. + // return;// RETURN_CONTINUE;
  20715. + //}
  20716. + //else
  20717. + //{
  20718. + // // Is this desirable? Debatable.
  20719. + // // TODO: In a group/raid with a healer you'd want this bot to focus on DPS (it's not specced/geared for healing either)
  20720. + // if (HealPlayer(m_bot) & RETURN_CONTINUE)
  20721. + // return;// RETURN_CONTINUE;
  20722. + //}
  20723. +
  20724. + //// buff group
  20725. + //if (Buff(&PlayerbotPaladinAI::BuffHelper, 1)) // Paladin's BuffHelper takes care of choosing the specific Blessing so just pass along a non-zero value
  20726. + // return;
  20727. +
  20728. + //// hp/mana check
  20729. + //if (m_bot->getStandState() != UNIT_STAND_STATE_STAND)
  20730. + // m_bot->SetStandState(UNIT_STAND_STATE_STAND);
  20731. +
  20732. + //if (EatDrinkBandage())
  20733. + // return;
  20734. +}
  20735. +
  20736. +/**
  20737. + * BuffHelper
  20738. + * BuffHelper is a static function, takes an AI, spellId (ignored for paladin) and a target and attempts to buff them as well as their pets as
  20739. + * best as possible.
  20740. + *
  20741. + * Return bool - returns true if a buff took place.
  20742. + */
  20743. +bool PlayerbotPaladinAI::BuffHelper(PlayerbotAI* ai, uint32 spellId, Unit *target)
  20744. +{
  20745. + return false;
  20746. + //if (!ai) return false;
  20747. + //if (spellId == 0) return false;
  20748. + //if (!target) return false;
  20749. +
  20750. + //uint8 SPELL_BLESSING = 2; // See SpellSpecific enum in SpellMgr.h
  20751. +
  20752. + //PlayerbotPaladinAI* c = (PlayerbotPaladinAI*) ai->GetClassAI();
  20753. + //uint32 bigSpellId = 0;
  20754. +
  20755. + //Pet* pet = target->GetTypeId() == TYPEID_PLAYER ? target->ToPlayer()->GetPet() : NULL;
  20756. + //uint32 petSpellId = 0, petBigSpellId = 0;
  20757. +
  20758. + //if (ai->CanReceiveSpecificSpell(SPELL_BLESSING, target))
  20759. + // return false;
  20760. +
  20761. + //// See which buff is appropriate according to class
  20762. + //// TODO: take into account other paladins in the group
  20763. + //switch (target->getClass())
  20764. + //{
  20765. + // case CLASS_DRUID:
  20766. + // case CLASS_SHAMAN:
  20767. + // case CLASS_PALADIN:
  20768. + // spellId = c->BLESSING_OF_MIGHT;
  20769. + // if (!spellId)
  20770. + // {
  20771. + // spellId = c->BLESSING_OF_KINGS;
  20772. + // if (!spellId)
  20773. + // {
  20774. + // spellId = c->BLESSING_OF_WISDOM;
  20775. + // if (!spellId)
  20776. + // {
  20777. + // spellId = c->BLESSING_OF_SANCTUARY;
  20778. + // if (!spellId)
  20779. + // return false;
  20780. + // }
  20781. + // }
  20782. + // }
  20783. + // break;
  20784. + // case CLASS_DEATH_KNIGHT:
  20785. + // case CLASS_HUNTER:
  20786. + // if (pet && ai->CanReceiveSpecificSpell(SPELL_BLESSING, pet) && !pet->HasAuraType(SPELL_AURA_MOD_UNATTACKABLE))
  20787. + // {
  20788. + // petSpellId = c->BLESSING_OF_MIGHT;
  20789. + // if (!petSpellId)
  20790. + // {
  20791. + // petSpellId = c->BLESSING_OF_KINGS;
  20792. + // if (!petSpellId)
  20793. + // petSpellId = c->BLESSING_OF_SANCTUARY;
  20794. + // }
  20795. + // }
  20796. + // case CLASS_ROGUE:
  20797. + // case CLASS_WARRIOR:
  20798. + // spellId = c->BLESSING_OF_MIGHT;
  20799. + // if (!spellId)
  20800. + // {
  20801. + // spellId = c->BLESSING_OF_KINGS;
  20802. + // if (!spellId)
  20803. + // {
  20804. + // spellId = c->BLESSING_OF_SANCTUARY;
  20805. + // if (!spellId)
  20806. + // return false;
  20807. + // }
  20808. + // }
  20809. + // break;
  20810. + // case CLASS_WARLOCK:
  20811. + // if (pet && ai->CanReceiveSpecificSpell(SPELL_BLESSING, pet) && !pet->HasAuraType(SPELL_AURA_MOD_UNATTACKABLE))
  20812. + // {
  20813. + // if (pet->getPowerType() == POWER_MANA)
  20814. + // petSpellId = c->BLESSING_OF_WISDOM;
  20815. + // else
  20816. + // petSpellId = c->BLESSING_OF_MIGHT;
  20817. +
  20818. + // if (!petSpellId)
  20819. + // {
  20820. + // petSpellId = c->BLESSING_OF_KINGS;
  20821. + // if (!petSpellId)
  20822. + // petSpellId = c->BLESSING_OF_SANCTUARY;
  20823. + // }
  20824. + // }
  20825. + // case CLASS_PRIEST:
  20826. + // case CLASS_MAGE:
  20827. + // spellId = c->BLESSING_OF_WISDOM;
  20828. + // if (!spellId)
  20829. + // {
  20830. + // spellId = c->BLESSING_OF_KINGS;
  20831. + // if (!spellId)
  20832. + // {
  20833. + // spellId = c->BLESSING_OF_SANCTUARY;
  20834. + // if (!spellId)
  20835. + // return false;
  20836. + // }
  20837. + // }
  20838. + // break;
  20839. + //}
  20840. +
  20841. + //if (petSpellId == c->BLESSING_OF_MIGHT)
  20842. + // petBigSpellId = c->GREATER_BLESSING_OF_MIGHT;
  20843. + //else if (petSpellId == c->BLESSING_OF_WISDOM)
  20844. + // petBigSpellId = c->GREATER_BLESSING_OF_WISDOM;
  20845. + //else if (petSpellId == c->BLESSING_OF_KINGS)
  20846. + // petBigSpellId = c->GREATER_BLESSING_OF_KINGS;
  20847. + //else if (petSpellId == c->BLESSING_OF_SANCTUARY)
  20848. + // petBigSpellId = c->GREATER_BLESSING_OF_SANCTUARY;
  20849. +
  20850. + //if (spellId == c->BLESSING_OF_MIGHT)
  20851. + // bigSpellId = c->GREATER_BLESSING_OF_MIGHT;
  20852. + //else if (spellId == c->BLESSING_OF_WISDOM)
  20853. + // bigSpellId = c->GREATER_BLESSING_OF_WISDOM;
  20854. + //else if (spellId == c->BLESSING_OF_KINGS)
  20855. + // bigSpellId = c->GREATER_BLESSING_OF_KINGS;
  20856. + //else if (spellId == c->BLESSING_OF_SANCTUARY)
  20857. + // bigSpellId = c->GREATER_BLESSING_OF_SANCTUARY;
  20858. +
  20859. + //if (bigSpellId && ai->HasSpellReagents(bigSpellId) && ((petSpellId && ai->Buff(petBigSpellId, pet)) || ai->Buff(bigSpellId, target)))
  20860. + // return true;
  20861. + //else
  20862. + // return ( (petSpellId && ai->Buff(petSpellId, target)) || ai->Buff(spellId, target) );
  20863. +}
  20864. +
  20865. +// Match up with "Pull()" below
  20866. +bool PlayerbotPaladinAI::CanPull()
  20867. +{
  20868. + //if (HAND_OF_RECKONING && !m_bot->HasSpellCooldown(HAND_OF_RECKONING))
  20869. + // return true;
  20870. + //if (EXORCISM && !m_bot->HasSpellCooldown(EXORCISM))
  20871. + // return true;
  20872. +
  20873. + return false;
  20874. +}
  20875. +
  20876. +// Match up with "CanPull()" above
  20877. +bool PlayerbotPaladinAI::Pull()
  20878. +{
  20879. + //if (HAND_OF_RECKONING && m_ai->CastSpell(HAND_OF_RECKONING))
  20880. + // return true;
  20881. + //if (EXORCISM && m_ai->CastSpell(EXORCISM))
  20882. + // return true;
  20883. +
  20884. + return false;
  20885. +}
  20886. +
  20887. +bool PlayerbotPaladinAI::CastHoTOnTank()
  20888. +{
  20889. + //if (!m_ai) return false;
  20890. +
  20891. + //if ((PlayerbotAI::ORDERS_HEAL & m_ai->GetCombatOrder()) == 0) return false;
  20892. +
  20893. + // Paladin: Sheath of Light (with talents), Flash of Light (with Infusion of Light talent and only on a target with the Sacred Shield buff),
  20894. + // Holy Shock (with Tier 8 set bonus)
  20895. + // None of these are HoTs to cast before pulling (I think)
  20896. +
  20897. + return false;
  20898. +}
  20899. diff --git a/src/server/game/AI/PlayerBots/bp_pal_ai.h b/src/server/game/AI/PlayerBots/bp_pal_ai.h
  20900. new file mode 100644
  20901. index 0000000..d2cafb1
  20902. --- /dev/null
  20903. +++ b/src/server/game/AI/PlayerBots/bp_pal_ai.h
  20904. @@ -0,0 +1,208 @@
  20905. +#ifndef _PlayerbotPaladinAI_H
  20906. +#define _PlayerbotPaladinAI_H
  20907. +
  20908. +#include "bp_cai.h"
  20909. +
  20910. +enum
  20911. +{
  20912. + Combat,
  20913. + Healing
  20914. +};
  20915. +
  20916. +enum PaladinSpells
  20917. +{
  20918. + AURA_MASTERY_1 = 31821,
  20919. + AVENGERS_SHIELD_1 = 31935,
  20920. + AVENGING_WRATH_1 = 31884,
  20921. + BEACON_OF_LIGHT_1 = 53563,
  20922. + BLESSING_OF_KINGS_1 = 20217,
  20923. + BLESSING_OF_MIGHT_1 = 19740,
  20924. + BLESSING_OF_SANCTUARY_1 = 20911,
  20925. + BLESSING_OF_WISDOM_1 = 19742,
  20926. + CLEANSE_1 = 4987,
  20927. + CONCENTRATION_AURA_1 = 19746,
  20928. + CONSECRATION_1 = 26573,
  20929. + CRUSADER_AURA_1 = 32223,
  20930. + CRUSADER_STRIKE_1 = 35395,
  20931. + DEVOTION_AURA_1 = 465,
  20932. + DIVINE_FAVOR_1 = 20216,
  20933. + DIVINE_ILLUMINATION_1 = 31842,
  20934. + DIVINE_INTERVENTION_1 = 19752,
  20935. + DIVINE_PLEA_1 = 54428,
  20936. + DIVINE_PROTECTION_1 = 498,
  20937. + DIVINE_SACRIFICE_1 = 64205,
  20938. + DIVINE_SHIELD_1 = 642,
  20939. + DIVINE_STORM_1 = 53385,
  20940. + EXORCISM_1 = 879,
  20941. + FIRE_RESISTANCE_AURA_1 = 19891,
  20942. + FLASH_OF_LIGHT_1 = 19750,
  20943. + FROST_RESISTANCE_AURA_1 = 19888,
  20944. + GREATER_BLESSING_OF_KINGS_1 = 25898,
  20945. + GREATER_BLESSING_OF_MIGHT_1 = 25782,
  20946. + GREATER_BLESSING_OF_SANCTUARY_1 = 25899,
  20947. + GREATER_BLESSING_OF_WISDOM_1 = 25894,
  20948. + HAMMER_OF_JUSTICE_1 = 853,
  20949. + HAMMER_OF_THE_RIGHTEOUS_1 = 53595,
  20950. + HAMMER_OF_WRATH_1 = 24275,
  20951. + HAND_OF_FREEDOM_1 = 1044,
  20952. + HAND_OF_PROTECTION_1 = 1022,
  20953. + HAND_OF_RECKONING_1 = 62124,
  20954. + HAND_OF_SACRIFICE_1 = 6940,
  20955. + HAND_OF_SALVATION_1 = 1038,
  20956. + HOLY_LIGHT_1 = 635,
  20957. + HOLY_SHIELD_1 = 20925,
  20958. + HOLY_SHOCK_1 = 20473,
  20959. + HOLY_WRATH_1 = 2812,
  20960. + JUDGEMENT_OF_JUSTICE_1 = 53407,
  20961. + JUDGEMENT_OF_LIGHT_1 = 20271,
  20962. + JUDGEMENT_OF_WISDOM_1 = 53408,
  20963. + LAY_ON_HANDS_1 = 633,
  20964. + PURIFY_1 = 1152,
  20965. + REDEMPTION_1 = 7328,
  20966. + REPENTANCE_1 = 20066,
  20967. + RETRIBUTION_AURA_1 = 7294,
  20968. + RIGHTEOUS_DEFENSE_1 = 31789,
  20969. + RIGHTEOUS_FURY_1 = 25780,
  20970. + SACRED_SHIELD_1 = 53601,
  20971. + SEAL_OF_COMMAND_1 = 20375,
  20972. + SEAL_OF_CORRUPTION_1 = 53736,
  20973. + SEAL_OF_JUSTICE_1 = 20164,
  20974. + SEAL_OF_LIGHT_1 = 20165,
  20975. + SEAL_OF_RIGHTEOUSNESS_1 = 21084,
  20976. + SEAL_OF_VENGEANCE_1 = 31801,
  20977. + SEAL_OF_WISDOM_1 = 20166,
  20978. + SENSE_UNDEAD_1 = 5502,
  20979. + SHADOW_RESISTANCE_AURA_1 = 19876,
  20980. + SHIELD_OF_RIGHTEOUSNESS_1 = 53600,
  20981. + TURN_EVIL_1 = 10326,
  20982. + //Max rank only
  20983. + ART_OF_WAR_1 = 53488
  20984. +};
  20985. +//class Player;
  20986. +
  20987. +class PlayerbotPaladinAI : PlayerbotClassAI
  20988. +{
  20989. +public:
  20990. + PlayerbotPaladinAI(Player * const master, Player * const bot, PlayerbotAI * const ai);
  20991. + virtual ~PlayerbotPaladinAI();
  20992. +
  20993. + // all combat actions go here
  20994. + CombatManeuverReturns DoFirstCombatManeuver(Unit* pTarget);
  20995. + CombatManeuverReturns DoNextCombatManeuver(Unit* pTarget);
  20996. + bool Pull();
  20997. +
  20998. + // all non combat actions go here, ex buffs, heals, rezzes
  20999. + void DoNonCombatActions();
  21000. +
  21001. + // Utility Functions
  21002. + bool CanPull();
  21003. + bool CastHoTOnTank();
  21004. +
  21005. +private:
  21006. + CombatManeuverReturns DoFirstCombatManeuverPVE(Unit* pTarget);
  21007. + CombatManeuverReturns DoNextCombatManeuverPVE(Unit* pTarget);
  21008. + CombatManeuverReturns DoFirstCombatManeuverPVP(Unit* pTarget);
  21009. + CombatManeuverReturns DoNextCombatManeuverPVP(Unit* pTarget);
  21010. +
  21011. + // Heals the target based off its hps
  21012. + CombatManeuverReturns HealPlayer(Player* target);
  21013. + Player* GetHealTarget() { return PlayerbotClassAI::GetHealTarget(); }
  21014. +
  21015. + //Changes aura according to spec/orders
  21016. + void CheckAuras();
  21017. + //Changes Seal according to spec
  21018. + void CheckSeals();
  21019. +
  21020. + static bool BuffHelper(PlayerbotAI* ai, uint32 spellId, Unit *target);
  21021. +
  21022. + // make this public so the static function can access it. Either that or make an accessor function for each
  21023. +public:
  21024. + // Retribution
  21025. + uint32 RETRIBUTION_AURA,
  21026. + SEAL_OF_COMMAND,
  21027. + JUDGEMENT_OF_LIGHT,
  21028. + JUDGEMENT_OF_WISDOM,
  21029. + GREATER_BLESSING_OF_WISDOM,
  21030. + GREATER_BLESSING_OF_MIGHT,
  21031. + BLESSING_OF_WISDOM,
  21032. + BLESSING_OF_MIGHT,
  21033. + HAMMER_OF_JUSTICE,
  21034. + RIGHTEOUS_FURY,
  21035. + CRUSADER_AURA,
  21036. + CRUSADER_STRIKE,
  21037. + AVENGING_WRATH,
  21038. + DIVINE_STORM,
  21039. + JUDGEMENT_OF_JUSTICE,
  21040. + ART_OF_WAR;
  21041. +
  21042. + // Holy
  21043. + uint32 FLASH_OF_LIGHT,
  21044. + HOLY_LIGHT,
  21045. + DIVINE_SHIELD,
  21046. + HAMMER_OF_WRATH,
  21047. + CONSECRATION,
  21048. + CONCENTRATION_AURA,
  21049. + DIVINE_FAVOR,
  21050. + SACRED_SHIELD,
  21051. + HOLY_SHOCK,
  21052. + HOLY_WRATH,
  21053. + LAY_ON_HANDS,
  21054. + EXORCISM,
  21055. + REDEMPTION,
  21056. + DIVINE_PLEA,
  21057. + SEAL_OF_CORRUPTION,
  21058. + SEAL_OF_JUSTICE,
  21059. + SEAL_OF_LIGHT,
  21060. + SEAL_OF_RIGHTEOUSNESS,
  21061. + SEAL_OF_VENGEANCE,
  21062. + SEAL_OF_WISDOM,
  21063. + PURIFY,
  21064. + CLEANSE;
  21065. +
  21066. + // Protection
  21067. + uint32 GREATER_BLESSING_OF_KINGS,
  21068. + BLESSING_OF_KINGS,
  21069. + HAND_OF_PROTECTION,
  21070. + SHADOW_RESISTANCE_AURA,
  21071. + DEVOTION_AURA,
  21072. + FIRE_RESISTANCE_AURA,
  21073. + FROST_RESISTANCE_AURA,
  21074. + DEFENSIVE_STANCE,
  21075. + BERSERKER_STANCE,
  21076. + BATTLE_STANCE,
  21077. + DIVINE_SACRIFICE,
  21078. + DIVINE_PROTECTION,
  21079. + DIVINE_INTERVENTION,
  21080. + HOLY_SHIELD,
  21081. + AVENGERS_SHIELD,
  21082. + RIGHTEOUS_DEFENSE,
  21083. + BLESSING_OF_SANCTUARY,
  21084. + GREATER_BLESSING_OF_SANCTUARY,
  21085. + HAND_OF_SACRIFICE,
  21086. + SHIELD_OF_RIGHTEOUSNESS,
  21087. + HAND_OF_RECKONING,
  21088. + HAMMER_OF_THE_RIGHTEOUS;
  21089. +
  21090. + // cannot be protected
  21091. + uint32 FORBEARANCE;
  21092. +
  21093. + // racial
  21094. + uint32 ARCANE_TORRENT,
  21095. + GIFT_OF_THE_NAARU,
  21096. + STONEFORM,
  21097. + ESCAPE_ARTIST,
  21098. + EVERY_MAN_FOR_HIMSELF,
  21099. + SHADOWMELD,
  21100. + BLOOD_FURY,
  21101. + WAR_STOMP,
  21102. + BERSERKING,
  21103. + WILL_OF_THE_FORSAKEN;
  21104. +
  21105. + //Non-Stacking buffs
  21106. + uint32 PRAYER_OF_SHADOW_PROTECTION;
  21107. +
  21108. +private:
  21109. + uint32 SpellSequence, CombatCounter, HealCounter;
  21110. +};
  21111. +
  21112. +#endif
  21113. diff --git a/src/server/game/AI/PlayerBots/bp_pri_ai.cpp b/src/server/game/AI/PlayerBots/bp_pri_ai.cpp
  21114. new file mode 100644
  21115. index 0000000..d3b8734
  21116. --- /dev/null
  21117. +++ b/src/server/game/AI/PlayerBots/bp_pri_ai.cpp
  21118. @@ -0,0 +1,514 @@
  21119. +#include "bp_ai.h"
  21120. +#include "bp_pri_ai.h"
  21121. +#include "SpellAuras.h"
  21122. +#include "Player.h"
  21123. +#include "Pet.h"
  21124. +
  21125. +PlayerbotPriestAI::PlayerbotPriestAI(Player* const master, Player* const bot, PlayerbotAI* const ai) : PlayerbotClassAI(master, bot, ai)
  21126. +{
  21127. + RENEW = PlayerbotAI::InitSpell(me, RENEW_1);
  21128. + LESSER_HEAL = PlayerbotAI::InitSpell(me, LESSER_HEAL_1);
  21129. + FLASH_HEAL = PlayerbotAI::InitSpell(me, FLASH_HEAL_1);
  21130. + (FLASH_HEAL > 0) ? FLASH_HEAL : FLASH_HEAL = LESSER_HEAL;
  21131. + HEAL = PlayerbotAI::InitSpell(me, HEAL_1);
  21132. + (HEAL > 0) ? HEAL : HEAL = FLASH_HEAL;
  21133. + GREATER_HEAL = PlayerbotAI::InitSpell(me, GREATER_HEAL_1);
  21134. + (GREATER_HEAL > 0) ? GREATER_HEAL : GREATER_HEAL = HEAL;
  21135. + RESURRECTION = PlayerbotAI::InitSpell(me, RESURRECTION_1);
  21136. + SMITE = PlayerbotAI::InitSpell(me, SMITE_1);
  21137. + MANA_BURN = PlayerbotAI::InitSpell(me, MANA_BURN_1);
  21138. + HOLY_NOVA = PlayerbotAI::InitSpell(me, HOLY_NOVA_1);
  21139. + HOLY_FIRE = PlayerbotAI::InitSpell(me, HOLY_FIRE_1);
  21140. + DESPERATE_PRAYER = PlayerbotAI::InitSpell(me, DESPERATE_PRAYER_1);
  21141. + PRAYER_OF_HEALING = PlayerbotAI::InitSpell(me, PRAYER_OF_HEALING_1);
  21142. + CIRCLE_OF_HEALING = PlayerbotAI::InitSpell(me, CIRCLE_OF_HEALING_1);
  21143. + BINDING_HEAL = PlayerbotAI::InitSpell(me, BINDING_HEAL_1);
  21144. + PRAYER_OF_MENDING = PlayerbotAI::InitSpell(me, PRAYER_OF_MENDING_1);
  21145. + CURE_DISEASE = PlayerbotAI::InitSpell(me, CURE_DISEASE_1);
  21146. +
  21147. + // SHADOW
  21148. + FADE = PlayerbotAI::InitSpell(me, FADE_1);
  21149. + SHADOW_WORD_PAIN = PlayerbotAI::InitSpell(me, SHADOW_WORD_PAIN_1);
  21150. + MIND_BLAST = PlayerbotAI::InitSpell(me, MIND_BLAST_1);
  21151. + SCREAM = PlayerbotAI::InitSpell(me, PSYCHIC_SCREAM_1);
  21152. + MIND_FLAY = PlayerbotAI::InitSpell(me, MIND_FLAY_1);
  21153. + DEVOURING_PLAGUE = PlayerbotAI::InitSpell(me, DEVOURING_PLAGUE_1);
  21154. + SHADOW_PROTECTION = PlayerbotAI::InitSpell(me, SHADOW_PROTECTION_1);
  21155. + VAMPIRIC_TOUCH = PlayerbotAI::InitSpell(me, VAMPIRIC_TOUCH_1);
  21156. + PRAYER_OF_SHADOW_PROTECTION = PlayerbotAI::InitSpell(me, PRAYER_OF_SHADOW_PROTECTION_1);
  21157. + SHADOWFIEND = PlayerbotAI::InitSpell(me, SHADOWFIEND_1);
  21158. + MIND_SEAR = PlayerbotAI::InitSpell(me, MIND_SEAR_1);
  21159. + SHADOWFORM = PlayerbotAI::InitSpell(me, SHADOWFORM_1);
  21160. + VAMPIRIC_EMBRACE = PlayerbotAI::InitSpell(me, VAMPIRIC_EMBRACE_1);
  21161. +
  21162. + // RANGED COMBAT
  21163. + SHOOT = PlayerbotAI::InitSpell(me, SHOOT_1);
  21164. +
  21165. + // DISCIPLINE
  21166. + PENANCE = PlayerbotAI::InitSpell(me, PENANCE_1);
  21167. + INNER_FIRE = PlayerbotAI::InitSpell(me, INNER_FIRE_1);
  21168. + POWER_WORD_SHIELD = PlayerbotAI::InitSpell(me, POWER_WORD_SHIELD_1);
  21169. + POWER_WORD_FORTITUDE = PlayerbotAI::InitSpell(me, POWER_WORD_FORTITUDE_1);
  21170. + PRAYER_OF_FORTITUDE = PlayerbotAI::InitSpell(me, PRAYER_OF_FORTITUDE_1);
  21171. + FEAR_WARD = PlayerbotAI::InitSpell(me, FEAR_WARD_1);
  21172. + DIVINE_SPIRIT = PlayerbotAI::InitSpell(me, DIVINE_SPIRIT_1);
  21173. + PRAYER_OF_SPIRIT = PlayerbotAI::InitSpell(me, PRAYER_OF_SPIRIT_1);
  21174. + MASS_DISPEL = PlayerbotAI::InitSpell(me, MASS_DISPEL_1);
  21175. + POWER_INFUSION = PlayerbotAI::InitSpell(me, POWER_INFUSION_1);
  21176. + INNER_FOCUS = PlayerbotAI::InitSpell(me, INNER_FOCUS_1);
  21177. +
  21178. + //RECENTLY_BANDAGED = 11196; // first aid check
  21179. +
  21180. + // racial
  21181. + ARCANE_TORRENT = PlayerbotAI::InitSpell(me, ARCANE_TORRENT_MANA_CLASSES);
  21182. + GIFT_OF_THE_NAARU = PlayerbotAI::InitSpell(me, GIFT_OF_THE_NAARU_PRIEST); // draenei
  21183. + STONEFORM = PlayerbotAI::InitSpell(me, STONEFORM_ALL); // dwarf
  21184. + EVERY_MAN_FOR_HIMSELF = PlayerbotAI::InitSpell(me, EVERY_MAN_FOR_HIMSELF_ALL); // human
  21185. + SHADOWMELD = PlayerbotAI::InitSpell(me, SHADOWMELD_ALL);
  21186. + BERSERKING = PlayerbotAI::InitSpell(me, BERSERKING_ALL); // troll
  21187. + WILL_OF_THE_FORSAKEN = PlayerbotAI::InitSpell(me, WILL_OF_THE_FORSAKEN_ALL); // undead
  21188. +}
  21189. +
  21190. +PlayerbotPriestAI::~PlayerbotPriestAI() {}
  21191. +
  21192. +CombatManeuverReturns PlayerbotPriestAI::DoFirstCombatManeuver(Unit* pTarget)
  21193. +{
  21194. + //// There are NPCs in BGs and Open World PvP, so don't filter this on PvP scenarios (of course if PvP targets anyone but tank, all bets are off anyway)
  21195. + //// Wait until the tank says so, until any non-tank gains aggro or X seconds - whichever is shortest
  21196. + //if (m_ai->GetCombatOrder() & PlayerbotAI::ORDERS_TEMP_WAIT_TANKAGGRO)
  21197. + //{
  21198. + // if (m_WaitUntil > m_ai->CurrentTime() && m_ai->GroupTankHoldsAggro())
  21199. + // {
  21200. + // if (PlayerbotAI::ORDERS_HEAL & m_ai->GetCombatOrder())
  21201. + // return HealPlayer(GetHealTarget());
  21202. + // else
  21203. + // return RETURN_NO_ACTION_OK; // wait it out
  21204. + // }
  21205. + // else
  21206. + // {
  21207. + // m_ai->ClearGroupCombatOrder(PlayerbotAI::ORDERS_TEMP_WAIT_TANKAGGRO);
  21208. + // }
  21209. + //}
  21210. +
  21211. + //if (m_ai->GetCombatOrder() & PlayerbotAI::ORDERS_TEMP_WAIT_OOC)
  21212. + //{
  21213. + // if (m_WaitUntil > m_ai->CurrentTime() && !m_ai->IsGroupInCombat())
  21214. + // return RETURN_NO_ACTION_OK; // wait it out
  21215. + // else
  21216. + // m_ai->ClearGroupCombatOrder(PlayerbotAI::ORDERS_TEMP_WAIT_OOC);
  21217. + //}
  21218. +
  21219. + //switch (m_ai->GetScenarioType())
  21220. + //{
  21221. + // case PlayerbotAI::SCENARIO_PVP_DUEL:
  21222. + // case PlayerbotAI::SCENARIO_PVP_BG:
  21223. + // case PlayerbotAI::SCENARIO_PVP_ARENA:
  21224. + // case PlayerbotAI::SCENARIO_PVP_OPENWORLD:
  21225. + // return DoFirstCombatManeuverPVP(pTarget);
  21226. + // case PlayerbotAI::SCENARIO_PVE:
  21227. + // case PlayerbotAI::SCENARIO_PVE_ELITE:
  21228. + // case PlayerbotAI::SCENARIO_PVE_RAID:
  21229. + // default:
  21230. + // return DoFirstCombatManeuverPVE(pTarget);
  21231. + // break;
  21232. + //}
  21233. +
  21234. + return RETURN_NO_ACTION_ERROR;
  21235. +}
  21236. +
  21237. +CombatManeuverReturns PlayerbotPriestAI::DoFirstCombatManeuverPVE(Unit* /*pTarget*/)
  21238. +{
  21239. + //if (!m_ai) return RETURN_NO_ACTION_ERROR;
  21240. + //if (!m_bot) return RETURN_NO_ACTION_ERROR;
  21241. +
  21242. + //if (m_ai->IsHealer())
  21243. + //{
  21244. + // // TODO: This must be done with toggles: FullHealth allowed
  21245. + // Unit* healTarget = GetHealTarget(JOB_TANK);
  21246. + // // This is cast on a target, which activates (and switches to another target within the group) upon receiving+healing damage
  21247. + // // Mana efficient even at one use
  21248. + // if (healTarget && PRAYER_OF_MENDING > 0 && !healTarget->HasAura(PRAYER_OF_MENDING, EFFECT_0) && CastSpell(PRAYER_OF_MENDING, healTarget) & RETURN_CONTINUE)
  21249. + // return RETURN_FINISHED_FIRST_MOVES;
  21250. + //}
  21251. + return RETURN_NO_ACTION_OK;
  21252. +}
  21253. +
  21254. +CombatManeuverReturns PlayerbotPriestAI::DoFirstCombatManeuverPVP(Unit* /*pTarget*/)
  21255. +{
  21256. + return RETURN_NO_ACTION_OK;
  21257. +}
  21258. +
  21259. +CombatManeuverReturns PlayerbotPriestAI::DoNextCombatManeuver(Unit *pTarget)
  21260. +{
  21261. + //switch (m_ai->GetScenarioType())
  21262. + //{
  21263. + // case PlayerbotAI::SCENARIO_PVP_DUEL:
  21264. + // case PlayerbotAI::SCENARIO_PVP_BG:
  21265. + // case PlayerbotAI::SCENARIO_PVP_ARENA:
  21266. + // case PlayerbotAI::SCENARIO_PVP_OPENWORLD:
  21267. + // return DoNextCombatManeuverPVP(pTarget);
  21268. + // case PlayerbotAI::SCENARIO_PVE:
  21269. + // case PlayerbotAI::SCENARIO_PVE_ELITE:
  21270. + // case PlayerbotAI::SCENARIO_PVE_RAID:
  21271. + // default:
  21272. + // return DoNextCombatManeuverPVE(pTarget);
  21273. + // break;
  21274. + //}
  21275. +
  21276. + return RETURN_NO_ACTION_ERROR;
  21277. +}
  21278. +
  21279. +CombatManeuverReturns PlayerbotPriestAI::DoNextCombatManeuverPVE(Unit *pTarget)
  21280. +{
  21281. + //if (!m_ai) return RETURN_NO_ACTION_ERROR;
  21282. + //if (!m_bot) return RETURN_NO_ACTION_ERROR;
  21283. +
  21284. + //float dist = pTarget->GetCombatReach();
  21285. + //uint32 spec = m_bot->GetSpec();
  21286. +
  21287. + //if (m_ai->GetCombatStyle() != PlayerbotAI::COMBAT_RANGED && dist > ATTACK_DISTANCE)
  21288. + // m_ai->SetCombatStyle(PlayerbotAI::COMBAT_RANGED);
  21289. + //// if in melee range OR can't shoot OR have no ranged (wand) equipped
  21290. + //else if(m_ai->GetCombatStyle() != PlayerbotAI::COMBAT_MELEE
  21291. + // && (SHOOT == 0 || !m_bot->GetWeaponForAttack(RANGED_ATTACK, true))
  21292. + // && !m_ai->IsHealer())
  21293. + // m_ai->SetCombatStyle(PlayerbotAI::COMBAT_MELEE);
  21294. +
  21295. + ////Used to determine if this bot is highest on threat
  21296. + //Unit* newTarget = m_ai->FindAttacker((PlayerbotAI::ATTACKERINFOTYPE) (PlayerbotAI::AIT_VICTIMSELF | PlayerbotAI::AIT_HIGHESTTHREAT), m_bot);
  21297. + //if (newTarget) // TODO: && party has a tank
  21298. + //{
  21299. + // if (newTarget && FADE > 0 && !m_bot->HasAura(FADE, EFFECT_0))
  21300. + // {
  21301. + // if (CastSpell(FADE, m_bot))
  21302. + // {
  21303. + // //m_ai->TellMaster("I'm casting fade.");
  21304. + // return RETURN_CONTINUE;
  21305. + // }
  21306. + // else
  21307. + // m_ai->TellMaster("I have AGGRO.");
  21308. + // }
  21309. +
  21310. + // // Heal myself
  21311. + // // TODO: move to HealTarget code
  21312. + // // TODO: you forgot to check for the 'temporarily immune to PW:S because you only just got it cast on you' effect
  21313. + // // - which is different effect from the actual shield.
  21314. + // if (m_bot->GetHealthPct() < 25 && POWER_WORD_SHIELD > 0 && !m_bot->HasAura(POWER_WORD_SHIELD, EFFECT_0))
  21315. + // {
  21316. + // if (CastSpell(POWER_WORD_SHIELD) & RETURN_CONTINUE)
  21317. + // {
  21318. + // //m_ai->TellMaster("I'm casting PW:S on myself.");
  21319. + // return RETURN_CONTINUE;
  21320. + // }
  21321. + // else if (m_ai->IsHealer()) // Even if any other RETURN_ANY_OK - aside from RETURN_CONTINUE
  21322. + // m_ai->TellMaster("Your healer's about TO DIE. HELP ME.");
  21323. + // }
  21324. + // if (m_bot->GetHealthPct() < 35 && DESPERATE_PRAYER > 0 && CastSpell(DESPERATE_PRAYER, m_bot) & RETURN_CONTINUE)
  21325. + // {
  21326. + // //m_ai->TellMaster("I'm casting desperate prayer.");
  21327. + // return RETURN_CONTINUE;
  21328. + // }
  21329. +
  21330. + // // Already healed self or tank. If healer, do nothing else to anger mob.
  21331. + // if (m_ai->IsHealer())
  21332. + // return RETURN_NO_ACTION_OK; // In a sense, mission accomplished.
  21333. +
  21334. + // // Have threat, can't quickly lower it. 3 options remain: Stop attacking, lowlevel damage (wand), keep on keeping on.
  21335. + // if (newTarget->GetHealthPct() > 25)
  21336. + // {
  21337. + // // If elite, do nothing and pray tank gets aggro off you
  21338. + // // TODO: Is there an IsElite function? If so, find it and insert.
  21339. + // //if (newTarget->IsElite())
  21340. + // // return;
  21341. +
  21342. + // // Not an elite. You could insert PSYCHIC SCREAM here but in any PvE situation that's 90-95% likely
  21343. + // // to worsen the situation for the group. ... So please don't.
  21344. + // return CastSpell(SHOOT, pTarget);
  21345. + // }
  21346. + //}
  21347. +
  21348. + //// Heal
  21349. + //if (m_ai->IsHealer())
  21350. + //{
  21351. + // if (HealPlayer(GetHealTarget()) & RETURN_CONTINUE)
  21352. + // return RETURN_CONTINUE;
  21353. + //}
  21354. + //else
  21355. + //{
  21356. + // // Is this desirable? Debatable.
  21357. + // // ... Certainly could be very detrimental to a shadow priest
  21358. + // // TODO: In a group/raid with a healer you'd want this bot to focus on DPS (it's not specced/geared for healing either)
  21359. + // if (HealPlayer(m_bot) & RETURN_CONTINUE)
  21360. + // return RETURN_CONTINUE;
  21361. + //}
  21362. +
  21363. + //// Do damage tweaking for healers here
  21364. + //if (m_ai->IsHealer())
  21365. + //{
  21366. + // // TODO: elite exception
  21367. + // //if (Any target is an Elite)
  21368. + // // return;
  21369. +
  21370. + // return CastSpell(SHOOT, pTarget);
  21371. + //}
  21372. +
  21373. + //// Damage Spells
  21374. + //switch (spec)
  21375. + //{
  21376. + // case PRIEST_SPEC_HOLY:
  21377. + // if (HOLY_FIRE > 0 && !pTarget->HasAura(HOLY_FIRE, EFFECT_0) && CastSpell(HOLY_FIRE, pTarget))
  21378. + // return RETURN_CONTINUE;
  21379. + // if (SMITE > 0 && CastSpell(SMITE, pTarget))
  21380. + // return RETURN_CONTINUE;
  21381. + // //if (HOLY_NOVA > 0 && dist <= ATTACK_DISTANCE && m_ai->CastSpell(HOLY_NOVA))
  21382. + // // return RETURN_CONTINUE;
  21383. + // break;
  21384. +
  21385. + // case PRIEST_SPEC_SHADOW:
  21386. + // if (DEVOURING_PLAGUE > 0 && !pTarget->HasAura(DEVOURING_PLAGUE, EFFECT_0) && CastSpell(DEVOURING_PLAGUE, pTarget))
  21387. + // return RETURN_CONTINUE;
  21388. + // if (VAMPIRIC_TOUCH > 0 && !pTarget->HasAura(VAMPIRIC_TOUCH, EFFECT_0) && CastSpell(VAMPIRIC_TOUCH, pTarget))
  21389. + // return RETURN_CONTINUE;
  21390. + // if (SHADOW_WORD_PAIN > 0 && !pTarget->HasAura(SHADOW_WORD_PAIN, EFFECT_0) && CastSpell(SHADOW_WORD_PAIN, pTarget))
  21391. + // return RETURN_CONTINUE;
  21392. + // if (MIND_BLAST > 0 && (!m_bot->HasSpellCooldown(MIND_BLAST)) && CastSpell(MIND_BLAST, pTarget))
  21393. + // return RETURN_CONTINUE;
  21394. + // if (MIND_FLAY > 0 && CastSpell(MIND_FLAY, pTarget))
  21395. + // {
  21396. + // //m_ai->SetIgnoreUpdateTime(3);
  21397. + // return RETURN_CONTINUE;
  21398. + // }
  21399. + // if (SHADOWFIEND > 0 && !m_bot->GetPet() && CastSpell(SHADOWFIEND))
  21400. + // return RETURN_CONTINUE;
  21401. + // /*if (MIND_SEAR > 0 && m_ai->GetAttackerCount() >= 3 && CastSpell(MIND_SEAR, pTarget))
  21402. + // {
  21403. + // //m_ai->SetIgnoreUpdateTime(5);
  21404. + // return RETURN_CONTINUE;
  21405. + // }*/
  21406. + // if (SHADOWFORM == 0 && MIND_FLAY == 0 && SMITE > 0 && CastSpell(SMITE, pTarget)) // low levels
  21407. + // return RETURN_CONTINUE;
  21408. + // break;
  21409. +
  21410. + // case PRIEST_SPEC_DISCIPLINE:
  21411. + // if (POWER_INFUSION > 0 && CastSpell(POWER_INFUSION, GetMaster())) // TODO: just master?
  21412. + // return RETURN_CONTINUE;
  21413. + // if (INNER_FOCUS > 0 && !m_bot->HasAura(INNER_FOCUS, EFFECT_0) && CastSpell(INNER_FOCUS, m_bot))
  21414. + // return RETURN_CONTINUE;
  21415. + // if (PENANCE > 0 && CastSpell(PENANCE))
  21416. + // return RETURN_CONTINUE;
  21417. + // if (SMITE > 0 && CastSpell(SMITE, pTarget))
  21418. + // return RETURN_CONTINUE;
  21419. + // break;
  21420. + //}
  21421. +
  21422. + //// No spec due to low level OR no spell found yet
  21423. + //if (MIND_BLAST > 0 && (!m_bot->HasSpellCooldown(MIND_BLAST)) && CastSpell(MIND_BLAST, pTarget))
  21424. + // return RETURN_CONTINUE;
  21425. + //if (SHADOW_WORD_PAIN > 0 && !pTarget->HasAura(SHADOW_WORD_PAIN, EFFECT_0) && CastSpell(SHADOW_WORD_PAIN, pTarget))
  21426. + // return RETURN_CONTINUE;
  21427. + //if (MIND_FLAY > 0 && CastSpell(MIND_FLAY, pTarget))
  21428. + //{
  21429. + // //m_ai->SetIgnoreUpdateTime(3);
  21430. + // return RETURN_CONTINUE;
  21431. + //}
  21432. + //if (SHADOWFORM == 0 && SMITE > 0 && CastSpell(SMITE, pTarget))
  21433. + // return RETURN_CONTINUE;
  21434. +
  21435. + return RETURN_NO_ACTION_OK;
  21436. +} // end DoNextCombatManeuver
  21437. +
  21438. +CombatManeuverReturns PlayerbotPriestAI::DoNextCombatManeuverPVP(Unit* pTarget)
  21439. +{
  21440. + //switch (m_ai->GetScenarioType())
  21441. + //{
  21442. + // case PlayerbotAI::SCENARIO_PVP_DUEL:
  21443. + // // TODO: spec tweaking
  21444. + // if (m_ai->HasAura(SCREAM, *pTarget) && m_bot->GetHealthPct() < 60 && HEAL && CastSpell(HEAL) & RETURN_ANY_OK)
  21445. + // return RETURN_CONTINUE;
  21446. +
  21447. + // if (SHADOW_WORD_PAIN && CastSpell(SHADOW_WORD_PAIN) & RETURN_ANY_OK) // TODO: Check whether enemy has it active yet
  21448. + // return RETURN_CONTINUE;
  21449. +
  21450. + // if (m_bot->GetHealthPct() < 80 && RENEW && CastSpell(RENEW) & RETURN_ANY_OK) // TODO: Check whether you have renew active on you
  21451. + // return RETURN_CONTINUE;
  21452. +
  21453. + // if (pTarget->GetCombatReach() <= 5 && SCREAM && CastSpell(SCREAM) & RETURN_ANY_OK) // TODO: Check for cooldown
  21454. + // return RETURN_CONTINUE;
  21455. +
  21456. + // if (MIND_BLAST && CastSpell(MIND_BLAST) & RETURN_ANY_OK) // TODO: Check for cooldown
  21457. + // return RETURN_CONTINUE;
  21458. +
  21459. + // if (m_bot->GetHealthPct() < 50 && GREATER_HEAL && CastSpell(GREATER_HEAL) & RETURN_ANY_OK)
  21460. + // return RETURN_CONTINUE;
  21461. +
  21462. + // if (SMITE && CastSpell(SMITE) & RETURN_ANY_OK)
  21463. + // return RETURN_CONTINUE;
  21464. +
  21465. + // m_ai->TellMaster("Couldn't find a spell to cast while dueling");
  21466. + // default:
  21467. + // break;
  21468. + //}
  21469. +
  21470. + return DoNextCombatManeuverPVE(pTarget); // TODO: bad idea perhaps, but better than the alternative
  21471. +}
  21472. +
  21473. +CombatManeuverReturns PlayerbotPriestAI::HealPlayer(Player* target)
  21474. +{
  21475. + //CombatManeuverReturns r = PlayerbotClassAI::HealPlayer(target);
  21476. + //if (r != RETURN_NO_ACTION_OK)
  21477. + // return r;
  21478. +
  21479. + //if (!target->isAlive())
  21480. + //{
  21481. + // if (RESURRECTION && m_ai->CastSpell(RESURRECTION, *target))
  21482. + // {
  21483. + // std::string msg = "Resurrecting ";
  21484. + // msg += target->GetName();
  21485. + // m_bot->Say(msg, LANG_UNIVERSAL);
  21486. + // return RETURN_CONTINUE;
  21487. + // }
  21488. + // return RETURN_NO_ACTION_ERROR; // not error per se - possibly just OOM
  21489. + //}
  21490. +
  21491. + //if (CURE_DISEASE > 0 && (m_ai->GetCombatOrder() & PlayerbotAI::ORDERS_NODISPEL) == 0)
  21492. + //{
  21493. + // uint32 dispelMask = SpellInfo::GetDispelMask(DISPEL_DISEASE);
  21494. + // Unit::AuraMap const& auras = target->GetOwnedAuras();
  21495. + // for (Unit::AuraMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr)
  21496. + // {
  21497. + // Aura *holder = itr->second;
  21498. + // if ((1 << holder->GetSpellInfo()->Dispel) & dispelMask)
  21499. + // {
  21500. + // if (holder->GetSpellInfo()->Dispel == DISPEL_DISEASE)
  21501. + // {
  21502. + // m_ai->CastSpell(CURE_DISEASE, *target);
  21503. + // return RETURN_CONTINUE;
  21504. + // }
  21505. + // }
  21506. + // }
  21507. + //}
  21508. +
  21509. + //uint8 hp = target->GetHealthPct();
  21510. + //uint8 hpSelf = m_bot->GetHealthPct();
  21511. +
  21512. + //if (hp >= 90)
  21513. + // return RETURN_NO_ACTION_OK;
  21514. +
  21515. + //// TODO: Integrate shield here
  21516. + //if (hp < 35 && FLASH_HEAL > 0 && m_ai->CastSpell(FLASH_HEAL, *target))
  21517. + // return RETURN_CONTINUE;
  21518. + //if (hp < 45 && GREATER_HEAL > 0 && m_ai->CastSpell(GREATER_HEAL, *target))
  21519. + // return RETURN_CONTINUE;
  21520. + //// Heals target AND self for equal amount
  21521. + //if (hp < 60 && hpSelf < 80 && BINDING_HEAL > 0 && m_ai->CastSpell(BINDING_HEAL, *target))
  21522. + // return RETURN_CONTINUE;
  21523. + //if (hp < 60 && PRAYER_OF_MENDING > 0 && !target->HasAura(PRAYER_OF_MENDING, EFFECT_0) && CastSpell(PRAYER_OF_MENDING, target))
  21524. + // return RETURN_FINISHED_FIRST_MOVES;
  21525. + //if (hp < 60 && HEAL > 0 && m_ai->CastSpell(HEAL, *target))
  21526. + // return RETURN_CONTINUE;
  21527. + //if (hp < 90 && RENEW > 0 && !target->HasAura(RENEW) && m_ai->CastSpell(RENEW, *target))
  21528. + // return RETURN_CONTINUE;
  21529. +
  21530. + // Group heal. Not really useful until a group check is available?
  21531. + //if (hp < 40 && PRAYER_OF_HEALING > 0 && m_ai->CastSpell(PRAYER_OF_HEALING, *target) & RETURN_CONTINUE)
  21532. + // return RETURN_CONTINUE;
  21533. + // Group heal. Not really useful until a group check is available?
  21534. + //if (hp < 50 && CIRCLE_OF_HEALING > 0 && m_ai->CastSpell(CIRCLE_OF_HEALING, *target) & RETURN_CONTINUE)
  21535. + // return RETURN_CONTINUE;
  21536. +
  21537. + return RETURN_NO_ACTION_OK;
  21538. +} // end HealTarget
  21539. +
  21540. +void PlayerbotPriestAI::DoNonCombatActions()
  21541. +{
  21542. + //if (!m_ai) return;
  21543. + //if (!m_bot) return;
  21544. +
  21545. + //if (!m_bot->isAlive() || m_bot->IsInDuel()) return;
  21546. +
  21547. + //uint32 spec = m_bot->GetSpec();
  21548. +
  21549. + //// selfbuff goes first
  21550. + //if (m_ai->SelfBuff(INNER_FIRE))
  21551. + // return;
  21552. +
  21553. + //// Revive
  21554. + //if (HealPlayer(GetResurrectionTarget()) & RETURN_CONTINUE)
  21555. + // return;
  21556. +
  21557. + //// After revive
  21558. + //if (spec == PRIEST_SPEC_SHADOW && SHADOWFORM > 0)
  21559. + // m_ai->SelfBuff(SHADOWFORM);
  21560. + //if (VAMPIRIC_EMBRACE > 0)
  21561. + // m_ai->SelfBuff(VAMPIRIC_EMBRACE);
  21562. +
  21563. + //// Heal
  21564. + //if (m_ai->IsHealer())
  21565. + //{
  21566. + // if (HealPlayer(GetHealTarget()) & RETURN_CONTINUE)
  21567. + // return;// RETURN_CONTINUE;
  21568. + //}
  21569. + //else
  21570. + //{
  21571. + // // Is this desirable? Debatable.
  21572. + // // TODO: In a group/raid with a healer you'd want this bot to focus on DPS (it's not specced/geared for healing either)
  21573. + // if (HealPlayer(m_bot) & RETURN_CONTINUE)
  21574. + // return;// RETURN_CONTINUE;
  21575. + //}
  21576. +
  21577. + //// Buff
  21578. + //if (m_bot->GetGroup())
  21579. + //{
  21580. + // if (PRAYER_OF_FORTITUDE && m_ai->HasSpellReagents(PRAYER_OF_FORTITUDE) && m_ai->Buff(PRAYER_OF_FORTITUDE, m_bot))
  21581. + // return;
  21582. +
  21583. + // if (PRAYER_OF_SPIRIT && m_ai->HasSpellReagents(PRAYER_OF_SPIRIT) && m_ai->Buff(PRAYER_OF_SPIRIT, m_bot))
  21584. + // return;
  21585. +
  21586. + // if (PRAYER_OF_SHADOW_PROTECTION && m_ai->HasSpellReagents(PRAYER_OF_SHADOW_PROTECTION) && m_ai->Buff(PRAYER_OF_SHADOW_PROTECTION, m_bot))
  21587. + // return;
  21588. + //}
  21589. + //if (Buff(&PlayerbotPriestAI::BuffHelper, POWER_WORD_FORTITUDE))
  21590. + // return;
  21591. + //if (Buff(&PlayerbotPriestAI::BuffHelper, DIVINE_SPIRIT, (JOB_ALL | JOB_MANAONLY)))
  21592. + // return;
  21593. + //if (Buff(&PlayerbotPriestAI::BuffHelper, SHADOW_PROTECTION, (JOB_TANK | JOB_HEAL) ))
  21594. + // return;
  21595. +
  21596. + //// hp/mana check
  21597. + //if (m_bot->getStandState() != UNIT_STAND_STATE_STAND)
  21598. + // m_bot->SetStandState(UNIT_STAND_STATE_STAND);
  21599. +
  21600. + //if (EatDrinkBandage())
  21601. + // return;
  21602. +} // end DoNonCombatActions
  21603. +
  21604. +// TODO: this and mage's BuffHelper are identical and thus could probably go in PlayerbotClassAI.cpp somewhere
  21605. +bool PlayerbotPriestAI::BuffHelper(PlayerbotAI* ai, uint32 spellId, Unit *target)
  21606. +{
  21607. + //if (!ai) return false;
  21608. + //if (spellId == 0) return false;
  21609. + //if (!target) return false;
  21610. +
  21611. + //Pet * pet = target->GetTypeId() == TYPEID_PLAYER ? target->ToPlayer()->GetPet() : NULL;
  21612. + //if (pet && !pet->HasAuraType(SPELL_AURA_MOD_UNATTACKABLE) && ai->Buff(spellId, pet))
  21613. + // return true;
  21614. +
  21615. + //if (ai->Buff(spellId, target))
  21616. + // return true;
  21617. +
  21618. + return false;
  21619. +}
  21620. +
  21621. +bool PlayerbotPriestAI::CastHoTOnTank()
  21622. +{
  21623. + //if (!m_ai) return false;
  21624. +
  21625. + //if ((PlayerbotAI::ORDERS_HEAL & m_ai->GetCombatOrder()) == 0) return false;
  21626. +
  21627. + //// Priest HoTs: Renew, Penance (with talents, channeled)
  21628. + //if (RENEW)
  21629. + // return (RETURN_CONTINUE & CastSpell(RENEW, m_ai->GetGroupTank()));
  21630. +
  21631. + return false;
  21632. +}
  21633. diff --git a/src/server/game/AI/PlayerBots/bp_pri_ai.h b/src/server/game/AI/PlayerBots/bp_pri_ai.h
  21634. new file mode 100644
  21635. index 0000000..33553d5
  21636. --- /dev/null
  21637. +++ b/src/server/game/AI/PlayerBots/bp_pri_ai.h
  21638. @@ -0,0 +1,166 @@
  21639. +#ifndef _PLAYERBOTPRIESTAI_H
  21640. +#define _PLAYERBOTPRIESTAI_H
  21641. +
  21642. +#include "bp_cai.h"
  21643. +
  21644. +enum
  21645. +{
  21646. + SPELL_HOLY,
  21647. + SPELL_SHADOWMAGIC,
  21648. + SPELL_DISCIPLINE
  21649. +};
  21650. +
  21651. +enum PriestSpells
  21652. +{
  21653. + ABOLISH_DISEASE_1 = 552,
  21654. + BINDING_HEAL_1 = 32546,
  21655. + BLESSED_HEALING_1 = 70772,
  21656. + CIRCLE_OF_HEALING_1 = 34861,
  21657. + CURE_DISEASE_1 = 528,
  21658. + DESPERATE_PRAYER_1 = 19236,
  21659. + DEVOURING_PLAGUE_1 = 2944,
  21660. + DISPEL_MAGIC_1 = 527,
  21661. + DISPERSION_1 = 47585,
  21662. + DIVINE_HYMN_1 = 64843,
  21663. + DIVINE_SPIRIT_1 = 14752,
  21664. + FADE_1 = 586,
  21665. + FEAR_WARD_1 = 6346,
  21666. + FLASH_HEAL_1 = 2061,
  21667. + GREATER_HEAL_1 = 2060,
  21668. + GUARDIAN_SPIRIT_1 = 47788,
  21669. + HEAL_1 = 2054,
  21670. + HOLY_FIRE_1 = 14914,
  21671. + HOLY_NOVA_1 = 15237,
  21672. + HYMN_OF_HOPE_1 = 64901,
  21673. + INNER_FIRE_1 = 588,
  21674. + INNER_FOCUS_1 = 14751,
  21675. + LESSER_HEAL_1 = 2050,
  21676. + LEVITATE_1 = 1706,
  21677. + LIGHTWELL_1 = 724,
  21678. + MANA_BURN_1 = 8129,
  21679. + MASS_DISPEL_1 = 32375,
  21680. + MIND_BLAST_1 = 8092,
  21681. + MIND_CONTROL_1 = 605,
  21682. + MIND_FLAY_1 = 15407,
  21683. + MIND_SEAR_1 = 48045,
  21684. + MIND_SOOTHE_1 = 453,
  21685. + MIND_VISION_1 = 2096,
  21686. + PAIN_SUPPRESSION_1 = 33206,
  21687. + PENANCE_1 = 47540,
  21688. + POWER_INFUSION_1 = 10060,
  21689. + POWER_WORD_FORTITUDE_1 = 1243,
  21690. + POWER_WORD_SHIELD_1 = 17,
  21691. + PRAYER_OF_FORTITUDE_1 = 21562,
  21692. + PRAYER_OF_HEALING_1 = 596,
  21693. + PRAYER_OF_MENDING_1 = 33076,
  21694. + PRAYER_OF_SHADOW_PROTECTION_1 = 27683,
  21695. + PRAYER_OF_SPIRIT_1 = 27681,
  21696. + PSYCHIC_HORROR_1 = 64044,
  21697. + PSYCHIC_SCREAM_1 = 8122,
  21698. + RENEW_1 = 139,
  21699. + RESURRECTION_1 = 2006,
  21700. + SHACKLE_UNDEAD_1 = 9484,
  21701. + SHADOW_PROTECTION_1 = 976,
  21702. + SHADOW_WORD_DEATH_1 = 32379,
  21703. + SHADOW_WORD_PAIN_1 = 589,
  21704. + SHADOWFIEND_1 = 34433,
  21705. + SHADOWFORM_1 = 15473,
  21706. + SHOOT_1 = 5019,
  21707. + SILENCE_1 = 15487,
  21708. + SMITE_1 = 585,
  21709. + VAMPIRIC_EMBRACE_1 = 15286,
  21710. + VAMPIRIC_TOUCH_1 = 34914
  21711. +};
  21712. +//class Player;
  21713. +
  21714. +class PlayerbotPriestAI : PlayerbotClassAI
  21715. +{
  21716. +public:
  21717. + PlayerbotPriestAI(Player * const master, Player * const bot, PlayerbotAI * const ai);
  21718. + virtual ~PlayerbotPriestAI();
  21719. +
  21720. + // all combat actions go here
  21721. + CombatManeuverReturns DoFirstCombatManeuver(Unit* pTarget);
  21722. + CombatManeuverReturns DoNextCombatManeuver(Unit* pTarget);
  21723. +
  21724. + // all non combat actions go here, ex buffs, heals, rezzes
  21725. + void DoNonCombatActions();
  21726. +
  21727. + // Utility Functions
  21728. + bool CastHoTOnTank();
  21729. +
  21730. +private:
  21731. + CombatManeuverReturns DoFirstCombatManeuverPVE(Unit* pTarget);
  21732. + CombatManeuverReturns DoNextCombatManeuverPVE(Unit* pTarget);
  21733. + CombatManeuverReturns DoFirstCombatManeuverPVP(Unit* pTarget);
  21734. + CombatManeuverReturns DoNextCombatManeuverPVP(Unit* pTarget);
  21735. +
  21736. + CombatManeuverReturns CastSpell(uint32 nextAction, Unit *pTarget = NULL) { return CastSpellWand(nextAction, pTarget, SHOOT); }
  21737. +
  21738. + // Heals the target based off its hps
  21739. + CombatManeuverReturns HealPlayer(Player* target);
  21740. +
  21741. + static bool BuffHelper(PlayerbotAI* ai, uint32 spellId, Unit *target);
  21742. +
  21743. + // holy
  21744. + uint32 BINDING_HEAL,
  21745. + CIRCLE_OF_HEALING,
  21746. + CLEARCASTING,
  21747. + DESPERATE_PRAYER,
  21748. + FLASH_HEAL,
  21749. + GREATER_HEAL,
  21750. + HEAL,
  21751. + HOLY_FIRE,
  21752. + HOLY_NOVA,
  21753. + LESSER_HEAL,
  21754. + MANA_BURN,
  21755. + PRAYER_OF_HEALING,
  21756. + PRAYER_OF_MENDING,
  21757. + RENEW,
  21758. + RESURRECTION,
  21759. + SMITE,
  21760. + CURE_DISEASE;
  21761. + // ranged
  21762. + uint32 SHOOT;
  21763. +
  21764. + // shadowmagic
  21765. + uint32 FADE,
  21766. + SHADOW_WORD_PAIN,
  21767. + MIND_BLAST,
  21768. + SCREAM,
  21769. + MIND_FLAY,
  21770. + DEVOURING_PLAGUE,
  21771. + SHADOW_PROTECTION,
  21772. + VAMPIRIC_TOUCH,
  21773. + PRAYER_OF_SHADOW_PROTECTION,
  21774. + SHADOWFIEND,
  21775. + MIND_SEAR,
  21776. + SHADOWFORM,
  21777. + VAMPIRIC_EMBRACE;
  21778. +
  21779. + // discipline
  21780. + uint32 POWER_WORD_SHIELD,
  21781. + INNER_FIRE,
  21782. + POWER_WORD_FORTITUDE,
  21783. + PRAYER_OF_FORTITUDE,
  21784. + FEAR_WARD,
  21785. + POWER_INFUSION,
  21786. + MASS_DISPEL,
  21787. + PENANCE,
  21788. + DIVINE_SPIRIT,
  21789. + PRAYER_OF_SPIRIT,
  21790. + INNER_FOCUS;
  21791. +
  21792. + // racial
  21793. + uint32 ARCANE_TORRENT,
  21794. + GIFT_OF_THE_NAARU,
  21795. + STONEFORM,
  21796. + ESCAPE_ARTIST,
  21797. + EVERY_MAN_FOR_HIMSELF,
  21798. + SHADOWMELD,
  21799. + WAR_STOMP,
  21800. + BERSERKING,
  21801. + WILL_OF_THE_FORSAKEN;
  21802. +};
  21803. +
  21804. +#endif
  21805. diff --git a/src/server/game/AI/PlayerBots/bp_rog_ai.cpp b/src/server/game/AI/PlayerBots/bp_rog_ai.cpp
  21806. new file mode 100644
  21807. index 0000000..82ec574
  21808. --- /dev/null
  21809. +++ b/src/server/game/AI/PlayerBots/bp_rog_ai.cpp
  21810. @@ -0,0 +1,408 @@
  21811. +/*
  21812. + Name : PlayerbotRogueAI.cpp
  21813. + Complete: maybe around 28%
  21814. + Author : Natsukawa
  21815. + Version : 0.37
  21816. + */
  21817. +#include "bp_ai.h"
  21818. +#include "bp_mgr.h"
  21819. +#include "bp_rog_ai.h"
  21820. +#include "Player.h"
  21821. +
  21822. +PlayerbotRogueAI::PlayerbotRogueAI(Player* const master, Player* const bot, PlayerbotAI* const ai) : PlayerbotClassAI(master, bot, ai)
  21823. +{
  21824. + SINISTER_STRIKE = PlayerbotAI::InitSpell(me, SINISTER_STRIKE_1);
  21825. + BACKSTAB = PlayerbotAI::InitSpell(me, BACKSTAB_1);
  21826. + KICK = PlayerbotAI::InitSpell(me, KICK_1);
  21827. + FEINT = PlayerbotAI::InitSpell(me, FEINT_1);
  21828. + FAN_OF_KNIVES = PlayerbotAI::InitSpell(me, FAN_OF_KNIVES_1);
  21829. + GOUGE = PlayerbotAI::InitSpell(me, GOUGE_1);
  21830. + SPRINT = PlayerbotAI::InitSpell(me, SPRINT_1);
  21831. +
  21832. + SHADOWSTEP = PlayerbotAI::InitSpell(me, SHADOWSTEP_1);
  21833. + STEALTH = PlayerbotAI::InitSpell(me, STEALTH_1);
  21834. + VANISH = PlayerbotAI::InitSpell(me, VANISH_1);
  21835. + EVASION = PlayerbotAI::InitSpell(me, EVASION_1);
  21836. + CLOAK_OF_SHADOWS = PlayerbotAI::InitSpell(me, CLOAK_OF_SHADOWS_1);
  21837. + HEMORRHAGE = PlayerbotAI::InitSpell(me, HEMORRHAGE_1);
  21838. + GHOSTLY_STRIKE = PlayerbotAI::InitSpell(me, GHOSTLY_STRIKE_1);
  21839. + SHADOW_DANCE = PlayerbotAI::InitSpell(me, SHADOW_DANCE_1);
  21840. + BLIND = PlayerbotAI::InitSpell(me, BLIND_1);
  21841. + DISTRACT = PlayerbotAI::InitSpell(me, DISTRACT_1);
  21842. + PREPARATION = PlayerbotAI::InitSpell(me, PREPARATION_1);
  21843. + PREMEDITATION = PlayerbotAI::InitSpell(me, PREMEDITATION_1);
  21844. + PICK_POCKET = PlayerbotAI::InitSpell(me, PICK_POCKET_1);
  21845. +
  21846. + EVISCERATE = PlayerbotAI::InitSpell(me, EVISCERATE_1);
  21847. + KIDNEY_SHOT = PlayerbotAI::InitSpell(me, KIDNEY_SHOT_1);
  21848. + SLICE_DICE = PlayerbotAI::InitSpell(me, SLICE_AND_DICE_1);
  21849. + GARROTE = PlayerbotAI::InitSpell(me, GARROTE_1);
  21850. + EXPOSE_ARMOR = PlayerbotAI::InitSpell(me, EXPOSE_ARMOR_1);
  21851. + RUPTURE = PlayerbotAI::InitSpell(me, RUPTURE_1);
  21852. + DISMANTLE = PlayerbotAI::InitSpell(me, DISMANTLE_1);
  21853. + CHEAP_SHOT = PlayerbotAI::InitSpell(me, CHEAP_SHOT_1);
  21854. + AMBUSH = PlayerbotAI::InitSpell(me, AMBUSH_1);
  21855. + MUTILATE = PlayerbotAI::InitSpell(me, MUTILATE_1);
  21856. +
  21857. + //RECENTLY_BANDAGED = 11196; // first aid check
  21858. + // racial
  21859. + ARCANE_TORRENT = PlayerbotAI::InitSpell(me, ARCANE_TORRENT_ROGUE);
  21860. + STONEFORM = PlayerbotAI::InitSpell(me, STONEFORM_ALL); // dwarf
  21861. + ESCAPE_ARTIST = PlayerbotAI::InitSpell(me, ESCAPE_ARTIST_ALL); // gnome
  21862. + EVERY_MAN_FOR_HIMSELF = PlayerbotAI::InitSpell(me, EVERY_MAN_FOR_HIMSELF_ALL); // human
  21863. + SHADOWMELD = PlayerbotAI::InitSpell(me, SHADOWMELD_ALL);
  21864. + BLOOD_FURY = PlayerbotAI::InitSpell(me, BLOOD_FURY_MELEE_CLASSES); // orc
  21865. + BERSERKING = PlayerbotAI::InitSpell(me, BERSERKING_ALL); // troll
  21866. + WILL_OF_THE_FORSAKEN = PlayerbotAI::InitSpell(me, WILL_OF_THE_FORSAKEN_ALL); // undead
  21867. +}
  21868. +
  21869. +PlayerbotRogueAI::~PlayerbotRogueAI() {}
  21870. +
  21871. +CombatManeuverReturns PlayerbotRogueAI::DoFirstCombatManeuver(Unit* pTarget)
  21872. +{
  21873. + //// There are NPCs in BGs and Open World PvP, so don't filter this on PvP scenarios (of course if PvP targets anyone but tank, all bets are off anyway)
  21874. + //// Wait until the tank says so, until any non-tank gains aggro or X seconds - whichever is shortest
  21875. + //if (m_ai->GetCombatOrder() & PlayerbotAI::ORDERS_TEMP_WAIT_TANKAGGRO)
  21876. + //{
  21877. + // if (m_WaitUntil > m_ai->CurrentTime() && m_ai->GroupTankHoldsAggro())
  21878. + // {
  21879. + // return RETURN_NO_ACTION_OK; // wait it out
  21880. + // }
  21881. + // else
  21882. + // {
  21883. + // m_ai->ClearGroupCombatOrder(PlayerbotAI::ORDERS_TEMP_WAIT_TANKAGGRO);
  21884. + // }
  21885. + //}
  21886. +
  21887. + //if (m_ai->GetCombatOrder() & PlayerbotAI::ORDERS_TEMP_WAIT_OOC)
  21888. + //{
  21889. + // if (m_WaitUntil > m_ai->CurrentTime() && !m_ai->IsGroupInCombat())
  21890. + // return RETURN_NO_ACTION_OK; // wait it out
  21891. + // else
  21892. + // m_ai->ClearGroupCombatOrder(PlayerbotAI::ORDERS_TEMP_WAIT_OOC);
  21893. + //}
  21894. +
  21895. + //if (!m_ai) return RETURN_NO_ACTION_ERROR;
  21896. + //if (!m_bot) return RETURN_NO_ACTION_ERROR;
  21897. +
  21898. + //switch (m_ai->GetScenarioType())
  21899. + //{
  21900. + // case PlayerbotAI::SCENARIO_PVP_DUEL:
  21901. + // case PlayerbotAI::SCENARIO_PVP_BG:
  21902. + // case PlayerbotAI::SCENARIO_PVP_ARENA:
  21903. + // case PlayerbotAI::SCENARIO_PVP_OPENWORLD:
  21904. + // return DoFirstCombatManeuverPVP(pTarget);
  21905. + // case PlayerbotAI::SCENARIO_PVE:
  21906. + // case PlayerbotAI::SCENARIO_PVE_ELITE:
  21907. + // case PlayerbotAI::SCENARIO_PVE_RAID:
  21908. + // default:
  21909. + // return DoFirstCombatManeuverPVE(pTarget);
  21910. + // break;
  21911. + //}
  21912. +
  21913. + return RETURN_NO_ACTION_ERROR;
  21914. +}
  21915. +
  21916. +CombatManeuverReturns PlayerbotRogueAI::DoFirstCombatManeuverPVE(Unit *pTarget)
  21917. +{
  21918. + //if (STEALTH > 0 && !m_bot->HasAura(STEALTH, EFFECT_0) && m_ai->CastSpell(STEALTH, *m_bot))
  21919. + //{
  21920. + // m_bot->AddUnitState(UNIT_STATE_CHASE); // ensure that the bot does not use MoveChase(), as this doesn't seem to work with STEALTH
  21921. + // return RETURN_FINISHED_FIRST_MOVES; // DoNextCombatManeuver handles active stealth
  21922. + //}
  21923. + //else if (m_bot->HasAura(STEALTH, EFFECT_0))
  21924. + //{
  21925. + // m_bot->GetMotionMaster()->MoveFollow(pTarget, 4.5f, m_bot->GetOrientation()); // TODO: this isn't the place for movement code, is it?
  21926. + // return RETURN_FINISHED_FIRST_MOVES; // DoNextCombatManeuver handles active stealth
  21927. + //}
  21928. +
  21929. + // Not in stealth, can't cast stealth; Off to DoNextCombatManeuver
  21930. + return RETURN_NO_ACTION_OK;
  21931. +}
  21932. +
  21933. +// TODO: blatant copy of PVE for now, please PVP-port it
  21934. +CombatManeuverReturns PlayerbotRogueAI::DoFirstCombatManeuverPVP(Unit *pTarget)
  21935. +{
  21936. + //if (STEALTH > 0 && !m_bot->HasAura(STEALTH, EFFECT_0) && m_ai->CastSpell(STEALTH, *m_bot))
  21937. + //{
  21938. + // m_bot->AddUnitState(UNIT_STATE_CHASE); // ensure that the bot does not use MoveChase(), as this doesn't seem to work with STEALTH
  21939. + // return RETURN_FINISHED_FIRST_MOVES; // DoNextCombatManeuver handles active stealth
  21940. + //}
  21941. + //else if (m_bot->HasAura(STEALTH, EFFECT_0))
  21942. + //{
  21943. + // m_bot->GetMotionMaster()->MoveFollow(pTarget, 4.5f, m_bot->GetOrientation()); // TODO: this isn't the place for movement code, is it?
  21944. + // return RETURN_FINISHED_FIRST_MOVES; // DoNextCombatManeuver handles active stealth
  21945. + //}
  21946. +
  21947. + // Not in stealth, can't cast stealth; Off to DoNextCombatManeuver
  21948. + return RETURN_NO_ACTION_OK;
  21949. +}
  21950. +
  21951. +CombatManeuverReturns PlayerbotRogueAI::DoNextCombatManeuverPVE(Unit *pTarget)
  21952. +{
  21953. + //switch (m_ai->GetScenarioType())
  21954. + //{
  21955. + // case PlayerbotAI::SCENARIO_PVP_DUEL:
  21956. + // case PlayerbotAI::SCENARIO_PVP_BG:
  21957. + // case PlayerbotAI::SCENARIO_PVP_ARENA:
  21958. + // case PlayerbotAI::SCENARIO_PVP_OPENWORLD:
  21959. + // return DoNextCombatManeuverPVP(pTarget);
  21960. + // case PlayerbotAI::SCENARIO_PVE:
  21961. + // case PlayerbotAI::SCENARIO_PVE_ELITE:
  21962. + // case PlayerbotAI::SCENARIO_PVE_RAID:
  21963. + // default:
  21964. + // return DoNextCombatManeuverPVE(pTarget);
  21965. + // break;
  21966. + //}
  21967. +
  21968. + return RETURN_NO_ACTION_ERROR;
  21969. +}
  21970. +
  21971. +CombatManeuverReturns PlayerbotRogueAI::DoNextCombatManeuver(Unit *pTarget)
  21972. +{
  21973. + //if (!pTarget) return RETURN_NO_ACTION_ERROR;
  21974. + //if (!m_ai) return RETURN_NO_ACTION_ERROR;
  21975. + //if (!m_bot) return RETURN_NO_ACTION_ERROR;
  21976. +
  21977. + //Unit* pVictim = pTarget->getVictim();
  21978. + //float fTargetDist = pTarget->GetCombatReach();
  21979. +
  21980. + //// TODO: make this work better...
  21981. + ///*if (pVictim)
  21982. + // {
  21983. + // if( pVictim!=m_bot && !m_bot->HasUnitState(UNIT_STATE_FOLLOW) && !pTarget->isInBackInMap(m_bot,10) ) {
  21984. + // m_ai->TellMaster( "getting behind target" );
  21985. + // m_bot->GetMotionMaster()->Clear( true );
  21986. + // m_bot->GetMotionMaster()->MoveFollow( pTarget, 1, 2*M_PI );
  21987. + // }
  21988. + // else if( pVictim==m_bot && m_bot->HasUnitState(UNIT_STATE_FOLLOW) )
  21989. + // {
  21990. + // m_ai->TellMaster( "chasing attacking target" );
  21991. + // m_bot->GetMotionMaster()->Clear( true );
  21992. + // m_bot->GetMotionMaster()->MoveChase( pTarget );
  21993. + // }
  21994. + // }*/
  21995. +
  21996. + //// Rogue like behaviour ^^
  21997. + ///*if (VANISH > 0 && GetMaster()->isDead()) { //Causes the server to crash :( removed for now.
  21998. + // m_bot->AttackStop();
  21999. + // m_bot->RemoveAllAttackers();
  22000. + // m_ai->CastSpell(VANISH);
  22001. + // //m_bot->RemoveAllSpellCooldown();
  22002. + // m_ai->TellMaster("AttackStop, CombatStop, Vanish");
  22003. + //}*/
  22004. +
  22005. + //// decide what to do:
  22006. + //if (pVictim == m_bot && CLOAK_OF_SHADOWS > 0 && m_bot->HasAura(SPELL_AURA_PERIODIC_DAMAGE) && !m_bot->HasAura(CLOAK_OF_SHADOWS, EFFECT_0) && m_ai->CastSpell(CLOAK_OF_SHADOWS))
  22007. + //{
  22008. + // if (m_ai->GetManager()->m_confDebugWhisper)
  22009. + // m_ai->TellMaster("CoS!");
  22010. + // return RETURN_CONTINUE;
  22011. + //}
  22012. + //else if (m_bot->HasAura(STEALTH, EFFECT_0))
  22013. + // SpellSequence = RogueStealth;
  22014. + //else if (pTarget->IsNonMeleeSpellCasted(true))
  22015. + // SpellSequence = RogueSpellPreventing;
  22016. + //else if (pVictim == m_bot && m_bot->GetHealthPct() < 40)
  22017. + // SpellSequence = RogueThreat;
  22018. + //else
  22019. + // SpellSequence = RogueCombat;
  22020. +
  22021. + //// we fight in melee, target is not in range, skip the next part!
  22022. + //if (fTargetDist > ATTACK_DISTANCE)
  22023. + // return RETURN_CONTINUE;
  22024. +
  22025. + //std::ostringstream out;
  22026. + //switch (SpellSequence)
  22027. + //{
  22028. + // case RogueStealth:
  22029. + // if (PICK_POCKET > 0 && (pTarget->GetCreatureTypeMask() & CREATURE_TYPEMASK_HUMANOID_OR_UNDEAD) != 0 &&
  22030. + // !((Creature *) pTarget)->lootForPickPocketed && m_ai->CastSpell(PICK_POCKET, *pTarget))
  22031. + // return RETURN_CONTINUE;
  22032. + // if (PREMEDITATION > 0 && m_ai->CastSpell(PREMEDITATION, *pTarget))
  22033. + // return RETURN_CONTINUE;
  22034. + // if (AMBUSH > 0 && m_ai->CastSpell(AMBUSH, *pTarget))
  22035. + // return RETURN_CONTINUE;
  22036. + // if (CHEAP_SHOT > 0 && !pTarget->HasAura(CHEAP_SHOT, EFFECT_0) && m_ai->CastSpell(CHEAP_SHOT, *pTarget))
  22037. + // return RETURN_CONTINUE;
  22038. + // if (GARROTE > 0 && m_ai->CastSpell(GARROTE, *pTarget))
  22039. + // return RETURN_CONTINUE;
  22040. +
  22041. + // // No appropriate action found, remove stealth
  22042. + // m_bot->RemoveAurasByType(SPELL_AURA_MOD_STEALTH);
  22043. + // return RETURN_CONTINUE;
  22044. +
  22045. + // case RogueThreat:
  22046. + // if (GOUGE > 0 && !pTarget->HasAura(GOUGE, EFFECT_0) && m_ai->CastSpell(GOUGE, *pTarget))
  22047. + // return RETURN_CONTINUE;
  22048. + // if (EVASION > 0 && m_bot->GetHealthPct() <= 35 && !m_bot->HasAura(EVASION, EFFECT_0) && m_ai->CastSpell(EVASION))
  22049. + // return RETURN_CONTINUE;
  22050. + // if (BLIND > 0 && m_bot->GetHealthPct() <= 30 && !pTarget->HasAura(BLIND, EFFECT_0) && m_ai->CastSpell(BLIND, *pTarget))
  22051. + // return RETURN_CONTINUE;
  22052. + // if (FEINT > 0 && m_bot->GetHealthPct() <= 25 && m_ai->CastSpell(FEINT))
  22053. + // return RETURN_CONTINUE;
  22054. + // if (VANISH > 0 && m_bot->GetHealthPct() <= 20 && !m_bot->HasAura(FEINT, EFFECT_0) && m_ai->CastSpell(VANISH))
  22055. + // return RETURN_CONTINUE;
  22056. + // if (PREPARATION > 0 && m_ai->CastSpell(PREPARATION))
  22057. + // return RETURN_CONTINUE;
  22058. + // if (m_bot->getRace() == RACE_NIGHTELF && m_bot->GetHealthPct() <= 15 && !m_bot->HasAura(SHADOWMELD, EFFECT_0) && m_ai->CastSpell(SHADOWMELD, *m_bot))
  22059. + // return RETURN_CONTINUE;
  22060. + // break;
  22061. +
  22062. + // case RogueSpellPreventing:
  22063. + // if (KIDNEY_SHOT > 0 && m_bot->GetComboPoints() >= 2 && m_ai->CastSpell(KIDNEY_SHOT, *pTarget))
  22064. + // return RETURN_CONTINUE;
  22065. + // else if (KICK > 0 && m_ai->CastSpell(KICK, *pTarget))
  22066. + // return RETURN_CONTINUE;
  22067. + // // break; // No action? Go combat!
  22068. +
  22069. + // case RogueCombat:
  22070. + // default:
  22071. + // if (m_bot->GetComboPoints() >= 5)
  22072. + // {
  22073. + // // wait for energy
  22074. + // if (m_bot->GetPower(POWER_ENERGY) < 25 && (KIDNEY_SHOT || SLICE_DICE || EXPOSE_ARMOR))
  22075. + // return RETURN_NO_ACTION_OK;
  22076. +
  22077. + // switch (pTarget->getClass())
  22078. + // {
  22079. + // case CLASS_SHAMAN:
  22080. + // if (KIDNEY_SHOT > 0 && m_ai->CastSpell(KIDNEY_SHOT, *pTarget)) // 25 energy (checked above)
  22081. + // return RETURN_CONTINUE;
  22082. + // break;
  22083. +
  22084. + // case CLASS_WARLOCK:
  22085. + // case CLASS_HUNTER:
  22086. + // if (SLICE_DICE > 0 && m_ai->CastSpell(SLICE_DICE, *pTarget)) // 25 energy (checked above)
  22087. + // return RETURN_CONTINUE;
  22088. + // break;
  22089. +
  22090. + // case CLASS_WARRIOR:
  22091. + // case CLASS_PALADIN:
  22092. + // case CLASS_DEATH_KNIGHT:
  22093. + // if (EXPOSE_ARMOR > 0 && !pTarget->HasAura(EXPOSE_ARMOR, EFFECT_0) && m_ai->CastSpell(EXPOSE_ARMOR, *pTarget)) // 25 energy (checked above)
  22094. + // return RETURN_CONTINUE;
  22095. + // break;
  22096. +
  22097. +
  22098. + // case CLASS_MAGE:
  22099. + // case CLASS_PRIEST:
  22100. + // if (RUPTURE > 0 && m_ai->CastSpell(RUPTURE, *pTarget)) // 25 energy (checked above)
  22101. + // return RETURN_CONTINUE;
  22102. + // break;
  22103. +
  22104. + // case CLASS_ROGUE:
  22105. + // case CLASS_DRUID:
  22106. + // default:
  22107. + // break; // fall through to below
  22108. + // }
  22109. +
  22110. + // // default combo action for rogue/druid or if other combo action is unavailable/failed
  22111. + // // wait for energy
  22112. + // if (m_bot->GetPower(POWER_ENERGY) < 35 && EVISCERATE)
  22113. + // return RETURN_NO_ACTION_OK;
  22114. + // if (EVISCERATE > 0 && m_ai->CastSpell(EVISCERATE, *pTarget))
  22115. + // return RETURN_CONTINUE;
  22116. +
  22117. + // // failed for some (non-energy related) reason, fall through to normal attacks to maximize DPS
  22118. + // }
  22119. +
  22120. + // if (SHADOW_DANCE > 0 && !m_bot->HasAura(SHADOW_DANCE, EFFECT_0) && m_ai->CastSpell(SHADOW_DANCE, *m_bot))
  22121. + // return RETURN_CONTINUE;
  22122. + // if (CHEAP_SHOT > 0 && m_bot->HasAura(SHADOW_DANCE, EFFECT_0) && !pTarget->HasAura(CHEAP_SHOT, EFFECT_0) && m_ai->CastSpell(CHEAP_SHOT, *pTarget))
  22123. + // return RETURN_CONTINUE;
  22124. + // if (AMBUSH > 0 && m_bot->HasAura(SHADOW_DANCE, EFFECT_0) && m_ai->CastSpell(AMBUSH, *pTarget))
  22125. + // return RETURN_CONTINUE;
  22126. + // if (GARROTE > 0 && m_bot->HasAura(SHADOW_DANCE, EFFECT_0) && m_ai->CastSpell(GARROTE, *pTarget))
  22127. + // return RETURN_CONTINUE;
  22128. + // if (BACKSTAB > 0 && pTarget->isInBackInMap(m_bot, 1) && m_ai->CastSpell(BACKSTAB, *pTarget))
  22129. + // return RETURN_CONTINUE;
  22130. + // if (MUTILATE > 0 && m_ai->CastSpell(MUTILATE, *pTarget))
  22131. + // return RETURN_CONTINUE;
  22132. + // if (SINISTER_STRIKE > 0 && m_ai->CastSpell(SINISTER_STRIKE, *pTarget))
  22133. + // return RETURN_CONTINUE;
  22134. + // if (GHOSTLY_STRIKE > 0 && m_ai->CastSpell(GHOSTLY_STRIKE, *pTarget))
  22135. + // return RETURN_CONTINUE;
  22136. + // if (HEMORRHAGE > 0 && m_ai->CastSpell(HEMORRHAGE, *pTarget))
  22137. + // return RETURN_CONTINUE;
  22138. + // if (DISMANTLE > 0 && !pTarget->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISARMED) && m_ai->CastSpell(DISMANTLE, *pTarget))
  22139. + // return RETURN_CONTINUE;
  22140. + // if (SHADOWSTEP > 0 && m_ai->CastSpell(SHADOWSTEP, *pTarget))
  22141. + // return RETURN_CONTINUE;
  22142. + // if (m_bot->getRace() == RACE_BLOODELF && !pTarget->HasAura(ARCANE_TORRENT, EFFECT_0) && m_ai->CastSpell(ARCANE_TORRENT, *pTarget))
  22143. + // return RETURN_CONTINUE;
  22144. + // if ((m_bot->getRace() == RACE_HUMAN && (m_bot->HasUnitState(UNIT_STATE_STUNNED)) || m_bot->HasAuraType(SPELL_AURA_MOD_FEAR) || m_bot->HasAuraType(SPELL_AURA_MOD_DECREASE_SPEED) || (m_bot->HasAuraType(SPELL_AURA_MOD_CHARM)) && m_ai->CastSpell(EVERY_MAN_FOR_HIMSELF, *m_bot)))
  22145. + // return RETURN_CONTINUE;
  22146. + // if ((m_bot->getRace() == RACE_UNDEAD_PLAYER && (m_bot->HasAuraType(SPELL_AURA_MOD_FEAR)) || (m_bot->HasAuraType(SPELL_AURA_MOD_CHARM)) && m_ai->CastSpell(WILL_OF_THE_FORSAKEN, *m_bot)))
  22147. + // return RETURN_CONTINUE;
  22148. + // if (m_bot->getRace() == RACE_DWARF && m_bot->HasAuraState(AURA_STATE_DEADLY_POISON) && m_ai->CastSpell(STONEFORM, *m_bot))
  22149. + // return RETURN_CONTINUE;
  22150. + // if ((m_bot->getRace() == RACE_GNOME && (m_bot->HasUnitState(UNIT_STATE_STUNNED)) || (m_bot->HasAuraType(SPELL_AURA_MOD_DECREASE_SPEED)) && m_ai->CastSpell(ESCAPE_ARTIST, *m_bot)))
  22151. + // return RETURN_CONTINUE;
  22152. + // else if (m_bot->getRace() == RACE_ORC && !m_bot->HasAura(BLOOD_FURY, EFFECT_0) && m_ai->CastSpell(BLOOD_FURY, *m_bot))
  22153. + // return RETURN_CONTINUE;
  22154. + // else if (m_bot->getRace() == RACE_TROLL && !m_bot->HasAura(BERSERKING, EFFECT_0) && m_ai->CastSpell(BERSERKING, *m_bot))
  22155. + // return RETURN_CONTINUE;
  22156. + // break;
  22157. + //}
  22158. +
  22159. + return RETURN_NO_ACTION_OK;
  22160. +} // end DoNextCombatManeuver
  22161. +
  22162. +CombatManeuverReturns PlayerbotRogueAI::DoNextCombatManeuverPVP(Unit* pTarget)
  22163. +{
  22164. + //if (m_ai->CastSpell(SINISTER_STRIKE))
  22165. + // return RETURN_CONTINUE;
  22166. +
  22167. + return DoNextCombatManeuverPVE(pTarget); // TODO: bad idea perhaps, but better than the alternative
  22168. +}
  22169. +
  22170. +void PlayerbotRogueAI::DoNonCombatActions()
  22171. +{
  22172. + //if (!m_ai) return;
  22173. + //if (!m_bot) return;
  22174. +
  22175. + //// remove stealth
  22176. + //if (m_bot->HasAura(STEALTH))
  22177. + // m_bot->RemoveAurasByType(SPELL_AURA_MOD_STEALTH);
  22178. +
  22179. + //// hp check
  22180. + //if (m_bot->getStandState() != UNIT_STAND_STATE_STAND)
  22181. + // m_bot->SetStandState(UNIT_STAND_STATE_STAND);
  22182. +
  22183. + //if (EatDrinkBandage(false))
  22184. + // return;
  22185. +
  22186. + //// Search and apply poisons to weapons
  22187. + //// Mainhand ...
  22188. + //Item * poison, * weapon;
  22189. + //weapon = m_bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND);
  22190. + //if (weapon && weapon->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT) == 0)
  22191. + //{
  22192. + // poison = m_ai->FindConsumable(INSTANT_POISON_DISPLAYID);
  22193. + // if (!poison)
  22194. + // poison = m_ai->FindConsumable(WOUND_POISON_DISPLAYID);
  22195. + // if (!poison)
  22196. + // poison = m_ai->FindConsumable(DEADLY_POISON_DISPLAYID);
  22197. + // if (poison)
  22198. + // {
  22199. + // m_ai->UseItem(poison, EQUIPMENT_SLOT_MAINHAND);
  22200. + // //m_ai->SetIgnoreUpdateTime(5);
  22201. + // }
  22202. + //}
  22203. + ////... and offhand
  22204. + //weapon = m_bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND);
  22205. + //if (weapon && weapon->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT) == 0)
  22206. + //{
  22207. + // poison = m_ai->FindConsumable(DEADLY_POISON_DISPLAYID);
  22208. + // if (!poison)
  22209. + // poison = m_ai->FindConsumable(WOUND_POISON_DISPLAYID);
  22210. + // if (!poison)
  22211. + // poison = m_ai->FindConsumable(INSTANT_POISON_DISPLAYID);
  22212. + // if (poison)
  22213. + // {
  22214. + // m_ai->UseItem(poison, EQUIPMENT_SLOT_OFFHAND);
  22215. + // //m_ai->SetIgnoreUpdateTime(5);
  22216. + // }
  22217. + //}
  22218. +} // end DoNonCombatActions
  22219. diff --git a/src/server/game/AI/PlayerBots/bp_rog_ai.h b/src/server/game/AI/PlayerBots/bp_rog_ai.h
  22220. new file mode 100644
  22221. index 0000000..07dc796
  22222. --- /dev/null
  22223. +++ b/src/server/game/AI/PlayerBots/bp_rog_ai.h
  22224. @@ -0,0 +1,104 @@
  22225. +
  22226. +#ifndef _PlayerbotRogueAI_H
  22227. +#define _PlayerbotRogueAI_H
  22228. +
  22229. +#include "bp_cai.h"
  22230. +
  22231. +enum
  22232. +{
  22233. + RogueCombat,
  22234. + RogueSpellPreventing,
  22235. + RogueThreat,
  22236. + RogueStealth
  22237. +};
  22238. +
  22239. +enum RoguePoisonDisplayId
  22240. +{
  22241. + DEADLY_POISON_DISPLAYID = 13707,
  22242. + INSTANT_POISON_DISPLAYID = 13710,
  22243. + WOUND_POISON_DISPLAYID = 37278
  22244. +};
  22245. +
  22246. +enum RogueSpells
  22247. +{
  22248. + ADRENALINE_RUSH_1 = 13750,
  22249. + AMBUSH_1 = 8676,
  22250. + BACKSTAB_1 = 53,
  22251. + BLADE_FLURRY_1 = 13877,
  22252. + BLIND_1 = 2094,
  22253. + CHEAP_SHOT_1 = 1833,
  22254. + CLOAK_OF_SHADOWS_1 = 31224,
  22255. + COLD_BLOOD_1 = 14177,
  22256. + DEADLY_THROW_1 = 26679,
  22257. + DISARM_TRAP_1 = 1842,
  22258. + DISMANTLE_1 = 51722,
  22259. + DISTRACT_1 = 1725,
  22260. + ENVENOM_1 = 32645,
  22261. + EVASION_1 = 5277,
  22262. + EVISCERATE_1 = 2098,
  22263. + EXPOSE_ARMOR_1 = 8647,
  22264. + FAN_OF_KNIVES_1 = 51723,
  22265. + FEINT_1 = 1966,
  22266. + GARROTE_1 = 703,
  22267. + GHOSTLY_STRIKE_1 = 14278,
  22268. + GOUGE_1 = 1776,
  22269. + HEMORRHAGE_1 = 16511,
  22270. + HUNGER_FOR_BLOOD_1 = 51662,
  22271. + KICK_1 = 1766,
  22272. + KIDNEY_SHOT_1 = 408,
  22273. + KILLING_SPREE_1 = 51690,
  22274. + MUTILATE_1 = 1329,
  22275. + PICK_LOCK_1 = 1804,
  22276. + PICK_POCKET_1 = 921,
  22277. + PREMEDITATION_1 = 14183,
  22278. + PREPARATION_1 = 14185,
  22279. + RIPOSTE_1 = 14251,
  22280. + RUPTURE_1 = 1943,
  22281. + SAP_1 = 6770,
  22282. + SHADOW_DANCE_1 = 51713,
  22283. + SHADOWSTEP_1 = 36554,
  22284. + SHIV_1 = 5938,
  22285. + SINISTER_STRIKE_1 = 1752,
  22286. + SLICE_AND_DICE_1 = 5171,
  22287. + SPRINT_1 = 2983,
  22288. + STEALTH_1 = 1784,
  22289. + TRICKS_OF_THE_TRADE_1 = 57934,
  22290. + VANISH_1 = 1856
  22291. +};
  22292. +//class Player;
  22293. +
  22294. +class PlayerbotRogueAI : PlayerbotClassAI
  22295. +{
  22296. +public:
  22297. + PlayerbotRogueAI(Player * const master, Player * const bot, PlayerbotAI * const ai);
  22298. + virtual ~PlayerbotRogueAI();
  22299. +
  22300. + // all combat actions go here
  22301. + CombatManeuverReturns DoFirstCombatManeuver(Unit* pTarget);
  22302. + CombatManeuverReturns DoNextCombatManeuver(Unit* pTarget);
  22303. +
  22304. + // all non combat actions go here, ex buffs, heals, rezzes
  22305. + void DoNonCombatActions();
  22306. +
  22307. +private:
  22308. + CombatManeuverReturns DoFirstCombatManeuverPVE(Unit* pTarget);
  22309. + CombatManeuverReturns DoNextCombatManeuverPVE(Unit* pTarget);
  22310. + CombatManeuverReturns DoFirstCombatManeuverPVP(Unit* pTarget);
  22311. + CombatManeuverReturns DoNextCombatManeuverPVP(Unit* pTarget);
  22312. +
  22313. + // COMBAT
  22314. + uint32 SINISTER_STRIKE, BACKSTAB, GOUGE, EVASION, SPRINT, KICK, FEINT, SHIV, FAN_OF_KNIVES;
  22315. +
  22316. + // SUBTLETY
  22317. + uint32 SHADOWSTEP, STEALTH, VANISH, HEMORRHAGE, BLIND, SHADOW_DANCE, PICK_POCKET, CLOAK_OF_SHADOWS, TRICK_TRADE, CRIPPLING_POISON, DEADLY_POISON, MIND_NUMBING_POISON, GHOSTLY_STRIKE, DISTRACT, PREPARATION, PREMEDITATION;
  22318. +
  22319. + // ASSASSINATION
  22320. + uint32 EVISCERATE, SLICE_DICE, GARROTE, EXPOSE_ARMOR, AMBUSH, RUPTURE, DISMANTLE, CHEAP_SHOT, KIDNEY_SHOT, MUTILATE, ENVENOM, DEADLY_THROW;
  22321. +
  22322. + // racial
  22323. + uint32 ARCANE_TORRENT, GIFT_OF_THE_NAARU, STONEFORM, ESCAPE_ARTIST, EVERY_MAN_FOR_HIMSELF, SHADOWMELD, BLOOD_FURY, WAR_STOMP, BERSERKING, WILL_OF_THE_FORSAKEN;
  22324. +
  22325. + uint32 SpellSequence, LastSpellCombat, LastSpellSubtlety, LastSpellAssassination, Aura;
  22326. +};
  22327. +
  22328. +#endif
  22329. diff --git a/src/server/game/AI/PlayerBots/bp_sha_ai.cpp b/src/server/game/AI/PlayerBots/bp_sha_ai.cpp
  22330. new file mode 100644
  22331. index 0000000..1f12e4b
  22332. --- /dev/null
  22333. +++ b/src/server/game/AI/PlayerBots/bp_sha_ai.cpp
  22334. @@ -0,0 +1,1660 @@
  22335. +#include "bp_ai.h"
  22336. +#include "bp_dk_ai.h"
  22337. +#include "bp_sha_ai.h"
  22338. +#include "Chat.h"
  22339. +#include "Player.h"
  22340. +#include "SpellAuras.h"
  22341. +#include "Totem.h"
  22342. +
  22343. +PlayerbotShamanAI::PlayerbotShamanAI(Player* const master, Player* const bot, PlayerbotAI* const ai) : PlayerbotClassAI(master, bot, ai)
  22344. +{
  22345. + // restoration
  22346. + CHAIN_HEAL = PlayerbotAI::InitSpell(me, CHAIN_HEAL_1);
  22347. + HEALING_WAVE = PlayerbotAI::InitSpell(me, HEALING_WAVE_1);
  22348. + LESSER_HEALING_WAVE = PlayerbotAI::InitSpell(me, LESSER_HEALING_WAVE_1);
  22349. + RIPTIDE = PlayerbotAI::InitSpell(me, RIPTIDE_1);
  22350. + ANCESTRAL_SPIRIT = PlayerbotAI::InitSpell(me, ANCESTRAL_SPIRIT_1);
  22351. + EARTH_SHIELD = PlayerbotAI::InitSpell(me, EARTH_SHIELD_1);
  22352. + WATER_SHIELD = PlayerbotAI::InitSpell(me, WATER_SHIELD_1);
  22353. + EARTHLIVING_WEAPON = PlayerbotAI::InitSpell(me, EARTHLIVING_WEAPON_1);
  22354. + TREMOR_TOTEM = PlayerbotAI::InitSpell(me, TREMOR_TOTEM_1); // totems
  22355. + HEALING_STREAM_TOTEM = PlayerbotAI::InitSpell(me, HEALING_STREAM_TOTEM_1);
  22356. + MANA_SPRING_TOTEM = PlayerbotAI::InitSpell(me, MANA_SPRING_TOTEM_1);
  22357. + MANA_TIDE_TOTEM = PlayerbotAI::InitSpell(me, MANA_TIDE_TOTEM_1);
  22358. + CURE_TOXINS = PlayerbotAI::InitSpell(me, CURE_TOXINS_1);
  22359. + CLEANSE_SPIRIT = PlayerbotAI::InitSpell(me, CLEANSE_SPIRIT_1);
  22360. + NATURES_SWIFTNESS_SHAMAN = PlayerbotAI::InitSpell(me, NATURES_SWIFTNESS_SHAMAN_1);
  22361. + TIDAL_FORCE = PlayerbotAI::InitSpell(me, TIDAL_FORCE_1);
  22362. + // enhancement
  22363. + FOCUSED = 0; // Focused what?
  22364. + STORMSTRIKE = PlayerbotAI::InitSpell(me, STORMSTRIKE_1);
  22365. + LAVA_LASH = PlayerbotAI::InitSpell(me, LAVA_LASH_1);
  22366. + SHAMANISTIC_RAGE = PlayerbotAI::InitSpell(me, SHAMANISTIC_RAGE_1);
  22367. + BLOODLUST = PlayerbotAI::InitSpell(me, BLOODLUST_1);
  22368. + HEROISM = PlayerbotAI::InitSpell(me, HEROISM_1);
  22369. + FERAL_SPIRIT = PlayerbotAI::InitSpell(me, FERAL_SPIRIT_1);
  22370. + LIGHTNING_SHIELD = PlayerbotAI::InitSpell(me, LIGHTNING_SHIELD_1);
  22371. + ROCKBITER_WEAPON = PlayerbotAI::InitSpell(me, ROCKBITER_WEAPON_1);
  22372. + FLAMETONGUE_WEAPON = PlayerbotAI::InitSpell(me, FLAMETONGUE_WEAPON_1);
  22373. + FROSTBRAND_WEAPON = PlayerbotAI::InitSpell(me, FROSTBRAND_WEAPON_1);
  22374. + WINDFURY_WEAPON = PlayerbotAI::InitSpell(me, WINDFURY_WEAPON_1);
  22375. + STONESKIN_TOTEM = PlayerbotAI::InitSpell(me, STONESKIN_TOTEM_1); // totems
  22376. + STRENGTH_OF_EARTH_TOTEM = PlayerbotAI::InitSpell(me, STRENGTH_OF_EARTH_TOTEM_1);
  22377. + FROST_RESISTANCE_TOTEM = PlayerbotAI::InitSpell(me, FROST_RESISTANCE_TOTEM_1);
  22378. + FLAMETONGUE_TOTEM = PlayerbotAI::InitSpell(me, FLAMETONGUE_TOTEM_1);
  22379. + FIRE_RESISTANCE_TOTEM = PlayerbotAI::InitSpell(me, FIRE_RESISTANCE_TOTEM_1);
  22380. + GROUNDING_TOTEM = PlayerbotAI::InitSpell(me, GROUNDING_TOTEM_1);
  22381. + NATURE_RESISTANCE_TOTEM = PlayerbotAI::InitSpell(me, NATURE_RESISTANCE_TOTEM_1);
  22382. + WIND_FURY_TOTEM = PlayerbotAI::InitSpell(me, WINDFURY_TOTEM_1);
  22383. + STONESKIN_TOTEM = PlayerbotAI::InitSpell(me, STONESKIN_TOTEM_1);
  22384. + WRATH_OF_AIR_TOTEM = PlayerbotAI::InitSpell(me, WRATH_OF_AIR_TOTEM_1);
  22385. + EARTH_ELEMENTAL_TOTEM = PlayerbotAI::InitSpell(me, EARTH_ELEMENTAL_TOTEM_1);
  22386. + MAELSTROM_WEAPON = PlayerbotAI::InitSpell(me, MAELSTROM_WEAPON_1);
  22387. + // elemental
  22388. + LIGHTNING_BOLT = PlayerbotAI::InitSpell(me, LIGHTNING_BOLT_1);
  22389. + EARTH_SHOCK = PlayerbotAI::InitSpell(me, EARTH_SHOCK_1);
  22390. + FLAME_SHOCK = PlayerbotAI::InitSpell(me, FLAME_SHOCK_1);
  22391. + PURGE = PlayerbotAI::InitSpell(me, PURGE_1);
  22392. + WIND_SHOCK = 0; //NPC spell
  22393. + FROST_SHOCK = PlayerbotAI::InitSpell(me, FROST_SHOCK_1);
  22394. + CHAIN_LIGHTNING = PlayerbotAI::InitSpell(me, CHAIN_LIGHTNING_1);
  22395. + LAVA_BURST = PlayerbotAI::InitSpell(me, LAVA_BURST_1);
  22396. + HEX = PlayerbotAI::InitSpell(me, HEX_1);
  22397. + STONECLAW_TOTEM = PlayerbotAI::InitSpell(me, STONECLAW_TOTEM_1); // totems
  22398. + SEARING_TOTEM = PlayerbotAI::InitSpell(me, SEARING_TOTEM_1);
  22399. + FIRE_NOVA_TOTEM = 0; // NPC only spell, check FIRE_NOVA_1
  22400. + MAGMA_TOTEM = PlayerbotAI::InitSpell(me, MAGMA_TOTEM_1);
  22401. + EARTHBIND_TOTEM = PlayerbotAI::InitSpell(me, EARTHBIND_TOTEM_1);
  22402. + TOTEM_OF_WRATH = PlayerbotAI::InitSpell(me, TOTEM_OF_WRATH_1);
  22403. + FIRE_ELEMENTAL_TOTEM = PlayerbotAI::InitSpell(me, FIRE_ELEMENTAL_TOTEM_1);
  22404. + ELEMENTAL_MASTERY = PlayerbotAI::InitSpell(me, ELEMENTAL_MASTERY_1);
  22405. + THUNDERSTORM = PlayerbotAI::InitSpell(me, THUNDERSTORM_1);
  22406. +
  22407. + //RECENTLY_BANDAGED = 11196; // first aid check
  22408. +
  22409. + // racial
  22410. + GIFT_OF_THE_NAARU = PlayerbotAI::InitSpell(me, GIFT_OF_THE_NAARU_SHAMAN); // draenei
  22411. + BLOOD_FURY = PlayerbotAI::InitSpell(me, BLOOD_FURY_SHAMAN); // orc
  22412. + WAR_STOMP = PlayerbotAI::InitSpell(me, WAR_STOMP_ALL); // tauren
  22413. + BERSERKING = PlayerbotAI::InitSpell(me, BERSERKING_ALL); // troll
  22414. +
  22415. + // totem buffs
  22416. + STRENGTH_OF_EARTH_EFFECT = PlayerbotAI::InitSpell(me, STRENGTH_OF_EARTH_EFFECT_1);
  22417. + FLAMETONGUE_EFFECT = PlayerbotAI::InitSpell(me, FLAMETONGUE_EFFECT_1);
  22418. + MAGMA_TOTEM_EFFECT = PlayerbotAI::InitSpell(me, MAGMA_TOTEM_EFFECT_1);
  22419. + STONECLAW_EFFECT = PlayerbotAI::InitSpell(me, STONECLAW_EFFECT_1);
  22420. + FIRE_RESISTANCE_EFFECT = PlayerbotAI::InitSpell(me, FIRE_RESISTANCE_EFFECT_1);
  22421. + FROST_RESISTANCE_EFFECT = PlayerbotAI::InitSpell(me, FROST_RESISTANCE_EFFECT_1);
  22422. + GROUDNING_EFFECT = PlayerbotAI::InitSpell(me, GROUDNING_EFFECT_1);
  22423. + NATURE_RESISTANCE_EFFECT = PlayerbotAI::InitSpell(me, NATURE_RESISTANCE_EFFECT_1);
  22424. + STONESKIN_EFFECT = PlayerbotAI::InitSpell(me, STONESKIN_EFFECT_1);
  22425. + WINDFURY_EFFECT = PlayerbotAI::InitSpell(me, WINDFURY_EFFECT_1);
  22426. + WRATH_OF_AIR_EFFECT = PlayerbotAI::InitSpell(me, WRATH_OF_AIR_EFFECT_1);
  22427. + CLEANSING_TOTEM_EFFECT = PlayerbotAI::InitSpell(me, CLEANSING_TOTEM_EFFECT_1);
  22428. + HEALING_STREAM_EFFECT = PlayerbotAI::InitSpell(me, HEALING_STREAM_EFFECT_1);
  22429. + MANA_SPRING_EFFECT = PlayerbotAI::InitSpell(me, MANA_SPRING_EFFECT_1);
  22430. + TREMOR_TOTEM_EFFECT = PlayerbotAI::InitSpell(me, TREMOR_TOTEM_EFFECT_1);
  22431. + TOTEM_OF_WRATH_EFFECT = PlayerbotAI::InitSpell(me, TOTEM_OF_WRATH_EFFECT_1);
  22432. + STONECLAW_EFFECT = PlayerbotAI::InitSpell(me, STONECLAW_EFFECT_1);
  22433. + EARTHBIND_EFFECT = PlayerbotAI::InitSpell(me, EARTHBIND_EFFECT_1);
  22434. +
  22435. + // Buffs that don't stack with totems
  22436. + IMPROVED_ICY_TALONS = PlayerbotAI::InitSpell(me, IMPROVED_ICY_TALONS_1);
  22437. + HORN_OF_WINTER = PlayerbotAI::InitSpell(me, HORN_OF_WINTER_1);
  22438. +}
  22439. +
  22440. +PlayerbotShamanAI::~PlayerbotShamanAI() {}
  22441. +
  22442. +void PlayerbotShamanAI::SendClassDebugInfo()
  22443. +{
  22444. + uint32 talentcost = 0;
  22445. + uint32 rank = 0;
  22446. + SpellInfo const* learnSpellInfo = NULL;
  22447. + ChatHandler ch(m_master->GetSession());
  22448. + uint8 loc = uint8(me->GetSession()->GetSessionDbcLocale());
  22449. + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(CHAIN_HEAL);
  22450. + if (spellInfo)
  22451. + {
  22452. + std::ostringstream str;
  22453. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  22454. + str << ' ' << localeNames[loc] << "]|h|r";
  22455. + talentcost = GetTalentSpellCost(spellInfo->Id);
  22456. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  22457. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  22458. + rank = talentcost;
  22459. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  22460. + rank = spellInfo->GetRank();
  22461. + if (rank > 0)
  22462. + str << " Rank " << rank;
  22463. + ch.PSendSysMessage(str.str().c_str());
  22464. + str.clear();
  22465. + }
  22466. + spellInfo = sSpellMgr->GetSpellInfo(HEALING_WAVE);
  22467. + if (spellInfo)
  22468. + {
  22469. + std::ostringstream str;
  22470. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  22471. + str << ' ' << localeNames[loc] << "]|h|r";
  22472. + talentcost = GetTalentSpellCost(spellInfo->Id);
  22473. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  22474. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  22475. + rank = talentcost;
  22476. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  22477. + rank = spellInfo->GetRank();
  22478. + if (rank > 0)
  22479. + str << " Rank " << rank;
  22480. + ch.PSendSysMessage(str.str().c_str());
  22481. + str.clear();
  22482. + }
  22483. + spellInfo = sSpellMgr->GetSpellInfo(LESSER_HEALING_WAVE);
  22484. + if (spellInfo)
  22485. + {
  22486. + std::ostringstream str;
  22487. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  22488. + str << ' ' << localeNames[loc] << "]|h|r";
  22489. + talentcost = GetTalentSpellCost(spellInfo->Id);
  22490. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  22491. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  22492. + rank = talentcost;
  22493. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  22494. + rank = spellInfo->GetRank();
  22495. + if (rank > 0)
  22496. + str << " Rank " << rank;
  22497. + ch.PSendSysMessage(str.str().c_str());
  22498. + str.clear();
  22499. + }
  22500. + spellInfo = sSpellMgr->GetSpellInfo(RIPTIDE);
  22501. + if (spellInfo)
  22502. + {
  22503. + std::ostringstream str;
  22504. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  22505. + str << ' ' << localeNames[loc] << "]|h|r";
  22506. + talentcost = GetTalentSpellCost(spellInfo->Id);
  22507. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  22508. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  22509. + rank = talentcost;
  22510. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  22511. + rank = spellInfo->GetRank();
  22512. + if (rank > 0)
  22513. + str << " Rank " << rank;
  22514. + ch.PSendSysMessage(str.str().c_str());
  22515. + str.clear();
  22516. + }
  22517. + spellInfo = sSpellMgr->GetSpellInfo(ANCESTRAL_SPIRIT);
  22518. + if (spellInfo)
  22519. + {
  22520. + std::ostringstream str;
  22521. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  22522. + str << ' ' << localeNames[loc] << "]|h|r";
  22523. + talentcost = GetTalentSpellCost(spellInfo->Id);
  22524. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  22525. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  22526. + rank = talentcost;
  22527. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  22528. + rank = spellInfo->GetRank();
  22529. + if (rank > 0)
  22530. + str << " Rank " << rank;
  22531. + ch.PSendSysMessage(str.str().c_str());
  22532. + str.clear();
  22533. + }
  22534. + spellInfo = sSpellMgr->GetSpellInfo(EARTH_SHIELD);
  22535. + if (spellInfo)
  22536. + {
  22537. + std::ostringstream str;
  22538. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  22539. + str << ' ' << localeNames[loc] << "]|h|r";
  22540. + talentcost = GetTalentSpellCost(spellInfo->Id);
  22541. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  22542. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  22543. + rank = talentcost;
  22544. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  22545. + rank = spellInfo->GetRank();
  22546. + if (rank > 0)
  22547. + str << " Rank " << rank;
  22548. + ch.PSendSysMessage(str.str().c_str());
  22549. + str.clear();
  22550. + }
  22551. + spellInfo = sSpellMgr->GetSpellInfo(WATER_SHIELD);
  22552. + if (spellInfo)
  22553. + {
  22554. + std::ostringstream str;
  22555. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  22556. + str << ' ' << localeNames[loc] << "]|h|r";
  22557. + talentcost = GetTalentSpellCost(spellInfo->Id);
  22558. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  22559. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  22560. + rank = talentcost;
  22561. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  22562. + rank = spellInfo->GetRank();
  22563. + if (rank > 0)
  22564. + str << " Rank " << rank;
  22565. + ch.PSendSysMessage(str.str().c_str());
  22566. + str.clear();
  22567. + }
  22568. + spellInfo = sSpellMgr->GetSpellInfo(EARTHLIVING_WEAPON);
  22569. + if (spellInfo)
  22570. + {
  22571. + std::ostringstream str;
  22572. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  22573. + str << ' ' << localeNames[loc] << "]|h|r";
  22574. + talentcost = GetTalentSpellCost(spellInfo->Id);
  22575. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  22576. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  22577. + rank = talentcost;
  22578. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  22579. + rank = spellInfo->GetRank();
  22580. + if (rank > 0)
  22581. + str << " Rank " << rank;
  22582. + ch.PSendSysMessage(str.str().c_str());
  22583. + str.clear();
  22584. + }
  22585. + spellInfo = sSpellMgr->GetSpellInfo(TREMOR_TOTEM);
  22586. + if (spellInfo)
  22587. + {
  22588. + std::ostringstream str;
  22589. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  22590. + str << ' ' << localeNames[loc] << "]|h|r";
  22591. + talentcost = GetTalentSpellCost(spellInfo->Id);
  22592. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  22593. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  22594. + rank = talentcost;
  22595. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  22596. + rank = spellInfo->GetRank();
  22597. + if (rank > 0)
  22598. + str << " Rank " << rank;
  22599. + ch.PSendSysMessage(str.str().c_str());
  22600. + str.clear();
  22601. + }
  22602. + spellInfo = sSpellMgr->GetSpellInfo(HEALING_STREAM_TOTEM);
  22603. + if (spellInfo)
  22604. + {
  22605. + std::ostringstream str;
  22606. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  22607. + str << ' ' << localeNames[loc] << "]|h|r";
  22608. + talentcost = GetTalentSpellCost(spellInfo->Id);
  22609. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  22610. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  22611. + rank = talentcost;
  22612. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  22613. + rank = spellInfo->GetRank();
  22614. + if (rank > 0)
  22615. + str << " Rank " << rank;
  22616. + ch.PSendSysMessage(str.str().c_str());
  22617. + str.clear();
  22618. + }
  22619. + spellInfo = sSpellMgr->GetSpellInfo(MANA_SPRING_TOTEM);
  22620. + if (spellInfo)
  22621. + {
  22622. + std::ostringstream str;
  22623. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  22624. + str << ' ' << localeNames[loc] << "]|h|r";
  22625. + talentcost = GetTalentSpellCost(spellInfo->Id);
  22626. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  22627. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  22628. + rank = talentcost;
  22629. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  22630. + rank = spellInfo->GetRank();
  22631. + if (rank > 0)
  22632. + str << " Rank " << rank;
  22633. + ch.PSendSysMessage(str.str().c_str());
  22634. + str.clear();
  22635. + }
  22636. + spellInfo = sSpellMgr->GetSpellInfo(MANA_TIDE_TOTEM);
  22637. + if (spellInfo)
  22638. + {
  22639. + std::ostringstream str;
  22640. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  22641. + str << ' ' << localeNames[loc] << "]|h|r";
  22642. + talentcost = GetTalentSpellCost(spellInfo->Id);
  22643. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  22644. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  22645. + rank = talentcost;
  22646. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  22647. + rank = spellInfo->GetRank();
  22648. + if (rank > 0)
  22649. + str << " Rank " << rank;
  22650. + ch.PSendSysMessage(str.str().c_str());
  22651. + str.clear();
  22652. + }
  22653. + spellInfo = sSpellMgr->GetSpellInfo(CURE_TOXINS);
  22654. + if (spellInfo)
  22655. + {
  22656. + std::ostringstream str;
  22657. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  22658. + str << ' ' << localeNames[loc] << "]|h|r";
  22659. + talentcost = GetTalentSpellCost(spellInfo->Id);
  22660. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  22661. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  22662. + rank = talentcost;
  22663. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  22664. + rank = spellInfo->GetRank();
  22665. + if (rank > 0)
  22666. + str << " Rank " << rank;
  22667. + ch.PSendSysMessage(str.str().c_str());
  22668. + str.clear();
  22669. + }
  22670. + spellInfo = sSpellMgr->GetSpellInfo(CLEANSE_SPIRIT);
  22671. + if (spellInfo)
  22672. + {
  22673. + std::ostringstream str;
  22674. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  22675. + str << ' ' << localeNames[loc] << "]|h|r";
  22676. + talentcost = GetTalentSpellCost(spellInfo->Id);
  22677. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  22678. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  22679. + rank = talentcost;
  22680. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  22681. + rank = spellInfo->GetRank();
  22682. + if (rank > 0)
  22683. + str << " Rank " << rank;
  22684. + ch.PSendSysMessage(str.str().c_str());
  22685. + str.clear();
  22686. + }
  22687. + spellInfo = sSpellMgr->GetSpellInfo(NATURES_SWIFTNESS_SHAMAN);
  22688. + if (spellInfo)
  22689. + {
  22690. + std::ostringstream str;
  22691. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  22692. + str << ' ' << localeNames[loc] << "]|h|r";
  22693. + talentcost = GetTalentSpellCost(spellInfo->Id);
  22694. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  22695. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  22696. + rank = talentcost;
  22697. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  22698. + rank = spellInfo->GetRank();
  22699. + if (rank > 0)
  22700. + str << " Rank " << rank;
  22701. + ch.PSendSysMessage(str.str().c_str());
  22702. + str.clear();
  22703. + }
  22704. + spellInfo = sSpellMgr->GetSpellInfo(TIDAL_FORCE);
  22705. + if (spellInfo)
  22706. + {
  22707. + std::ostringstream str;
  22708. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  22709. + str << ' ' << localeNames[loc] << "]|h|r";
  22710. + talentcost = GetTalentSpellCost(spellInfo->Id);
  22711. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  22712. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  22713. + rank = talentcost;
  22714. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  22715. + rank = spellInfo->GetRank();
  22716. + if (rank > 0)
  22717. + str << " Rank " << rank;
  22718. + ch.PSendSysMessage(str.str().c_str());
  22719. + str.clear();
  22720. + }
  22721. + spellInfo = sSpellMgr->GetSpellInfo(STORMSTRIKE);
  22722. + if (spellInfo)
  22723. + {
  22724. + std::ostringstream str;
  22725. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  22726. + str << ' ' << localeNames[loc] << "]|h|r";
  22727. + talentcost = GetTalentSpellCost(spellInfo->Id);
  22728. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  22729. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  22730. + rank = talentcost;
  22731. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  22732. + rank = spellInfo->GetRank();
  22733. + if (rank > 0)
  22734. + str << " Rank " << rank;
  22735. + ch.PSendSysMessage(str.str().c_str());
  22736. + str.clear();
  22737. + }
  22738. + spellInfo = sSpellMgr->GetSpellInfo(LAVA_LASH);
  22739. + if (spellInfo)
  22740. + {
  22741. + std::ostringstream str;
  22742. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  22743. + str << ' ' << localeNames[loc] << "]|h|r";
  22744. + talentcost = GetTalentSpellCost(spellInfo->Id);
  22745. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  22746. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  22747. + rank = talentcost;
  22748. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  22749. + rank = spellInfo->GetRank();
  22750. + if (rank > 0)
  22751. + str << " Rank " << rank;
  22752. + ch.PSendSysMessage(str.str().c_str());
  22753. + str.clear();
  22754. + }
  22755. + spellInfo = sSpellMgr->GetSpellInfo(SHAMANISTIC_RAGE);
  22756. + if (spellInfo)
  22757. + {
  22758. + std::ostringstream str;
  22759. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  22760. + str << ' ' << localeNames[loc] << "]|h|r";
  22761. + talentcost = GetTalentSpellCost(spellInfo->Id);
  22762. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  22763. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  22764. + rank = talentcost;
  22765. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  22766. + rank = spellInfo->GetRank();
  22767. + if (rank > 0)
  22768. + str << " Rank " << rank;
  22769. + ch.PSendSysMessage(str.str().c_str());
  22770. + str.clear();
  22771. + }
  22772. + spellInfo = sSpellMgr->GetSpellInfo(BLOODLUST);
  22773. + if (spellInfo)
  22774. + {
  22775. + std::ostringstream str;
  22776. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  22777. + str << ' ' << localeNames[loc] << "]|h|r";
  22778. + talentcost = GetTalentSpellCost(spellInfo->Id);
  22779. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  22780. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  22781. + rank = talentcost;
  22782. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  22783. + rank = spellInfo->GetRank();
  22784. + if (rank > 0)
  22785. + str << " Rank " << rank;
  22786. + ch.PSendSysMessage(str.str().c_str());
  22787. + str.clear();
  22788. + }
  22789. + spellInfo = sSpellMgr->GetSpellInfo(HEROISM);
  22790. + if (spellInfo)
  22791. + {
  22792. + std::ostringstream str;
  22793. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  22794. + str << ' ' << localeNames[loc] << "]|h|r";
  22795. + talentcost = GetTalentSpellCost(spellInfo->Id);
  22796. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  22797. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  22798. + rank = talentcost;
  22799. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  22800. + rank = spellInfo->GetRank();
  22801. + if (rank > 0)
  22802. + str << " Rank " << rank;
  22803. + ch.PSendSysMessage(str.str().c_str());
  22804. + str.clear();
  22805. + }
  22806. + spellInfo = sSpellMgr->GetSpellInfo(FERAL_SPIRIT);
  22807. + if (spellInfo)
  22808. + {
  22809. + std::ostringstream str;
  22810. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  22811. + str << ' ' << localeNames[loc] << "]|h|r";
  22812. + talentcost = GetTalentSpellCost(spellInfo->Id);
  22813. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  22814. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  22815. + rank = talentcost;
  22816. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  22817. + rank = spellInfo->GetRank();
  22818. + if (rank > 0)
  22819. + str << " Rank " << rank;
  22820. + ch.PSendSysMessage(str.str().c_str());
  22821. + str.clear();
  22822. + }
  22823. + spellInfo = sSpellMgr->GetSpellInfo(LIGHTNING_SHIELD);
  22824. + if (spellInfo)
  22825. + {
  22826. + std::ostringstream str;
  22827. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  22828. + str << ' ' << localeNames[loc] << "]|h|r";
  22829. + talentcost = GetTalentSpellCost(spellInfo->Id);
  22830. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  22831. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  22832. + rank = talentcost;
  22833. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  22834. + rank = spellInfo->GetRank();
  22835. + if (rank > 0)
  22836. + str << " Rank " << rank;
  22837. + ch.PSendSysMessage(str.str().c_str());
  22838. + str.clear();
  22839. + }
  22840. + spellInfo = sSpellMgr->GetSpellInfo(ROCKBITER_WEAPON);
  22841. + if (spellInfo)
  22842. + {
  22843. + std::ostringstream str;
  22844. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  22845. + str << ' ' << localeNames[loc] << "]|h|r";
  22846. + talentcost = GetTalentSpellCost(spellInfo->Id);
  22847. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  22848. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  22849. + rank = talentcost;
  22850. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  22851. + rank = spellInfo->GetRank();
  22852. + if (rank > 0)
  22853. + str << " Rank " << rank;
  22854. + ch.PSendSysMessage(str.str().c_str());
  22855. + str.clear();
  22856. + }
  22857. + spellInfo = sSpellMgr->GetSpellInfo(FLAMETONGUE_WEAPON);
  22858. + if (spellInfo)
  22859. + {
  22860. + std::ostringstream str;
  22861. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  22862. + str << ' ' << localeNames[loc] << "]|h|r";
  22863. + talentcost = GetTalentSpellCost(spellInfo->Id);
  22864. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  22865. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  22866. + rank = talentcost;
  22867. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  22868. + rank = spellInfo->GetRank();
  22869. + if (rank > 0)
  22870. + str << " Rank " << rank;
  22871. + ch.PSendSysMessage(str.str().c_str());
  22872. + str.clear();
  22873. + }
  22874. + spellInfo = sSpellMgr->GetSpellInfo(FROSTBRAND_WEAPON);
  22875. + if (spellInfo)
  22876. + {
  22877. + std::ostringstream str;
  22878. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  22879. + str << ' ' << localeNames[loc] << "]|h|r";
  22880. + talentcost = GetTalentSpellCost(spellInfo->Id);
  22881. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  22882. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  22883. + rank = talentcost;
  22884. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  22885. + rank = spellInfo->GetRank();
  22886. + if (rank > 0)
  22887. + str << " Rank " << rank;
  22888. + ch.PSendSysMessage(str.str().c_str());
  22889. + str.clear();
  22890. + }
  22891. + spellInfo = sSpellMgr->GetSpellInfo(WINDFURY_WEAPON);
  22892. + if (spellInfo)
  22893. + {
  22894. + std::ostringstream str;
  22895. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  22896. + str << ' ' << localeNames[loc] << "]|h|r";
  22897. + talentcost = GetTalentSpellCost(spellInfo->Id);
  22898. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  22899. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  22900. + rank = talentcost;
  22901. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  22902. + rank = spellInfo->GetRank();
  22903. + if (rank > 0)
  22904. + str << " Rank " << rank;
  22905. + ch.PSendSysMessage(str.str().c_str());
  22906. + str.clear();
  22907. + }
  22908. + spellInfo = sSpellMgr->GetSpellInfo(STONESKIN_TOTEM);
  22909. + if (spellInfo)
  22910. + {
  22911. + std::ostringstream str;
  22912. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  22913. + str << ' ' << localeNames[loc] << "]|h|r";
  22914. + talentcost = GetTalentSpellCost(spellInfo->Id);
  22915. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  22916. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  22917. + rank = talentcost;
  22918. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  22919. + rank = spellInfo->GetRank();
  22920. + if (rank > 0)
  22921. + str << " Rank " << rank;
  22922. + ch.PSendSysMessage(str.str().c_str());
  22923. + str.clear();
  22924. + }
  22925. + spellInfo = sSpellMgr->GetSpellInfo(STRENGTH_OF_EARTH_TOTEM);
  22926. + if (spellInfo)
  22927. + {
  22928. + std::ostringstream str;
  22929. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  22930. + str << ' ' << localeNames[loc] << "]|h|r";
  22931. + talentcost = GetTalentSpellCost(spellInfo->Id);
  22932. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  22933. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  22934. + rank = talentcost;
  22935. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  22936. + rank = spellInfo->GetRank();
  22937. + if (rank > 0)
  22938. + str << " Rank " << rank;
  22939. + ch.PSendSysMessage(str.str().c_str());
  22940. + str.clear();
  22941. + }
  22942. + spellInfo = sSpellMgr->GetSpellInfo(FROST_RESISTANCE_TOTEM);
  22943. + if (spellInfo)
  22944. + {
  22945. + std::ostringstream str;
  22946. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  22947. + str << ' ' << localeNames[loc] << "]|h|r";
  22948. + talentcost = GetTalentSpellCost(spellInfo->Id);
  22949. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  22950. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  22951. + rank = talentcost;
  22952. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  22953. + rank = spellInfo->GetRank();
  22954. + if (rank > 0)
  22955. + str << " Rank " << rank;
  22956. + ch.PSendSysMessage(str.str().c_str());
  22957. + str.clear();
  22958. + }
  22959. + spellInfo = sSpellMgr->GetSpellInfo(FLAMETONGUE_TOTEM);
  22960. + if (spellInfo)
  22961. + {
  22962. + std::ostringstream str;
  22963. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  22964. + str << ' ' << localeNames[loc] << "]|h|r";
  22965. + talentcost = GetTalentSpellCost(spellInfo->Id);
  22966. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  22967. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  22968. + rank = talentcost;
  22969. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  22970. + rank = spellInfo->GetRank();
  22971. + if (rank > 0)
  22972. + str << " Rank " << rank;
  22973. + ch.PSendSysMessage(str.str().c_str());
  22974. + str.clear();
  22975. + }
  22976. + spellInfo = sSpellMgr->GetSpellInfo(FIRE_RESISTANCE_TOTEM);
  22977. + if (spellInfo)
  22978. + {
  22979. + std::ostringstream str;
  22980. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  22981. + str << ' ' << localeNames[loc] << "]|h|r";
  22982. + talentcost = GetTalentSpellCost(spellInfo->Id);
  22983. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  22984. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  22985. + rank = talentcost;
  22986. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  22987. + rank = spellInfo->GetRank();
  22988. + if (rank > 0)
  22989. + str << " Rank " << rank;
  22990. + ch.PSendSysMessage(str.str().c_str());
  22991. + str.clear();
  22992. + }
  22993. + spellInfo = sSpellMgr->GetSpellInfo(GROUNDING_TOTEM);
  22994. + if (spellInfo)
  22995. + {
  22996. + std::ostringstream str;
  22997. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  22998. + str << ' ' << localeNames[loc] << "]|h|r";
  22999. + talentcost = GetTalentSpellCost(spellInfo->Id);
  23000. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  23001. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  23002. + rank = talentcost;
  23003. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  23004. + rank = spellInfo->GetRank();
  23005. + if (rank > 0)
  23006. + str << " Rank " << rank;
  23007. + ch.PSendSysMessage(str.str().c_str());
  23008. + str.clear();
  23009. + }
  23010. + spellInfo = sSpellMgr->GetSpellInfo(NATURE_RESISTANCE_TOTEM);
  23011. + if (spellInfo)
  23012. + {
  23013. + std::ostringstream str;
  23014. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  23015. + str << ' ' << localeNames[loc] << "]|h|r";
  23016. + talentcost = GetTalentSpellCost(spellInfo->Id);
  23017. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  23018. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  23019. + rank = talentcost;
  23020. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  23021. + rank = spellInfo->GetRank();
  23022. + if (rank > 0)
  23023. + str << " Rank " << rank;
  23024. + ch.PSendSysMessage(str.str().c_str());
  23025. + str.clear();
  23026. + }
  23027. + spellInfo = sSpellMgr->GetSpellInfo(WIND_FURY_TOTEM);
  23028. + if (spellInfo)
  23029. + {
  23030. + std::ostringstream str;
  23031. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  23032. + str << ' ' << localeNames[loc] << "]|h|r";
  23033. + talentcost = GetTalentSpellCost(spellInfo->Id);
  23034. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  23035. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  23036. + rank = talentcost;
  23037. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  23038. + rank = spellInfo->GetRank();
  23039. + if (rank > 0)
  23040. + str << " Rank " << rank;
  23041. + ch.PSendSysMessage(str.str().c_str());
  23042. + str.clear();
  23043. + }
  23044. + spellInfo = sSpellMgr->GetSpellInfo(STONESKIN_TOTEM);
  23045. + if (spellInfo)
  23046. + {
  23047. + std::ostringstream str;
  23048. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  23049. + str << ' ' << localeNames[loc] << "]|h|r";
  23050. + talentcost = GetTalentSpellCost(spellInfo->Id);
  23051. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  23052. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  23053. + rank = talentcost;
  23054. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  23055. + rank = spellInfo->GetRank();
  23056. + if (rank > 0)
  23057. + str << " Rank " << rank;
  23058. + ch.PSendSysMessage(str.str().c_str());
  23059. + str.clear();
  23060. + }
  23061. + spellInfo = sSpellMgr->GetSpellInfo(WRATH_OF_AIR_TOTEM);
  23062. + if (spellInfo)
  23063. + {
  23064. + std::ostringstream str;
  23065. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  23066. + str << ' ' << localeNames[loc] << "]|h|r";
  23067. + talentcost = GetTalentSpellCost(spellInfo->Id);
  23068. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  23069. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  23070. + rank = talentcost;
  23071. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  23072. + rank = spellInfo->GetRank();
  23073. + if (rank > 0)
  23074. + str << " Rank " << rank;
  23075. + ch.PSendSysMessage(str.str().c_str());
  23076. + str.clear();
  23077. + }
  23078. + spellInfo = sSpellMgr->GetSpellInfo(EARTH_ELEMENTAL_TOTEM);
  23079. + if (spellInfo)
  23080. + {
  23081. + std::ostringstream str;
  23082. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  23083. + str << ' ' << localeNames[loc] << "]|h|r";
  23084. + talentcost = GetTalentSpellCost(spellInfo->Id);
  23085. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  23086. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  23087. + rank = talentcost;
  23088. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  23089. + rank = spellInfo->GetRank();
  23090. + if (rank > 0)
  23091. + str << " Rank " << rank;
  23092. + ch.PSendSysMessage(str.str().c_str());
  23093. + str.clear();
  23094. + }
  23095. + spellInfo = sSpellMgr->GetSpellInfo(MAELSTROM_WEAPON);
  23096. + if (spellInfo)
  23097. + {
  23098. + std::ostringstream str;
  23099. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  23100. + str << ' ' << localeNames[loc] << "]|h|r";
  23101. + talentcost = GetTalentSpellCost(spellInfo->Id);
  23102. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  23103. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  23104. + rank = talentcost;
  23105. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  23106. + rank = spellInfo->GetRank();
  23107. + if (rank > 0)
  23108. + str << " Rank " << rank;
  23109. + ch.PSendSysMessage(str.str().c_str());
  23110. + str.clear();
  23111. + }
  23112. + spellInfo = sSpellMgr->GetSpellInfo(LIGHTNING_BOLT);
  23113. + if (spellInfo)
  23114. + {
  23115. + std::ostringstream str;
  23116. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  23117. + str << ' ' << localeNames[loc] << "]|h|r";
  23118. + talentcost = GetTalentSpellCost(spellInfo->Id);
  23119. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  23120. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  23121. + rank = talentcost;
  23122. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  23123. + rank = spellInfo->GetRank();
  23124. + if (rank > 0)
  23125. + str << " Rank " << rank;
  23126. + ch.PSendSysMessage(str.str().c_str());
  23127. + str.clear();
  23128. + }
  23129. + spellInfo = sSpellMgr->GetSpellInfo(EARTH_SHOCK);
  23130. + if (spellInfo)
  23131. + {
  23132. + std::ostringstream str;
  23133. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  23134. + str << ' ' << localeNames[loc] << "]|h|r";
  23135. + talentcost = GetTalentSpellCost(spellInfo->Id);
  23136. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  23137. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  23138. + rank = talentcost;
  23139. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  23140. + rank = spellInfo->GetRank();
  23141. + if (rank > 0)
  23142. + str << " Rank " << rank;
  23143. + ch.PSendSysMessage(str.str().c_str());
  23144. + str.clear();
  23145. + }
  23146. + spellInfo = sSpellMgr->GetSpellInfo(FLAME_SHOCK);
  23147. + if (spellInfo)
  23148. + {
  23149. + std::ostringstream str;
  23150. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  23151. + str << ' ' << localeNames[loc] << "]|h|r";
  23152. + talentcost = GetTalentSpellCost(spellInfo->Id);
  23153. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  23154. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  23155. + rank = talentcost;
  23156. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  23157. + rank = spellInfo->GetRank();
  23158. + if (rank > 0)
  23159. + str << " Rank " << rank;
  23160. + ch.PSendSysMessage(str.str().c_str());
  23161. + str.clear();
  23162. + }
  23163. + spellInfo = sSpellMgr->GetSpellInfo(PURGE);
  23164. + if (spellInfo)
  23165. + {
  23166. + std::ostringstream str;
  23167. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  23168. + str << ' ' << localeNames[loc] << "]|h|r";
  23169. + talentcost = GetTalentSpellCost(spellInfo->Id);
  23170. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  23171. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  23172. + rank = talentcost;
  23173. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  23174. + rank = spellInfo->GetRank();
  23175. + if (rank > 0)
  23176. + str << " Rank " << rank;
  23177. + ch.PSendSysMessage(str.str().c_str());
  23178. + str.clear();
  23179. + }
  23180. + spellInfo = sSpellMgr->GetSpellInfo(WIND_SHOCK);
  23181. + if (spellInfo)
  23182. + {
  23183. + std::ostringstream str;
  23184. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  23185. + str << ' ' << localeNames[loc] << "]|h|r";
  23186. + talentcost = GetTalentSpellCost(spellInfo->Id);
  23187. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  23188. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  23189. + rank = talentcost;
  23190. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  23191. + rank = spellInfo->GetRank();
  23192. + if (rank > 0)
  23193. + str << " Rank " << rank;
  23194. + ch.PSendSysMessage(str.str().c_str());
  23195. + str.clear();
  23196. + }
  23197. + spellInfo = sSpellMgr->GetSpellInfo(FROST_SHOCK);
  23198. + if (spellInfo)
  23199. + {
  23200. + std::ostringstream str;
  23201. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  23202. + str << ' ' << localeNames[loc] << "]|h|r";
  23203. + talentcost = GetTalentSpellCost(spellInfo->Id);
  23204. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  23205. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  23206. + rank = talentcost;
  23207. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  23208. + rank = spellInfo->GetRank();
  23209. + if (rank > 0)
  23210. + str << " Rank " << rank;
  23211. + ch.PSendSysMessage(str.str().c_str());
  23212. + str.clear();
  23213. + }
  23214. + spellInfo = sSpellMgr->GetSpellInfo(CHAIN_LIGHTNING);
  23215. + if (spellInfo)
  23216. + {
  23217. + std::ostringstream str;
  23218. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  23219. + str << ' ' << localeNames[loc] << "]|h|r";
  23220. + talentcost = GetTalentSpellCost(spellInfo->Id);
  23221. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  23222. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  23223. + rank = talentcost;
  23224. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  23225. + rank = spellInfo->GetRank();
  23226. + if (rank > 0)
  23227. + str << " Rank " << rank;
  23228. + ch.PSendSysMessage(str.str().c_str());
  23229. + str.clear();
  23230. + }
  23231. + spellInfo = sSpellMgr->GetSpellInfo(LAVA_BURST);
  23232. + if (spellInfo)
  23233. + {
  23234. + std::ostringstream str;
  23235. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  23236. + str << ' ' << localeNames[loc] << "]|h|r";
  23237. + talentcost = GetTalentSpellCost(spellInfo->Id);
  23238. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  23239. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  23240. + rank = talentcost;
  23241. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  23242. + rank = spellInfo->GetRank();
  23243. + if (rank > 0)
  23244. + str << " Rank " << rank;
  23245. + ch.PSendSysMessage(str.str().c_str());
  23246. + str.clear();
  23247. + }
  23248. + spellInfo = sSpellMgr->GetSpellInfo(HEX);
  23249. + if (spellInfo)
  23250. + {
  23251. + std::ostringstream str;
  23252. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  23253. + str << ' ' << localeNames[loc] << "]|h|r";
  23254. + talentcost = GetTalentSpellCost(spellInfo->Id);
  23255. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  23256. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  23257. + rank = talentcost;
  23258. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  23259. + rank = spellInfo->GetRank();
  23260. + if (rank > 0)
  23261. + str << " Rank " << rank;
  23262. + ch.PSendSysMessage(str.str().c_str());
  23263. + str.clear();
  23264. + }
  23265. + spellInfo = sSpellMgr->GetSpellInfo(STONECLAW_TOTEM);
  23266. + if (spellInfo)
  23267. + {
  23268. + std::ostringstream str;
  23269. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  23270. + str << ' ' << localeNames[loc] << "]|h|r";
  23271. + talentcost = GetTalentSpellCost(spellInfo->Id);
  23272. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  23273. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  23274. + rank = talentcost;
  23275. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  23276. + rank = spellInfo->GetRank();
  23277. + if (rank > 0)
  23278. + str << " Rank " << rank;
  23279. + ch.PSendSysMessage(str.str().c_str());
  23280. + str.clear();
  23281. + }
  23282. + spellInfo = sSpellMgr->GetSpellInfo(SEARING_TOTEM);
  23283. + if (spellInfo)
  23284. + {
  23285. + std::ostringstream str;
  23286. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  23287. + str << ' ' << localeNames[loc] << "]|h|r";
  23288. + talentcost = GetTalentSpellCost(spellInfo->Id);
  23289. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  23290. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  23291. + rank = talentcost;
  23292. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  23293. + rank = spellInfo->GetRank();
  23294. + if (rank > 0)
  23295. + str << " Rank " << rank;
  23296. + ch.PSendSysMessage(str.str().c_str());
  23297. + str.clear();
  23298. + }
  23299. + spellInfo = sSpellMgr->GetSpellInfo(FIRE_NOVA_TOTEM);
  23300. + if (spellInfo)
  23301. + {
  23302. + std::ostringstream str;
  23303. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  23304. + str << ' ' << localeNames[loc] << "]|h|r";
  23305. + talentcost = GetTalentSpellCost(spellInfo->Id);
  23306. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  23307. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  23308. + rank = talentcost;
  23309. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  23310. + rank = spellInfo->GetRank();
  23311. + if (rank > 0)
  23312. + str << " Rank " << rank;
  23313. + ch.PSendSysMessage(str.str().c_str());
  23314. + str.clear();
  23315. + }
  23316. + spellInfo = sSpellMgr->GetSpellInfo(MAGMA_TOTEM);
  23317. + if (spellInfo)
  23318. + {
  23319. + std::ostringstream str;
  23320. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  23321. + str << ' ' << localeNames[loc] << "]|h|r";
  23322. + talentcost = GetTalentSpellCost(spellInfo->Id);
  23323. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  23324. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  23325. + rank = talentcost;
  23326. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  23327. + rank = spellInfo->GetRank();
  23328. + if (rank > 0)
  23329. + str << " Rank " << rank;
  23330. + ch.PSendSysMessage(str.str().c_str());
  23331. + str.clear();
  23332. + }
  23333. + spellInfo = sSpellMgr->GetSpellInfo(EARTHBIND_TOTEM);
  23334. + if (spellInfo)
  23335. + {
  23336. + std::ostringstream str;
  23337. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  23338. + str << ' ' << localeNames[loc] << "]|h|r";
  23339. + talentcost = GetTalentSpellCost(spellInfo->Id);
  23340. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  23341. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  23342. + rank = talentcost;
  23343. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  23344. + rank = spellInfo->GetRank();
  23345. + if (rank > 0)
  23346. + str << " Rank " << rank;
  23347. + ch.PSendSysMessage(str.str().c_str());
  23348. + str.clear();
  23349. + }
  23350. + spellInfo = sSpellMgr->GetSpellInfo(TOTEM_OF_WRATH);
  23351. + if (spellInfo)
  23352. + {
  23353. + std::ostringstream str;
  23354. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  23355. + str << ' ' << localeNames[loc] << "]|h|r";
  23356. + talentcost = GetTalentSpellCost(spellInfo->Id);
  23357. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  23358. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  23359. + rank = talentcost;
  23360. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  23361. + rank = spellInfo->GetRank();
  23362. + if (rank > 0)
  23363. + str << " Rank " << rank;
  23364. + ch.PSendSysMessage(str.str().c_str());
  23365. + str.clear();
  23366. + }
  23367. + spellInfo = sSpellMgr->GetSpellInfo(FIRE_ELEMENTAL_TOTEM);
  23368. + if (spellInfo)
  23369. + {
  23370. + std::ostringstream str;
  23371. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  23372. + str << ' ' << localeNames[loc] << "]|h|r";
  23373. + talentcost = GetTalentSpellCost(spellInfo->Id);
  23374. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  23375. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  23376. + rank = talentcost;
  23377. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  23378. + rank = spellInfo->GetRank();
  23379. + if (rank > 0)
  23380. + str << " Rank " << rank;
  23381. + ch.PSendSysMessage(str.str().c_str());
  23382. + str.clear();
  23383. + }
  23384. + spellInfo = sSpellMgr->GetSpellInfo(ELEMENTAL_MASTERY);
  23385. + if (spellInfo)
  23386. + {
  23387. + std::ostringstream str;
  23388. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  23389. + str << ' ' << localeNames[loc] << "]|h|r";
  23390. + talentcost = GetTalentSpellCost(spellInfo->Id);
  23391. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  23392. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  23393. + rank = talentcost;
  23394. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  23395. + rank = spellInfo->GetRank();
  23396. + if (rank > 0)
  23397. + str << " Rank " << rank;
  23398. + ch.PSendSysMessage(str.str().c_str());
  23399. + str.clear();
  23400. + }
  23401. + spellInfo = sSpellMgr->GetSpellInfo(THUNDERSTORM);
  23402. + if (spellInfo)
  23403. + {
  23404. + std::ostringstream str;
  23405. + str << spellInfo->Id << " - |cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[loc];
  23406. + str << ' ' << localeNames[loc] << "]|h|r";
  23407. + talentcost = GetTalentSpellCost(spellInfo->Id);
  23408. + learnSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[0].TriggerSpell);
  23409. + if (talentcost > 0 && spellInfo->GetNextRankSpell())
  23410. + rank = talentcost;
  23411. + else if (learnSpellInfo && learnSpellInfo->GetNextRankSpell())
  23412. + rank = spellInfo->GetRank();
  23413. + if (rank > 0)
  23414. + str << " Rank " << rank;
  23415. + ch.PSendSysMessage(str.str().c_str());
  23416. + str.clear();
  23417. + }
  23418. +}
  23419. +
  23420. +CombatManeuverReturns PlayerbotShamanAI::DoFirstCombatManeuver(Unit* /*pTarget*/)
  23421. +{
  23422. + //// There are NPCs in BGs and Open World PvP, so don't filter this on PvP scenarios (of course if PvP targets anyone but tank, all bets are off anyway)
  23423. + //// Wait until the tank says so, until any non-tank gains aggro or X seconds - whichever is shortest
  23424. + //if (m_ai->GetCombatOrder() & PlayerbotAI::ORDERS_TEMP_WAIT_TANKAGGRO)
  23425. + //{
  23426. + // if (m_WaitUntil > m_ai->CurrentTime() && m_ai->GroupTankHoldsAggro())
  23427. + // {
  23428. + // if (PlayerbotAI::ORDERS_HEAL & m_ai->GetCombatOrder())
  23429. + // return HealPlayer(GetHealTarget());
  23430. + // else
  23431. + // return RETURN_NO_ACTION_OK; // wait it out
  23432. + // }
  23433. + // else
  23434. + // {
  23435. + // m_ai->ClearGroupCombatOrder(PlayerbotAI::ORDERS_TEMP_WAIT_TANKAGGRO);
  23436. + // }
  23437. + //}
  23438. +
  23439. + //if (m_ai->GetCombatOrder() & PlayerbotAI::ORDERS_TEMP_WAIT_OOC)
  23440. + //{
  23441. + // if (m_WaitUntil > m_ai->CurrentTime() && !m_ai->IsGroupInCombat())
  23442. + // return RETURN_NO_ACTION_OK; // wait it out
  23443. + // else
  23444. + // m_ai->ClearGroupCombatOrder(PlayerbotAI::ORDERS_TEMP_WAIT_OOC);
  23445. + //}
  23446. +
  23447. + //switch (m_ai->GetScenarioType())
  23448. + //{
  23449. + // case PlayerbotAI::SCENARIO_PVP_DUEL:
  23450. + // case PlayerbotAI::SCENARIO_PVP_BG:
  23451. + // case PlayerbotAI::SCENARIO_PVP_ARENA:
  23452. + // case PlayerbotAI::SCENARIO_PVP_OPENWORLD:
  23453. + // return DoFirstCombatManeuverPVP(pTarget);
  23454. + // case PlayerbotAI::SCENARIO_PVE:
  23455. + // case PlayerbotAI::SCENARIO_PVE_ELITE:
  23456. + // case PlayerbotAI::SCENARIO_PVE_RAID:
  23457. + // default:
  23458. + // return DoFirstCombatManeuverPVE(pTarget);
  23459. + // break;
  23460. + //}
  23461. +
  23462. + return RETURN_NO_ACTION_ERROR;
  23463. +}
  23464. +
  23465. +CombatManeuverReturns PlayerbotShamanAI::DoFirstCombatManeuverPVE(Unit* /*pTarget*/)
  23466. +{
  23467. + return RETURN_NO_ACTION_OK;
  23468. +}
  23469. +
  23470. +CombatManeuverReturns PlayerbotShamanAI::DoFirstCombatManeuverPVP(Unit* /*pTarget*/)
  23471. +{
  23472. + return RETURN_NO_ACTION_OK;
  23473. +}
  23474. +
  23475. +CombatManeuverReturns PlayerbotShamanAI::DoNextCombatManeuver(Unit* /*pTarget*/)
  23476. +{
  23477. + //switch (m_ai->GetScenarioType())
  23478. + //{
  23479. + // case PlayerbotAI::SCENARIO_PVP_DUEL:
  23480. + // case PlayerbotAI::SCENARIO_PVP_BG:
  23481. + // case PlayerbotAI::SCENARIO_PVP_ARENA:
  23482. + // case PlayerbotAI::SCENARIO_PVP_OPENWORLD:
  23483. + // return DoNextCombatManeuverPVP(pTarget);
  23484. + // case PlayerbotAI::SCENARIO_PVE:
  23485. + // case PlayerbotAI::SCENARIO_PVE_ELITE:
  23486. + // case PlayerbotAI::SCENARIO_PVE_RAID:
  23487. + // default:
  23488. + // return DoNextCombatManeuverPVE(pTarget);
  23489. + // break;
  23490. + //}
  23491. +
  23492. + return RETURN_NO_ACTION_ERROR;
  23493. +}
  23494. +
  23495. +CombatManeuverReturns PlayerbotShamanAI::DoNextCombatManeuverPVE(Unit* /*pTarget*/)
  23496. +{
  23497. + //if (!m_ai) return RETURN_NO_ACTION_ERROR;
  23498. + //if (!me) return RETURN_NO_ACTION_ERROR;
  23499. +
  23500. + //uint32 spec = me->GetSpec();
  23501. +
  23502. + //// Make sure healer stays put, don't even melee (aggro) if in range.
  23503. + //if (m_ai->IsHealer() && m_ai->GetCombatStyle() != PlayerbotAI::COMBAT_RANGED)
  23504. + // m_ai->SetCombatStyle(PlayerbotAI::COMBAT_RANGED);
  23505. + //else if (!m_ai->IsHealer() && m_ai->GetCombatStyle() != PlayerbotAI::COMBAT_MELEE)
  23506. + // m_ai->SetCombatStyle(PlayerbotAI::COMBAT_MELEE);
  23507. +
  23508. + //// Heal
  23509. + //if (m_ai->IsHealer())
  23510. + //{
  23511. + // if (HealPlayer(GetHealTarget()) & (RETURN_NO_ACTION_OK | RETURN_CONTINUE))
  23512. + // return RETURN_CONTINUE;
  23513. + //}
  23514. + //else
  23515. + //{
  23516. + // // Is this desirable? Debatable.
  23517. + // // TODO: In a group/raid with a healer you'd want this bot to focus on DPS (it's not specced/geared for healing either)
  23518. + // if (HealPlayer(me) & RETURN_CONTINUE)
  23519. + // return RETURN_CONTINUE;
  23520. + //}
  23521. +
  23522. + //// Damage Spells
  23523. + //DropTotems();
  23524. + //CheckShields();
  23525. + //UseCooldowns();
  23526. + //switch (spec)
  23527. + //{
  23528. + // case SHAMAN_SPEC_ENHANCEMENT:
  23529. + // if (STORMSTRIKE > 0 && (!me->HasSpellCooldown(STORMSTRIKE)) && m_ai->CastSpell(STORMSTRIKE, *pTarget))
  23530. + // return RETURN_CONTINUE;
  23531. + // if (FLAME_SHOCK > 0 && (!pTarget->HasAura(FLAME_SHOCK)) && m_ai->CastSpell(FLAME_SHOCK, *pTarget))
  23532. + // return RETURN_CONTINUE;
  23533. + // if (EARTH_SHOCK > 0 && (!me->HasSpellCooldown(EARTH_SHOCK)) && m_ai->CastSpell(EARTH_SHOCK, *pTarget))
  23534. + // return RETURN_CONTINUE;
  23535. + // if (LAVA_LASH > 0 && (!me->HasSpellCooldown(LAVA_LASH)) && m_ai->CastSpell(LAVA_LASH, *pTarget))
  23536. + // return RETURN_CONTINUE;
  23537. + // if (MAELSTROM_WEAPON > 0 && LIGHTNING_BOLT > 0 && me->HasAura(MAELSTROM_WEAPON) && m_ai->CastSpell(LIGHTNING_BOLT, *pTarget))
  23538. + // return RETURN_CONTINUE;
  23539. + // /*if (FOCUSED > 0 && m_ai->CastSpell(FOCUSED, *pTarget))
  23540. + // return RETURN_CONTINUE;*/
  23541. + // break;
  23542. +
  23543. + // case SHAMAN_SPEC_RESTORATION:
  23544. + // // fall through to elemental
  23545. +
  23546. + // case SHAMAN_SPEC_ELEMENTAL:
  23547. + // if (FLAME_SHOCK > 0 && (!pTarget->HasAura(FLAME_SHOCK)) && m_ai->CastSpell(FLAME_SHOCK, *pTarget))
  23548. + // return RETURN_CONTINUE;
  23549. + // if (LAVA_BURST > 0 && (pTarget->HasAura(FLAME_SHOCK)) && (!me->HasSpellCooldown(LAVA_BURST)) && m_ai->CastSpell(LAVA_BURST, *pTarget))
  23550. + // return RETURN_CONTINUE;
  23551. + // if (LIGHTNING_BOLT > 0 && m_ai->CastSpell(LIGHTNING_BOLT, *pTarget))
  23552. + // return RETURN_CONTINUE;
  23553. + // /*if (PURGE > 0 && m_ai->CastSpell(PURGE, *pTarget))
  23554. + // return RETURN_CONTINUE;*/
  23555. + // /*if (WIND_SHOCK > 0 && m_ai->CastSpell(WIND_SHOCK, *pTarget))
  23556. + // return RETURN_CONTINUE;*/
  23557. + // /*if (FROST_SHOCK > 0 && !pTarget->HasAura(FROST_SHOCK, EFFECT_0) && m_ai->CastSpell(FROST_SHOCK, *pTarget))
  23558. + // return RETURN_CONTINUE;*/
  23559. + // /*if (CHAIN_LIGHTNING > 0 && m_ai->CastSpell(CHAIN_LIGHTNING, *pTarget))
  23560. + // return RETURN_CONTINUE;*/
  23561. + // /*if (HEX > 0 && !pTarget->HasAura(HEX, EFFECT_0) && m_ai->CastSpell(HEX))
  23562. + // return RETURN_CONTINUE;*/
  23563. + //}
  23564. +
  23565. + return RETURN_NO_ACTION_OK;
  23566. +} // end DoNextCombatManeuver
  23567. +
  23568. +CombatManeuverReturns PlayerbotShamanAI::DoNextCombatManeuverPVP(Unit* pTarget)
  23569. +{
  23570. + //DropTotems();
  23571. + //CheckShields();
  23572. + //UseCooldowns();
  23573. +
  23574. + //Player* healTarget = (m_ai->GetScenarioType() == PlayerbotAI::SCENARIO_PVP_DUEL) ? GetHealTarget() : me;
  23575. + //if (HealPlayer(healTarget) & (RETURN_NO_ACTION_OK | RETURN_CONTINUE))
  23576. + // return RETURN_CONTINUE;
  23577. + //if (m_ai->CastSpell(LIGHTNING_BOLT))
  23578. + // return RETURN_CONTINUE;
  23579. +
  23580. + return DoNextCombatManeuverPVE(pTarget); // TODO: bad idea perhaps, but better than the alternative
  23581. +}
  23582. +
  23583. +CombatManeuverReturns PlayerbotShamanAI::HealPlayer(Player* /*target*/)
  23584. +{
  23585. + //CombatManeuverReturns r = PlayerbotClassAI::HealPlayer(target);
  23586. + //if (r != RETURN_NO_ACTION_OK)
  23587. + // return r;
  23588. +
  23589. + //if (!target->isAlive())
  23590. + //{
  23591. + // if (ANCESTRAL_SPIRIT && m_ai->CastSpell(ANCESTRAL_SPIRIT, *target))
  23592. + // {
  23593. + // std::string msg = "Resurrecting ";
  23594. + // msg += target->GetName();
  23595. + // me->Say(msg, LANG_UNIVERSAL);
  23596. + // return RETURN_CONTINUE;
  23597. + // }
  23598. + // return RETURN_NO_ACTION_ERROR; // not error per se - possibly just OOM
  23599. + //}
  23600. +
  23601. + //// Dispel if necessary
  23602. + //if (CURE_TOXINS > 0 && (m_ai->GetCombatOrder() & PlayerbotAI::ORDERS_NODISPEL) == 0)
  23603. + //{
  23604. + // uint32 DISPEL = CLEANSE_SPIRIT > 0 ? CLEANSE_SPIRIT : CURE_TOXINS;
  23605. + // uint32 dispelMask = SpellInfo::GetDispelMask(DISPEL_POISON);
  23606. + // uint32 dispelMask2 = SpellInfo::GetDispelMask(DISPEL_DISEASE);
  23607. + // uint32 dispelMask3 = SpellInfo::GetDispelMask(DISPEL_CURSE);
  23608. + // Unit::AuraMap const& auras = target->GetOwnedAuras();
  23609. + // for (Unit::AuraMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr)
  23610. + // {
  23611. + // Aura *holder = itr->second;
  23612. + // if ((1 << holder->GetSpellInfo()->Dispel) & dispelMask)
  23613. + // {
  23614. + // if (holder->GetSpellInfo()->Dispel == DISPEL_POISON)
  23615. + // {
  23616. + // if (m_ai->CastSpell(DISPEL, *target))
  23617. + // return RETURN_CONTINUE;
  23618. + // return RETURN_NO_ACTION_ERROR;
  23619. + // }
  23620. + // }
  23621. + // else if ((1 << holder->GetSpellInfo()->Dispel) & dispelMask2)
  23622. + // {
  23623. + // if (holder->GetSpellInfo()->Dispel == DISPEL_DISEASE)
  23624. + // {
  23625. + // if (m_ai->CastSpell(DISPEL, *target))
  23626. + // return RETURN_CONTINUE;
  23627. + // return RETURN_NO_ACTION_ERROR;
  23628. + // }
  23629. + // }
  23630. + // else if ((1 << holder->GetSpellInfo()->Dispel) & dispelMask3 & (DISPEL == CLEANSE_SPIRIT))
  23631. + // {
  23632. + // if (holder->GetSpellInfo()->Dispel == DISPEL_CURSE)
  23633. + // {
  23634. + // if (m_ai->CastSpell(DISPEL, *target))
  23635. + // return RETURN_CONTINUE;
  23636. + // return RETURN_NO_ACTION_ERROR;
  23637. + // }
  23638. + // }
  23639. + // }
  23640. + //}
  23641. +
  23642. + //// Everyone is healthy enough, return OK. MUST correlate to highest value below (should be last HP check)
  23643. + //if (target->GetHealthPct() >= 80)
  23644. + // return RETURN_NO_ACTION_OK;
  23645. +
  23646. + //// Technically the best rotation is CHAIN + LHW + LHW, or RIPTIDE + LHW + LHW (proc Tidal Waves then two short LHW), subbing in HW for trouble (bad mana efficiency)
  23647. + //if (target->GetHealthPct() < 30 && HEALING_WAVE > 0 && m_ai->CastSpell(HEALING_WAVE, *target))
  23648. + // return RETURN_CONTINUE;
  23649. + //if (target->GetHealthPct() < 50 && LESSER_HEALING_WAVE > 0 && m_ai->CastSpell(LESSER_HEALING_WAVE, *target))
  23650. + // return RETURN_CONTINUE;
  23651. + //if (target->GetHealthPct() < 60 && RIPTIDE > 0 && !target->HasAura(RIPTIDE, EFFECT_0) && m_ai->CastSpell(RIPTIDE, *target))
  23652. + // return RETURN_CONTINUE;
  23653. + //if (target->GetHealthPct() < 80 && CHAIN_HEAL > 0 && m_ai->CastSpell(CHAIN_HEAL, *target))
  23654. + // return RETURN_CONTINUE;
  23655. +
  23656. + return RETURN_NO_ACTION_UNKNOWN;
  23657. +} // end HealTarget
  23658. +
  23659. +void PlayerbotShamanAI::DropTotems()
  23660. +{
  23661. + //if (!m_ai) return;
  23662. + //if (!me) return;
  23663. +
  23664. + //uint32 spec = me->GetSpec();
  23665. +
  23666. + //Totem* earth = NULL;
  23667. + //Totem* fire = NULL;
  23668. + //Totem* water = NULL;
  23669. + //Totem* air = NULL;
  23670. +
  23671. + //for (uint8 slot = SUMMON_SLOT_TOTEM; slot < MAX_TOTEM_SLOT; ++slot)
  23672. + //{
  23673. + // if (!me->m_SummonSlot[slot])
  23674. + // continue;
  23675. +
  23676. + // Creature* tot = me->GetMap()->GetCreature(me->m_SummonSlot[slot]);
  23677. + // if (tot && tot->isTotem())
  23678. + // {
  23679. + // Totem* totem = tot->ToTotem();
  23680. + // if (totem->GetTotemType() == SUMMON_TYPE_TOTEM_EARTH)
  23681. + // earth = totem;
  23682. + // else if (totem->GetTotemType() == SUMMON_TYPE_TOTEM_FIRE)
  23683. + // fire = totem;
  23684. + // else if (totem->GetTotemType() == SUMMON_TYPE_TOTEM_WATER)
  23685. + // water = totem;
  23686. + // else if (totem->GetTotemType() == SUMMON_TYPE_TOTEM_AIR)
  23687. + // air = totem;
  23688. + // }
  23689. + //}
  23690. +
  23691. + //// Earth Totems
  23692. + //if ((earth == NULL) || (me->GetDistance(earth) > 30))
  23693. + //{
  23694. + // if (STRENGTH_OF_EARTH_TOTEM > 0 && m_ai->CastSpell(STRENGTH_OF_EARTH_TOTEM))
  23695. + // {}
  23696. + //}
  23697. +
  23698. + //// Fire Totems
  23699. + //if ((fire == NULL) || (me->GetDistance(fire) > 30))
  23700. + //{
  23701. + // if (m_ai->GetCombatOrder() & PlayerbotAI::ORDERS_RESIST_FROST && FROST_RESISTANCE_TOTEM > 0 && m_ai->CastSpell(FROST_RESISTANCE_TOTEM))
  23702. + // {}
  23703. + // else if (spec == SHAMAN_SPEC_ELEMENTAL && TOTEM_OF_WRATH > 0 && m_ai->CastSpell(TOTEM_OF_WRATH))
  23704. + // {}
  23705. + // // If the spec didn't take totem of wrath, use flametongue
  23706. + // else if ((spec != SHAMAN_SPEC_ELEMENTAL || TOTEM_OF_WRATH == 0) && FLAMETONGUE_TOTEM > 0 && m_ai->CastSpell(FLAMETONGUE_TOTEM))
  23707. + // {}
  23708. + //}
  23709. +
  23710. + //// Air totems
  23711. + //if ((air == NULL) || (me->GetDistance(air) > 30))
  23712. + //{
  23713. + // if (m_ai->GetCombatOrder() & PlayerbotAI::ORDERS_RESIST_NATURE && NATURE_RESISTANCE_TOTEM > 0 && m_ai->CastSpell(NATURE_RESISTANCE_TOTEM))
  23714. + // {}
  23715. + // else if (spec == SHAMAN_SPEC_ENHANCEMENT)
  23716. + // {
  23717. + // if (WIND_FURY_TOTEM > 0 /*&& !me->HasAura(IMPROVED_ICY_TALONS)*/ && m_ai->CastSpell(WIND_FURY_TOTEM))
  23718. + // {}
  23719. + // }
  23720. + // else
  23721. + // {
  23722. + // if (WRATH_OF_AIR_TOTEM > 0 && m_ai->CastSpell(WRATH_OF_AIR_TOTEM))
  23723. + // {}
  23724. + // }
  23725. + //}
  23726. +
  23727. + //// Water Totems
  23728. + //if ((water == NULL) || (me->GetDistance(water) > 30))
  23729. + //{
  23730. + // if (m_ai->GetCombatOrder() & PlayerbotAI::ORDERS_RESIST_FIRE && FIRE_RESISTANCE_TOTEM > 0 && m_ai->CastSpell(FIRE_RESISTANCE_TOTEM))
  23731. + // {}
  23732. + // else if (MANA_SPRING_TOTEM > 0 && m_ai->CastSpell(MANA_SPRING_TOTEM))
  23733. + // {}
  23734. + //}
  23735. +
  23736. + /*if (EARTH_ELEMENTAL_TOTEM > 0 && m_ai->CastSpell(EARTH_ELEMENTAL_TOTEM))
  23737. + return RETURN_CONTINUE;*/
  23738. + /*if (EARTHBIND_TOTEM > 0 && !pTarget->HasAura(EARTHBIND_TOTEM, EFFECT_0) && !me->HasAura(STRENGTH_OF_EARTH_TOTEM, EFFECT_0) && m_ai->CastSpell(EARTHBIND_TOTEM))
  23739. + return RETURN_CONTINUE;*/
  23740. + /*if (FIRE_ELEMENTAL_TOTEM > 0 && m_ai->CastSpell(FIRE_ELEMENTAL_TOTEM))
  23741. + return RETURN_CONTINUE;*/
  23742. + /*if (FIRE_NOVA_TOTEM > 0 && m_ai->CastSpell(FIRE_NOVA_TOTEM))
  23743. + return RETURN_CONTINUE;*/
  23744. + /*if (GROUNDING_TOTEM > 0 && !me->HasAura(GROUNDING_TOTEM, EFFECT_0) && !me->HasAura(WRATH_OF_AIR_TOTEM, EFFECT_0) && !me->HasAura(WIND_FURY_TOTEM, EFFECT_0) && m_ai->CastSpell(GROUNDING_TOTEM))
  23745. + return RETURN_CONTINUE;*/
  23746. + /*if (HEALING_STREAM_TOTEM > 0 && me->GetHealthPct() < 50 && !me->HasAura(HEALING_STREAM_TOTEM, EFFECT_0) && !me->HasAura(MANA_SPRING_TOTEM, EFFECT_0) && m_ai->CastSpell(HEALING_STREAM_TOTEM))
  23747. + return RETURN_CONTINUE;*/
  23748. + /*if (MAGMA_TOTEM > 0 && (!me->HasAura(TOTEM_OF_WRATH, EFFECT_0)) && m_ai->CastSpell(MAGMA_TOTEM))
  23749. + return RETURN_CONTINUE;*/
  23750. + /*if (SEARING_TOTEM > 0 && !pTarget->HasAura(SEARING_TOTEM, EFFECT_0) && !me->HasAura(TOTEM_OF_WRATH, EFFECT_0) && m_ai->CastSpell(SEARING_TOTEM))
  23751. + return RETURN_CONTINUE;*/
  23752. + /*if (STONECLAW_TOTEM > 0 && me->GetHealthPct() < 51 && !pTarget->HasAura(STONECLAW_TOTEM, EFFECT_0) && !pTarget->HasAura(EARTHBIND_TOTEM, EFFECT_0) && !me->HasAura(STRENGTH_OF_EARTH_TOTEM, EFFECT_0) && m_ai->CastSpell(STONECLAW_TOTEM))
  23753. + return RETURN_CONTINUE;*/
  23754. + /*if (STONESKIN_TOTEM > 0 && !me->HasAura(STONESKIN_TOTEM, EFFECT_0) && !me->HasAura(STRENGTH_OF_EARTH_TOTEM, EFFECT_0) && m_ai->CastSpell(STONESKIN_TOTEM))
  23755. + return RETURN_CONTINUE;*/
  23756. + /*if (TREMOR_TOTEM > 0 && !me->HasAura(STRENGTH_OF_EARTH_TOTEM, EFFECT_0) && m_ai->CastSpell(TREMOR_TOTEM))
  23757. + return RETURN_CONTINUE;*/
  23758. +}
  23759. +
  23760. +void PlayerbotShamanAI::CheckShields()
  23761. +{
  23762. + //if (!m_ai) return;
  23763. + //if (!me) return;
  23764. +
  23765. + //uint32 spec = me->GetSpec();
  23766. +
  23767. + //if (spec == SHAMAN_SPEC_ENHANCEMENT && LIGHTNING_SHIELD > 0 && !me->HasAura(LIGHTNING_SHIELD, EFFECT_0))
  23768. + // m_ai->CastSpell(LIGHTNING_SHIELD, *me);
  23769. + //else if ((spec == SHAMAN_SPEC_ELEMENTAL || spec == SHAMAN_SPEC_RESTORATION) && WATER_SHIELD > 0 && !me->HasAura(WATER_SHIELD, EFFECT_0))
  23770. + // m_ai->CastSpell(WATER_SHIELD, *me);
  23771. + //if (EARTH_SHIELD > 0 && !GetMaster()->HasAura(EARTH_SHIELD, EFFECT_0))
  23772. + // m_ai->CastSpell(EARTH_SHIELD, *(GetMaster()));
  23773. +}
  23774. +
  23775. +void PlayerbotShamanAI::UseCooldowns()
  23776. +{
  23777. + //if (!m_ai) return;
  23778. + //if (!me) return;
  23779. +
  23780. + //uint32 spec = me->GetSpec();
  23781. +
  23782. + //if (BLOODLUST > 0 && (!GetMaster()->HasAura(BLOODLUST, EFFECT_0)) && m_ai->CastSpell(BLOODLUST))
  23783. + //{}
  23784. + //else if (HEROISM > 0 && (!GetMaster()->HasAura(HEROISM, EFFECT_0)) && m_ai->CastSpell(HEROISM))
  23785. + //{}
  23786. +
  23787. + //switch (spec)
  23788. + //{
  23789. + // case SHAMAN_SPEC_ENHANCEMENT:
  23790. + // if (SHAMANISTIC_RAGE > 0 && m_ai->CastSpell(SHAMANISTIC_RAGE, *me))
  23791. + // return;
  23792. + // else if (FERAL_SPIRIT > 0 && m_ai->CastSpell(FERAL_SPIRIT))
  23793. + // return;
  23794. + // break;
  23795. +
  23796. + // case SHAMAN_SPEC_ELEMENTAL:
  23797. + // if (ELEMENTAL_MASTERY > 0 && m_ai->CastSpell(ELEMENTAL_MASTERY, *me))
  23798. + // return;
  23799. + // else if (THUNDERSTORM > 0 && m_ai->CastSpell(THUNDERSTORM, *me))
  23800. + // return;
  23801. + // break;
  23802. +
  23803. + // case SHAMAN_SPEC_RESTORATION:
  23804. + // if (MANA_TIDE_TOTEM > 0 && (me->GetPower(POWER_MANA)*100/me->GetMaxPower(POWER_MANA)) < 50 && m_ai->CastSpell(MANA_TIDE_TOTEM))
  23805. + // return;
  23806. + // else if (NATURES_SWIFTNESS_SHAMAN > 0 && m_ai->CastSpell(NATURES_SWIFTNESS_SHAMAN))
  23807. + // return;
  23808. + // else if (TIDAL_FORCE > 0 && m_ai->CastSpell(TIDAL_FORCE))
  23809. + // return;
  23810. +
  23811. + // default:
  23812. + // break;
  23813. + //}
  23814. +}
  23815. +
  23816. +void PlayerbotShamanAI::DoNonCombatActions()
  23817. +{
  23818. +// if (!m_ai) return;
  23819. +// if (!me) return;
  23820. +//
  23821. +// if (!me->isAlive() || me->IsInDuel()) return;
  23822. +//
  23823. +// uint32 spec = me->GetSpec();
  23824. +//
  23825. +// CheckShields();
  23826. +///*
  23827. +// // buff myself weapon
  23828. +// if (ROCKBITER_WEAPON > 0)
  23829. +// (!me->HasAura(ROCKBITER_WEAPON, EFFECT_0) && !me->HasAura(EARTHLIVING_WEAPON, EFFECT_0) && !me->HasAura(WINDFURY_WEAPON, EFFECT_0) && !me->HasAura(FLAMETONGUE_WEAPON, EFFECT_0) && !me->HasAura(FROSTBRAND_WEAPON, EFFECT_0) && m_ai->CastSpell(ROCKBITER_WEAPON,*me) );
  23830. +// else if (EARTHLIVING_WEAPON > 0)
  23831. +// (!me->HasAura(EARTHLIVING_WEAPON, EFFECT_0) && !me->HasAura(EARTHLIVING_WEAPON, EFFECT_0) && !me->HasAura(FLAMETONGUE_WEAPON, EFFECT_0) && !me->HasAura(FROSTBRAND_WEAPON, EFFECT_0) && !me->HasAura(ROCKBITER_WEAPON, EFFECT_0) && m_ai->CastSpell(WINDFURY_WEAPON,*me) );
  23832. +// else if (WINDFURY_WEAPON > 0)
  23833. +// (!me->HasAura(WINDFURY_WEAPON, EFFECT_0) && !me->HasAura(EARTHLIVING_WEAPON, EFFECT_0) && !me->HasAura(FLAMETONGUE_WEAPON, EFFECT_0) && !me->HasAura(FROSTBRAND_WEAPON, EFFECT_0) && !me->HasAura(ROCKBITER_WEAPON, EFFECT_0) && m_ai->CastSpell(WINDFURY_WEAPON,*me) );
  23834. +// else if (FLAMETONGUE_WEAPON > 0)
  23835. +// (!me->HasAura(FLAMETONGUE_WEAPON, EFFECT_0) && !me->HasAura(EARTHLIVING_WEAPON, EFFECT_0) && !me->HasAura(WINDFURY_WEAPON, EFFECT_0) && !me->HasAura(FROSTBRAND_WEAPON, EFFECT_0) && !me->HasAura(ROCKBITER_WEAPON, EFFECT_0) && m_ai->CastSpell(FLAMETONGUE_WEAPON,*me) );
  23836. +// else if (FROSTBRAND_WEAPON > 0)
  23837. +// (!me->HasAura(FROSTBRAND_WEAPON, EFFECT_0) && !me->HasAura(EARTHLIVING_WEAPON, EFFECT_0) && !me->HasAura(WINDFURY_WEAPON, EFFECT_0) && !me->HasAura(FLAMETONGUE_WEAPON, EFFECT_0) && !me->HasAura(ROCKBITER_WEAPON, EFFECT_0) && m_ai->CastSpell(FROSTBRAND_WEAPON,*me) );
  23838. +// */
  23839. +// // Mainhand
  23840. +// Item* weapon;
  23841. +// weapon = me->GetItemByPos(EQUIPMENT_SLOT_MAINHAND);
  23842. +// if (weapon && (weapon->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT) == 0) && spec == SHAMAN_SPEC_RESTORATION)
  23843. +// m_ai->CastSpell(EARTHLIVING_WEAPON, *me);
  23844. +// else if (weapon && (weapon->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT) == 0) && spec == SHAMAN_SPEC_ELEMENTAL)
  23845. +// m_ai->CastSpell(FLAMETONGUE_WEAPON, *me);
  23846. +// else if (weapon && (weapon->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT) == 0) && spec == SHAMAN_SPEC_ENHANCEMENT)
  23847. +// m_ai->CastSpell(WINDFURY_WEAPON, *me);
  23848. +//
  23849. +// //Offhand
  23850. +// weapon = me->GetItemByPos(EQUIPMENT_SLOT_OFFHAND);
  23851. +// if (weapon && (weapon->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT) == 0) && spec == SHAMAN_SPEC_ENHANCEMENT)
  23852. +// m_ai->CastSpell(FLAMETONGUE_WEAPON, *me);
  23853. +//
  23854. +// // Revive
  23855. +// if (HealPlayer(GetResurrectionTarget()) & RETURN_CONTINUE)
  23856. +// return;
  23857. +//
  23858. +// // Heal
  23859. +// if (m_ai->IsHealer())
  23860. +// {
  23861. +// if (HealPlayer(GetHealTarget()) & RETURN_CONTINUE)
  23862. +// return;// RETURN_CONTINUE;
  23863. +// }
  23864. +// else
  23865. +// {
  23866. +// // Is this desirable? Debatable.
  23867. +// // TODO: In a group/raid with a healer you'd want this bot to focus on DPS (it's not specced/geared for healing either)
  23868. +// if (HealPlayer(me) & RETURN_CONTINUE)
  23869. +// return;// RETURN_CONTINUE;
  23870. +// }
  23871. +//
  23872. +// // hp/mana check
  23873. +// if (me->getStandState() != UNIT_STAND_STATE_STAND)
  23874. +// me->SetStandState(UNIT_STAND_STATE_STAND);
  23875. +//
  23876. +// if (EatDrinkBandage())
  23877. +// return;
  23878. +} // end DoNonCombatActions
  23879. +
  23880. +bool PlayerbotShamanAI::CastHoTOnTank()
  23881. +{
  23882. + //if (!m_ai) return false;
  23883. +
  23884. + //if ((PlayerbotAI::ORDERS_HEAL & m_ai->GetCombatOrder()) == 0) return false;
  23885. +
  23886. + // Shaman: Healing Stream Totem, Earthliving Weapon, and Riptide (with talents)
  23887. + // None of these are cast before Pulling
  23888. +
  23889. + return false;
  23890. +}
  23891. +
  23892. +void PlayerbotShamanAI::UpdateGroupActions(uint32 const diff)
  23893. +{
  23894. + if (me->IsMounted() || me->HasUnitState(UNIT_STATE_CASTING) ||
  23895. + me->HasAuraType(SPELL_AURA_MOD_SILENCE) ||
  23896. + me->HasAuraType(SPELL_AURA_MOD_PACIFY_SILENCE) ||
  23897. + (me->GetInterruptMask() & AURA_INTERRUPT_FLAG_NOT_SEATED))
  23898. + return;
  23899. +
  23900. + MainSpec spec = MainSpec(m_ai->GetMySpec());
  23901. + //1 Shaman specific
  23902. + //1.1 item enchancements
  23903. + //casts are instant so check even in combat
  23904. + //1.1.1 mainhand
  23905. + Item* weapon = me->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND);
  23906. + if (weapon && !weapon->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT))
  23907. + {
  23908. + //sLog->outError(LOG_FILTER_PLAYER, "PlayerbotShamanAI:: %s has found unenchanted mainhand weapon %s", me->GetName().c_str(), weapon->GetTemplate()->Name1.c_str());
  23909. + switch (spec)
  23910. + {
  23911. + case SHAMAN_SPEC_RESTORATION:
  23912. + if (EARTHLIVING_WEAPON)
  23913. + {
  23914. + m_ai->CastSpell(weapon, EARTHLIVING_WEAPON);
  23915. + //sLog->outError(LOG_FILTER_PLAYER, "PlayerbotShamanAI:: %s tries to enchant with Earthliving", me->GetName().c_str());
  23916. + }
  23917. + break;
  23918. + case SHAMAN_SPEC_ELEMENTAL:
  23919. + if (FLAMETONGUE_WEAPON)
  23920. + {
  23921. + m_ai->CastSpell(weapon, FLAMETONGUE_WEAPON);
  23922. + //sLog->outError(LOG_FILTER_PLAYER, "PlayerbotShamanAI:: %s tries to enchant with Flametongue", me->GetName().c_str());
  23923. + }
  23924. + break;
  23925. + case SHAMAN_SPEC_ENHANCEMENT:
  23926. + if (WINDFURY_WEAPON)
  23927. + {
  23928. + m_ai->CastSpell(weapon, WINDFURY_WEAPON);
  23929. + //sLog->outError(LOG_FILTER_PLAYER, "PlayerbotShamanAI:: %s tries to enchant with Windfury", me->GetName().c_str());
  23930. + }
  23931. + else if (FROSTBRAND_WEAPON)
  23932. + {
  23933. + m_ai->CastSpell(weapon, FROSTBRAND_WEAPON);
  23934. + //sLog->outError(LOG_FILTER_PLAYER, "PlayerbotShamanAI:: %s tries to enchant with Frostbrand", me->GetName().c_str());
  23935. + }
  23936. + break;
  23937. + default:
  23938. + break;
  23939. + }
  23940. + }
  23941. + //1.1.2 offhand
  23942. + weapon = me->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND);
  23943. + if (weapon && !weapon->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT))
  23944. + {
  23945. + switch (spec)
  23946. + {
  23947. + case SHAMAN_SPEC_RESTORATION:
  23948. + if (EARTHLIVING_WEAPON)
  23949. + m_ai->CastSpell(weapon, EARTHLIVING_WEAPON);
  23950. + break;
  23951. + case SHAMAN_SPEC_ELEMENTAL:
  23952. + if (FLAMETONGUE_WEAPON)
  23953. + m_ai->CastSpell(weapon, FLAMETONGUE_WEAPON);
  23954. + break;
  23955. + case SHAMAN_SPEC_ENHANCEMENT:
  23956. + if (WINDFURY_WEAPON)
  23957. + m_ai->CastSpell(weapon, WINDFURY_WEAPON);
  23958. + else if (FROSTBRAND_WEAPON)
  23959. + m_ai->CastSpell(weapon, FROSTBRAND_WEAPON);
  23960. + break;
  23961. + default:
  23962. + break;
  23963. + }
  23964. + }
  23965. + //1.2 elemental shield: self
  23966. + if (spec == SHAMAN_SPEC_RESTORATION && WATER_SHIELD && !me->HasAura(WATER_SHIELD))
  23967. + {
  23968. + m_ai->CastSpell(me, WATER_SHIELD);
  23969. + }
  23970. + else if (LIGHTNING_SHIELD && !me->HasAura(LIGHTNING_SHIELD))
  23971. + {
  23972. + m_ai->CastSpell(me, LIGHTNING_SHIELD);
  23973. + }
  23974. + //end Shaman specific
  23975. +
  23976. + //2 Heal
  23977. +
  23978. + //3 Cure
  23979. + //3.1 Cure Toxins
  23980. + if (CURE_TOXINS)
  23981. + m_ai->CureGroup(me, CURE_TOXINS, diff);
  23982. + //3.2 Tremor totem, TODO:
  23983. + //if (TREMOR_TOTEM)
  23984. + //{}
  23985. + //end Cure
  23986. +
  23987. + //4 Non-combat Actions
  23988. +
  23989. +
  23990. +
  23991. +
  23992. + if (me->duel && me->duel->startTimer != 0)
  23993. + return;
  23994. +}
  23995. diff --git a/src/server/game/AI/PlayerBots/bp_sha_ai.h b/src/server/game/AI/PlayerBots/bp_sha_ai.h
  23996. new file mode 100644
  23997. index 0000000..bd722db
  23998. --- /dev/null
  23999. +++ b/src/server/game/AI/PlayerBots/bp_sha_ai.h
  24000. @@ -0,0 +1,246 @@
  24001. +#ifndef _PLAYERBOTSHAMANAI_H
  24002. +#define _PLAYERBOTSHAMANAI_H
  24003. +
  24004. +#include "bp_cai.h"
  24005. +
  24006. +enum
  24007. +{
  24008. + SPELL_ENHANCEMENT,
  24009. + SPELL_RESTORATION,
  24010. + SPELL_ELEMENTAL
  24011. +};
  24012. +
  24013. +enum
  24014. +{
  24015. + ANCESTRAL_SPIRIT_1 = 2008,
  24016. + ASTRAL_RECALL_1 = 556,
  24017. + BLOODLUST_1 = 2825,
  24018. + CALL_OF_THE_ANCESTORS_1 = 66843,
  24019. + CALL_OF_THE_ELEMENTS_1 = 66842,
  24020. + CALL_OF_THE_SPIRITS_1 = 66844,
  24021. + CHAIN_HEAL_1 = 1064,
  24022. + CHAIN_LIGHTNING_1 = 421,
  24023. + CHAINED_HEAL_1 = 70809,
  24024. + CLEANSE_SPIRIT_1 = 51886,
  24025. + CLEANSING_TOTEM_1 = 8170,
  24026. + CURE_TOXINS_1 = 526,
  24027. + EARTH_ELEMENTAL_TOTEM_1 = 2062,
  24028. + EARTH_SHIELD_1 = 974,
  24029. + EARTH_SHOCK_1 = 8042,
  24030. + EARTHBIND_TOTEM_1 = 2484,
  24031. + EARTHLIVING_WEAPON_1 = 51730,
  24032. + ELEMENTAL_MASTERY_1 = 16166,
  24033. + FERAL_SPIRIT_1 = 51533,
  24034. + FIRE_ELEMENTAL_TOTEM_1 = 2894,
  24035. + FIRE_NOVA_1 = 1535,
  24036. + FIRE_RESISTANCE_TOTEM_1 = 8184,
  24037. + FLAME_SHOCK_1 = 8050,
  24038. + FLAMETONGUE_TOTEM_1 = 8227,
  24039. + FLAMETONGUE_WEAPON_1 = 8024,
  24040. + FROST_RESISTANCE_TOTEM_1 = 8181,
  24041. + FROST_SHOCK_1 = 8056,
  24042. + FROSTBRAND_WEAPON_1 = 8033,
  24043. + GHOST_WOLF_1 = 2645,
  24044. + GROUNDING_TOTEM_1 = 8177,
  24045. + HEALING_STREAM_TOTEM_1 = 5394,
  24046. + HEALING_WAVE_1 = 331,
  24047. + HEROISM_1 = 32182,
  24048. + HEX_1 = 51514,
  24049. + LAVA_BURST_1 = 51505,
  24050. + LAVA_LASH_1 = 60103,
  24051. + LESSER_HEALING_WAVE_1 = 8004,
  24052. + LIGHTNING_BOLT_1 = 403,
  24053. + LIGHTNING_SHIELD_1 = 324,
  24054. + MAGMA_TOTEM_1 = 8190,
  24055. + MANA_SPRING_TOTEM_1 = 5675,
  24056. + MANA_TIDE_TOTEM_1 = 16190,
  24057. + NATURE_RESISTANCE_TOTEM_1 = 10595,
  24058. + NATURES_SWIFTNESS_SHAMAN_1 = 16188,
  24059. + PURGE_1 = 370,
  24060. + RIPTIDE_1 = 61295,
  24061. + ROCKBITER_WEAPON_1 = 8017,
  24062. + SEARING_TOTEM_1 = 3599,
  24063. + SENTRY_TOTEM_1 = 6495,
  24064. + SHAMANISTIC_RAGE_1 = 30823,
  24065. + STONECLAW_TOTEM_1 = 5730,
  24066. + STONESKIN_TOTEM_1 = 8071,
  24067. + STORMSTRIKE_1 = 17364,
  24068. + STRENGTH_OF_EARTH_TOTEM_1 = 8075,
  24069. + THUNDERSTORM_1 = 51490,
  24070. + TIDAL_FORCE_1 = 55198,
  24071. + TOTEM_OF_WRATH_1 = 30706,
  24072. + TOTEMIC_RECALL_1 = 36936,
  24073. + TREMOR_TOTEM_1 = 8143,
  24074. + WATER_BREATHING_1 = 131,
  24075. + WATER_SHIELD_1 = 52127,
  24076. + WATER_WALKING_1 = 546,
  24077. + WIND_SHEAR_1 = 57994,
  24078. + WINDFURY_TOTEM_1 = 8512,
  24079. + WINDFURY_WEAPON_1 = 8232,
  24080. + WRATH_OF_AIR_TOTEM_1 = 3738,
  24081. +
  24082. + //Totem Buffs
  24083. + STRENGTH_OF_EARTH_EFFECT_1 = 8076,
  24084. + FLAMETONGUE_EFFECT_1 = 52109,
  24085. + MAGMA_TOTEM_EFFECT_1 = 8188,
  24086. + STONECLAW_EFFECT_1 = 5728,
  24087. + FIRE_RESISTANCE_EFFECT_1 = 8185,
  24088. + FROST_RESISTANCE_EFFECT_1 = 8182,
  24089. + GROUDNING_EFFECT_1 = 8178,
  24090. + NATURE_RESISTANCE_EFFECT_1 = 10596,
  24091. + STONESKIN_EFFECT_1 = 8072,
  24092. + WINDFURY_EFFECT_1 = 8515,
  24093. + WRATH_OF_AIR_EFFECT_1 = 2895,
  24094. + CLEANSING_TOTEM_EFFECT_1 = 8172,
  24095. + HEALING_STREAM_EFFECT_1 = 52042,
  24096. + MANA_SPRING_EFFECT_1 = 5677,
  24097. + TREMOR_TOTEM_EFFECT_1 = 8145,
  24098. + TOTEM_OF_WRATH_EFFECT_1 = 57658,
  24099. + EARTHBIND_EFFECT_1 = 6474,
  24100. + // FIRE_ELEMENTAL_TOTEM uses spell effect index 2
  24101. + // SEARING_TOTEM uses spell effect index 0
  24102. + // EARTH_ELEMENTAL_TOTEM uses spell effect indexes 1 and 2
  24103. +
  24104. + //Spec buffs
  24105. + MAELSTROM_WEAPON_1 = 51532, //This is the final rank only, the spell family thing doesn't work so I decided to go with max only
  24106. +
  24107. + // Buffs that don't stack with totems
  24108. + //IMPROVED_ICY_TALONS_1 = 55610,
  24109. + //HORN_OF_WINTER_1 = 57330
  24110. +};
  24111. +//class Player;
  24112. +
  24113. +class PlayerbotShamanAI : PlayerbotClassAI
  24114. +{
  24115. +public:
  24116. + PlayerbotShamanAI(Player * const master, Player * const bot, PlayerbotAI * const ai);
  24117. + virtual ~PlayerbotShamanAI();
  24118. +
  24119. + // all combat actions go here
  24120. + CombatManeuverReturns DoFirstCombatManeuver(Unit* pTarget);
  24121. + CombatManeuverReturns DoNextCombatManeuver(Unit* pTarget);
  24122. +
  24123. + // all non combat actions go here, ex buffs, heals, rezzes
  24124. + void DoNonCombatActions();
  24125. +
  24126. + // Utility Functions
  24127. + bool CastHoTOnTank();
  24128. +
  24129. + void UpdateGroupActions(uint32 const diff);
  24130. + void SendClassDebugInfo();
  24131. +
  24132. +private:
  24133. + CombatManeuverReturns DoFirstCombatManeuverPVE(Unit* pTarget);
  24134. + CombatManeuverReturns DoNextCombatManeuverPVE(Unit* pTarget);
  24135. + CombatManeuverReturns DoFirstCombatManeuverPVP(Unit* pTarget);
  24136. + CombatManeuverReturns DoNextCombatManeuverPVP(Unit* pTarget);
  24137. +
  24138. + // Heals the target based off its hps
  24139. + CombatManeuverReturns HealPlayer(Player* target);
  24140. + Player* GetHealTarget() { return PlayerbotClassAI::GetHealTarget(); }
  24141. + void DropTotems();
  24142. + void CheckShields();
  24143. + void UseCooldowns();
  24144. +
  24145. + // ENHANCEMENT
  24146. + uint32 ROCKBITER_WEAPON,
  24147. + STONESKIN_TOTEM,
  24148. + LIGHTNING_SHIELD,
  24149. + FLAMETONGUE_WEAPON,
  24150. + STRENGTH_OF_EARTH_TOTEM,
  24151. + FOCUSED,
  24152. + FROSTBRAND_WEAPON,
  24153. + FROST_RESISTANCE_TOTEM,
  24154. + FLAMETONGUE_TOTEM,
  24155. + FIRE_RESISTANCE_TOTEM,
  24156. + WINDFURY_WEAPON,
  24157. + GROUNDING_TOTEM,
  24158. + NATURE_RESISTANCE_TOTEM,
  24159. + WIND_FURY_TOTEM,
  24160. + STORMSTRIKE,
  24161. + LAVA_LASH,
  24162. + SHAMANISTIC_RAGE,
  24163. + WRATH_OF_AIR_TOTEM,
  24164. + EARTH_ELEMENTAL_TOTEM,
  24165. + BLOODLUST,
  24166. + HEROISM,
  24167. + FERAL_SPIRIT,
  24168. + MAELSTROM_WEAPON;
  24169. +
  24170. + // RESTORATION
  24171. + uint32 HEALING_WAVE,
  24172. + LESSER_HEALING_WAVE,
  24173. + ANCESTRAL_SPIRIT,
  24174. + TREMOR_TOTEM,
  24175. + HEALING_STREAM_TOTEM,
  24176. + MANA_SPRING_TOTEM,
  24177. + CHAIN_HEAL,
  24178. + MANA_TIDE_TOTEM,
  24179. + EARTH_SHIELD,
  24180. + WATER_SHIELD,
  24181. + EARTHLIVING_WEAPON,
  24182. + RIPTIDE,
  24183. + CURE_TOXINS,
  24184. + CLEANSE_SPIRIT,
  24185. + NATURES_SWIFTNESS_SHAMAN,
  24186. + TIDAL_FORCE;
  24187. +
  24188. + // ELEMENTAL
  24189. + uint32 LIGHTNING_BOLT,
  24190. + EARTH_SHOCK,
  24191. + STONECLAW_TOTEM,
  24192. + FLAME_SHOCK,
  24193. + SEARING_TOTEM,
  24194. + PURGE,
  24195. + FIRE_NOVA_TOTEM,
  24196. + WIND_SHOCK,
  24197. + FROST_SHOCK,
  24198. + MAGMA_TOTEM,
  24199. + CHAIN_LIGHTNING,
  24200. + TOTEM_OF_WRATH,
  24201. + FIRE_ELEMENTAL_TOTEM,
  24202. + LAVA_BURST,
  24203. + EARTHBIND_TOTEM,
  24204. + HEX,
  24205. + ELEMENTAL_MASTERY,
  24206. + THUNDERSTORM;
  24207. +
  24208. + // racial
  24209. + uint32 ARCANE_TORRENT,
  24210. + GIFT_OF_THE_NAARU,
  24211. + STONEFORM,
  24212. + ESCAPE_ARTIST,
  24213. + EVERY_MAN_FOR_HIMSELF,
  24214. + SHADOWMELD,
  24215. + BLOOD_FURY,
  24216. + WAR_STOMP,
  24217. + BERSERKING,
  24218. + WILL_OF_THE_FORSAKEN;
  24219. +
  24220. + // totem buffs
  24221. + uint32 STRENGTH_OF_EARTH_EFFECT,
  24222. + FLAMETONGUE_EFFECT,
  24223. + MAGMA_TOTEM_EFFECT,
  24224. + STONECLAW_EFFECT,
  24225. + FIRE_RESISTANCE_EFFECT,
  24226. + FROST_RESISTANCE_EFFECT,
  24227. + GROUDNING_EFFECT,
  24228. + NATURE_RESISTANCE_EFFECT,
  24229. + STONESKIN_EFFECT,
  24230. + WINDFURY_EFFECT,
  24231. + WRATH_OF_AIR_EFFECT,
  24232. + CLEANSING_TOTEM_EFFECT,
  24233. + HEALING_STREAM_EFFECT,
  24234. + MANA_SPRING_EFFECT,
  24235. + TREMOR_TOTEM_EFFECT,
  24236. + TOTEM_OF_WRATH_EFFECT,
  24237. + EARTHBIND_EFFECT;
  24238. +
  24239. + // Buffs that dont stack with totems
  24240. + uint32 IMPROVED_ICY_TALONS,
  24241. + HORN_OF_WINTER;
  24242. +
  24243. + uint32 SpellSequence, LastSpellEnhancement, LastSpellRestoration, LastSpellElemental;
  24244. +};
  24245. +
  24246. +#endif
  24247. diff --git a/src/server/game/AI/PlayerBots/bp_warl_ai.cpp b/src/server/game/AI/PlayerBots/bp_warl_ai.cpp
  24248. new file mode 100644
  24249. index 0000000..e1745f5
  24250. --- /dev/null
  24251. +++ b/src/server/game/AI/PlayerBots/bp_warl_ai.cpp
  24252. @@ -0,0 +1,550 @@
  24253. +#include "bp_ai.h"
  24254. +#include "bp_warl_ai.h"
  24255. +#include "Player.h"
  24256. +#include "Pet.h"
  24257. +
  24258. +PlayerbotWarlockAI::PlayerbotWarlockAI(Player* const master, Player* const bot, PlayerbotAI* const ai) : PlayerbotClassAI(master, bot, ai)
  24259. +{
  24260. + // DESTRUCTION
  24261. + SHADOW_BOLT = PlayerbotAI::InitSpell(me, SHADOW_BOLT_1);
  24262. + IMMOLATE = PlayerbotAI::InitSpell(me, IMMOLATE_1);
  24263. + INCINERATE = PlayerbotAI::InitSpell(me, INCINERATE_1);
  24264. + SEARING_PAIN = PlayerbotAI::InitSpell(me, SEARING_PAIN_1);
  24265. + CONFLAGRATE = PlayerbotAI::InitSpell(me, CONFLAGRATE_1);
  24266. + SHADOWFURY = PlayerbotAI::InitSpell(me, SHADOWFURY_1);
  24267. + CHAOS_BOLT = PlayerbotAI::InitSpell(me, CHAOS_BOLT_1);
  24268. + SHADOWFLAME = PlayerbotAI::InitSpell(me, SHADOWFLAME_1);
  24269. + HELLFIRE = PlayerbotAI::InitSpell(me, HELLFIRE_1);
  24270. + RAIN_OF_FIRE = PlayerbotAI::InitSpell(me, RAIN_OF_FIRE_1);
  24271. + SOUL_FIRE = PlayerbotAI::InitSpell(me, SOUL_FIRE_1); // soul shard spells
  24272. + SHADOWBURN = PlayerbotAI::InitSpell(me, SHADOWBURN_1);
  24273. + // CURSE
  24274. + CURSE_OF_WEAKNESS = PlayerbotAI::InitSpell(me, CURSE_OF_WEAKNESS_1);
  24275. + CURSE_OF_THE_ELEMENTS = PlayerbotAI::InitSpell(me, CURSE_OF_THE_ELEMENTS_1);
  24276. + CURSE_OF_AGONY = PlayerbotAI::InitSpell(me, CURSE_OF_AGONY_1);
  24277. + CURSE_OF_EXHAUSTION = PlayerbotAI::InitSpell(me, CURSE_OF_EXHAUSTION_1);
  24278. + CURSE_OF_TONGUES = PlayerbotAI::InitSpell(me, CURSE_OF_TONGUES_1);
  24279. + CURSE_OF_DOOM = PlayerbotAI::InitSpell(me, CURSE_OF_DOOM_1);
  24280. + // AFFLICTION
  24281. + CORRUPTION = PlayerbotAI::InitSpell(me, CORRUPTION_1);
  24282. + DRAIN_SOUL = PlayerbotAI::InitSpell(me, DRAIN_SOUL_1);
  24283. + DRAIN_LIFE = PlayerbotAI::InitSpell(me, DRAIN_LIFE_1);
  24284. + DRAIN_MANA = PlayerbotAI::InitSpell(me, DRAIN_MANA_1);
  24285. + LIFE_TAP = PlayerbotAI::InitSpell(me, LIFE_TAP_1);
  24286. + UNSTABLE_AFFLICTION = PlayerbotAI::InitSpell(me, UNSTABLE_AFFLICTION_1);
  24287. + HAUNT = PlayerbotAI::InitSpell(me, HAUNT_1);
  24288. + SEED_OF_CORRUPTION = PlayerbotAI::InitSpell(me, SEED_OF_CORRUPTION_1);
  24289. + DARK_PACT = PlayerbotAI::InitSpell(me, DARK_PACT_1);
  24290. + HOWL_OF_TERROR = PlayerbotAI::InitSpell(me, HOWL_OF_TERROR_1);
  24291. + FEAR = PlayerbotAI::InitSpell(me, FEAR_1);
  24292. + // DEMONOLOGY
  24293. + DEMON_SKIN = PlayerbotAI::InitSpell(me, DEMON_SKIN_1);
  24294. + DEMON_ARMOR = PlayerbotAI::InitSpell(me, DEMON_ARMOR_1);
  24295. + DEMONIC_EMPOWERMENT = PlayerbotAI::InitSpell(me, DEMONIC_EMPOWERMENT_1);
  24296. + FEL_ARMOR = PlayerbotAI::InitSpell(me, FEL_ARMOR_1);
  24297. + SHADOW_WARD = PlayerbotAI::InitSpell(me, SHADOW_WARD_1);
  24298. + SOULSHATTER = PlayerbotAI::InitSpell(me, SOULSHATTER_1);
  24299. + SOUL_LINK = PlayerbotAI::InitSpell(me, SOUL_LINK_1);
  24300. + SOUL_LINK_AURA = 25228; // dummy aura applied, after spell SOUL_LINK
  24301. + HEALTH_FUNNEL = PlayerbotAI::InitSpell(me, HEALTH_FUNNEL_1);
  24302. + DETECT_INVISIBILITY = PlayerbotAI::InitSpell(me, DETECT_INVISIBILITY_1);
  24303. + CREATE_FIRESTONE = PlayerbotAI::InitSpell(me, CREATE_FIRESTONE_1);
  24304. + CREATE_HEALTHSTONE = PlayerbotAI::InitSpell(me, CREATE_HEALTHSTONE_1);
  24305. + CREATE_SOULSTONE = PlayerbotAI::InitSpell(me, CREATE_SOULSTONE_1);
  24306. + CREATE_SPELLSTONE = PlayerbotAI::InitSpell(me, CREATE_SPELLSTONE_1);
  24307. + // demon summon
  24308. + SUMMON_IMP = PlayerbotAI::InitSpell(me, SUMMON_IMP_1);
  24309. + SUMMON_VOIDWALKER = PlayerbotAI::InitSpell(me, SUMMON_VOIDWALKER_1);
  24310. + SUMMON_SUCCUBUS = PlayerbotAI::InitSpell(me, SUMMON_SUCCUBUS_1);
  24311. + SUMMON_FELHUNTER = PlayerbotAI::InitSpell(me, SUMMON_FELHUNTER_1);
  24312. + SUMMON_FELGUARD = PlayerbotAI::InitSpell(me, SUMMON_FELGUARD_1);
  24313. + // demon skills should be initialized on demons
  24314. + BLOOD_PACT = 0; // imp skill
  24315. + CONSUME_SHADOWS = 0; // voidwalker skill
  24316. + FEL_INTELLIGENCE = 0; // felhunter skill
  24317. + // RANGED COMBAT
  24318. + SHOOT = PlayerbotAI::InitSpell(me, SHOOT_3);
  24319. +
  24320. + //RECENTLY_BANDAGED = 11196; // first aid check
  24321. +
  24322. + // racial
  24323. + ARCANE_TORRENT = PlayerbotAI::InitSpell(me, ARCANE_TORRENT_MANA_CLASSES); // blood elf
  24324. + ESCAPE_ARTIST = PlayerbotAI::InitSpell(me, ESCAPE_ARTIST_ALL); // gnome
  24325. + EVERY_MAN_FOR_HIMSELF = PlayerbotAI::InitSpell(me, EVERY_MAN_FOR_HIMSELF_ALL); // human
  24326. + BLOOD_FURY = PlayerbotAI::InitSpell(me, BLOOD_FURY_WARLOCK); // orc
  24327. + WILL_OF_THE_FORSAKEN = PlayerbotAI::InitSpell(me, WILL_OF_THE_FORSAKEN_ALL); // undead
  24328. +
  24329. + m_lastDemon = 0;
  24330. + m_demonOfChoice = DEMON_IMP;
  24331. + m_isTempImp = false;
  24332. +}
  24333. +
  24334. +PlayerbotWarlockAI::~PlayerbotWarlockAI() {}
  24335. +
  24336. +CombatManeuverReturns PlayerbotWarlockAI::DoFirstCombatManeuver(Unit* pTarget)
  24337. +{
  24338. + //// There are NPCs in BGs and Open World PvP, so don't filter this on PvP scenarios (of course if PvP targets anyone but tank, all bets are off anyway)
  24339. + //// Wait until the tank says so, until any non-tank gains aggro or X seconds - whichever is shortest
  24340. + //if (m_ai->GetCombatOrder() & PlayerbotAI::ORDERS_TEMP_WAIT_TANKAGGRO)
  24341. + //{
  24342. + // if (m_WaitUntil > m_ai->CurrentTime() && m_ai->GroupTankHoldsAggro())
  24343. + // {
  24344. + // return RETURN_NO_ACTION_OK; // wait it out
  24345. + // }
  24346. + // else
  24347. + // {
  24348. + // m_ai->ClearGroupCombatOrder(PlayerbotAI::ORDERS_TEMP_WAIT_TANKAGGRO);
  24349. + // }
  24350. + //}
  24351. +
  24352. + //if (m_ai->GetCombatOrder() & PlayerbotAI::ORDERS_TEMP_WAIT_OOC)
  24353. + //{
  24354. + // if (m_WaitUntil > m_ai->CurrentTime() && !m_ai->IsGroupInCombat())
  24355. + // return RETURN_NO_ACTION_OK; // wait it out
  24356. + // else
  24357. + // m_ai->ClearGroupCombatOrder(PlayerbotAI::ORDERS_TEMP_WAIT_OOC);
  24358. + //}
  24359. +
  24360. + //switch (m_ai->GetScenarioType())
  24361. + //{
  24362. + // case PlayerbotAI::SCENARIO_PVP_DUEL:
  24363. + // case PlayerbotAI::SCENARIO_PVP_BG:
  24364. + // case PlayerbotAI::SCENARIO_PVP_ARENA:
  24365. + // case PlayerbotAI::SCENARIO_PVP_OPENWORLD:
  24366. + // return DoFirstCombatManeuverPVP(pTarget);
  24367. + // case PlayerbotAI::SCENARIO_PVE:
  24368. + // case PlayerbotAI::SCENARIO_PVE_ELITE:
  24369. + // case PlayerbotAI::SCENARIO_PVE_RAID:
  24370. + // default:
  24371. + // return DoFirstCombatManeuverPVE(pTarget);
  24372. + // break;
  24373. + //}
  24374. +
  24375. + return RETURN_NO_ACTION_ERROR;
  24376. +}
  24377. +
  24378. +CombatManeuverReturns PlayerbotWarlockAI::DoFirstCombatManeuverPVE(Unit* /*pTarget*/)
  24379. +{
  24380. + return RETURN_NO_ACTION_OK;
  24381. +}
  24382. +
  24383. +CombatManeuverReturns PlayerbotWarlockAI::DoFirstCombatManeuverPVP(Unit* /*pTarget*/)
  24384. +{
  24385. + return RETURN_NO_ACTION_OK;
  24386. +}
  24387. +
  24388. +CombatManeuverReturns PlayerbotWarlockAI::DoNextCombatManeuver(Unit *pTarget)
  24389. +{
  24390. + //switch (m_ai->GetScenarioType())
  24391. + //{
  24392. + // case PlayerbotAI::SCENARIO_PVP_DUEL:
  24393. + // case PlayerbotAI::SCENARIO_PVP_BG:
  24394. + // case PlayerbotAI::SCENARIO_PVP_ARENA:
  24395. + // case PlayerbotAI::SCENARIO_PVP_OPENWORLD:
  24396. + // return DoNextCombatManeuverPVP(pTarget);
  24397. + // case PlayerbotAI::SCENARIO_PVE:
  24398. + // case PlayerbotAI::SCENARIO_PVE_ELITE:
  24399. + // case PlayerbotAI::SCENARIO_PVE_RAID:
  24400. + // default:
  24401. + // return DoNextCombatManeuverPVE(pTarget);
  24402. + // break;
  24403. + //}
  24404. +
  24405. + return RETURN_NO_ACTION_ERROR;
  24406. +}
  24407. +
  24408. +CombatManeuverReturns PlayerbotWarlockAI::DoNextCombatManeuverPVE(Unit *pTarget)
  24409. +{
  24410. + //if (!m_ai) return RETURN_NO_ACTION_ERROR;
  24411. + //if (!m_bot) return RETURN_NO_ACTION_ERROR;
  24412. +
  24413. + ////Unit* pVictim = pTarget->getVictim();
  24414. + //float dist = pTarget->GetCombatReach();
  24415. + //Pet *pet = m_bot->GetPet();
  24416. + //uint32 spec = m_bot->GetSpec();
  24417. + //uint8 shardCount = m_bot->GetItemCount(SOUL_SHARD, false, NULL);
  24418. +
  24419. + ////If we have UA it will replace immolate in our rotation
  24420. + //uint32 FIRE = (UNSTABLE_AFFLICTION > 0 ? UNSTABLE_AFFLICTION : IMMOLATE);
  24421. +
  24422. + //// Voidwalker is near death - sacrifice it for a shield
  24423. + //if (pet && pet->GetEntry() == DEMON_VOIDWALKER && SACRIFICE && !m_bot->HasAura(SACRIFICE) && pet->GetHealthPct() < 10)
  24424. + // m_ai->CastPetSpell(SACRIFICE);
  24425. +
  24426. + //// Use healthstone
  24427. + //if (m_bot->GetHealthPct() < 30)
  24428. + //{
  24429. + // Item* healthStone = m_ai->FindConsumable(HEALTHSTONE_DISPLAYID);
  24430. + // if (healthStone)
  24431. + // m_ai->UseItem(healthStone);
  24432. + //}
  24433. +
  24434. + //// Voidwalker sacrifice gives shield - but you lose the pet (and it's DPS/tank) - use only as last resort for your own health!
  24435. + //if (m_bot->GetHealthPct() < 20 && pet && pet->GetEntry() == DEMON_VOIDWALKER && SACRIFICE && !m_bot->HasAura(SACRIFICE))
  24436. + // m_ai->CastPetSpell(SACRIFICE);
  24437. +
  24438. + //if (m_ai->GetCombatStyle() != PlayerbotAI::COMBAT_RANGED && dist > ATTACK_DISTANCE)
  24439. + // m_ai->SetCombatStyle(PlayerbotAI::COMBAT_RANGED);
  24440. + //// if in melee range OR can't shoot OR have no ranged (wand) equipped
  24441. + //else if(m_ai->GetCombatStyle() != PlayerbotAI::COMBAT_MELEE && (dist <= ATTACK_DISTANCE || SHOOT == 0 || !m_bot->GetWeaponForAttack(RANGED_ATTACK, true)))
  24442. + // m_ai->SetCombatStyle(PlayerbotAI::COMBAT_MELEE);
  24443. +
  24444. + ////Used to determine if this bot is highest on threat
  24445. + //Unit *newTarget = m_ai->FindAttacker((PlayerbotAI::ATTACKERINFOTYPE) (PlayerbotAI::AIT_VICTIMSELF | PlayerbotAI::AIT_HIGHESTTHREAT), m_bot);
  24446. + //if (newTarget) // TODO: && party has a tank
  24447. + //{
  24448. + // if (SOULSHATTER > 0 && shardCount > 0 && !m_bot->HasSpellCooldown(SOULSHATTER))
  24449. + // if (CastSpell(SOULSHATTER, m_bot))
  24450. + // return RETURN_CONTINUE;
  24451. +
  24452. + // // Have threat, can't quickly lower it. 3 options remain: Stop attacking, lowlevel damage (wand), keep on keeping on.
  24453. + // if (newTarget->GetHealthPct() > 25)
  24454. + // {
  24455. + // // If elite, do nothing and pray tank gets aggro off you
  24456. + // // TODO: Is there an IsElite function? If so, find it and insert.
  24457. + // //if (newTarget->IsElite())
  24458. + // // return;
  24459. +
  24460. + // // Not an elite. You could insert FEAR here but in any PvE situation that's 90-95% likely
  24461. + // // to worsen the situation for the group. ... So please don't.
  24462. + // return CastSpell(SHOOT, pTarget);
  24463. + // }
  24464. + //}
  24465. +
  24466. + //// Damage Spells
  24467. + //switch (spec)
  24468. + //{
  24469. + // case WARLOCK_SPEC_AFFLICTION:
  24470. + // if (CURSE_OF_AGONY && !pTarget->HasAura(CURSE_OF_AGONY) && CastSpell(CURSE_OF_AGONY, pTarget))
  24471. + // return RETURN_CONTINUE;
  24472. + // if (CORRUPTION && !pTarget->HasAura(CORRUPTION) && CastSpell(CORRUPTION, pTarget))
  24473. + // return RETURN_CONTINUE;
  24474. + // if (FIRE && !pTarget->HasAura(FIRE) && CastSpell(FIRE, pTarget))
  24475. + // return RETURN_CONTINUE;
  24476. + // if (HAUNT && !m_bot->HasSpellCooldown(HAUNT) && CastSpell(HAUNT, pTarget))
  24477. + // return RETURN_CONTINUE;
  24478. + // if (SHADOW_BOLT && CastSpell(SHADOW_BOLT, pTarget))
  24479. + // return RETURN_CONTINUE;
  24480. +
  24481. + // return RETURN_NO_ACTION_OK;
  24482. +
  24483. + // case WARLOCK_SPEC_DEMONOLOGY:
  24484. + // if (pet && DEMONIC_EMPOWERMENT && !m_bot->HasSpellCooldown(DEMONIC_EMPOWERMENT) && CastSpell(DEMONIC_EMPOWERMENT))
  24485. + // return RETURN_CONTINUE;
  24486. + // if (CURSE_OF_AGONY && !pTarget->HasAura(CURSE_OF_AGONY) && CastSpell(CURSE_OF_AGONY, pTarget))
  24487. + // return RETURN_CONTINUE;
  24488. + // if (CORRUPTION && !pTarget->HasAura(CORRUPTION) && CastSpell(CORRUPTION, pTarget))
  24489. + // return RETURN_CONTINUE;
  24490. + // if (FIRE && !pTarget->HasAura(FIRE) && CastSpell(FIRE, pTarget))
  24491. + // return RETURN_CONTINUE;
  24492. + // if (INCINERATE && pTarget->HasAura(FIRE) && CastSpell(INCINERATE, pTarget))
  24493. + // return RETURN_CONTINUE;
  24494. + // if (SHADOW_BOLT && CastSpell(SHADOW_BOLT, pTarget))
  24495. + // return RETURN_CONTINUE;
  24496. +
  24497. + // return RETURN_NO_ACTION_OK;
  24498. +
  24499. + // case WARLOCK_SPEC_DESTRUCTION:
  24500. + // if (CURSE_OF_AGONY && !pTarget->HasAura(CURSE_OF_AGONY) && CastSpell(CURSE_OF_AGONY, pTarget))
  24501. + // return RETURN_CONTINUE;
  24502. + // if (CORRUPTION && !pTarget->HasAura(CORRUPTION) && CastSpell(CORRUPTION, pTarget))
  24503. + // return RETURN_CONTINUE;
  24504. + // if (FIRE && !pTarget->HasAura(FIRE) && CastSpell(FIRE, pTarget))
  24505. + // return RETURN_CONTINUE;
  24506. + // if (CONFLAGRATE && pTarget->HasAura(FIRE) && !m_bot->HasSpellCooldown(CONFLAGRATE) && CastSpell(CONFLAGRATE, pTarget))
  24507. + // return RETURN_CONTINUE;
  24508. + // if (CHAOS_BOLT && !m_bot->HasSpellCooldown(CHAOS_BOLT) && CastSpell(CHAOS_BOLT, pTarget))
  24509. + // return RETURN_CONTINUE;
  24510. + // if (INCINERATE && pTarget->HasAura(FIRE) && CastSpell(INCINERATE, pTarget))
  24511. + // return RETURN_CONTINUE;
  24512. + // if (SHADOW_BOLT && CastSpell(SHADOW_BOLT, pTarget))
  24513. + // return RETURN_CONTINUE;
  24514. +
  24515. + // return RETURN_NO_ACTION_OK;
  24516. +
  24517. + // //if (LIFE_TAP && LastSpellAffliction < 1 && (m_bot->GetPower(POWER_MANA)*100/m_bot->GetMaxPower(POWER_MANA)) <= 50 && m_bot->GetHealthPct() > 50)
  24518. + // // m_ai->CastSpell(LIFE_TAP, *m_bot);
  24519. + // //else if (DRAIN_SOUL && pTarget->GetHealth() < pTarget->GetMaxHealth() * 0.40 && !pTarget->HasAura(DRAIN_SOUL) && LastSpellAffliction < 3)
  24520. + // // m_ai->CastSpell(DRAIN_SOUL, *pTarget);
  24521. + // // ////m_ai->SetIgnoreUpdateTime(15);
  24522. + // //else if (DRAIN_LIFE && LastSpellAffliction < 4 && !pTarget->HasAura(DRAIN_SOUL) && !pTarget->HasAura(SEED_OF_CORRUPTION) && !pTarget->HasAura(DRAIN_LIFE) && !pTarget->HasAura(DRAIN_MANA) && m_bot->GetHealthPct() <= 70)
  24523. + // // m_ai->CastSpell(DRAIN_LIFE, *pTarget);
  24524. + // // ////m_ai->SetIgnoreUpdateTime(5);
  24525. + // //else if (SEED_OF_CORRUPTION && !pTarget->HasAura(SEED_OF_CORRUPTION) && LastSpellAffliction < 7)
  24526. + // // m_ai->CastSpell(SEED_OF_CORRUPTION, *pTarget);
  24527. + // //else if (HOWL_OF_TERROR && !pTarget->HasAura(HOWL_OF_TERROR) && m_ai->GetAttackerCount() > 3 && LastSpellAffliction < 8)
  24528. + // // m_ai->CastSpell(HOWL_OF_TERROR, *pTarget);
  24529. + // // m_ai->TellMaster("casting howl of terror!");
  24530. + // //else if (FEAR && !pTarget->HasAura(FEAR) && pVictim == m_bot && m_ai->GetAttackerCount() >= 2 && LastSpellAffliction < 9)
  24531. + // // m_ai->CastSpell(FEAR, *pTarget);
  24532. + // // //m_ai->TellMaster("casting fear!");
  24533. + // // ////m_ai->SetIgnoreUpdateTime(1.5);
  24534. + // //else if ((pet) && (DARK_PACT > 0 && (m_bot->GetPower(POWER_MANA)*100/m_bot->GetMaxPower(POWER_MANA)) <= 50 && LastSpellAffliction < 10 && pet->GetPower(POWER_MANA) > 0))
  24535. + // // m_ai->CastSpell(DARK_PACT, *m_bot);
  24536. + // //if (SHADOWFURY && LastSpellDestruction < 1 && !pTarget->HasAura(SHADOWFURY))
  24537. + // // m_ai->CastSpell(SHADOWFURY, *pTarget);
  24538. + // //else if (RAIN_OF_FIRE && LastSpellDestruction < 3 && m_ai->GetAttackerCount() >= 3)
  24539. + // // m_ai->CastSpell(RAIN_OF_FIRE, *pTarget);
  24540. + // // //m_ai->TellMaster("casting rain of fire!");
  24541. + // // ////m_ai->SetIgnoreUpdateTime(8);
  24542. + // //else if (SHADOWFLAME && !pTarget->HasAura(SHADOWFLAME) && LastSpellDestruction < 4)
  24543. + // // m_ai->CastSpell(SHADOWFLAME, *pTarget);
  24544. + // //else if (SEARING_PAIN && LastSpellDestruction < 8)
  24545. + // // m_ai->CastSpell(SEARING_PAIN, *pTarget);
  24546. + // //else if (SOUL_FIRE && LastSpellDestruction < 9)
  24547. + // // m_ai->CastSpell(SOUL_FIRE, *pTarget);
  24548. + // // ////m_ai->SetIgnoreUpdateTime(6);
  24549. + // //else if (SHADOWBURN && LastSpellDestruction < 11 && pTarget->GetHealth() < pTarget->GetMaxHealth() * 0.20 && !pTarget->HasAura(SHADOWBURN))
  24550. + // // m_ai->CastSpell(SHADOWBURN, *pTarget);
  24551. + // //else if (HELLFIRE && LastSpellDestruction < 12 && !m_bot->HasAura(HELLFIRE) && m_ai->GetAttackerCount() >= 5 && m_bot->GetHealthPct() >= 50)
  24552. + // // m_ai->CastSpell(HELLFIRE);
  24553. + // // m_ai->TellMaster("casting hellfire!");
  24554. + // // ////m_ai->SetIgnoreUpdateTime(15);
  24555. + // //else if (CURSE_OF_THE_ELEMENTS && !pTarget->HasAura(CURSE_OF_THE_ELEMENTS) && !pTarget->HasAura(SHADOWFLAME) && !pTarget->HasAura(CURSE_OF_AGONY) && !pTarget->HasAura(CURSE_OF_WEAKNESS) && LastSpellCurse < 2)
  24556. + // // m_ai->CastSpell(CURSE_OF_THE_ELEMENTS, *pTarget);
  24557. + // //else if (CURSE_OF_WEAKNESS && !pTarget->HasAura(CURSE_OF_WEAKNESS) && !pTarget->HasAura(SHADOWFLAME) && !pTarget->HasAura(CURSE_OF_AGONY) && !pTarget->HasAura(CURSE_OF_THE_ELEMENTS) && LastSpellCurse < 3)
  24558. + // // m_ai->CastSpell(CURSE_OF_WEAKNESS, *pTarget);
  24559. + // //else if (CURSE_OF_TONGUES && !pTarget->HasAura(CURSE_OF_TONGUES) && !pTarget->HasAura(SHADOWFLAME) && !pTarget->HasAura(CURSE_OF_WEAKNESS) && !pTarget->HasAura(CURSE_OF_AGONY) && !pTarget->HasAura(CURSE_OF_THE_ELEMENTS) && LastSpellCurse < 4)
  24560. + // // m_ai->CastSpell(CURSE_OF_TONGUES, *pTarget);
  24561. + //}
  24562. +
  24563. + //// No spec due to low level OR no spell found yet
  24564. + //if (CORRUPTION && !pTarget->HasAura(CORRUPTION) && CastSpell(CORRUPTION, pTarget))
  24565. + // return RETURN_CONTINUE;
  24566. + //if (FIRE && !pTarget->HasAura(FIRE) && CastSpell(FIRE, pTarget))
  24567. + // return RETURN_CONTINUE;
  24568. + //if (SHADOW_BOLT)
  24569. + // return CastSpell(SHADOW_BOLT, pTarget);
  24570. +
  24571. + return RETURN_NO_ACTION_OK;
  24572. +} // end DoNextCombatManeuver
  24573. +
  24574. +CombatManeuverReturns PlayerbotWarlockAI::DoNextCombatManeuverPVP(Unit* pTarget)
  24575. +{
  24576. + //if (m_ai->CastSpell(FEAR, *pTarget))
  24577. + // return RETURN_CONTINUE;
  24578. + //if (m_ai->CastSpell(SHADOW_BOLT))
  24579. + // return RETURN_CONTINUE;
  24580. +
  24581. + return DoNextCombatManeuverPVE(pTarget); // TODO: bad idea perhaps, but better than the alternative
  24582. +}
  24583. +
  24584. +void PlayerbotWarlockAI::CheckDemon()
  24585. +{
  24586. + //uint32 spec = m_bot->GetSpec();
  24587. + //uint8 shardCount = m_bot->GetItemCount(SOUL_SHARD, false, NULL);
  24588. + //Pet *pet = m_bot->GetPet();
  24589. +
  24590. + ////Assign demon of choice
  24591. + //if (spec == WARLOCK_SPEC_AFFLICTION)
  24592. + // m_demonOfChoice = DEMON_FELHUNTER;
  24593. + //else if (spec == WARLOCK_SPEC_DEMONOLOGY)
  24594. + // m_demonOfChoice = (DEMON_FELGUARD > 0 ? DEMON_FELGUARD : DEMON_SUCCUBUS);
  24595. + //else if (spec == WARLOCK_SPEC_DESTRUCTION)
  24596. + // m_demonOfChoice = DEMON_IMP;
  24597. +
  24598. + //// Summon demon
  24599. + //if (!pet || m_isTempImp || pet->GetEntry() != m_demonOfChoice)
  24600. + //{
  24601. + // uint32 summonSpellId;
  24602. + // if (m_demonOfChoice != DEMON_IMP && shardCount > 0)
  24603. + // {
  24604. + // switch (m_demonOfChoice)
  24605. + // {
  24606. + // case DEMON_VOIDWALKER:
  24607. + // summonSpellId = SUMMON_VOIDWALKER;
  24608. + // break;
  24609. +
  24610. + // case DEMON_FELGUARD:
  24611. + // summonSpellId = SUMMON_FELGUARD;
  24612. + // break;
  24613. +
  24614. + // case DEMON_FELHUNTER:
  24615. + // summonSpellId = SUMMON_FELHUNTER;
  24616. + // break;
  24617. +
  24618. + // case DEMON_SUCCUBUS:
  24619. + // summonSpellId = SUMMON_SUCCUBUS;
  24620. + // break;
  24621. +
  24622. + // default:
  24623. + // summonSpellId = 0;
  24624. + // }
  24625. +
  24626. + // if (m_ai->CastSpell(summonSpellId))
  24627. + // {
  24628. + // //m_ai->TellMaster("Summoning favorite demon...");
  24629. + // m_isTempImp = false;
  24630. + // return;
  24631. + // }
  24632. + // }
  24633. + // else if (!pet && SUMMON_IMP && m_ai->CastSpell(SUMMON_IMP))
  24634. + // {
  24635. + // if (m_demonOfChoice != DEMON_IMP)
  24636. + // m_isTempImp = true;
  24637. +
  24638. + // //m_ai->TellMaster("Summoning Imp...");
  24639. + // return;
  24640. + // }
  24641. + //}
  24642. +}
  24643. +
  24644. +void PlayerbotWarlockAI::DoNonCombatActions()
  24645. +{
  24646. + //if (!m_ai) return;
  24647. + //if (!m_bot) return;
  24648. +
  24649. + ////uint32 spec = m_bot->GetSpec();
  24650. + //Pet *pet = m_bot->GetPet();
  24651. +
  24652. + //// Initialize pet spells
  24653. + //if (pet && pet->GetEntry() != m_lastDemon)
  24654. + //{
  24655. + // switch (pet->GetEntry())
  24656. + // {
  24657. + // case DEMON_IMP:
  24658. + // BLOOD_PACT = m_ai->initPetSpell(BLOOD_PACT_ICON);
  24659. + // FIREBOLT = m_ai->initPetSpell(FIREBOLT_ICON);
  24660. + // FIRE_SHIELD = m_ai->initPetSpell(FIRE_SHIELD_ICON);
  24661. + // break;
  24662. +
  24663. + // case DEMON_VOIDWALKER:
  24664. + // CONSUME_SHADOWS = m_ai->initPetSpell(CONSUME_SHADOWS_ICON);
  24665. + // SACRIFICE = m_ai->initPetSpell(SACRIFICE_ICON);
  24666. + // SUFFERING = m_ai->initPetSpell(SUFFERING_ICON);
  24667. + // TORMENT = m_ai->initPetSpell(TORMENT_ICON);
  24668. + // break;
  24669. +
  24670. + // case DEMON_SUCCUBUS:
  24671. + // LASH_OF_PAIN = m_ai->initPetSpell(LASH_OF_PAIN_ICON);
  24672. + // SEDUCTION = m_ai->initPetSpell(SEDUCTION_ICON);
  24673. + // SOOTHING_KISS = m_ai->initPetSpell(SOOTHING_KISS_ICON);
  24674. + // break;
  24675. +
  24676. + // case DEMON_FELHUNTER:
  24677. + // DEVOUR_MAGIC = m_ai->initPetSpell(DEVOUR_MAGIC_ICON);
  24678. + // FEL_INTELLIGENCE = m_ai->initPetSpell(FEL_INTELLIGENCE_ICON);
  24679. + // SHADOW_BITE = m_ai->initPetSpell(SHADOW_BITE_ICON);
  24680. + // SPELL_LOCK = m_ai->initPetSpell(SPELL_LOCK_ICON);
  24681. + // break;
  24682. +
  24683. + // case DEMON_FELGUARD:
  24684. + // ANGUISH = m_ai->initPetSpell(ANGUISH_ICON);
  24685. + // CLEAVE = m_ai->initPetSpell(CLEAVE_ICON);
  24686. + // INTERCEPT = m_ai->initPetSpell(INTERCEPT_ICON);
  24687. + // break;
  24688. + // }
  24689. +
  24690. + // m_lastDemon = pet->GetEntry();
  24691. +
  24692. + // //if (!m_isTempImp)
  24693. + // // m_demonOfChoice = pet->GetEntry();
  24694. + //}
  24695. +
  24696. + //// Destroy extra soul shards
  24697. + //uint8 shardCount = m_bot->GetItemCount(SOUL_SHARD, false, NULL);
  24698. + //uint8 freeSpace = m_ai->GetFreeBagSpace();
  24699. + //if (shardCount > MAX_SHARD_COUNT || (freeSpace == 0 && shardCount > 1))
  24700. + // m_bot->DestroyItemCount(SOUL_SHARD, shardCount > MAX_SHARD_COUNT ? shardCount - MAX_SHARD_COUNT : 1, true, false);
  24701. +
  24702. + //// buff myself DEMON_SKIN, DEMON_ARMOR, FEL_ARMOR - Strongest one available is chosen
  24703. + //if (FEL_ARMOR)
  24704. + //{
  24705. + // if (m_ai->SelfBuff(FEL_ARMOR))
  24706. + // return;
  24707. + //}
  24708. + //else if (DEMON_ARMOR)
  24709. + //{
  24710. + // if (m_ai->SelfBuff(DEMON_ARMOR))
  24711. + // return;
  24712. + //}
  24713. + //else if (DEMON_SKIN)
  24714. + // if (m_ai->SelfBuff(DEMON_SKIN))
  24715. + // return;
  24716. +
  24717. + //// healthstone creation
  24718. + //if (CREATE_HEALTHSTONE && shardCount > 0)
  24719. + //{
  24720. + // Item* const healthStone = m_ai->FindConsumable(HEALTHSTONE_DISPLAYID);
  24721. + // if (!healthStone && m_ai->CastSpell(CREATE_HEALTHSTONE))
  24722. + // return;
  24723. + //}
  24724. +
  24725. + //// soulstone creation and use
  24726. + //if (CREATE_SOULSTONE)
  24727. + //{
  24728. + // Item* soulStone = m_ai->FindConsumable(SOULSTONE_DISPLAYID);
  24729. + // if (!soulStone)
  24730. + // {
  24731. + // if (shardCount > 0 && !m_bot->HasSpellCooldown(CREATE_SOULSTONE) && m_ai->CastSpell(CREATE_SOULSTONE))
  24732. + // return;
  24733. + // }
  24734. + // else
  24735. + // {
  24736. + // uint32 soulStoneSpell = soulStone->GetTemplate()->Spells[0].SpellId;
  24737. + // Player* master = GetMaster();
  24738. + // if (!master->HasAura(soulStoneSpell) && !m_bot->HasSpellCooldown(soulStoneSpell))
  24739. + // {
  24740. + // m_ai->UseItem(soulStone, master);
  24741. + // return;
  24742. + // }
  24743. + // }
  24744. + //}
  24745. +
  24746. + //// Spellstone creation and use (Spellstone dominates firestone completely as I understand it)
  24747. + //Item* const weapon = m_bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND);
  24748. + //if (weapon && weapon->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT) == 0)
  24749. + //{
  24750. + // Item* const stone = m_ai->FindConsumable(SPELLSTONE_DISPLAYID);
  24751. + // Item* const stone2 = m_ai->FindConsumable(FIRESTONE_DISPLAYID);
  24752. + // if (!stone && !stone2)
  24753. + // {
  24754. + // if (CREATE_SPELLSTONE && shardCount > 0 && m_ai->CastSpell(CREATE_SPELLSTONE))
  24755. + // return;
  24756. + // else if (CREATE_SPELLSTONE == 0 && CREATE_FIRESTONE > 0 && shardCount > 0 && m_ai->CastSpell(CREATE_FIRESTONE))
  24757. + // return;
  24758. + // }
  24759. + // else if (stone)
  24760. + // {
  24761. + // m_ai->UseItem(stone, EQUIPMENT_SLOT_MAINHAND);
  24762. + // return;
  24763. + // }
  24764. + // else
  24765. + // {
  24766. + // m_ai->UseItem(stone2, EQUIPMENT_SLOT_MAINHAND);
  24767. + // return;
  24768. + // }
  24769. + //}
  24770. +
  24771. + //if (m_bot->getStandState() != UNIT_STAND_STATE_STAND)
  24772. + // m_bot->SetStandState(UNIT_STAND_STATE_STAND);
  24773. +
  24774. + //// hp/mana check
  24775. + //if (pet && DARK_PACT && pet->GetPower(POWER_MANA) > 0 && (m_bot->GetPower(POWER_MANA)*100/m_bot->GetMaxPower(POWER_MANA)) <= 50)
  24776. + // if (m_ai->CastSpell(DARK_PACT, *m_bot))
  24777. + // return;
  24778. +
  24779. + //if (LIFE_TAP && (m_bot->GetPower(POWER_MANA)*100/m_bot->GetMaxPower(POWER_MANA)) <= 50 && m_bot->GetHealthPct() > 50)
  24780. + // if (m_ai->CastSpell(LIFE_TAP, *m_bot))
  24781. + // return;
  24782. +
  24783. + //if (EatDrinkBandage())
  24784. + // return;
  24785. +
  24786. + ////Heal Voidwalker
  24787. + //if (pet && pet->GetEntry() == DEMON_VOIDWALKER && CONSUME_SHADOWS && pet->GetHealthPct() < 75 && !pet->HasAura(CONSUME_SHADOWS))
  24788. + // m_ai->CastPetSpell(CONSUME_SHADOWS);
  24789. +
  24790. + //CheckDemon();
  24791. +
  24792. + //// Soul link demon
  24793. + //if (pet && SOUL_LINK && !m_bot->HasAura(SOUL_LINK_AURA) && m_ai->CastSpell(SOUL_LINK, *m_bot))
  24794. + // return;
  24795. +
  24796. + //// Check demon buffs
  24797. + //if (pet && pet->GetEntry() == DEMON_IMP && BLOOD_PACT && !m_bot->HasAura(BLOOD_PACT) && m_ai->CastPetSpell(BLOOD_PACT))
  24798. + // return;
  24799. +
  24800. + //if (pet && pet->GetEntry() == DEMON_FELHUNTER && FEL_INTELLIGENCE && !m_bot->HasAura(FEL_INTELLIGENCE) && m_ai->CastPetSpell(FEL_INTELLIGENCE))
  24801. + // return;
  24802. +} // end DoNonCombatActions
  24803. diff --git a/src/server/game/AI/PlayerBots/bp_warl_ai.h b/src/server/game/AI/PlayerBots/bp_warl_ai.h
  24804. new file mode 100644
  24805. index 0000000..e4561df
  24806. --- /dev/null
  24807. +++ b/src/server/game/AI/PlayerBots/bp_warl_ai.h
  24808. @@ -0,0 +1,254 @@
  24809. +#ifndef _PlayerbotWarlockAI_H
  24810. +#define _PlayerbotWarlockAI_H
  24811. +
  24812. +#include "bp_cai.h"
  24813. +
  24814. +#define SOUL_SHARD 6265
  24815. +#define MAX_SHARD_COUNT 4 // Maximum soul shard count bot should keep
  24816. +
  24817. +enum
  24818. +{
  24819. + SPELL_CURSES,
  24820. + SPELL_AFFLICTION,
  24821. + SPELL_DESTRUCTION,
  24822. + SPELL_DEMONOLOGY
  24823. +};
  24824. +
  24825. +enum StoneDisplayId
  24826. +{
  24827. + FIRESTONE_DISPLAYID = 7409,
  24828. + SPELLSTONE_DISPLAYID = 13291,
  24829. + SOULSTONE_DISPLAYID = 6009,
  24830. + HEALTHSTONE_DISPLAYID = 8026
  24831. +};
  24832. +
  24833. +enum DemonEntry
  24834. +{
  24835. + DEMON_IMP = 416,
  24836. + DEMON_VOIDWALKER = 1860,
  24837. + DEMON_SUCCUBUS = 1863,
  24838. + DEMON_FELHUNTER = 417,
  24839. + DEMON_FELGUARD = 17252
  24840. +};
  24841. +
  24842. +enum DemonSpellIconIds
  24843. +{
  24844. + // Imp
  24845. + BLOOD_PACT_ICON = 541,
  24846. + FIREBOLT_ICON = 18,
  24847. + FIRE_SHIELD_ICON = 16,
  24848. + // Felguard
  24849. + ANGUISH_ICON = 173,
  24850. + CLEAVE_ICON = 277,
  24851. + INTERCEPT_ICON = 516,
  24852. + // Felhunter
  24853. + DEVOUR_MAGIC_ICON = 47,
  24854. + FEL_INTELLIGENCE_ICON = 1940,
  24855. + SHADOW_BITE_ICON = 2027,
  24856. + SPELL_LOCK_ICON = 77,
  24857. + // Succubus
  24858. + LASH_OF_PAIN_ICON = 596,
  24859. + SEDUCTION_ICON = 48,
  24860. + SOOTHING_KISS_ICON = 694,
  24861. + // Voidwalker
  24862. + CONSUME_SHADOWS_ICON = 207,
  24863. + SACRIFICE_ICON = 693,
  24864. + SUFFERING_ICON = 9,
  24865. + TORMENT_ICON = 173
  24866. +};
  24867. +
  24868. +enum WarlockSpells
  24869. +{
  24870. + BANISH_1 = 710,
  24871. + CHALLENGING_HOWL_1 = 59671,
  24872. + CHAOS_BOLT_1 = 50796,
  24873. + CONFLAGRATE_1 = 17962,
  24874. + CORRUPTION_1 = 172,
  24875. + CREATE_FIRESTONE_1 = 6366,
  24876. + CREATE_HEALTHSTONE_1 = 6201,
  24877. + CREATE_SOULSTONE_1 = 693,
  24878. + CREATE_SPELLSTONE_1 = 2362,
  24879. + CURSE_OF_AGONY_1 = 980,
  24880. + CURSE_OF_DOOM_1 = 603,
  24881. + CURSE_OF_EXHAUSTION_1 = 18223,
  24882. + CURSE_OF_THE_ELEMENTS_1 = 1490,
  24883. + CURSE_OF_TONGUES_1 = 1714,
  24884. + CURSE_OF_WEAKNESS_1 = 702,
  24885. + DARK_PACT_1 = 18220,
  24886. + DEATH_COIL_WARLOCK_1 = 6789,
  24887. + DEMON_ARMOR_1 = 706,
  24888. + DEMON_CHARGE_1 = 54785,
  24889. + DEMON_SKIN_1 = 687,
  24890. + DEMONIC_CIRCLE_SUMMON_1 = 48018,
  24891. + DEMONIC_CIRCLE_TELEPORT_1 = 48020,
  24892. + DEMONIC_EMPOWERMENT_1 = 47193,
  24893. + DEMONIC_IMMOLATE_1 = 75445,
  24894. + DETECT_INVISIBILITY_1 = 132,
  24895. + DRAIN_LIFE_1 = 689,
  24896. + DRAIN_MANA_1 = 5138,
  24897. + DRAIN_SOUL_1 = 1120,
  24898. + ENSLAVE_DEMON_1 = 1098,
  24899. + EYE_OF_KILROGG_1 = 126,
  24900. + FEAR_1 = 5782,
  24901. + FEL_ARMOR_1 = 28176,
  24902. + FEL_DOMINATION_1 = 18708,
  24903. + HAUNT_1 = 48181,
  24904. + HEALTH_FUNNEL_1 = 755,
  24905. + HELLFIRE_1 = 1949,
  24906. + HOWL_OF_TERROR_1 = 5484,
  24907. + IMMOLATE_1 = 348,
  24908. + IMMOLATION_AURA_1 = 50589,
  24909. + INCINERATE_1 = 29722,
  24910. + INFERNO_1 = 1122,
  24911. + LIFE_TAP_1 = 1454,
  24912. + METAMORPHOSIS_1 = 59672,
  24913. + RAIN_OF_FIRE_1 = 5740,
  24914. + RITUAL_OF_DOOM_1 = 18540,
  24915. + RITUAL_OF_SOULS_1 = 29893,
  24916. + RITUAL_OF_SUMMONING_1 = 698,
  24917. + SEARING_PAIN_1 = 5676,
  24918. + SEED_OF_CORRUPTION_1 = 27243,
  24919. + SENSE_DEMONS_1 = 5500,
  24920. + SHADOW_BOLT_1 = 686,
  24921. + SHADOW_CLEAVE_1 = 50581,
  24922. + SHADOW_WARD_1 = 6229,
  24923. + SHADOWBURN_1 = 17877,
  24924. + SHADOWFLAME_1 = 47897,
  24925. + SHADOWFURY_1 = 30283,
  24926. + SHOOT_3 = 5019,
  24927. + SOUL_FIRE_1 = 6353,
  24928. + SOUL_LINK_1 = 19028,
  24929. + SOULSHATTER_1 = 29858,
  24930. + SUMMON_FELGUARD_1 = 30146,
  24931. + SUMMON_FELHUNTER_1 = 691,
  24932. + SUMMON_IMP_1 = 688,
  24933. + SUMMON_SUCCUBUS_1 = 712,
  24934. + SUMMON_VOIDWALKER_1 = 697,
  24935. + UNENDING_BREATH_1 = 5697,
  24936. + UNSTABLE_AFFLICTION_1 = 30108
  24937. +};
  24938. +
  24939. +//class Player;
  24940. +class PlayerbotWarlockAI : PlayerbotClassAI
  24941. +{
  24942. +public:
  24943. + PlayerbotWarlockAI(Player * const master, Player * const bot, PlayerbotAI * const ai);
  24944. + virtual ~PlayerbotWarlockAI();
  24945. +
  24946. + // all combat actions go here
  24947. + CombatManeuverReturns DoFirstCombatManeuver(Unit* pTarget);
  24948. + CombatManeuverReturns DoNextCombatManeuver(Unit* pTarget);
  24949. +
  24950. + // all non combat actions go here, ex buffs, heals, rezzes
  24951. + void DoNonCombatActions();
  24952. +
  24953. + // buff a specific player, usually a real PC who is not in group
  24954. + //void BuffPlayer(Player *target);
  24955. +
  24956. +private:
  24957. + CombatManeuverReturns DoFirstCombatManeuverPVE(Unit* pTarget);
  24958. + CombatManeuverReturns DoNextCombatManeuverPVE(Unit* pTarget);
  24959. + CombatManeuverReturns DoFirstCombatManeuverPVP(Unit* pTarget);
  24960. + CombatManeuverReturns DoNextCombatManeuverPVP(Unit* pTarget);
  24961. +
  24962. + CombatManeuverReturns CastSpell(uint32 nextAction, Unit *pTarget = NULL) { return CastSpellWand(nextAction, pTarget, SHOOT); }
  24963. +
  24964. + void CheckDemon();
  24965. +
  24966. + // CURSES
  24967. + uint32 CURSE_OF_WEAKNESS,
  24968. + CURSE_OF_AGONY,
  24969. + CURSE_OF_EXHAUSTION,
  24970. + CURSE_OF_TONGUES,
  24971. + CURSE_OF_THE_ELEMENTS,
  24972. + CURSE_OF_DOOM;
  24973. + // ranged
  24974. + uint32 SHOOT;
  24975. +
  24976. + // AFFLICTION
  24977. + uint32 CORRUPTION,
  24978. + DRAIN_SOUL,
  24979. + DRAIN_LIFE,
  24980. + DRAIN_MANA,
  24981. + LIFE_TAP,
  24982. + UNSTABLE_AFFLICTION,
  24983. + HAUNT,
  24984. + SEED_OF_CORRUPTION,
  24985. + DARK_PACT,
  24986. + HOWL_OF_TERROR,
  24987. + FEAR;
  24988. +
  24989. + // DESTRUCTION
  24990. + uint32 SHADOW_BOLT,
  24991. + IMMOLATE,
  24992. + INCINERATE,
  24993. + SEARING_PAIN,
  24994. + CONFLAGRATE,
  24995. + SOUL_FIRE,
  24996. + SHADOWFURY,
  24997. + CHAOS_BOLT,
  24998. + SHADOWFLAME,
  24999. + HELLFIRE,
  25000. + RAIN_OF_FIRE,
  25001. + SHADOWBURN;
  25002. +
  25003. + // DEMONOLOGY
  25004. + uint32 DEMON_SKIN,
  25005. + DEMON_ARMOR,
  25006. + DEMONIC_EMPOWERMENT,
  25007. + SHADOW_WARD,
  25008. + FEL_ARMOR,
  25009. + SOULSHATTER,
  25010. + SOUL_LINK,
  25011. + SOUL_LINK_AURA,
  25012. + HEALTH_FUNNEL,
  25013. + DETECT_INVISIBILITY,
  25014. + CREATE_FIRESTONE,
  25015. + CREATE_SOULSTONE,
  25016. + CREATE_HEALTHSTONE,
  25017. + CREATE_SPELLSTONE;
  25018. +
  25019. + // DEMON SUMMON
  25020. + uint32 SUMMON_IMP,
  25021. + SUMMON_VOIDWALKER,
  25022. + SUMMON_SUCCUBUS,
  25023. + SUMMON_FELHUNTER,
  25024. + SUMMON_FELGUARD;
  25025. +
  25026. + // DEMON SKILLS
  25027. + uint32 BLOOD_PACT,
  25028. + FIREBOLT,
  25029. + FIRE_SHIELD,
  25030. + ANGUISH,
  25031. + CLEAVE,
  25032. + INTERCEPT,
  25033. + DEVOUR_MAGIC,
  25034. + FEL_INTELLIGENCE,
  25035. + SHADOW_BITE,
  25036. + SPELL_LOCK,
  25037. + LASH_OF_PAIN,
  25038. + SEDUCTION,
  25039. + SOOTHING_KISS,
  25040. + CONSUME_SHADOWS,
  25041. + SACRIFICE,
  25042. + SUFFERING,
  25043. + TORMENT;
  25044. +
  25045. + // racial
  25046. + uint32 ARCANE_TORRENT,
  25047. + GIFT_OF_THE_NAARU,
  25048. + STONEFORM,
  25049. + ESCAPE_ARTIST,
  25050. + EVERY_MAN_FOR_HIMSELF,
  25051. + SHADOWMELD,
  25052. + BLOOD_FURY,
  25053. + WAR_STOMP,
  25054. + BERSERKING,
  25055. + WILL_OF_THE_FORSAKEN;
  25056. +
  25057. + uint32 m_lastDemon; // Last demon entry used for spell initialization
  25058. + uint32 m_demonOfChoice; // Preferred demon entry
  25059. + bool m_isTempImp; // True if imp summoned temporarily until soul shard acquired for demon of choice.
  25060. +};
  25061. +
  25062. +#endif
  25063. diff --git a/src/server/game/AI/PlayerBots/bp_warr_ai.cpp b/src/server/game/AI/PlayerBots/bp_warr_ai.cpp
  25064. new file mode 100644
  25065. index 0000000..54f23fe
  25066. --- /dev/null
  25067. +++ b/src/server/game/AI/PlayerBots/bp_warr_ai.cpp
  25068. @@ -0,0 +1,551 @@
  25069. +/*
  25070. + Name : PlayerbotWarriorAI.cpp
  25071. + Complete: maybe around 37%
  25072. + Author : Natsukawa
  25073. + Version : 0.39
  25074. + */
  25075. +#include "bp_ai.h"
  25076. +#include "bp_warr_ai.h"
  25077. +#include "Player.h"
  25078. +
  25079. +class PlayerbotAI;
  25080. +PlayerbotWarriorAI::PlayerbotWarriorAI(Player* const master, Player* const bot, PlayerbotAI* const ai) : PlayerbotClassAI(master, bot, ai)
  25081. +{
  25082. + AUTO_SHOT = PlayerbotAI::InitSpell(me, AUTO_SHOT_2); // GENERAL
  25083. +
  25084. + BATTLE_STANCE = PlayerbotAI::InitSpell(me, BATTLE_STANCE_1); //ARMS
  25085. + CHARGE = PlayerbotAI::InitSpell(me, CHARGE_1); //ARMS
  25086. + OVERPOWER = PlayerbotAI::InitSpell(me, OVERPOWER_1); // ARMS
  25087. + HEROIC_STRIKE = PlayerbotAI::InitSpell(me, HEROIC_STRIKE_1); //ARMS
  25088. + REND = PlayerbotAI::InitSpell(me, REND_1); //ARMS
  25089. + THUNDER_CLAP = PlayerbotAI::InitSpell(me, THUNDER_CLAP_1); //ARMS
  25090. + HAMSTRING = PlayerbotAI::InitSpell(me, HAMSTRING_1); //ARMS
  25091. + MOCKING_BLOW = PlayerbotAI::InitSpell(me, MOCKING_BLOW_1); //ARMS
  25092. + RETALIATION = PlayerbotAI::InitSpell(me, RETALIATION_1); //ARMS
  25093. + SWEEPING_STRIKES = PlayerbotAI::InitSpell(me, SWEEPING_STRIKES_1); //ARMS
  25094. + MORTAL_STRIKE = PlayerbotAI::InitSpell(me, MORTAL_STRIKE_1); //ARMS
  25095. + BLADESTORM = PlayerbotAI::InitSpell(me, BLADESTORM_1); //ARMS
  25096. + HEROIC_THROW = PlayerbotAI::InitSpell(me, HEROIC_THROW_1); //ARMS
  25097. + SHATTERING_THROW = PlayerbotAI::InitSpell(me, SHATTERING_THROW_1); //ARMS
  25098. + BLOODRAGE = PlayerbotAI::InitSpell(me, BLOODRAGE_1); //PROTECTION
  25099. + DEFENSIVE_STANCE = PlayerbotAI::InitSpell(me, DEFENSIVE_STANCE_1); //PROTECTION
  25100. + DEVASTATE = PlayerbotAI::InitSpell(me, DEVASTATE_1); //PROTECTION
  25101. + SUNDER_ARMOR = PlayerbotAI::InitSpell(me, SUNDER_ARMOR_1); //PROTECTION
  25102. + TAUNT = PlayerbotAI::InitSpell(me, TAUNT_1); //PROTECTION
  25103. + SHIELD_BASH = PlayerbotAI::InitSpell(me, SHIELD_BASH_1); //PROTECTION
  25104. + REVENGE = PlayerbotAI::InitSpell(me, REVENGE_1); //PROTECTION
  25105. + SHIELD_BLOCK = PlayerbotAI::InitSpell(me, SHIELD_BLOCK_1); //PROTECTION
  25106. + DISARM = PlayerbotAI::InitSpell(me, DISARM_1); //PROTECTION
  25107. + SHIELD_WALL = PlayerbotAI::InitSpell(me, SHIELD_WALL_1); //PROTECTION
  25108. + SHIELD_SLAM = PlayerbotAI::InitSpell(me, SHIELD_SLAM_1); //PROTECTION
  25109. + VIGILANCE = PlayerbotAI::InitSpell(me, VIGILANCE_1); //PROTECTION
  25110. + DEVASTATE = PlayerbotAI::InitSpell(me, DEVASTATE_1); //PROTECTION
  25111. + SHOCKWAVE = PlayerbotAI::InitSpell(me, SHOCKWAVE_1); //PROTECTION
  25112. + CONCUSSION_BLOW = PlayerbotAI::InitSpell(me, CONCUSSION_BLOW_1); //PROTECTION
  25113. + SPELL_REFLECTION = PlayerbotAI::InitSpell(me, SPELL_REFLECTION_1); //PROTECTION
  25114. + LAST_STAND = PlayerbotAI::InitSpell(me, LAST_STAND_1); //PROTECTION
  25115. + BATTLE_SHOUT = PlayerbotAI::InitSpell(me, BATTLE_SHOUT_1); //FURY
  25116. + DEMORALIZING_SHOUT = PlayerbotAI::InitSpell(me, DEMORALIZING_SHOUT_1); //FURY
  25117. + CLEAVE = PlayerbotAI::InitSpell(me, CLEAVE_1); //FURY
  25118. + INTIMIDATING_SHOUT = PlayerbotAI::InitSpell(me, INTIMIDATING_SHOUT_1); //FURY
  25119. + EXECUTE = PlayerbotAI::InitSpell(me, EXECUTE_1); //FURY
  25120. + CHALLENGING_SHOUT = PlayerbotAI::InitSpell(me, CHALLENGING_SHOUT_1); //FURY
  25121. + SLAM = PlayerbotAI::InitSpell(me, SLAM_1); //FURY
  25122. + BERSERKER_STANCE = PlayerbotAI::InitSpell(me, BERSERKER_STANCE_1); //FURY
  25123. + INTERCEPT = PlayerbotAI::InitSpell(me, INTERCEPT_1); //FURY
  25124. + DEATH_WISH = PlayerbotAI::InitSpell(me, DEATH_WISH_1); //FURY
  25125. + BERSERKER_RAGE = PlayerbotAI::InitSpell(me, BERSERKER_RAGE_1); //FURY
  25126. + WHIRLWIND = PlayerbotAI::InitSpell(me, WHIRLWIND_1); //FURY
  25127. + PUMMEL = PlayerbotAI::InitSpell(me, PUMMEL_1); //FURY
  25128. + BLOODTHIRST = PlayerbotAI::InitSpell(me, BLOODTHIRST_1); //FURY
  25129. + RECKLESSNESS = PlayerbotAI::InitSpell(me, RECKLESSNESS_1); //FURY
  25130. + RAMPAGE = 0; // passive
  25131. + HEROIC_FURY = PlayerbotAI::InitSpell(me, HEROIC_FURY_1); //FURY
  25132. + COMMANDING_SHOUT = PlayerbotAI::InitSpell(me, COMMANDING_SHOUT_1); //FURY
  25133. + ENRAGED_REGENERATION = PlayerbotAI::InitSpell(me, ENRAGED_REGENERATION_1); //FURY
  25134. + PIERCING_HOWL = PlayerbotAI::InitSpell(me, PIERCING_HOWL_1); //FURY
  25135. +
  25136. + //RECENTLY_BANDAGED = 11196; // first aid check
  25137. +
  25138. + // racial
  25139. + GIFT_OF_THE_NAARU = PlayerbotAI::InitSpell(me, GIFT_OF_THE_NAARU_WARRIOR); // draenei
  25140. + STONEFORM = PlayerbotAI::InitSpell(me, STONEFORM_ALL); // dwarf
  25141. + ESCAPE_ARTIST = PlayerbotAI::InitSpell(me, ESCAPE_ARTIST_ALL); // gnome
  25142. + EVERY_MAN_FOR_HIMSELF = PlayerbotAI::InitSpell(me, EVERY_MAN_FOR_HIMSELF_ALL); // human
  25143. + SHADOWMELD = PlayerbotAI::InitSpell(me, SHADOWMELD_ALL); // night elf
  25144. + BLOOD_FURY = PlayerbotAI::InitSpell(me, BLOOD_FURY_MELEE_CLASSES); // orc
  25145. + WAR_STOMP = PlayerbotAI::InitSpell(me, WAR_STOMP_ALL); // tauren
  25146. + BERSERKING = PlayerbotAI::InitSpell(me, BERSERKING_ALL); // troll
  25147. + WILL_OF_THE_FORSAKEN = PlayerbotAI::InitSpell(me, WILL_OF_THE_FORSAKEN_ALL); // undead
  25148. +
  25149. + //Procs
  25150. + SLAM_PROC = PlayerbotAI::InitSpell(me, SLAM_PROC_1);
  25151. + BLOODSURGE = PlayerbotAI::InitSpell(me, BLOODSURGE_1);
  25152. + TASTE_FOR_BLOOD = PlayerbotAI::InitSpell(me, TASTE_FOR_BLOOD_1);
  25153. + SUDDEN_DEATH = PlayerbotAI::InitSpell(me, SUDDEN_DEATH_1);
  25154. +}
  25155. +PlayerbotWarriorAI::~PlayerbotWarriorAI() {}
  25156. +
  25157. +CombatManeuverReturns PlayerbotWarriorAI::DoFirstCombatManeuver(Unit* pTarget)
  25158. +{
  25159. + //// There are NPCs in BGs and Open World PvP, so don't filter this on PvP scenarios (of course if PvP targets anyone but tank, all bets are off anyway)
  25160. + //// Wait until the tank says so, until any non-tank gains aggro or X seconds - whichever is shortest
  25161. + //if (m_ai->GetCombatOrder() & PlayerbotAI::ORDERS_TEMP_WAIT_TANKAGGRO)
  25162. + //{
  25163. + // if (m_WaitUntil > m_ai->CurrentTime() && m_ai->GroupTankHoldsAggro())
  25164. + // {
  25165. + // if (PlayerbotAI::ORDERS_TANK & m_ai->GetCombatOrder())
  25166. + // {
  25167. + // if (pTarget->GetCombatReach() <= ATTACK_DISTANCE)
  25168. + // {
  25169. + // // Set everyone's UpdateAI() waiting to 2 seconds
  25170. + // m_ai->SetGroupIgnoreUpdateTime(2);
  25171. + // // Clear their TEMP_WAIT_TANKAGGRO flag
  25172. + // m_ai->ClearGroupCombatOrder(PlayerbotAI::ORDERS_TEMP_WAIT_TANKAGGRO);
  25173. + // // Start attacking, force target on current target
  25174. + // m_ai->Attack(m_ai->GetCurrentTarget());
  25175. +
  25176. + // // While everyone else is waiting 2 second, we need to build up aggro, so don't return
  25177. + // }
  25178. + // else
  25179. + // {
  25180. + // // TODO: add check if target is ranged
  25181. + // return RETURN_NO_ACTION_OK; // wait for target to get nearer
  25182. + // }
  25183. + // }
  25184. + // else
  25185. + // return RETURN_NO_ACTION_OK; // wait it out
  25186. + // }
  25187. + // else
  25188. + // {
  25189. + // m_ai->ClearGroupCombatOrder(PlayerbotAI::ORDERS_TEMP_WAIT_TANKAGGRO);
  25190. + // }
  25191. + //}
  25192. +
  25193. + //if (m_ai->GetCombatOrder() & PlayerbotAI::ORDERS_TEMP_WAIT_OOC)
  25194. + //{
  25195. + // if (m_WaitUntil > m_ai->CurrentTime() && !m_ai->IsGroupInCombat())
  25196. + // return RETURN_NO_ACTION_OK; // wait it out
  25197. + // else
  25198. + // m_ai->ClearGroupCombatOrder(PlayerbotAI::ORDERS_TEMP_WAIT_OOC);
  25199. + //}
  25200. +
  25201. + //switch (m_ai->GetScenarioType())
  25202. + //{
  25203. + // case PlayerbotAI::SCENARIO_PVP_DUEL:
  25204. + // case PlayerbotAI::SCENARIO_PVP_BG:
  25205. + // case PlayerbotAI::SCENARIO_PVP_ARENA:
  25206. + // case PlayerbotAI::SCENARIO_PVP_OPENWORLD:
  25207. + // return DoFirstCombatManeuverPVP(pTarget);
  25208. + // case PlayerbotAI::SCENARIO_PVE:
  25209. + // case PlayerbotAI::SCENARIO_PVE_ELITE:
  25210. + // case PlayerbotAI::SCENARIO_PVE_RAID:
  25211. + // default:
  25212. + // return DoFirstCombatManeuverPVE(pTarget);
  25213. + // break;
  25214. + //}
  25215. +
  25216. + return RETURN_NO_ACTION_ERROR;
  25217. +}
  25218. +
  25219. +CombatManeuverReturns PlayerbotWarriorAI::DoFirstCombatManeuverPVE(Unit* pTarget)
  25220. +{
  25221. + //if (!m_ai) return RETURN_NO_ACTION_ERROR;
  25222. + //if (!m_bot) return RETURN_NO_ACTION_ERROR;
  25223. +
  25224. + //float fTargetDist = pTarget->GetCombatReach();
  25225. +
  25226. + //if (DEFENSIVE_STANCE && (m_ai->GetCombatOrder() & PlayerbotAI::ORDERS_TANK))
  25227. + //{
  25228. + // if (!m_bot->HasAura(DEFENSIVE_STANCE, EFFECT_0) && m_ai->CastSpell(DEFENSIVE_STANCE))
  25229. + // return RETURN_CONTINUE;
  25230. + // else if (TAUNT > 0 && m_bot->HasAura(DEFENSIVE_STANCE, EFFECT_0) && m_ai->CastSpell(TAUNT, *pTarget))
  25231. + // return RETURN_FINISHED_FIRST_MOVES;
  25232. + //}
  25233. +
  25234. + //if (BERSERKER_STANCE)
  25235. + //{
  25236. + // if (!m_bot->HasAura(BERSERKER_STANCE, EFFECT_0) && m_ai->CastSpell(BERSERKER_STANCE))
  25237. + // return RETURN_CONTINUE;
  25238. + // if (BLOODRAGE > 0 && m_bot->HasAura(BERSERKER_STANCE, EFFECT_0) && m_bot->GetPower(POWER_RAGE)/10 <= 10)
  25239. + // return m_ai->CastSpell(BLOODRAGE) ? RETURN_FINISHED_FIRST_MOVES : RETURN_NO_ACTION_ERROR;
  25240. + // if (INTERCEPT > 0 && m_bot->HasAura(BERSERKER_STANCE, EFFECT_0))
  25241. + // {
  25242. + // if (fTargetDist < 8.0f)
  25243. + // return RETURN_NO_ACTION_OK;
  25244. + // else if (fTargetDist > 25.0f)
  25245. + // return RETURN_CONTINUE; // wait to come into range
  25246. + // else if (INTERCEPT > 0 && m_ai->CastSpell(INTERCEPT, *pTarget))
  25247. + // {
  25248. + // float x, y, z;
  25249. + // pTarget->GetContactPoint(m_bot, x, y, z, 3.666666f);
  25250. + // m_bot->Relocate(x, y, z);
  25251. + // return RETURN_FINISHED_FIRST_MOVES;
  25252. + // }
  25253. + // }
  25254. + //}
  25255. +
  25256. + //if (BATTLE_STANCE)
  25257. + //{
  25258. + // if (!m_bot->HasAura(BATTLE_STANCE, EFFECT_0) && m_ai->CastSpell(BATTLE_STANCE))
  25259. + // return RETURN_CONTINUE;
  25260. + // if (CHARGE > 0 && m_bot->HasAura(BATTLE_STANCE, EFFECT_0))
  25261. + // {
  25262. + // if (fTargetDist < 8.0f)
  25263. + // return RETURN_NO_ACTION_OK;
  25264. + // if (fTargetDist > 25.0f)
  25265. + // return RETURN_CONTINUE; // wait to come into range
  25266. + // else if (CHARGE > 0 && m_ai->CastSpell(CHARGE, *pTarget))
  25267. + // {
  25268. + // float x, y, z;
  25269. + // pTarget->GetContactPoint(m_bot, x, y, z, 3.666666f);
  25270. + // m_bot->Relocate(x, y, z);
  25271. + // return RETURN_FINISHED_FIRST_MOVES;
  25272. + // }
  25273. + // }
  25274. + //}
  25275. +
  25276. + return RETURN_NO_ACTION_OK;
  25277. +}
  25278. +
  25279. +// TODO: blatant copy of PVE for now, please PVP-port it
  25280. +CombatManeuverReturns PlayerbotWarriorAI::DoFirstCombatManeuverPVP(Unit *pTarget)
  25281. +{
  25282. + //if (!m_ai) return RETURN_NO_ACTION_ERROR;
  25283. + //if (!m_bot) return RETURN_NO_ACTION_ERROR;
  25284. +
  25285. + //float fTargetDist = pTarget->GetCombatReach();
  25286. +
  25287. + //if (DEFENSIVE_STANCE && (m_ai->GetCombatOrder() & PlayerbotAI::ORDERS_TANK))
  25288. + //{
  25289. + // if (!m_bot->HasAura(DEFENSIVE_STANCE, EFFECT_0) && m_ai->CastSpell(DEFENSIVE_STANCE))
  25290. + // return RETURN_CONTINUE;
  25291. + // else if (TAUNT > 0 && m_bot->HasAura(DEFENSIVE_STANCE, EFFECT_0) && m_ai->CastSpell(TAUNT, *pTarget))
  25292. + // return RETURN_FINISHED_FIRST_MOVES;
  25293. + //}
  25294. +
  25295. + //if (BERSERKER_STANCE)
  25296. + //{
  25297. + // if (!m_bot->HasAura(BERSERKER_STANCE, EFFECT_0) && m_ai->CastSpell(BERSERKER_STANCE))
  25298. + // return RETURN_CONTINUE;
  25299. + // if (BLOODRAGE > 0 && m_bot->HasAura(BERSERKER_STANCE, EFFECT_0) && m_bot->GetPower(POWER_RAGE)/10 <= 10)
  25300. + // return m_ai->CastSpell(BLOODRAGE) ? RETURN_FINISHED_FIRST_MOVES : RETURN_NO_ACTION_ERROR;
  25301. + // if (INTERCEPT > 0 && m_bot->HasAura(BERSERKER_STANCE, EFFECT_0))
  25302. + // {
  25303. + // if (fTargetDist < 8.0f)
  25304. + // return RETURN_NO_ACTION_OK;
  25305. + // else if (fTargetDist > 25.0f)
  25306. + // return RETURN_CONTINUE; // wait to come into range
  25307. + // else if (INTERCEPT > 0 && m_ai->CastSpell(INTERCEPT, *pTarget))
  25308. + // {
  25309. + // float x, y, z;
  25310. + // pTarget->GetContactPoint(m_bot, x, y, z, 3.666666f);
  25311. + // m_bot->Relocate(x, y, z);
  25312. + // return RETURN_FINISHED_FIRST_MOVES;
  25313. + // }
  25314. + // }
  25315. + //}
  25316. +
  25317. + //if (BATTLE_STANCE)
  25318. + //{
  25319. + // if (!m_bot->HasAura(BATTLE_STANCE, EFFECT_0) && m_ai->CastSpell(BATTLE_STANCE))
  25320. + // return RETURN_CONTINUE;
  25321. + // if (CHARGE > 0 && m_bot->HasAura(BATTLE_STANCE, EFFECT_0))
  25322. + // {
  25323. + // if (fTargetDist < 8.0f)
  25324. + // return RETURN_NO_ACTION_OK;
  25325. + // if (fTargetDist > 25.0f)
  25326. + // return RETURN_CONTINUE; // wait to come into range
  25327. + // else if (CHARGE > 0 && m_ai->CastSpell(CHARGE, *pTarget))
  25328. + // {
  25329. + // float x, y, z;
  25330. + // pTarget->GetContactPoint(m_bot, x, y, z, 3.666666f);
  25331. + // m_bot->Relocate(x, y, z);
  25332. + // return RETURN_FINISHED_FIRST_MOVES;
  25333. + // }
  25334. + // }
  25335. + //}
  25336. +
  25337. + return RETURN_NO_ACTION_OK;
  25338. +}
  25339. +
  25340. +CombatManeuverReturns PlayerbotWarriorAI::DoNextCombatManeuver(Unit *pTarget)
  25341. +{
  25342. + //switch (m_ai->GetScenarioType())
  25343. + //{
  25344. + // case PlayerbotAI::SCENARIO_PVP_DUEL:
  25345. + // case PlayerbotAI::SCENARIO_PVP_BG:
  25346. + // case PlayerbotAI::SCENARIO_PVP_ARENA:
  25347. + // case PlayerbotAI::SCENARIO_PVP_OPENWORLD:
  25348. + // return DoNextCombatManeuverPVP(pTarget);
  25349. + // case PlayerbotAI::SCENARIO_PVE:
  25350. + // case PlayerbotAI::SCENARIO_PVE_ELITE:
  25351. + // case PlayerbotAI::SCENARIO_PVE_RAID:
  25352. + // default:
  25353. + // return DoNextCombatManeuverPVE(pTarget);
  25354. + // break;
  25355. + //}
  25356. +
  25357. + return RETURN_NO_ACTION_ERROR;
  25358. +}
  25359. +
  25360. +CombatManeuverReturns PlayerbotWarriorAI::DoNextCombatManeuverPVE(Unit *pTarget)
  25361. +{
  25362. + //if (!m_ai) return RETURN_NO_ACTION_ERROR;
  25363. + //if (!m_bot) return RETURN_NO_ACTION_ERROR;
  25364. +
  25365. + ////Unit* pVictim = pTarget->getVictim();
  25366. + ////float fTargetDist = pTarget->GetCombatReach();
  25367. + //uint32 spec = m_bot->GetSpec();
  25368. +
  25369. + ////If we have devastate it will replace SA in our rotation
  25370. + ////uint32 SUNDER = (DEVASTATE > 0 ? DEVASTATE : SUNDER_ARMOR);
  25371. +
  25372. + ////Used to determine if this bot is highest on threat
  25373. + //Unit* newTarget = m_ai->FindAttacker((PlayerbotAI::ATTACKERINFOTYPE) (PlayerbotAI::AIT_VICTIMSELF | PlayerbotAI::AIT_HIGHESTTHREAT), m_bot);
  25374. +
  25375. + //// do shouts, berserker rage, etc...
  25376. + //if (BERSERKER_RAGE > 0 && !m_bot->HasAura(BERSERKER_RAGE, EFFECT_0))
  25377. + // m_ai->CastSpell(BERSERKER_RAGE);
  25378. + //else if (BLOODRAGE > 0 && m_bot->GetPower(POWER_RAGE)/10 <= 10)
  25379. + // m_ai->CastSpell(BLOODRAGE);
  25380. +
  25381. + //CheckShouts();
  25382. +
  25383. + //switch (spec)
  25384. + //{
  25385. + // case WARRIOR_SPEC_ARMS:
  25386. + // // Execute doesn't scale too well with extra rage and uses up *all* rage preventing use of other skills
  25387. + // //Haven't found a way to make sudden death work yet, either wrong spell or it needs an effect index(probably)
  25388. + // if (EXECUTE > 0 && (pTarget->GetHealthPct() < 20 || m_bot->HasAura(SUDDEN_DEATH)) && m_bot->GetPower(POWER_RAGE)/10 < 30 && m_ai->CastSpell (EXECUTE, *pTarget))
  25389. + // return RETURN_CONTINUE;
  25390. + // if (REND > 0 && !pTarget->HasAura(REND, EFFECT_0) && m_ai->CastSpell(REND, *pTarget))
  25391. + // return RETURN_CONTINUE;
  25392. + // if (MORTAL_STRIKE > 0 && !m_bot->HasSpellCooldown(MORTAL_STRIKE) && m_ai->CastSpell(MORTAL_STRIKE, *pTarget))
  25393. + // return RETURN_CONTINUE;
  25394. + // if (SHATTERING_THROW > 0 && !pTarget->HasAura(SHATTERING_THROW, EFFECT_0) && !m_bot->HasSpellCooldown(SHATTERING_THROW) && m_ai->CastSpell(SHATTERING_THROW, *pTarget))
  25395. + // return RETURN_CONTINUE;
  25396. + // if (BLADESTORM > 0 && !m_bot->HasSpellCooldown(BLADESTORM) /*&& m_ai->GetAttackerCount() >= 3*/ && m_ai->CastSpell(BLADESTORM, *pTarget))
  25397. + // return RETURN_CONTINUE;
  25398. + // // No way to tell if overpower is active (yet), however taste for blood works
  25399. + // if (OVERPOWER > 0 && m_bot->HasAura(TASTE_FOR_BLOOD) && m_ai->CastSpell(OVERPOWER, *pTarget))
  25400. + // return RETURN_CONTINUE;
  25401. + // if (HEROIC_STRIKE > 0 && m_ai->CastSpell(HEROIC_STRIKE, *pTarget))
  25402. + // return RETURN_CONTINUE;
  25403. + // if (SLAM > 0 && m_ai->CastSpell(SLAM, *pTarget))
  25404. + // {
  25405. + // //m_ai->SetIgnoreUpdateTime(1.5); // TODO: SetIgnoreUpdateTime takes a uint8 - how will 1.5 work as a value?
  25406. + // return RETURN_CONTINUE;
  25407. + // }
  25408. +
  25409. + // case WARRIOR_SPEC_FURY:
  25410. + // if (EXECUTE > 0 && pTarget->GetHealthPct() < 20 && m_ai->CastSpell (EXECUTE, *pTarget))
  25411. + // return RETURN_CONTINUE;
  25412. + // if (BLOODTHIRST > 0 && !m_bot->HasSpellCooldown(BLOODTHIRST) && m_ai->CastSpell(BLOODTHIRST, *pTarget))
  25413. + // return RETURN_CONTINUE;
  25414. + // if (WHIRLWIND > 0 && !m_bot->HasSpellCooldown(WHIRLWIND) && m_ai->CastSpell(WHIRLWIND, *pTarget))
  25415. + // return RETURN_CONTINUE;
  25416. + // if (SLAM > 0 && m_bot->HasAura(BLOODSURGE, EFFECT_0) && m_ai->CastSpell(SLAM, *pTarget))
  25417. + // return RETURN_CONTINUE;
  25418. + // if (HEROIC_STRIKE > 0 && m_ai->CastSpell(HEROIC_STRIKE, *pTarget))
  25419. + // return RETURN_CONTINUE;
  25420. +
  25421. + // case WARRIOR_SPEC_PROTECTION:
  25422. + // if (m_ai->GetCombatOrder() & PlayerbotAI::ORDERS_TANK && !newTarget && TAUNT > 0 && !m_bot->HasSpellCooldown(TAUNT) && m_ai->CastSpell(TAUNT, *pTarget))
  25423. + // return RETURN_CONTINUE;
  25424. + // // No way to tell if revenge is active (yet)
  25425. + // /*if (REVENGE > 0 && m_ai->CastSpell(REVENGE, *pTarget))
  25426. + // return RETURN_CONTINUE;*/
  25427. + // if (REND > 0 && !pTarget->HasAura(REND, EFFECT_0) && m_ai->CastSpell(REND, *pTarget))
  25428. + // return RETURN_CONTINUE;
  25429. + // if (THUNDER_CLAP > 0 && !pTarget->HasAura(THUNDER_CLAP) && m_ai->CastSpell(THUNDER_CLAP, *pTarget))
  25430. + // return RETURN_CONTINUE;
  25431. + // if (DEMORALIZING_SHOUT > 0 && !pTarget->HasAura(DEMORALIZING_SHOUT, EFFECT_0) && m_ai->CastSpell(DEMORALIZING_SHOUT, *pTarget))
  25432. + // return RETURN_CONTINUE;
  25433. + // if (CONCUSSION_BLOW > 0 && !m_bot->HasSpellCooldown(CONCUSSION_BLOW) && m_ai->CastSpell(CONCUSSION_BLOW, *pTarget))
  25434. + // return RETURN_CONTINUE;
  25435. + // if (SHOCKWAVE > 0 && !m_bot->HasSpellCooldown(SHOCKWAVE) && m_ai->CastSpell(SHOCKWAVE, *pTarget))
  25436. + // return RETURN_CONTINUE;
  25437. + // if (SHIELD_SLAM > 0 && !m_bot->HasSpellCooldown(SHIELD_SLAM) && m_ai->CastSpell(SHIELD_SLAM, *pTarget))
  25438. + // return RETURN_CONTINUE;
  25439. + // /*if (SUNDER > 0 && !pTarget->HasAura(SUNDER_ARMOR) && m_ai->CastSpell(SUNDER, *pTarget))
  25440. + // return RETURN_CONTINUE;*/
  25441. + // if (HEROIC_STRIKE > 0 && m_ai->CastSpell(HEROIC_STRIKE, *pTarget))
  25442. + // return RETURN_CONTINUE;
  25443. +
  25444. + // /*case WarriorSpellPreventing:
  25445. + // if (SHIELD_BASH > 0 && m_ai->CastSpell(SHIELD_BASH, *pTarget))
  25446. + // return RETURN_CONTINUE;
  25447. + // if (PUMMEL > 0 && m_ai->CastSpell(PUMMEL, *pTarget))
  25448. + // return RETURN_CONTINUE;
  25449. + // if (SPELL_REFLECTION > 0 && !m_bot->HasAura(SPELL_REFLECTION, EFFECT_0) && m_ai->CastSpell(SPELL_REFLECTION, *m_bot))
  25450. + // return RETURN_CONTINUE;
  25451. + // break;
  25452. +
  25453. + // case WarriorBattle:
  25454. + // if (LAST_STAND > 0 && !m_bot->HasAura(LAST_STAND, EFFECT_0) && m_bot->GetHealthPct() < 50 && m_ai->CastSpell(LAST_STAND, *m_bot))
  25455. + // return RETURN_CONTINUE;
  25456. + // if (DEATH_WISH > 0 && !m_bot->HasAura(DEATH_WISH, EFFECT_0) && m_ai->CastSpell(DEATH_WISH, *m_bot))
  25457. + // return RETURN_CONTINUE;
  25458. + // if (RETALIATION > 0 && pVictim == m_bot && m_ai->GetAttackerCount() >= 2 && !m_bot->HasAura(RETALIATION, EFFECT_0) && m_ai->CastSpell(RETALIATION, *m_bot))
  25459. + // return RETURN_CONTINUE;
  25460. + // if (SWEEPING_STRIKES > 0 && m_ai->GetAttackerCount() >= 2 && !m_bot->HasAura(SWEEPING_STRIKES, EFFECT_0) && m_ai->CastSpell(SWEEPING_STRIKES, *m_bot))
  25461. + // return RETURN_CONTINUE;
  25462. + // if (INTIMIDATING_SHOUT > 0 && m_ai->GetAttackerCount() > 5 && m_ai->CastSpell(INTIMIDATING_SHOUT, *pTarget))
  25463. + // return RETURN_CONTINUE;
  25464. + // if (ENRAGED_REGENERATION > 0 && !m_bot->HasAura(BERSERKER_RAGE, EFFECT_0) && !m_bot->HasAura(ENRAGED_REGENERATION, EFFECT_0) && m_bot->GetHealth() < m_bot->GetMaxHealth() * 0.5 && m_ai->CastSpell(ENRAGED_REGENERATION, *m_bot))
  25465. + // return RETURN_CONTINUE;
  25466. + // if (HAMSTRING > 0 && !pTarget->HasAura(HAMSTRING, EFFECT_0) && m_ai->CastSpell(HAMSTRING, *pTarget))
  25467. + // return RETURN_CONTINUE;
  25468. + // if (CHALLENGING_SHOUT > 0 && pVictim != m_bot && m_bot->GetHealthPct() > 25 && !pTarget->HasAura(MOCKING_BLOW, EFFECT_0) && !pTarget->HasAura(CHALLENGING_SHOUT, EFFECT_0) && m_ai->CastSpell(CHALLENGING_SHOUT, *pTarget))
  25469. + // return RETURN_CONTINUE;
  25470. + // if (CLEAVE > 0 && m_ai->CastSpell(CLEAVE, *pTarget))
  25471. + // return RETURN_CONTINUE;
  25472. + // if (PIERCING_HOWL > 0 && && m_ai->GetAttackerCount() >= 3 && !pTarget->HasAura(WAR_STOMP, EFFECT_0) && !pTarget->HasAura(PIERCING_HOWL, EFFECT_0) && !pTarget->HasAura(SHOCKWAVE, EFFECT_0) && !pTarget->HasAura(CONCUSSION_BLOW, EFFECT_0) && m_ai->CastSpell(PIERCING_HOWL, *pTarget))
  25473. + // return RETURN_CONTINUE;
  25474. + // if (MOCKING_BLOW > 0 && pVictim != m_bot && m_bot->GetHealthPct() > 25 && !pTarget->HasAura(MOCKING_BLOW, EFFECT_0) && !pTarget->HasAura(CHALLENGING_SHOUT, EFFECT_0) && m_ai->CastSpell(MOCKING_BLOW, *pTarget))
  25475. + // return RETURN_CONTINUE;
  25476. + // if (HEROIC_THROW > 0 && m_ai->CastSpell(HEROIC_THROW, *pTarget))
  25477. + // return RETURN_CONTINUE;
  25478. + // if (m_bot->getRace() == RACE_TAUREN && !pTarget->HasAura(WAR_STOMP, EFFECT_0) && !pTarget->HasAura(PIERCING_HOWL, EFFECT_0) && !pTarget->HasAura(SHOCKWAVE, EFFECT_0) && !pTarget->HasAura(CONCUSSION_BLOW, EFFECT_0) && m_ai->CastSpell(WAR_STOMP, *pTarget))
  25479. + // return RETURN_CONTINUE;
  25480. + // if (m_bot->getRace() == RACE_HUMAN && m_bot->HasUnitState(UNIT_STAT_STUNNED) || m_bot->HasAuraType(SPELL_AURA_MOD_FEAR) || m_bot->HasAuraType(SPELL_AURA_MOD_DECREASE_SPEED) || m_bot->HasAuraType(SPELL_AURA_MOD_CHARM) && m_ai->CastSpell(EVERY_MAN_FOR_HIMSELF, *m_bot))
  25481. + // return RETURN_CONTINUE;
  25482. + // if (m_bot->getRace() == RACE_UNDEAD_PLAYER && m_bot->HasAuraType(SPELL_AURA_MOD_FEAR) || m_bot->HasAuraType(SPELL_AURA_MOD_CHARM) && m_ai->CastSpell(WILL_OF_THE_FORSAKEN, *m_bot))
  25483. + // return RETURN_CONTINUE;
  25484. + // if (m_bot->getRace() == RACE_DWARF && m_bot->HasAuraState(AURA_STATE_DEADLY_POISON) && m_ai->CastSpell(STONEFORM, *m_bot))
  25485. + // return RETURN_CONTINUE;
  25486. + // if (m_bot->getRace() == RACE_GNOME && m_bot->HasUnitState(UNIT_STAT_STUNNED) || m_bot->HasAuraType(SPELL_AURA_MOD_DECREASE_SPEED) && m_ai->CastSpell(ESCAPE_ARTIST, *m_bot))
  25487. + // return RETURN_CONTINUE;
  25488. + // if (m_bot->getRace() == RACE_NIGHTELF && pVictim == m_bot && m_bot->GetHealthPct() < 25 && !m_bot->HasAura(SHADOWMELD, EFFECT_0) && m_ai->CastSpell(SHADOWMELD, *m_bot))
  25489. + // return RETURN_CONTINUE;
  25490. + // if (m_bot->getRace() == RACE_ORC && !m_bot->HasAura(BLOOD_FURY, EFFECT_0) && m_ai->CastSpell(BLOOD_FURY, *m_bot))
  25491. + // return RETURN_CONTINUE;
  25492. + // if (m_bot->getRace() == RACE_TROLL && !m_bot->HasAura(BERSERKING, EFFECT_0) && m_ai->CastSpell(BERSERKING, *m_bot))
  25493. + // return RETURN_CONTINUE;
  25494. + // if (m_bot->getRace() == RACE_DRAENEI && m_bot->GetHealthPct() < 25 && !m_bot->HasAura(GIFT_OF_THE_NAARU, EFFECT_0) && m_ai->CastSpell(GIFT_OF_THE_NAARU, *m_bot))
  25495. + // return RETURN_CONTINUE;
  25496. + // break;
  25497. +
  25498. + // case WarriorDefensive:
  25499. + // if (DISARM > 0 && !pTarget->HasAura(DISARM, EFFECT_0) && m_ai->CastSpell(DISARM, *pTarget))
  25500. + // return RETURN_CONTINUE;
  25501. + // if (SHIELD_BLOCK > 0 && !m_bot->HasAura(SHIELD_BLOCK, EFFECT_0) && m_ai->CastSpell(SHIELD_BLOCK, *m_bot))
  25502. + // return RETURN_CONTINUE;
  25503. + // if (SHIELD_WALL > 0 && !m_bot->HasAura(SHIELD_WALL, EFFECT_0) && m_ai->CastSpell(SHIELD_WALL, *m_bot))
  25504. + // return RETURN_CONTINUE;
  25505. + // break;*/
  25506. + //}
  25507. +
  25508. + return RETURN_NO_ACTION_OK;
  25509. +}
  25510. +
  25511. +CombatManeuverReturns PlayerbotWarriorAI::DoNextCombatManeuverPVP(Unit* pTarget)
  25512. +{
  25513. + //if (m_ai->CastSpell(HEROIC_STRIKE))
  25514. + // return RETURN_CONTINUE;
  25515. +
  25516. + return DoNextCombatManeuverPVE(pTarget); // TODO: bad idea perhaps, but better than the alternative
  25517. +}
  25518. +
  25519. +//Buff and rebuff shouts
  25520. +void PlayerbotWarriorAI::CheckShouts()
  25521. +{
  25522. + //if (!m_ai) return;
  25523. + //if (!m_bot) return;
  25524. +
  25525. + //if (m_bot->GetSpec() == WARRIOR_SPEC_PROTECTION && COMMANDING_SHOUT > 0)
  25526. + //{
  25527. + // if (!m_bot->HasAura(COMMANDING_SHOUT, EFFECT_0) && m_ai->CastSpell(COMMANDING_SHOUT))
  25528. + // return;
  25529. + //}
  25530. + //else // Not prot, or prot but no Commanding Shout yet
  25531. + //{
  25532. + // if (!m_bot->HasAura(BATTLE_SHOUT, EFFECT_0) && m_ai->CastSpell(BATTLE_SHOUT))
  25533. + // return;
  25534. + //}
  25535. +}
  25536. +
  25537. +void PlayerbotWarriorAI::DoNonCombatActions()
  25538. +{
  25539. + //if (!m_ai) return;
  25540. + //if (!m_bot) return;
  25541. +
  25542. + //uint32 spec = m_bot->GetSpec();
  25543. +
  25544. + ////Stance Check
  25545. + //if (spec == WARRIOR_SPEC_ARMS && !m_bot->HasAura(BATTLE_STANCE, EFFECT_0))
  25546. + // m_ai->CastSpell(BATTLE_STANCE);
  25547. + //else if (spec == WARRIOR_SPEC_FURY && !m_bot->HasAura(BERSERKER_STANCE, EFFECT_0))
  25548. + // m_ai->CastSpell(BERSERKER_STANCE);
  25549. + //else if (spec == WARRIOR_SPEC_PROTECTION && !m_bot->HasAura(DEFENSIVE_STANCE, EFFECT_0))
  25550. + // m_ai->CastSpell(DEFENSIVE_STANCE);
  25551. +
  25552. + //// buff master with VIGILANCE
  25553. + //if (VIGILANCE > 0)
  25554. + // (!GetMaster()->HasAura(VIGILANCE, EFFECT_0) && m_ai->CastSpell(VIGILANCE, *GetMaster()));
  25555. +
  25556. + //// hp check
  25557. + //if (m_bot->getStandState() != UNIT_STAND_STATE_STAND)
  25558. + // m_bot->SetStandState(UNIT_STAND_STATE_STAND);
  25559. +
  25560. + //if (EatDrinkBandage(false))
  25561. + // return;
  25562. +
  25563. + //if (m_bot->getRace() == RACE_DRAENEI && !m_bot->HasAura(GIFT_OF_THE_NAARU, EFFECT_0) && m_bot->GetHealthPct() < 70)
  25564. + //{
  25565. + // m_ai->TellMaster("I'm casting gift of the naaru.");
  25566. + // m_ai->CastSpell(GIFT_OF_THE_NAARU, *m_bot);
  25567. + // return;
  25568. + //}
  25569. +} // end DoNonCombatActions
  25570. +
  25571. +// Match up with "Pull()" below
  25572. +bool PlayerbotWarriorAI::CanPull()
  25573. +{
  25574. + if (!me) return false;
  25575. + if (!m_ai) return false;
  25576. +
  25577. + if (me->GetUInt32Value(PLAYER_AMMO_ID)) // Having ammo equipped means a weapon is equipped as well. Probably. [TODO: does this work with throwing knives? Can a playerbot 'cheat' ammo into the slot without a proper weapon?]
  25578. + {
  25579. + // Can't do this, CanPull CANNOT check for anything that requires a target
  25580. + //if (!m_ai->IsInRange(m_ai->GetCurrentTarget(), AUTO_SHOT))
  25581. + //{
  25582. + // m_ai->TellMaster("I'm out of range.");
  25583. + // return false;
  25584. + //}
  25585. + return true;
  25586. + }
  25587. +
  25588. + return false;
  25589. +}
  25590. +
  25591. +// Match up with "CanPull()" above
  25592. +bool PlayerbotWarriorAI::Pull()
  25593. +{
  25594. + //if (!m_bot) return false;
  25595. + //if (!m_ai) return false;
  25596. +
  25597. + //if (m_ai->GetCurrentTarget() && m_ai->GetCurrentTarget()->GetCombatReach() > ATTACK_DISTANCE)
  25598. + //{
  25599. + // if (!m_ai->IsInRange(m_ai->GetCurrentTarget(), AUTO_SHOT))
  25600. + // {
  25601. + // m_ai->TellMaster("I'm out of range.");
  25602. + // return false;
  25603. + // }
  25604. +
  25605. + // // activate auto shot: Reworked to account for AUTO_SHOT being a triggered spell
  25606. + // if (AUTO_SHOT && m_ai->GetCurrentSpellId() != AUTO_SHOT)
  25607. + // {
  25608. + // m_bot->CastSpell(m_ai->GetCurrentTarget(), AUTO_SHOT, true);
  25609. + // return true;
  25610. + // }
  25611. + //}
  25612. + //else // target is in melee range
  25613. + //{
  25614. + // m_ai->Attack(m_ai->GetCurrentTarget());
  25615. + // return true;
  25616. + //}
  25617. +
  25618. + return false;
  25619. +}
  25620. diff --git a/src/server/game/AI/PlayerBots/bp_warr_ai.h b/src/server/game/AI/PlayerBots/bp_warr_ai.h
  25621. new file mode 100644
  25622. index 0000000..dcf1abc
  25623. --- /dev/null
  25624. +++ b/src/server/game/AI/PlayerBots/bp_warr_ai.h
  25625. @@ -0,0 +1,182 @@
  25626. +#ifndef _PlayerbotWarriorAI_H
  25627. +#define _PlayerbotWarriorAI_H
  25628. +
  25629. +#include "bp_cai.h"
  25630. +
  25631. +enum
  25632. +{
  25633. + WarriorSpellPreventing,
  25634. + WarriorBattle,
  25635. + WarriorDefensive,
  25636. + WarriorBerserker
  25637. +};
  25638. +
  25639. +enum WarriorSpells
  25640. +{
  25641. + AUTO_SHOT_2 = 75,
  25642. + BATTLE_SHOUT_1 = 6673,
  25643. + BATTLE_STANCE_1 = 2457,
  25644. + BERSERKER_RAGE_1 = 18499,
  25645. + BERSERKER_STANCE_1 = 2458,
  25646. + BLADESTORM_1 = 46924,
  25647. + BLOODRAGE_1 = 2687,
  25648. + BLOODTHIRST_1 = 23881,
  25649. + CHALLENGING_SHOUT_1 = 1161,
  25650. + CHARGE_1 = 100,
  25651. + CLEAVE_1 = 845,
  25652. + COMMANDING_SHOUT_1 = 469,
  25653. + CONCUSSION_BLOW_1 = 12809,
  25654. + DEATH_WISH_1 = 12292,
  25655. + DEFENSIVE_STANCE_1 = 71,
  25656. + DEMORALIZING_SHOUT_1 = 1160,
  25657. + DEVASTATE_1 = 20243,
  25658. + DISARM_1 = 676,
  25659. + ENRAGED_REGENERATION_1 = 55694,
  25660. + EXECUTE_1 = 5308,
  25661. + HAMSTRING_1 = 1715,
  25662. + HEROIC_FURY_1 = 60970,
  25663. + HEROIC_STRIKE_1 = 78,
  25664. + HEROIC_THROW_1 = 57755,
  25665. + INTERCEPT_1 = 20252,
  25666. + INTERVENE_1 = 3411,
  25667. + INTIMIDATING_SHOUT_1 = 5246,
  25668. + LAST_STAND_1 = 12975,
  25669. + MOCKING_BLOW_1 = 694,
  25670. + MORTAL_STRIKE_1 = 12294,
  25671. + OVERPOWER_1 = 7384,
  25672. + PIERCING_HOWL_1 = 12323,
  25673. + PUMMEL_1 = 6552,
  25674. + RECKLESSNESS_1 = 1719,
  25675. + REND_1 = 772,
  25676. + RETALIATION_1 = 20230,
  25677. + REVENGE_1 = 6572,
  25678. + SHATTERING_THROW_1 = 64382,
  25679. + SHIELD_BASH_1 = 72,
  25680. + SHIELD_BLOCK_1 = 2565,
  25681. + SHIELD_SLAM_1 = 23922,
  25682. + SHIELD_WALL_1 = 871,
  25683. + SHOCKWAVE_1 = 46968,
  25684. + SLAM_1 = 1464,
  25685. + SPELL_REFLECTION_1 = 23920,
  25686. + SUNDER_ARMOR_1 = 7386,
  25687. + SWEEPING_STRIKES_1 = 12328,
  25688. + TAUNT_1 = 355,
  25689. + THUNDER_CLAP_1 = 6343,
  25690. + VICTORY_RUSH_1 = 34428,
  25691. + VIGILANCE_1 = 50720,
  25692. + WHIRLWIND_1 = 1680,
  25693. +
  25694. + //Procs
  25695. + SLAM_PROC_1 = 46916,
  25696. + BLOODSURGE_1 = 46915,
  25697. + TASTE_FOR_BLOOD_1 = 56638,
  25698. + SUDDEN_DEATH_1 = 52437
  25699. +};
  25700. +
  25701. +//class Player;
  25702. +
  25703. +class PlayerbotWarriorAI : PlayerbotClassAI
  25704. +{
  25705. +public:
  25706. + PlayerbotWarriorAI(Player * const master, Player * const bot, PlayerbotAI * const ai);
  25707. + virtual ~PlayerbotWarriorAI();
  25708. +
  25709. + // all combat actions go here
  25710. + CombatManeuverReturns DoFirstCombatManeuver(Unit* pTarget);
  25711. + CombatManeuverReturns DoNextCombatManeuver(Unit* pTarget);
  25712. + bool Pull();
  25713. +
  25714. + // all non combat actions go here, ex buffs, heals, rezzes
  25715. + void DoNonCombatActions();
  25716. +
  25717. + //Buff/rebuff shouts
  25718. + void CheckShouts();
  25719. +
  25720. + // Utility Functions
  25721. + bool CanPull();
  25722. +
  25723. +private:
  25724. + CombatManeuverReturns DoFirstCombatManeuverPVE(Unit* pTarget);
  25725. + CombatManeuverReturns DoNextCombatManeuverPVE(Unit* pTarget);
  25726. + CombatManeuverReturns DoFirstCombatManeuverPVP(Unit* pTarget);
  25727. + CombatManeuverReturns DoNextCombatManeuverPVP(Unit* pTarget);
  25728. +
  25729. + // ARMS
  25730. + uint32 BATTLE_STANCE,
  25731. + CHARGE,
  25732. + HEROIC_STRIKE,
  25733. + REND,
  25734. + THUNDER_CLAP,
  25735. + HAMSTRING,
  25736. + MOCKING_BLOW,
  25737. + RETALIATION,
  25738. + SWEEPING_STRIKES,
  25739. + MORTAL_STRIKE,
  25740. + BLADESTORM,
  25741. + HEROIC_THROW,
  25742. + SHATTERING_THROW,
  25743. + TASTE_FOR_BLOOD,
  25744. + SUDDEN_DEATH;
  25745. +
  25746. + // PROTECTION
  25747. + uint32 DEFENSIVE_STANCE,
  25748. + BLOODRAGE,
  25749. + SUNDER_ARMOR,
  25750. + TAUNT,
  25751. + SHIELD_BASH,
  25752. + REVENGE,
  25753. + SHIELD_BLOCK,
  25754. + DISARM,
  25755. + SHIELD_WALL,
  25756. + SHIELD_SLAM,
  25757. + VIGILANCE,
  25758. + DEVASTATE,
  25759. + SHOCKWAVE,
  25760. + CONCUSSION_BLOW,
  25761. + SPELL_REFLECTION,
  25762. + LAST_STAND;
  25763. +
  25764. + // FURY
  25765. + uint32 BERSERKER_STANCE,
  25766. + BATTLE_SHOUT,
  25767. + DEMORALIZING_SHOUT,
  25768. + OVERPOWER,
  25769. + CLEAVE,
  25770. + INTIMIDATING_SHOUT,
  25771. + EXECUTE,
  25772. + CHALLENGING_SHOUT,
  25773. + SLAM,
  25774. + INTERCEPT,
  25775. + DEATH_WISH,
  25776. + BERSERKER_RAGE,
  25777. + WHIRLWIND,
  25778. + PUMMEL,
  25779. + BLOODTHIRST,
  25780. + RECKLESSNESS,
  25781. + RAMPAGE,
  25782. + HEROIC_FURY,
  25783. + COMMANDING_SHOUT,
  25784. + ENRAGED_REGENERATION,
  25785. + PIERCING_HOWL,
  25786. + SLAM_PROC,
  25787. + BLOODSURGE;
  25788. +
  25789. + // racial
  25790. + uint32 ARCANE_TORRENT,
  25791. + GIFT_OF_THE_NAARU,
  25792. + STONEFORM,
  25793. + ESCAPE_ARTIST,
  25794. + EVERY_MAN_FOR_HIMSELF,
  25795. + SHADOWMELD,
  25796. + BLOOD_FURY,
  25797. + WAR_STOMP,
  25798. + BERSERKING,
  25799. + WILL_OF_THE_FORSAKEN;
  25800. +
  25801. + // general
  25802. + uint32 AUTO_SHOT;
  25803. +
  25804. + uint32 SpellSequence;
  25805. +};
  25806. +
  25807. +#endif
  25808. diff --git a/src/server/game/CMakeLists.txt b/src/server/game/CMakeLists.txt
  25809. index f455610..2a13db8 100644
  25810. --- a/src/server/game/CMakeLists.txt
  25811. +++ b/src/server/game/CMakeLists.txt
  25812. @@ -129,6 +129,8 @@ include_directories(
  25813. ${CMAKE_CURRENT_SOURCE_DIR}/Achievements
  25814. ${CMAKE_CURRENT_SOURCE_DIR}/Addons
  25815. ${CMAKE_CURRENT_SOURCE_DIR}/AI
  25816. + ${CMAKE_CURRENT_SOURCE_DIR}/AI/NpcBots
  25817. + ${CMAKE_CURRENT_SOURCE_DIR}/AI/PlayerBots
  25818. ${CMAKE_CURRENT_SOURCE_DIR}/AI/CoreAI
  25819. ${CMAKE_CURRENT_SOURCE_DIR}/AI/EventAI
  25820. ${CMAKE_CURRENT_SOURCE_DIR}/AI/ScriptedAI
  25821. diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp
  25822. index 52ca60a..c752db4 100644
  25823. --- a/src/server/game/Entities/Creature/Creature.cpp
  25824. +++ b/src/server/game/Entities/Creature/Creature.cpp
  25825. @@ -54,6 +54,9 @@
  25826. // apply implementation of the singletons
  25827. +// npcbot
  25828. +#include "bot_ai.h"
  25829. +
  25830. TrainerSpell const* TrainerSpellData::Find(uint32 spell_id) const
  25831. {
  25832. TrainerSpellMap::const_iterator itr = spellList.find(spell_id);
  25833. @@ -165,6 +168,13 @@ m_creatureInfo(NULL), m_creatureData(NULL), m_path_id(0), m_formation(NULL)
  25834. ResetLootMode(); // restore default loot mode
  25835. TriggerJustRespawned = false;
  25836. m_isTempWorldObject = false;
  25837. +
  25838. + //bot
  25839. + m_bot_owner = NULL;
  25840. + m_creature_owner = NULL;
  25841. + m_bots_pet = NULL;
  25842. + m_bot_class = CLASS_NONE;
  25843. + bot_AI = NULL;
  25844. }
  25845. Creature::~Creature()
  25846. @@ -233,6 +243,8 @@ void Creature::RemoveCorpse(bool setSpawnTime)
  25847. {
  25848. if (getDeathState() != CORPSE)
  25849. return;
  25850. + if (bot_AI)
  25851. + return;
  25852. m_corpseRemoveTime = time(NULL);
  25853. setDeathState(DEAD);
  25854. @@ -2683,3 +2695,119 @@ Unit* Creature::SelectNearestHostileUnitInAggroRange(bool useLOS) const
  25855. return target;
  25856. }
  25857. +
  25858. +void Creature::SetIAmABot(bool bot)
  25859. +{
  25860. + if (!bot)
  25861. + {
  25862. + IsAIEnabled = false;
  25863. + bot_AI = NULL;
  25864. + SetUInt64Value(UNIT_FIELD_CREATEDBY, 0);
  25865. + }
  25866. +}
  25867. +
  25868. +void Creature::SetBotsPetDied()
  25869. +{
  25870. + if (!m_bots_pet)
  25871. + return;
  25872. + m_bots_pet->SetCharmerGUID(0);
  25873. + m_bots_pet->SetCreatureOwner(NULL);
  25874. + //m_bots_pet->GetBotPetAI()->SetCreatureOwner(NULL);
  25875. + m_bots_pet->SetIAmABot(false);
  25876. + m_bot_owner->SetMinion((Minion*)m_bots_pet, false);
  25877. + m_bots_pet->CleanupsBeforeDelete();
  25878. + m_bots_pet->AddObjectToRemoveList();
  25879. + m_bots_pet = NULL;
  25880. +}
  25881. +
  25882. +void Creature::SetBotTank(Unit* newtank)
  25883. +{
  25884. + if (!bot_AI || !IsAIEnabled)
  25885. + return;
  25886. + uint64 tankGuid = bot_AI->GetBotTankGuid();
  25887. + if (newtank && newtank->GetGUID() == tankGuid) return;
  25888. + Creature* oldtank = tankGuid && IS_CREATURE_GUID(tankGuid) ? sObjectAccessor->GetObjectInWorld(tankGuid, (Creature* )NULL) : NULL;
  25889. + if (oldtank && oldtank->IsInWorld() && (oldtank->GetIAmABot() || oldtank->GetIAmABotsPet()))
  25890. + {
  25891. + oldtank->RemoveAurasDueToSpell(DEFENSIVE_STANCE_PASSIVE);
  25892. + uint8 ClassOrPetType = oldtank->GetIAmABotsPet() ? bot_pet_ai::GetPetType(oldtank) : oldtank->GetBotClass();
  25893. + oldtank->GetBotAI()->ApplyPassives(ClassOrPetType);
  25894. + }
  25895. + if (newtank == this)
  25896. + {
  25897. + for (uint8 i = 0; i < 3; ++i)
  25898. + AddAura(DEFENSIVE_STANCE_PASSIVE, this);
  25899. + if (Player* owner = m_bot_owner)
  25900. + {
  25901. + switch (urand(1,5))
  25902. + {
  25903. + case 1: MonsterWhisper("I am tank here!", owner->GetGUID()); break;
  25904. + case 2: MonsterWhisper("I will tank now.", owner->GetGUID()); break;
  25905. + case 3: MonsterWhisper("I gonna tank", owner->GetGUID()); break;
  25906. + case 4: MonsterWhisper("I think I will be best tank here...", owner->GetGUID()); break;
  25907. + case 5: MonsterWhisper("I AM the tank!", owner->GetGUID()); break;
  25908. + }
  25909. + }
  25910. + bot_AI->UpdateHealth();
  25911. + if (!isInCombat())
  25912. + SetBotCommandState(COMMAND_FOLLOW, true);
  25913. + }
  25914. + bot_AI->SetBotTank(newtank);
  25915. +}
  25916. +
  25917. +void Creature::SetBotCommandState(CommandStates st, bool force)
  25918. +{
  25919. + if (bot_AI && IsAIEnabled)
  25920. + bot_AI->SetBotCommandState(st, force);
  25921. +}
  25922. +//Bot damage mods
  25923. +void Creature::ApplyBotDamageMultiplierMelee(uint32& damage, CalcDamageInfo& damageinfo) const
  25924. +{
  25925. + if (bot_AI)
  25926. + bot_AI->ApplyBotDamageMultiplierMelee(damage, damageinfo);
  25927. +}
  25928. +void Creature::ApplyBotDamageMultiplierMelee(int32& damage, SpellNonMeleeDamage& damageinfo, SpellInfo const* spellInfo, WeaponAttackType attackType, bool& crit) const
  25929. +{
  25930. + if (bot_AI)
  25931. + bot_AI->ApplyBotDamageMultiplierMelee(damage, damageinfo, spellInfo, attackType, crit);
  25932. +}
  25933. +void Creature::ApplyBotDamageMultiplierSpell(int32& damage, SpellNonMeleeDamage& damageinfo, SpellInfo const* spellInfo, WeaponAttackType attackType, bool& crit) const
  25934. +{
  25935. + if (bot_AI)
  25936. + bot_AI->ApplyBotDamageMultiplierSpell(damage, damageinfo, spellInfo, attackType, crit);
  25937. +}
  25938. +
  25939. +bool Creature::GetIAmABot() const
  25940. +{
  25941. + return bot_AI ? bot_AI->IsMinionAI() : false;
  25942. +}
  25943. +
  25944. +bool Creature::GetIAmABotsPet() const
  25945. +{
  25946. + return bot_AI ? bot_AI->IsPetAI() : false;
  25947. +}
  25948. +
  25949. +bot_minion_ai* Creature::GetBotMinionAI() const
  25950. +{
  25951. + return IsAIEnabled && bot_AI && bot_AI->IsMinionAI() ? const_cast<bot_minion_ai*>(bot_AI->GetMinionAI()) : NULL;
  25952. +}
  25953. +
  25954. +bot_pet_ai* Creature::GetBotPetAI() const
  25955. +{
  25956. + return IsAIEnabled && bot_AI && bot_AI->IsPetAI() ? const_cast<bot_pet_ai*>(bot_AI->GetPetAI()) : NULL;
  25957. +}
  25958. +
  25959. +void Creature::InitBotAI(bool asPet)
  25960. +{
  25961. + ASSERT(!bot_AI);
  25962. +
  25963. + if (asPet)
  25964. + bot_AI = (bot_pet_ai*)AI();
  25965. + else
  25966. + bot_AI = (bot_minion_ai*)AI();
  25967. +}
  25968. +
  25969. +void Creature::SetBotShouldUpdateStats()
  25970. +{
  25971. + if (bot_AI) bot_AI->SetShouldUpdateStats();
  25972. +}
  25973. diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h
  25974. index 6e771d2..8a78e77 100644
  25975. --- a/src/server/game/Entities/Creature/Creature.h
  25976. +++ b/src/server/game/Entities/Creature/Creature.h
  25977. @@ -37,6 +37,11 @@ class Player;
  25978. class WorldSession;
  25979. class CreatureGroup;
  25980. +// npcbot
  25981. +class bot_ai;
  25982. +class bot_minion_ai;
  25983. +class bot_pet_ai;
  25984. +
  25985. enum CreatureFlagsExtra
  25986. {
  25987. CREATURE_FLAG_EXTRA_INSTANCE_BIND = 0x00000001, // creature kill bind instance with killer and killer's group
  25988. @@ -694,6 +699,31 @@ class Creature : public Unit, public GridObject<Creature>, public MapCreature
  25989. bool m_isTempWorldObject; //true when possessed
  25990. + //Bot commands
  25991. + Player* GetBotOwner() const { return m_bot_owner; }
  25992. + void SetBotOwner(Player* newowner) { m_bot_owner = newowner; }
  25993. + Creature* GetCreatureOwner() const { return m_creature_owner; }
  25994. + void SetCreatureOwner(Creature* newCreOwner) { m_creature_owner = newCreOwner; }
  25995. + Creature* GetBotsPet() const { return m_bots_pet; }
  25996. + void SetBotsPetDied();
  25997. + void SetBotsPet(Creature* newpet) { /*ASSERT (!m_bots_pet);*/ m_bots_pet = newpet; }
  25998. + void SetIAmABot(bool bot = true);
  25999. + bool GetIAmABot() const;
  26000. + bool GetIAmABotsPet() const;
  26001. + void SetBotClass(uint8 myclass) { m_bot_class = myclass; }
  26002. + uint8 GetBotClass() const { return m_bot_class; }
  26003. + void SetBotTank(Unit* newtank);
  26004. + bot_ai* GetBotAI() const { return bot_AI; }
  26005. + bot_minion_ai* GetBotMinionAI() const;
  26006. + bot_pet_ai* GetBotPetAI() const;
  26007. + void InitBotAI(bool asPet = false);
  26008. + void SetBotCommandState(CommandStates st, bool force = false);
  26009. + void ApplyBotDamageMultiplierMelee(uint32& damage, CalcDamageInfo& damageinfo) const;
  26010. + void ApplyBotDamageMultiplierMelee(int32& damage, SpellNonMeleeDamage& damageinfo, SpellInfo const* spellInfo, WeaponAttackType attackType, bool& crit) const;
  26011. + void ApplyBotDamageMultiplierSpell(int32& damage, SpellNonMeleeDamage& damageinfo, SpellInfo const* spellInfo, WeaponAttackType attackType, bool& crit) const;
  26012. + void SetBotShouldUpdateStats();
  26013. + //Bot commands
  26014. +
  26015. protected:
  26016. bool CreateFromProto(uint32 guidlow, uint32 Entry, uint32 vehId, uint32 team, const CreatureData* data = NULL);
  26017. bool InitEntry(uint32 entry, uint32 team=ALLIANCE, const CreatureData* data=NULL);
  26018. @@ -744,6 +774,14 @@ class Creature : public Unit, public GridObject<Creature>, public MapCreature
  26019. bool IsInvisibleDueToDespawn() const;
  26020. bool CanAlwaysSee(WorldObject const* obj) const;
  26021. private:
  26022. + //bot system
  26023. + Player* m_bot_owner;
  26024. + Creature* m_creature_owner;
  26025. + Creature* m_bots_pet;
  26026. + bot_ai* bot_AI;
  26027. + uint8 m_bot_class;
  26028. + //end bot system
  26029. +
  26030. void ForcedDespawn(uint32 timeMSToDespawn = 0);
  26031. //WaypointMovementGenerator vars
  26032. diff --git a/src/server/game/Entities/Creature/TemporarySummon.cpp b/src/server/game/Entities/Creature/TemporarySummon.cpp
  26033. index d23af7e..805b6ad 100644
  26034. --- a/src/server/game/Entities/Creature/TemporarySummon.cpp
  26035. +++ b/src/server/game/Entities/Creature/TemporarySummon.cpp
  26036. @@ -253,6 +253,14 @@ void TempSummon::UnSummon(uint32 msTime)
  26037. if (owner && owner->GetTypeId() == TYPEID_UNIT && owner->ToCreature()->IsAIEnabled)
  26038. owner->ToCreature()->AI()->SummonedCreatureDespawn(this);
  26039. +//npcbot
  26040. + if (GetIAmABot() || GetIAmABotsPet())
  26041. + {
  26042. + //sLog->outError("TempSummon::UnSummon(): Trying to unsummon Bot %s(owner: %s). Aborted", GetName(), GetBotOwner()->GetName());
  26043. + return;
  26044. + }
  26045. +//end npcbots
  26046. +
  26047. AddObjectToRemoveList();
  26048. }
  26049. diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
  26050. index 49d210f..4038490 100644
  26051. --- a/src/server/game/Entities/Player/Player.cpp
  26052. +++ b/src/server/game/Entities/Player/Player.cpp
  26053. @@ -79,6 +79,11 @@
  26054. #include "WorldPacket.h"
  26055. #include "WorldSession.h"
  26056. +//Bot mod
  26057. +#include "Config.h"
  26058. +#include "bp_ai.h"
  26059. +#include "bp_mgr.h"
  26060. +
  26061. #define ZONE_UPDATE_INTERVAL (1*IN_MILLISECONDS)
  26062. #define PLAYER_SKILL_INDEX(x) (PLAYER_SKILL_INFO_1_1 + ((x)*3))
  26063. @@ -521,6 +526,18 @@ inline void KillRewarder::_RewardXP(Player* player, float rate)
  26064. for (Unit::AuraEffectList::const_iterator i = auras.begin(); i != auras.end(); ++i)
  26065. AddPct(xp, (*i)->GetAmount());
  26066. + //npcbot 4.2.2.1. Apply NpcBot XP reduction
  26067. + if (player->HaveBot() && player->GetNpcBotsCount() > 1)
  26068. + {
  26069. + if (uint8 xp_rate = player->GetNpcBotXpReduction())
  26070. + {
  26071. + int32 ratePct = 100 - (player->GetNpcBotsCount() - 1) * xp_rate;
  26072. + ratePct = std::max<int32>(ratePct, 10); // minimum
  26073. + //ratePct = std::min<int32>(ratePct, 100); // maximum // dead code
  26074. + xp = xp * ratePct / 100;
  26075. + }
  26076. + }
  26077. +
  26078. // 4.2.3. Give XP to player.
  26079. player->GiveXP(xp, _victim, _groupRate);
  26080. if (Pet* pet = player->GetPet())
  26081. @@ -858,6 +875,32 @@ Player::Player(WorldSession* session): Unit(true)
  26082. m_ChampioningFaction = 0;
  26083. + ///////////////////// Bot System ////////////////////////
  26084. + //Playerbot
  26085. + m_playerbotAI = NULL;
  26086. + m_playerbotMgr = NULL;
  26087. + //Npcbot
  26088. + m_botTimer = 500;
  26089. + m_bot = NULL;
  26090. + m_botTankGuid = 0;
  26091. + m_enableNpcBots = ConfigMgr::GetBoolDefault("Bot.EnableNpcBots", true);
  26092. + m_followdist = ConfigMgr::GetIntDefault("Bot.BaseFollowDistance", 30);
  26093. + m_maxNpcBots = std::min<uint8>(ConfigMgr::GetIntDefault("Bot.MaxNpcBots", 1), MAX_NPCBOTS);
  26094. + uint8 maxcbots = ConfigMgr::GetIntDefault("Bot.MaxNpcBotsPerClass", 1);
  26095. + m_maxClassNpcBots = maxcbots > 0 ? maxcbots : MAX_NPCBOTS;
  26096. + m_xpReductionNpcBots = std::min<uint8>(ConfigMgr::GetIntDefault("Bot.XpReductionPercent", 0), 100);
  26097. + m_enableAllNpcBots = ConfigMgr::GetBoolDefault("Bot.AllowAllClasses", false);
  26098. + m_enableNpcBotsArenas = ConfigMgr::GetBoolDefault("Bot.EnableInArenas", true);
  26099. + m_enableNpcBotsBGs = ConfigMgr::GetBoolDefault("Bot.EnableInBGs", true);
  26100. + m_enableNpcBotsDungeons = ConfigMgr::GetBoolDefault("Bot.EnableInDungeons", true);
  26101. + m_enableNpcBotsRaids = ConfigMgr::GetBoolDefault("Bot.EnableInRaids", true);
  26102. + m_limitNpcBotsDungeons = ConfigMgr::GetBoolDefault("Bot.InstanceLimit.Dungeons", false);
  26103. + m_limitNpcBotsRaids = ConfigMgr::GetBoolDefault("Bot.InstanceLimit.Raids", false);
  26104. + m_NpcBotsCost = ConfigMgr::GetIntDefault("Bot.Cost", 0);
  26105. + for (uint8 i = 0; i != GetMaxNpcBots(); ++i)
  26106. + m_botmap[i] = new NpcBotMap();
  26107. + ///////////////////// End Bot System ////////////////////////
  26108. +
  26109. for (uint8 i = 0; i < MAX_POWERS; ++i)
  26110. m_powerFraction[i] = 0;
  26111. @@ -910,6 +953,22 @@ Player::~Player()
  26112. delete m_achievementMgr;
  26113. delete m_reputationMgr;
  26114. + //Playerbot mod: delete mgr/ai
  26115. + if (m_playerbotAI)
  26116. + {
  26117. + delete m_playerbotAI;
  26118. + m_playerbotAI = NULL;
  26119. + }
  26120. + else if (m_playerbotMgr)
  26121. + {
  26122. + delete m_playerbotMgr;
  26123. + m_playerbotMgr = NULL;
  26124. + }
  26125. +
  26126. + //Npcbot mod: delete botmap
  26127. + for (uint8 i = 0; i != GetMaxNpcBots(); ++i)
  26128. + delete m_botmap[i];
  26129. +
  26130. sWorld->DecreasePlayerCount();
  26131. }
  26132. @@ -1834,6 +1893,23 @@ void Player::Update(uint32 p_time)
  26133. //because we don't want player's ghost teleported from graveyard
  26134. if (IsHasDelayedTeleport() && isAlive())
  26135. TeleportTo(m_teleport_dest, m_teleport_options);
  26136. +
  26137. + //Playerbot mod: Update
  26138. + if (m_playerbotAI)
  26139. + m_playerbotAI->UpdateAI(p_time);
  26140. + //else if (m_playerbotMgr)
  26141. + // m_playerbotMgr->Update(p_time);
  26142. +
  26143. + //NpcBot mod: Update
  26144. + if (m_botTimer > 0)
  26145. + {
  26146. + if (p_time >= m_botTimer)
  26147. + m_botTimer = 0;
  26148. + else
  26149. + m_botTimer -= p_time;
  26150. + }
  26151. + else
  26152. + RefreshBot(p_time);
  26153. }
  26154. void Player::setDeathState(DeathState s)
  26155. @@ -2097,6 +2173,19 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati
  26156. return false;
  26157. }
  26158. + //Playerbot mod: tell bots to stop following master
  26159. + //so they don't try to follow the master after he teleports
  26160. + //TODO: this should be deleted after pointed movement implementation
  26161. + if (m_playerbotMgr != NULL)
  26162. + m_playerbotMgr->Stay();
  26163. +
  26164. + //Npcbot mod: prevent crash on InstanceMap::DestroyInstance()... Unit::RemoveFromWorld()
  26165. + //if last player being kicked out of instance while having npcbots
  26166. + //we must remove creature Before it will be removed in Map::UnloadAll()
  26167. + if (GetMapId() != mapid)
  26168. + for (uint8 i = 0; i != GetMaxNpcBots(); ++i)
  26169. + RemoveBot(m_botmap[i]->m_guid);
  26170. +
  26171. // preparing unsummon pet if lost (we must get pet before teleportation or will not find it later)
  26172. Pet* pet = GetPet();
  26173. @@ -2461,6 +2550,764 @@ void Player::RemoveFromWorld()
  26174. }
  26175. }
  26176. +void Player::RefreshBot(uint32 diff)
  26177. +{
  26178. + if (m_botTimer > 0) return;
  26179. + m_botTimer = 100 + isInFlight()*3000;//x2 ms //temp hack
  26180. + if (!HaveBot()) return;
  26181. +
  26182. + //addition for revive timer (maybe we should check whole party?)
  26183. + bool partyInCombat = isInCombat();
  26184. + if (!partyInCombat)
  26185. + {
  26186. + for (uint8 i = 0; i != GetMaxNpcBots(); ++i)
  26187. + {
  26188. + if (Creature* bot = m_botmap[i]->m_creature)
  26189. + {
  26190. + if (bot->isInCombat())
  26191. + {
  26192. + partyInCombat = true;
  26193. + break;
  26194. + }
  26195. + else if (Creature* pet = bot->GetBotsPet())
  26196. + {
  26197. + if (pet->isInCombat())
  26198. + {
  26199. + partyInCombat = true;
  26200. + break;
  26201. + }
  26202. + }
  26203. + }
  26204. + }
  26205. + }
  26206. +
  26207. + for (uint8 i = 0; i != GetMaxNpcBots(); ++i)
  26208. + {
  26209. + uint64 guid = m_botmap[i]->m_guid;
  26210. + m_bot = m_botmap[i]->m_creature;
  26211. + if (!m_bot || !m_bot->IsInWorld())
  26212. + continue;
  26213. + //BOT REVIVE SUPPORT
  26214. + //Do not allow bot to be revived if master is in battle
  26215. + if (!partyInCombat)
  26216. + {
  26217. + if (m_botmap[i]->m_reviveTimer > diff)
  26218. + {
  26219. + if (!isInCombat())
  26220. + m_botmap[i]->m_reviveTimer -= diff;
  26221. + }
  26222. + else if (m_botmap[i]->m_reviveTimer > 0)
  26223. + m_botmap[i]->m_reviveTimer = 0;
  26224. + }
  26225. + if ((m_bot->isDead() || !m_bot->isAlive()) && isAlive() && !isInCombat() && !InArena() && !isInFlight() &&
  26226. + !HasFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_FEIGN_DEATH) &&
  26227. + m_botmap[i]->m_reviveTimer == 0 &&
  26228. + !HasInvisibilityAura() && !HasStealthAura())
  26229. + {
  26230. + CreateBot(0, 0, 0, false, true);//revive
  26231. + continue;
  26232. + }
  26233. + //BOT MUST DIE SUPPORT
  26234. + if (isInFlight() || !GetGroup() || !GetGroup()->IsMember(m_bot->GetGUID()))//even if bot is dead
  26235. + {
  26236. + RemoveBot(guid, !isInFlight());
  26237. + continue;
  26238. + }
  26239. + //TELEPORT/OUTRUN SUPPORT
  26240. + if (!isInFlight() && isAlive() && (m_bot->isAlive() || m_bot->GetMapId() != GetMapId()))
  26241. + {
  26242. + float maxdist;
  26243. + if (GetMap()->IsDungeon())
  26244. + maxdist = sWorld->GetMaxVisibleDistanceInInstances();
  26245. + else if (GetMap()->IsBattlegroundOrArena())
  26246. + maxdist = sWorld->GetMaxVisibleDistanceInBGArenas();
  26247. + else
  26248. + maxdist = sWorld->GetMaxVisibleDistanceOnContinents();
  26249. +
  26250. + maxdist += 20.0f; //allow player to recall it by moving back
  26251. +
  26252. + if (abs(m_bot->GetPositionX() - GetPositionX()) > maxdist ||
  26253. + abs(m_bot->GetPositionY() - GetPositionY()) > maxdist ||
  26254. + m_bot->GetMapId() != GetMapId() || RestrictBots())
  26255. + {
  26256. + RemoveBot(guid);
  26257. + continue;
  26258. + }
  26259. + }
  26260. + m_bot = NULL;
  26261. + }//end for botmap
  26262. + //BOT CREATION/RECREATION SUPPORT
  26263. + if (!isInFlight() && isAlive() && !HasUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT) && GetBotMustBeCreated() && !RestrictBots())
  26264. + for (uint8 pos = 0; pos != GetMaxNpcBots(); ++pos)
  26265. + if (m_botmap[pos]->m_entry != 0 && m_botmap[pos]->m_guid == 0)
  26266. + CreateBot(m_botmap[pos]->m_entry, m_botmap[pos]->m_race, m_botmap[pos]->m_class, m_botmap[pos]->tank);
  26267. +}
  26268. +
  26269. +void Player::SetBotMustBeCreated(uint32 m_entry, uint8 m_race, uint8 m_class, bool istank)
  26270. +{
  26271. + if (m_enableNpcBots == false)
  26272. + {
  26273. + ChatHandler ch(GetSession());
  26274. + ch.SendSysMessage("NpcBot system currently disabled. Please contact your administration.");
  26275. + ClearBotMustBeCreated(0, 0, true);
  26276. + return;
  26277. + }
  26278. + for (uint8 pos = 0; pos != GetMaxNpcBots(); ++pos)
  26279. + {
  26280. + if (m_botmap[pos]->m_entry == 0)
  26281. + {
  26282. + m_botmap[pos]->m_guid = 0;//we need it to make sure Player::CreateBot will find this slot
  26283. + m_botmap[pos]->m_entry = m_entry;
  26284. + m_botmap[pos]->m_race = m_race;
  26285. + m_botmap[pos]->m_class = m_class;
  26286. + m_botmap[pos]->tank = istank;
  26287. + break;
  26288. + }
  26289. + }
  26290. +}
  26291. +
  26292. +bool Player::GetBotMustBeCreated()
  26293. +{
  26294. + for (uint8 pos = 0; pos != GetMaxNpcBots(); ++pos)
  26295. + {
  26296. + if (m_botmap[pos]->m_entry != 0 &&
  26297. + (m_botmap[pos]->m_guid == 0 || !sObjectAccessor->FindUnit(m_botmap[pos]->m_guid)))
  26298. + {
  26299. + m_botmap[pos]->m_guid = 0;
  26300. + return true;
  26301. + }
  26302. + }
  26303. + return false;
  26304. +}
  26305. +
  26306. +void Player::ClearBotMustBeCreated(uint64 guidOrSlot, bool guid, bool fully)
  26307. +{
  26308. + for (uint8 pos = 0; pos != GetMaxNpcBots(); ++pos)
  26309. + {
  26310. + if ((guid == true && m_botmap[pos]->m_guid == guidOrSlot) ||
  26311. + (guid == false && pos == guidOrSlot) ||
  26312. + fully)
  26313. + {
  26314. + m_botmap[pos]->m_guid = 0;
  26315. + m_botmap[pos]->m_entry = 0;
  26316. + m_botmap[pos]->m_race = 0;
  26317. + m_botmap[pos]->m_class = 0;
  26318. + m_botmap[pos]->m_creature = NULL;
  26319. + m_botmap[pos]->tank = false;
  26320. + if (!fully)
  26321. + break;
  26322. + }
  26323. + }
  26324. +}
  26325. +
  26326. +void Player::RemoveBot(uint64 guid, bool final, bool eraseFromDB)
  26327. +{
  26328. + if (guid == 0) return;
  26329. + for (uint8 i = 0; i != GetMaxNpcBots(); ++i)
  26330. + {
  26331. + if (m_botmap[i]->m_guid == guid)
  26332. + {
  26333. + m_bot = m_botmap[i]->m_creature;
  26334. + break;
  26335. + }
  26336. + }
  26337. + if (!m_bot)
  26338. + m_bot = sObjectAccessor->GetObjectInWorld(guid, (Creature*)NULL);
  26339. + if (m_bot)
  26340. + {
  26341. + //do not disband group unless not in dungeon or forced or on logout (Check WorldSession::LogoutPlayer())
  26342. + Group* gr = GetGroup();
  26343. + if (gr && gr->IsMember(guid))
  26344. + {
  26345. + if (gr->GetMembersCount() > 2 || /*!GetMap()->Instanceable() || */(final && eraseFromDB))
  26346. + gr->RemoveMember(guid);
  26347. + else //just cleanup
  26348. + {
  26349. + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GROUP_MEMBER);
  26350. + stmt->setUInt32(0, GUID_LOPART(guid));
  26351. + CharacterDatabase.Execute(stmt);
  26352. + }
  26353. + }
  26354. +
  26355. + m_bot->SetBotsPetDied();
  26356. + m_bot->SetCharmerGUID(0);
  26357. + //m_bot->SetBotOwner(NULL);
  26358. + m_bot->SetIAmABot(false);
  26359. + SetMinion((Minion*)m_bot, false);
  26360. + m_bot->CleanupsBeforeDelete();
  26361. + m_bot->AddObjectToRemoveList();
  26362. +
  26363. + if (final)//on logout or by command
  26364. + {
  26365. + ClearBotMustBeCreated(guid);
  26366. + if (eraseFromDB)//by command
  26367. + {
  26368. + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_NPCBOT);
  26369. + stmt->setUInt32(0, GetGUIDLow());
  26370. + stmt->setUInt32(1, m_bot->GetEntry());
  26371. + CharacterDatabase.Execute(stmt);
  26372. + //CharacterDatabase.PExecute("DELETE FROM `character_npcbot` WHERE `owner` = '%u' AND `entry` = '%u'", GetGUIDLow(), m_bot->GetEntry());
  26373. + }
  26374. + }
  26375. + else
  26376. + {
  26377. + for (uint8 pos = 0; pos != GetMaxNpcBots(); ++pos)
  26378. + {
  26379. + if (m_botmap[pos]->m_guid == guid)
  26380. + {
  26381. + m_botmap[pos]->m_guid = 0;//reset guid so it can be set during recreation
  26382. + m_botmap[pos]->m_creature = NULL;
  26383. + }
  26384. + }
  26385. + }
  26386. + m_bot = NULL;
  26387. + }
  26388. +}
  26389. +
  26390. +void Player::CreateBot(uint32 botentry, uint8 botrace, uint8 botclass, bool istank, bool revive)
  26391. +{
  26392. + if (IsBeingTeleported() || isInFlight()) return; //don't create bot yet
  26393. + if (isDead() && !revive) return; //not to revive by command so abort
  26394. + if (isInCombat()) return;
  26395. +
  26396. + if (m_bot != NULL && revive)
  26397. + {
  26398. + m_bot->SetHealth(uint32(float(m_bot->GetCreateHealth()) * 0.15f));//15% of base health
  26399. + if (m_bot->getPowerType() == POWER_MANA)
  26400. + m_bot->SetPower(POWER_MANA, m_bot->GetCreateMana());
  26401. + m_bot->setDeathState(ALIVE);
  26402. + m_bot->SetBotCommandState(COMMAND_FOLLOW, true);
  26403. + return;
  26404. + }
  26405. + if (m_enableNpcBots == false && revive == false)
  26406. + {
  26407. + ChatHandler ch(GetSession());
  26408. + ch.SendSysMessage("NpcBot system currently disabled. Please contact administration.");
  26409. + for (uint8 pos = 0; pos != GetMaxNpcBots(); ++pos)
  26410. + if (m_botmap[pos]->m_entry == botentry)
  26411. + ClearBotMustBeCreated(pos, false);
  26412. + return;
  26413. + }
  26414. + if (!botentry || !botrace || !botclass)
  26415. + {
  26416. + sLog->outError(LOG_FILTER_PLAYER, "ERROR! CreateBot(): player %s (%u) trying to create bot with entry = %u, race = %u, class = %u, ignored", GetName().c_str(), GetGUIDLow(), botentry, botrace, botclass);
  26417. + for (uint8 pos = 0; pos != GetMaxNpcBots(); ++pos)
  26418. + if (m_botmap[pos]->m_entry == botentry)
  26419. + ClearBotMustBeCreated(pos, false);
  26420. + return;
  26421. + }
  26422. + //npcbot counter is already increased in SetBotMustBeCreated()
  26423. + if (GetNpcBotsCount() > GetMaxNpcBots())
  26424. + {
  26425. + ChatHandler ch(GetSession());
  26426. + for (uint8 pos = 0; pos != GetMaxNpcBots(); ++pos)
  26427. + if (m_botmap[pos]->m_entry == botentry)
  26428. + ClearBotMustBeCreated(pos, false);
  26429. + ch.PSendSysMessage("Youre exceed max npcbots");
  26430. + ch.SetSentErrorMessage(true);
  26431. + return;
  26432. + }
  26433. + //instance limit check
  26434. + if ((m_limitNpcBotsDungeons && GetMap()->IsNonRaidDungeon()) || (m_limitNpcBotsRaids && GetMap()->IsRaid()))
  26435. + {
  26436. + InstanceMap* map = (InstanceMap*)GetMap();
  26437. + uint32 count = 0;
  26438. + Map::PlayerList const& plMap = map->GetPlayers();
  26439. + for (Map::PlayerList::const_iterator itr = plMap.begin(); itr != plMap.end(); ++itr)
  26440. + if (Player* player = itr->getSource())
  26441. + count += (1 + player->GetNpcBotsCount());
  26442. +
  26443. + //check "more" cuz current bot is queued and we are to choose to remove it or not
  26444. + if (count > map->GetMaxPlayers())
  26445. + {
  26446. + ChatHandler ch(GetSession());
  26447. + for (uint8 pos = 0; pos != GetMaxNpcBots(); ++pos)
  26448. + if (m_botmap[pos]->m_entry == botentry)
  26449. + ClearBotMustBeCreated(pos, false);
  26450. + ch.PSendSysMessage("Instance players limit exceed");
  26451. + ch.SetSentErrorMessage(true);
  26452. + return;
  26453. + }
  26454. + }
  26455. + if (GetGroup() && GetGroup()->isRaidGroup() && GetGroup()->IsFull())
  26456. + {
  26457. + ChatHandler ch(GetSession());
  26458. + ch.PSendSysMessage("Your group is Full!");
  26459. + ch.SetSentErrorMessage(true);
  26460. + return;
  26461. + }
  26462. + for (uint8 pos = 0; pos != GetMaxNpcBots(); ++pos)
  26463. + if (m_botmap[pos]->m_entry == botentry)
  26464. + if (m_botmap[pos]->m_reviveTimer != 0)
  26465. + return;
  26466. +
  26467. + m_bot = SummonCreature(botentry, *this);
  26468. +
  26469. + //check if we have free slot
  26470. + bool _set = false;
  26471. + for (uint8 pos = 0; pos != GetMaxNpcBots(); ++pos)
  26472. + {
  26473. + if (m_botmap[pos]->m_entry == botentry && m_botmap[pos]->m_guid == 0)
  26474. + {
  26475. + m_botmap[pos]->m_guid = m_bot->GetGUID();
  26476. + m_botmap[pos]->m_creature = m_bot;//this will save some time but we need guid as well
  26477. + m_botmap[pos]->tank = istank;
  26478. + _set = true;
  26479. + break;
  26480. + }
  26481. + }
  26482. + if (!_set)
  26483. + {
  26484. + sLog->outError(LOG_FILTER_PLAYER, "character %s (%u) is failed to create npcbot! Removing all bots", GetName().c_str(), GetGUIDLow());
  26485. +
  26486. + m_bot->CombatStop();
  26487. + m_bot->CleanupsBeforeDelete();
  26488. + m_bot->AddObjectToRemoveList();
  26489. + for (uint8 pos = 0; pos != GetMaxNpcBots(); ++pos)
  26490. + RemoveBot(m_botmap[pos]->m_guid, true);
  26491. + ClearBotMustBeCreated(0, false, true);
  26492. + return;
  26493. + }
  26494. +
  26495. + m_bot->SetBotOwner(this);
  26496. + m_bot->SetUInt64Value(UNIT_FIELD_CREATEDBY, GetGUID());
  26497. + SetMinion((Minion*)m_bot, true);
  26498. + m_bot->CombatStop();
  26499. + m_bot->DeleteThreatList();
  26500. + m_bot->AddUnitTypeMask(UNIT_MASK_MINION);
  26501. +
  26502. + m_bot->SetByteValue(UNIT_FIELD_BYTES_0, 0, botrace);
  26503. + m_bot->setFaction(getFaction());
  26504. + m_bot->SetLevel(getLevel());
  26505. + m_bot->SetBotClass(botclass);
  26506. + m_bot->AIM_Initialize();
  26507. + m_bot->InitBotAI();
  26508. + m_bot->SetBotCommandState(COMMAND_FOLLOW, true);
  26509. +
  26510. + SQLTransaction trans = CharacterDatabase.BeginTransaction();
  26511. + //entry is unique for each master's bot so clean it up just in case
  26512. + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_NPCBOT);
  26513. + stmt->setUInt32(0, GetGUIDLow());
  26514. + stmt->setUInt32(1, botentry);
  26515. + trans->Append(stmt);
  26516. + //CharacterDatabase.Execute(stmt);
  26517. + //CharacterDatabase.PExecute("DELETE FROM `character_npcbot` WHERE `owner` = '%u' AND `entry` = '%u'", GetGUIDLow(), botentry);
  26518. + //add the new entry
  26519. + stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_NPCBOT);
  26520. + stmt->setUInt32(0, GetGUIDLow());
  26521. + stmt->setUInt32(1, botentry);
  26522. + stmt->setUInt8(2, botrace);
  26523. + stmt->setUInt8(3, botclass);
  26524. + stmt->setUInt8(4, uint8(istank));
  26525. + trans->Append(stmt);
  26526. + CharacterDatabase.CommitTransaction(trans);
  26527. + //CharacterDatabase.Execute(stmt);
  26528. + //CharacterDatabase.PExecute("INSERT INTO `character_npcbot` (owner,entry,race,class,istank) VALUES ('%u','%u','%u','%u','%u')", GetGUIDLow(), botentry, botrace, botclass, uint8(istank));
  26529. + //If we have a group, just add bot
  26530. + if (Group* gr = GetGroup())
  26531. + {
  26532. + if (!gr->IsFull())
  26533. + {
  26534. + if (!gr->AddMember((Player*)m_bot))
  26535. + RemoveBot(m_bot->GetGUID(), true);
  26536. + }
  26537. + else if (!gr->isRaidGroup()) //non-raid group is full
  26538. + {
  26539. + gr->ConvertToRaid();
  26540. + if (!gr->AddMember((Player*)m_bot))
  26541. + RemoveBot(m_bot->GetGUID(), true);
  26542. + }
  26543. + else //raid group is full
  26544. + RemoveBot(m_bot->GetGUID(), true);
  26545. + }
  26546. + else
  26547. + {
  26548. + gr = new Group;
  26549. + if (!gr->Create(this))
  26550. + {
  26551. + delete gr;
  26552. + return;
  26553. + }
  26554. + sGroupMgr->AddGroup(gr);
  26555. + if (!gr->AddMember((Player*)m_bot))
  26556. + RemoveBot(m_bot->GetGUID(), true);
  26557. + }
  26558. +
  26559. + if (Group* gr = GetGroup())
  26560. + {
  26561. + Group::MemberSlotList const a = gr->GetMemberSlots();
  26562. + //try to remove 'absent' bots
  26563. + for (Group::member_citerator itr = a.begin(); itr != a.end(); ++itr)
  26564. + {
  26565. + if (itr->guid == 0)
  26566. + continue;
  26567. + if (IS_PLAYER_GUID(itr->guid))
  26568. + continue;
  26569. + if (!sObjectAccessor->FindUnit(itr->guid))
  26570. + gr->RemoveMember(itr->guid);
  26571. + }
  26572. + }
  26573. +
  26574. +} //end Player::CreateBot
  26575. +
  26576. +uint8 Player::GetNpcBotsCount() const
  26577. +{
  26578. + uint8 bots = 0;
  26579. + for (uint8 pos = 0; pos != GetMaxNpcBots(); ++pos)
  26580. + if (m_botmap[pos]->m_entry != 0)
  26581. + ++bots;
  26582. + return bots;
  26583. +}
  26584. +
  26585. +uint8 Player::GetMaxNpcBots() const
  26586. +{
  26587. + return (GetSession()->GetSecurity() == SEC_PLAYER) ? m_maxNpcBots : MAX_NPCBOTS;
  26588. +}
  26589. +
  26590. +bool Player::HaveBot() const
  26591. +{
  26592. + for (uint8 i = 0; i != GetMaxNpcBots(); ++i)
  26593. + if (m_botmap[i]->m_entry != 0)
  26594. + return true;
  26595. + return false;
  26596. +}
  26597. +
  26598. +void Player::SendBotCommandState(Creature* cre, CommandStates state)
  26599. +{
  26600. + if (!cre) return;
  26601. + for (uint8 i = 0; i != GetMaxNpcBots(); ++i)
  26602. + if (m_botmap[i]->m_creature == cre)
  26603. + cre->SetBotCommandState(state, true);
  26604. +}
  26605. +//finds bot's slot into master's botmap
  26606. +uint8 Player::GetNpcBotSlot(uint64 guid) const
  26607. +{
  26608. + if (guid)
  26609. + for (uint8 i = 0; i != GetMaxNpcBots(); ++i)
  26610. + if (m_botmap[i]->m_guid == guid)
  26611. + return i;
  26612. + return 0;
  26613. +}
  26614. +
  26615. +void Player::SetBotTank(uint64 guid)
  26616. +{
  26617. + m_botTankGuid = guid;
  26618. + if (guid == 0 || !IS_CREATURE_GUID(guid)) //reset tank or set player - remove from all npcbots
  26619. + {
  26620. + for (uint8 i = 0; i != GetMaxNpcBots(); ++i)
  26621. + {
  26622. + if (m_botmap[i]->tank == true)
  26623. + {
  26624. + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_NPCBOT_TANK);
  26625. + stmt->setUInt8(0, uint8(0));
  26626. + stmt->setUInt32(1, GetGUIDLow());
  26627. + stmt->setUInt32(2, m_botmap[i]->m_entry);
  26628. + CharacterDatabase.Execute(stmt);
  26629. + //CharacterDatabase.PExecute("UPDATE `character_npcbot` SET `istank` = '0' WHERE `owner` = '%u' AND `entry` = '%u'", GetGUIDLow(), m_botmap[i]->m_entry);
  26630. + m_botmap[i]->tank = false;
  26631. + }
  26632. + }
  26633. + return;
  26634. + }
  26635. + for (uint8 i = 0; i != GetMaxNpcBots(); ++i)
  26636. + {
  26637. + if (m_botmap[i]->tank == true)
  26638. + {
  26639. + if (m_botmap[i]->m_guid != guid)
  26640. + {
  26641. + m_botmap[i]->tank = false;
  26642. + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_NPCBOT_TANK);
  26643. + stmt->setUInt8(0, uint8(0));
  26644. + stmt->setUInt32(1, GetGUIDLow());
  26645. + stmt->setUInt32(2, m_botmap[i]->m_entry);
  26646. + CharacterDatabase.Execute(stmt);
  26647. + //CharacterDatabase.PExecute("UPDATE `character_npcbot` SET `istank` = '0' WHERE `owner` = '%u' AND `entry` = '%u'", GetGUIDLow(), m_botmap[i]->m_entry);
  26648. + }
  26649. + }
  26650. + else if (m_botmap[i]->m_guid == guid)
  26651. + {
  26652. + m_botmap[i]->tank = true;
  26653. + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_NPCBOT_TANK);
  26654. + stmt->setUInt8(0, uint8(1));
  26655. + stmt->setUInt32(1, GetGUIDLow());
  26656. + stmt->setUInt32(2, m_botmap[i]->m_entry);
  26657. + CharacterDatabase.Execute(stmt);
  26658. + //CharacterDatabase.PExecute("UPDATE `character_npcbot` SET `istank` = '1' WHERE `owner` = '%u' AND `entry` = '%u'", GetGUIDLow(), m_botmap[i]->m_entry);
  26659. + break;
  26660. + }
  26661. + }
  26662. +}
  26663. +
  26664. +Unit* Player::GetBotTank(uint32 entry)
  26665. +{
  26666. + if (!entry) return NULL;
  26667. +
  26668. + for (uint8 i = 0; i != GetMaxNpcBots(); ++i)
  26669. + if (m_botmap[i]->m_entry == entry && m_botmap[i]->tank == true)
  26670. + return m_botmap[i]->m_creature;
  26671. +
  26672. + return NULL;
  26673. +}
  26674. +
  26675. +void Player::SetNpcBotDied(uint64 guid)
  26676. +{
  26677. + if (!guid) return;
  26678. + for (uint8 pos = 0; pos != GetMaxNpcBots(); ++pos)
  26679. + if (m_botmap[pos]->m_guid == guid)
  26680. + {
  26681. + m_botmap[pos]->m_reviveTimer = 15000;
  26682. + break;
  26683. + }
  26684. +}
  26685. +
  26686. +bool Player::RestrictBots() const
  26687. +{
  26688. + return
  26689. + (!m_enableNpcBotsBGs && GetMap()->IsBattleground()) ||
  26690. + (!m_enableNpcBotsArenas && GetMap()->IsBattleArena()) ||
  26691. + (!m_enableNpcBotsDungeons && GetMap()->IsNonRaidDungeon()) ||
  26692. + (!m_enableNpcBotsRaids && GetMap()->IsRaid());
  26693. +}
  26694. +
  26695. +uint32 Player::GetNpcBotCost() const
  26696. +{
  26697. + return m_NpcBotsCost ? uint32((m_NpcBotsCost / 80.f) * getLevel()) : 0;
  26698. +}
  26699. +
  26700. +std::string Player::GetNpcBotCostStr() const
  26701. +{
  26702. + std::ostringstream money;
  26703. +
  26704. + if (uint32 cost = GetNpcBotCost())
  26705. + {
  26706. + uint32 gold = uint32(cost / 10000);
  26707. + cost -= (gold * 10000);
  26708. + uint32 silver = uint32(cost / 100);
  26709. + cost -= (silver * 100);
  26710. +
  26711. + if (gold != 0)
  26712. + money << gold << " |TInterface\\Icons\\INV_Misc_Coin_01:8|t";
  26713. + if (silver != 0)
  26714. + money << silver << " |TInterface\\Icons\\INV_Misc_Coin_03:8|t";
  26715. + money << cost << " |TInterface\\Icons\\INV_Misc_Coin_05:8|t";
  26716. + }
  26717. + return money.str();
  26718. +}
  26719. +
  26720. +//NPCbot base setup
  26721. +void Player::CreateNPCBot(uint8 bot_class)
  26722. +{
  26723. + //check if we have too many bots of that class
  26724. + if (HaveBot())
  26725. + {
  26726. + uint8 count = 0;
  26727. + for (uint8 i = 0; i != GetMaxNpcBots(); ++i)
  26728. + if (m_botmap[i]->m_class == bot_class)
  26729. + ++count;
  26730. + if (count >= m_maxClassNpcBots)
  26731. + {
  26732. + SendBuyError(BUY_ERR_NOT_ENOUGHT_MONEY, 0, 0, 0);
  26733. + ChatHandler ch(GetSession());
  26734. + ch.PSendSysMessage("You cannot have more bots of that class! %u of %u", count, m_maxClassNpcBots);
  26735. + ch.SetSentErrorMessage(true);
  26736. + return;
  26737. + }
  26738. + }
  26739. +
  26740. + //check if not allowed class is chosen
  26741. + if (!m_enableAllNpcBots &&
  26742. + (bot_class != CLASS_WARRIOR &&
  26743. + bot_class != CLASS_PALADIN &&
  26744. + bot_class != CLASS_PRIEST &&
  26745. + bot_class != CLASS_MAGE &&
  26746. + bot_class != CLASS_DRUID &&
  26747. + bot_class != CLASS_WARLOCK &&
  26748. + bot_class != CLASS_ROGUE))
  26749. + {
  26750. + ChatHandler ch(GetSession());
  26751. + const char* bclass;
  26752. + switch (bot_class)
  26753. + {
  26754. + case CLASS_DEATH_KNIGHT: bclass = "DeathKnight"; break;
  26755. + case CLASS_SHAMAN: bclass = "Shaman"; break;
  26756. + case CLASS_HUNTER: bclass = "Hunter"; break;
  26757. + default: bclass = "Unknown Class"; break;
  26758. + }
  26759. + sLog->outError(LOG_FILTER_PLAYER, "Player::CreateNPCBot(): Character %u tried to create npcbot of class %s, which is not allowed on your server!", GetGUIDLow(), bclass);
  26760. + ch.PSendSysMessage("You've tried to create npcbot of class %s, which is not allowed on this server!", bclass);
  26761. + ch.SetSentErrorMessage(true);
  26762. + return;
  26763. + }
  26764. +
  26765. + //check if player cannot afford a bot
  26766. + uint32 cost = GetNpcBotCost();
  26767. + if (GetMoney() < cost)
  26768. + {
  26769. + ChatHandler ch(GetSession());
  26770. + std::string str = "You don't have enough money (";
  26771. + str += GetNpcBotCostStr();
  26772. + str += ")!";
  26773. + ch.SendSysMessage(str.c_str());
  26774. + ch.SetSentErrorMessage(true);
  26775. + return;
  26776. + }
  26777. +
  26778. + PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_NPCBOT_TEMPLATE);
  26779. + std::ostringstream classStr;
  26780. +
  26781. + switch (bot_class)
  26782. + {
  26783. + case CLASS_ROGUE:
  26784. + classStr << "rogue_bot"; break;
  26785. + case CLASS_PRIEST:
  26786. + classStr << "priest_bot"; break;
  26787. + case CLASS_DRUID:
  26788. + classStr << "druid_bot"; break;
  26789. + case CLASS_SHAMAN:
  26790. + classStr << "shaman_bot"; break;
  26791. + case CLASS_MAGE:
  26792. + classStr << "mage_bot"; break;
  26793. + case CLASS_WARLOCK:
  26794. + classStr << "warlock_bot"; break;
  26795. + case CLASS_WARRIOR:
  26796. + classStr << "warrior_bot"; break;
  26797. + case CLASS_PALADIN:
  26798. + classStr << "paladin_bot"; break;
  26799. + case CLASS_HUNTER:
  26800. + classStr << "hunter_bot"; break;
  26801. + //case CLASS_DEATH_KNIGHT:
  26802. + // classStr << "dk_bot"; break;
  26803. + default:
  26804. + ChatHandler ch(GetSession());
  26805. + ch.PSendSysMessage("ERROR! unknown bot_class %u", bot_class);
  26806. + ch.SetSentErrorMessage(true);
  26807. + sLog->outError(LOG_FILTER_PLAYER, "Player::CreateNPCBot() player %u(%s) tried to create bot of unknown/unsupported class %u!", GetGUIDLow(), GetName().c_str(), bot_class);
  26808. + return;
  26809. + }
  26810. +
  26811. + stmt->setString(0, classStr.str());
  26812. + stmt->setUInt8(1, bot_class);
  26813. +
  26814. + //maybe we should remove this check? ;ÜŠ+ switch (getRace())
  26815. + {
  26816. + case RACE_NONE:
  26817. + case RACE_HUMAN:
  26818. + case RACE_DWARF:
  26819. + case RACE_NIGHTELF:
  26820. + case RACE_GNOME:
  26821. + case RACE_DRAENEI:
  26822. + stmt->setUInt8(2, uint8(1));
  26823. + stmt->setUInt8(3, uint8(3));
  26824. + stmt->setUInt8(4, uint8(4));
  26825. + stmt->setUInt8(5, uint8(7));
  26826. + stmt->setUInt8(6, uint8(11));
  26827. + break;
  26828. +
  26829. + case RACE_ORC:
  26830. + case RACE_UNDEAD_PLAYER:
  26831. + case RACE_TAUREN:
  26832. + case RACE_TROLL:
  26833. + case RACE_BLOODELF:
  26834. + stmt->setUInt8(2, uint8(2));
  26835. + stmt->setUInt8(3, uint8(5));
  26836. + stmt->setUInt8(4, uint8(6));
  26837. + stmt->setUInt8(5, uint8(8));
  26838. + stmt->setUInt8(6, uint8(10));
  26839. + break;
  26840. + }
  26841. +
  26842. + PreparedQueryResult result = WorldDatabase.Query(stmt);
  26843. + if (!result)
  26844. + {
  26845. + sLog->outFatal(LOG_FILTER_PLAYER, "Player::CreateNPCBot() CANNOT create bot of class %u, not found in DB", bot_class);
  26846. + return;
  26847. + }
  26848. +
  26849. + uint32 entry = 0;
  26850. + uint32 bot_race = 0;
  26851. +
  26852. + //find a bot to add
  26853. + //first check randomly selected bot, second check any bot we can add
  26854. + typedef std::list< std::pair<uint32, uint8> > NpcBotsDataTemplate;
  26855. + NpcBotsDataTemplate npcBotsData;
  26856. + do
  26857. + {
  26858. + Field* fields = result->Fetch();
  26859. + uint32 temp_entry = fields[0].GetUInt32();
  26860. + uint8 temp_race = fields[1].GetUInt8();
  26861. + npcBotsData.push_back(std::pair<uint32, uint8>(temp_entry, temp_race));
  26862. + } while (result->NextRow());
  26863. +
  26864. + uint32 m_rand = urand(1, uint32(result->GetRowCount()));
  26865. + uint32 tmp_rand = 1;
  26866. + std::list< std::pair<uint32, uint8> >::const_iterator itr = npcBotsData.begin();
  26867. + bool haveSameBot = false;
  26868. + bool moveback = false;
  26869. + bool forcedCheck = false;
  26870. + bool secondCheck = false;
  26871. + while (true)
  26872. + {
  26873. + if (itr == npcBotsData.end()) //end of list is reached (selected bot is checked)
  26874. + {
  26875. + moveback = true;
  26876. + --itr; //tmp_rand is not needed anymore
  26877. + continue;
  26878. + }
  26879. + if (moveback && itr == npcBotsData.begin()) //search is finished, nothing found
  26880. + break;
  26881. + if (tmp_rand == m_rand || haveSameBot)
  26882. + {
  26883. + bool canAdd = true;
  26884. + for (uint8 i = 0; i != GetMaxNpcBots(); ++i)
  26885. + {
  26886. + if (m_botmap[i]->m_entry == itr->first)
  26887. + {
  26888. + haveSameBot = true;
  26889. + canAdd = false;
  26890. + if (!secondCheck)
  26891. + forcedCheck = true;
  26892. + secondCheck = true;
  26893. + break;
  26894. + }
  26895. + }
  26896. + if (canAdd)
  26897. + {
  26898. + entry = itr->first;
  26899. + bot_race = itr->second;
  26900. + break;
  26901. + }
  26902. + if (forcedCheck)
  26903. + {
  26904. + itr = npcBotsData.begin(); //reset searcher pos
  26905. + forcedCheck = false;
  26906. + continue;
  26907. + }
  26908. + }
  26909. + //move through
  26910. + if (moveback)
  26911. + --itr;
  26912. + else
  26913. + {
  26914. + ++itr;
  26915. + ++tmp_rand;
  26916. + }
  26917. + }
  26918. +
  26919. + if (!entry || !bot_race)
  26920. + {
  26921. + ChatHandler ch(GetSession());
  26922. + ch.SendSysMessage("No more bots of this class available");
  26923. + ch.SetSentErrorMessage(true);
  26924. + return;
  26925. + }
  26926. +
  26927. + SetBotMustBeCreated(entry, bot_race, bot_class);
  26928. +
  26929. + if (cost)
  26930. + ModifyMoney(-(int32(cost)));
  26931. +}
  26932. +
  26933. void Player::RegenerateAll()
  26934. {
  26935. //if (m_regenTimer <= 500)
  26936. @@ -2941,6 +3788,73 @@ void Player::RemoveFromGroup(Group* group, uint64 guid, RemoveMethod method /* =
  26937. {
  26938. if (group)
  26939. {
  26940. + uint8 players = 0;
  26941. + Group::MemberSlotList const& members = group->GetMemberSlots();
  26942. + for (Group::member_citerator itr = members.begin(); itr!= members.end(); ++itr)
  26943. + {
  26944. + if (Player* pl = ObjectAccessor::FindPlayer(itr->guid))
  26945. + if (!pl->IsPlayerBot())
  26946. + ++players;
  26947. + }
  26948. + if (Player* player = ObjectAccessor::FindPlayer(guid))
  26949. + {
  26950. + //first remove npcbots so group will be disbanded if only 1 player
  26951. + if (player->HaveBot())
  26952. + for (uint8 i = 0; i != player->GetMaxNpcBots(); ++i)
  26953. + player->RemoveBot(player->GetBotMap(i)->m_guid, players <= 1);
  26954. + group = player->GetGroup();
  26955. + if (!group)
  26956. + return; //group has been disbanded
  26957. + //second remove playerbots (temp)
  26958. + // if player is bot remove it by master
  26959. + // if only one player logout bots
  26960. + // in other cases uninvite bots and player and reinvite them into new group
  26961. + if (player->IsPlayerBot())
  26962. + {
  26963. + group->RemoveMember(guid, method, kicker, reason);
  26964. + group = NULL;
  26965. + player->GetSession()->m_master->GetPlayerbotMgr()->LogoutPlayerBot(guid, false);
  26966. + //bot will be removed from group and logged out so NULL pointer
  26967. + return;
  26968. + }
  26969. + else if (player->HavePBot())
  26970. + {
  26971. + PlayerbotMgr* mgr = player->GetPlayerbotMgr();
  26972. + if (players <= 1)
  26973. + {
  26974. + mgr->LogoutAllBots();
  26975. + //group is no more
  26976. + return;
  26977. + }
  26978. + else
  26979. + {
  26980. + std::set<Player*> botSet;
  26981. + for (PlayerBotMap::const_iterator it = mgr->GetPlayerBotsBegin(); it != mgr->GetPlayerBotsEnd(); ++it)
  26982. + if (it->second && it->second->GetGroup() && it->second->GetGroup() == group)
  26983. + botSet.insert(it->second);
  26984. + //remove all player's bots from this group
  26985. + mgr->RemoveAllBotsFromGroup(false);
  26986. + //check if group is disbanded (players > 1 but bot can be a leader)
  26987. + group = player->GetGroup();
  26988. + if (group)
  26989. + {
  26990. + group->RemoveMember(guid, method, kicker, reason);
  26991. + group = NULL;
  26992. + }
  26993. + for (std::set<Player*>::const_iterator itr = botSet.begin(); itr != botSet.end(); ++itr)
  26994. + {
  26995. + if ((*itr)->GetPlayerbotAI())
  26996. + (*itr)->GetPlayerbotAI()->InviteToMastersGroup();
  26997. + }
  26998. + //player is uninvited, group is recreated, nothing else is needed
  26999. + return;
  27000. + }
  27001. + }
  27002. +
  27003. + group = player->GetGroup();
  27004. + if (!group)
  27005. + return; //group has been disbanded
  27006. + }
  27007. group->RemoveMember(guid, method, kicker, reason);
  27008. group = NULL;
  27009. }
  27010. @@ -3123,6 +4037,9 @@ void Player::GiveLevel(uint8 level)
  27011. }
  27012. sScriptMgr->OnPlayerLevelChanged(this, oldLevel);
  27013. +
  27014. + if (m_playerbotAI)
  27015. + m_playerbotAI->GiveLevel(level);
  27016. }
  27017. void Player::InitTalentForLevel()
  27018. @@ -4985,6 +5902,13 @@ void Player::DeleteFromDB(uint64 playerguid, uint32 accountId, bool updateRealmC
  27019. stmt->setUInt32(0, guid);
  27020. trans->Append(stmt);
  27021. + //npcbot - erase npcbots
  27022. + stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_NPCBOTS);
  27023. + stmt->setUInt32(0, guid);
  27024. + trans->Append(stmt);
  27025. + //CharacterDatabase.PExecute("DELETE FROM `character_npcbot` WHERE `owner` = '%u'", guid);
  27026. + //end npcbot
  27027. +
  27028. CharacterDatabase.CommitTransaction(trans);
  27029. break;
  27030. }
  27031. @@ -6914,6 +7838,15 @@ uint32 Player::TeamForRace(uint8 race)
  27032. void Player::setFactionForRace(uint8 race)
  27033. {
  27034. + //Playerbot
  27035. + if (GetSession()->m_master != NULL)
  27036. + {
  27037. + m_team = GetSession()->m_master->GetTeam();
  27038. + setFaction(GetSession()->m_master->getFaction());
  27039. + return;
  27040. + }
  27041. + //end Playerbot
  27042. +
  27043. m_team = TeamForRace(race);
  27044. ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(race);
  27045. @@ -11938,11 +12871,15 @@ InventoryResult Player::CanUseItem(ItemTemplate const* proto) const
  27046. if (proto)
  27047. {
  27048. + //Playerbot
  27049. + if (GetSession()->m_master == NULL)
  27050. + {
  27051. if ((proto->Flags2 & ITEM_FLAGS_EXTRA_HORDE_ONLY) && GetTeam() != HORDE)
  27052. return EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM;
  27053. if ((proto->Flags2 & ITEM_FLAGS_EXTRA_ALLIANCE_ONLY) && GetTeam() != ALLIANCE)
  27054. return EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM;
  27055. + }
  27056. if ((proto->AllowableClass & getClassMask()) == 0 || (proto->AllowableRace & getRaceMask()) == 0)
  27057. return EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM;
  27058. diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
  27059. index 6458de4..dbc4a51 100644
  27060. --- a/src/server/game/Entities/Player/Player.h
  27061. +++ b/src/server/game/Entities/Player/Player.h
  27062. @@ -52,6 +52,13 @@ class PlayerSocial;
  27063. class SpellCastTargets;
  27064. class UpdateMask;
  27065. +// NpcBot mod
  27066. +struct NpcBotMap;
  27067. +#define MAX_NPCBOTS 40
  27068. +// Playerbot mod
  27069. +class PlayerbotMgr;
  27070. +class PlayerbotAI;
  27071. +
  27072. typedef std::deque<Mail*> PlayerMails;
  27073. #define PLAYER_MAX_SKILLS 127
  27074. @@ -2288,6 +2295,57 @@ class Player : public Unit, public GridObject<Player>
  27075. //! Return collision height sent to client
  27076. float GetCollisionHeight(bool mounted) const;
  27077. + /*********************************************************/
  27078. + /*** BOT SYSTEM ***/
  27079. + /*********************************************************/
  27080. + // Playerbot mod
  27081. + PlayerTalentMap* GetTalents(uint8 spec) const { return m_talents[spec]; }
  27082. + void chompAndTrim(std::string& str);
  27083. + bool getNextQuestId(std::string const& pString, uint32& pStartPos, uint32& pId);
  27084. + void GetSkills(std::list<uint32>& m_spellsToLearn);
  27085. + void MakeTalentGlyphLink(std::ostringstream& out);
  27086. + PlayerMails::reverse_iterator GetMailRBegin() { return m_mail.rbegin(); }
  27087. + PlayerMails::reverse_iterator GetMailREnd() { return m_mail.rend(); }
  27088. + void UpdateMail();
  27089. +
  27090. + // A Player can either have a playerbotMgr (to manage its bots), or have playerbotAI (if it is a bot)
  27091. + void SetPlayerbotAI(PlayerbotAI* ai) { ASSERT(!m_playerbotAI && !m_playerbotMgr); m_playerbotAI = ai; }
  27092. + PlayerbotAI* GetPlayerbotAI() const { return m_playerbotAI; }
  27093. + void SetPlayerbotMgr(PlayerbotMgr* mgr) { ASSERT(!m_playerbotAI && !m_playerbotMgr); m_playerbotMgr = mgr; }
  27094. + PlayerbotMgr* GetPlayerbotMgr() const { return m_playerbotMgr; }
  27095. +
  27096. + bool IsPlayerBot() const { return m_playerbotAI != NULL; }
  27097. + bool HavePBot() const;
  27098. +
  27099. + //npcbot
  27100. + void RefreshBot(uint32 p_time);
  27101. + void CreateBot(uint32 botentry, uint8 botrace, uint8 botclass, bool istank = false, bool revive = false);
  27102. + void CreateNPCBot(uint8 botclass);
  27103. + uint8 GetNpcBotSlot(uint64 guid) const;
  27104. + void SendBotCommandState(Creature* cre, CommandStates state);
  27105. + bool HaveBot() const;
  27106. + void RemoveBot(uint64 guid, bool final = false, bool eraseFromDB = true);
  27107. + void SetBot(Creature* cre) { m_bot = cre; }
  27108. + uint8 GetNpcBotsCount() const;
  27109. + void SetBotMustBeCreated(uint32 m_entry, uint8 m_race, uint8 m_class, bool istank = false);
  27110. + void ClearBotMustBeCreated(uint64 value, bool guid = true, bool fully = false);
  27111. + bool GetBotMustBeCreated();
  27112. + uint64 GetBotTankGuid() const { return m_botTankGuid; }
  27113. + void SetBotTank(uint64 guid);
  27114. + Unit* GetBotTank(uint32 entry);
  27115. + uint8 GetBotFollowDist() const { return m_followdist; }
  27116. + void SetBotFollowDist(int8 dist) { m_followdist = dist; }
  27117. + void SetNpcBotDied(uint64 guid);
  27118. + NpcBotMap const* GetBotMap(uint8 pos) const { return m_botmap[pos]; }
  27119. + uint8 GetMaxNpcBots() const;
  27120. + uint8 GetNpcBotXpReduction() const { return m_xpReductionNpcBots; }
  27121. + bool RestrictBots() const;
  27122. + uint32 GetNpcBotCost() const;
  27123. + std::string GetNpcBotCostStr() const;
  27124. + /*********************************************************/
  27125. + /*** END BOT SYSTEM ***/
  27126. + /*********************************************************/
  27127. +
  27128. protected:
  27129. // Gamemaster whisper whitelist
  27130. WhisperListContainer WhisperList;
  27131. @@ -2550,6 +2608,34 @@ class Player : public Unit, public GridObject<Player>
  27132. uint8 m_grantableLevels;
  27133. private:
  27134. + /*********************************************************/
  27135. + /*** BOT SYSTEM ***/
  27136. + /*********************************************************/
  27137. + //playerbot
  27138. + PlayerbotAI* m_playerbotAI;
  27139. + PlayerbotMgr* m_playerbotMgr;
  27140. + //npcbot
  27141. + Creature* m_bot;
  27142. + int8 m_followdist;
  27143. + uint64 m_botTankGuid;
  27144. + uint8 m_maxNpcBots;
  27145. + uint8 m_maxClassNpcBots;
  27146. + uint8 m_xpReductionNpcBots;
  27147. + bool m_enableNpcBots;
  27148. + bool m_enableAllNpcBots;
  27149. + bool m_enableNpcBotsArenas;
  27150. + bool m_enableNpcBotsBGs;
  27151. + bool m_enableNpcBotsDungeons;
  27152. + bool m_enableNpcBotsRaids;
  27153. + bool m_limitNpcBotsDungeons;
  27154. + bool m_limitNpcBotsRaids;
  27155. + uint32 m_NpcBotsCost;
  27156. + uint32 m_botTimer;
  27157. + NpcBotMap* m_botmap[MAX_NPCBOTS];
  27158. + /*********************************************************/
  27159. + /*** END BOT SYSTEM ***/
  27160. + /*********************************************************/
  27161. +
  27162. // internal common parts for CanStore/StoreItem functions
  27163. InventoryResult CanStoreItem_InSpecificSlot(uint8 bag, uint8 slot, ItemPosCountVec& dest, ItemTemplate const* pProto, uint32& count, bool swap, Item* pSrcItem) const;
  27164. InventoryResult CanStoreItem_InBag(uint8 bag, ItemPosCountVec& dest, ItemTemplate const* pProto, uint32& count, bool merge, bool non_specialized, Item* pSrcItem, uint8 skip_bag, uint8 skip_slot) const;
  27165. diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp
  27166. index 87ca7a6..e7bb1c7 100644
  27167. --- a/src/server/game/Entities/Unit/Unit.cpp
  27168. +++ b/src/server/game/Entities/Unit/Unit.cpp
  27169. @@ -354,6 +354,17 @@ void Unit::Update(uint32 p_time)
  27170. m_CombatTimer -= p_time;
  27171. }
  27172. }
  27173. + // update combat timer also for npcbots
  27174. + if (isInCombat() && GetTypeId() == TYPEID_UNIT && !getVictim() && (ToCreature()->GetIAmABot() || ToCreature()->GetIAmABotsPet()))
  27175. + {
  27176. + if (m_HostileRefManager.isEmpty())
  27177. + {
  27178. + if (m_CombatTimer <= p_time)
  27179. + ClearInCombat();
  27180. + else
  27181. + m_CombatTimer -= p_time;
  27182. + }
  27183. + }
  27184. // not implemented before 3.0.2
  27185. if (uint32 base_att = getAttackTimer(BASE_ATTACK))
  27186. @@ -585,6 +596,12 @@ uint32 Unit::DealDamage(Unit* victim, uint32 damage, CleanDamage const* cleanDam
  27187. if (pet && pet->isAlive())
  27188. pet->AI()->OwnerAttackedBy(this);
  27189. + // NpcBot mod: also signal owned npcbots
  27190. + for (ControlList::const_iterator itr = victim->ToPlayer()->m_Controlled.begin(); itr != victim->ToPlayer()->m_Controlled.end(); ++itr)
  27191. + if (Creature* cre = (*itr)->ToCreature())
  27192. + if (cre->GetIAmABot() && cre->IsAIEnabled)
  27193. + cre->AI()->OwnerAttackedBy(this);
  27194. +
  27195. if (victim->ToPlayer()->GetCommandStatus(CHEAT_GOD))
  27196. return 0;
  27197. }
  27198. @@ -993,6 +1010,11 @@ void Unit::CalculateSpellDamageTaken(SpellNonMeleeDamage* damageInfo, int32 dama
  27199. case SPELL_DAMAGE_CLASS_RANGED:
  27200. case SPELL_DAMAGE_CLASS_MELEE:
  27201. {
  27202. + //Npcbot mod: apply bot damage mods
  27203. + if (Creature* bot = ToCreature())
  27204. + if (bot->GetIAmABot() || bot->GetIAmABotsPet())
  27205. + bot->ApplyBotDamageMultiplierMelee(damage, *damageInfo, spellInfo, attackType, crit);
  27206. +
  27207. // Physical Damage
  27208. if (damageSchoolMask & SPELL_SCHOOL_MASK_NORMAL)
  27209. {
  27210. @@ -1050,6 +1072,11 @@ void Unit::CalculateSpellDamageTaken(SpellNonMeleeDamage* damageInfo, int32 dama
  27211. case SPELL_DAMAGE_CLASS_NONE:
  27212. case SPELL_DAMAGE_CLASS_MAGIC:
  27213. {
  27214. + //Npcbot mod: apply bot damage mods
  27215. + if (Creature* bot = ToCreature())
  27216. + if (bot->GetIAmABot() || bot->GetIAmABotsPet())
  27217. + bot->ApplyBotDamageMultiplierSpell(damage, *damageInfo, spellInfo, attackType, crit);
  27218. +
  27219. // If crit add critical bonus
  27220. if (crit)
  27221. {
  27222. @@ -1166,6 +1193,11 @@ void Unit::CalculateMeleeDamage(Unit* victim, uint32 damage, CalcDamageInfo* dam
  27223. // Script Hook For CalculateMeleeDamage -- Allow scripts to change the Damage pre class mitigation calculations
  27224. sScriptMgr->ModifyMeleeDamage(damageInfo->target, damageInfo->attacker, damage);
  27225. + //Npcbot mod: apply bot damage mods
  27226. + if (Creature* bot = ToCreature())
  27227. + if (bot->GetIAmABot() || bot->GetIAmABotsPet())
  27228. + bot->ApplyBotDamageMultiplierMelee(damage, *damageInfo);
  27229. +
  27230. // Calculate armor reduction
  27231. if (IsDamageReducedByArmor((SpellSchoolMask)(damageInfo->damageSchoolMask)))
  27232. {
  27233. @@ -10444,6 +10476,7 @@ bool Unit::isSpellCrit(Unit* victim, SpellInfo const* spellProto, SpellSchoolMas
  27234. //! Mobs can't crit with spells. Player Totems can
  27235. //! Fire Elemental (from totem) can too - but this part is a hack and needs more research
  27236. if (IS_CREATURE_GUID(GetGUID()) && !(isTotem() && IS_PLAYER_GUID(GetOwnerGUID())) && GetEntry() != 15438)
  27237. + if (!ToCreature()->GetIAmABot())
  27238. return false;
  27239. // not critting spell
  27240. @@ -13146,6 +13179,11 @@ bool Unit::HandleStatModifier(UnitMods unitMod, UnitModifierType modifierType, f
  27241. break;
  27242. }
  27243. + if (Player* master = ToPlayer())
  27244. + for (uint8 i = 0; i != master->GetMaxNpcBots(); ++i)
  27245. + if (Creature* bot = master->GetBotMap(i)->_Cre())
  27246. + bot->SetBotShouldUpdateStats();
  27247. +
  27248. return true;
  27249. }
  27250. @@ -16595,6 +16633,182 @@ uint32 Unit::GetModelForForm(ShapeshiftForm form) const
  27251. break;
  27252. }
  27253. }
  27254. + else if (ToCreature() && ToCreature()->GetIAmABot())
  27255. + {
  27256. + Player const *player = ToCreature()->GetBotOwner();
  27257. + //let's make druids alike for each player
  27258. + switch (form)
  27259. + {
  27260. + case FORM_CAT:
  27261. + // Based on master's Hair color
  27262. + if (player->getRace() == RACE_NIGHTELF)
  27263. + {
  27264. + uint8 hairColor = player->GetByteValue(PLAYER_BYTES, 3);
  27265. + switch (hairColor)
  27266. + {
  27267. + case 7: // Violet
  27268. + case 8:
  27269. + return 29405;
  27270. + case 3: // Light Blue
  27271. + return 29406;
  27272. + case 0: // Green
  27273. + case 1: // Light Green
  27274. + case 2: // Dark Green
  27275. + return 29407;
  27276. + case 4: // White
  27277. + return 29408;
  27278. + default: // original - Dark Blue
  27279. + return 892;
  27280. + }
  27281. + }
  27282. + // Based on master's Skin color
  27283. + else if (player->getRace() == RACE_TAUREN)
  27284. + {
  27285. + uint8 skinColor = player->GetByteValue(PLAYER_BYTES, 0);
  27286. + // Male master
  27287. + if (player->getGender() == GENDER_MALE)
  27288. + {
  27289. + switch (skinColor)
  27290. + {
  27291. + case 12: // White
  27292. + case 13:
  27293. + case 14:
  27294. + case 18: // Completly White
  27295. + return 29409;
  27296. + case 9: // Light Brown
  27297. + case 10:
  27298. + case 11:
  27299. + return 29410;
  27300. + case 6: // Brown
  27301. + case 7:
  27302. + case 8:
  27303. + return 29411;
  27304. + case 0: // Dark
  27305. + case 1:
  27306. + case 2:
  27307. + case 3: // Dark Grey
  27308. + case 4:
  27309. + case 5:
  27310. + return 29412;
  27311. + default: // original - Grey
  27312. + return 8571;
  27313. + }
  27314. + }
  27315. + // Female master
  27316. + else switch (skinColor)
  27317. + {
  27318. + case 10: // White
  27319. + return 29409;
  27320. + case 6: // Light Brown
  27321. + case 7:
  27322. + return 29410;
  27323. + case 4: // Brown
  27324. + case 5:
  27325. + return 29411;
  27326. + case 0: // Dark
  27327. + case 1:
  27328. + case 2:
  27329. + case 3:
  27330. + return 29412;
  27331. + default: // original - Grey
  27332. + return 8571;
  27333. + }
  27334. + }
  27335. + else if (Player::TeamForRace(player->getRace()) == ALLIANCE)
  27336. + return 892;
  27337. + else
  27338. + return 8571;
  27339. + case FORM_DIREBEAR:
  27340. + case FORM_BEAR:
  27341. + // Based on Hair color
  27342. + if (player->getRace() == RACE_NIGHTELF)
  27343. + {
  27344. + uint8 hairColor = player->GetByteValue(PLAYER_BYTES, 3);
  27345. + switch (hairColor)
  27346. + {
  27347. + case 0: // Green
  27348. + case 1: // Light Green
  27349. + case 2: // Dark Green
  27350. + return 29413; // 29415?
  27351. + case 6: // Dark Blue
  27352. + return 29414;
  27353. + case 4: // White
  27354. + return 29416;
  27355. + case 3: // Light Blue
  27356. + return 29417;
  27357. + default: // original - Violet
  27358. + return 2281;
  27359. + }
  27360. + }
  27361. + // Based on Skin color
  27362. + else if (player->getRace() == RACE_TAUREN)
  27363. + {
  27364. + uint8 skinColor = player->GetByteValue(PLAYER_BYTES, 0);
  27365. + // Male
  27366. + if (player->getGender() == GENDER_MALE)
  27367. + {
  27368. + switch (skinColor)
  27369. + {
  27370. + case 0: // Dark (Black)
  27371. + case 1:
  27372. + case 2:
  27373. + return 29418;
  27374. + case 3: // White
  27375. + case 4:
  27376. + case 5:
  27377. + case 12:
  27378. + case 13:
  27379. + case 14:
  27380. + return 29419;
  27381. + case 9: // Light Brown/Grey
  27382. + case 10:
  27383. + case 11:
  27384. + case 15:
  27385. + case 16:
  27386. + case 17:
  27387. + return 29420;
  27388. + case 18: // Completly White
  27389. + return 29421;
  27390. + default: // original - Brown
  27391. + return 2289;
  27392. + }
  27393. + }
  27394. + // Female
  27395. + else switch (skinColor)
  27396. + {
  27397. + case 0: // Dark (Black)
  27398. + case 1:
  27399. + return 29418;
  27400. + case 2: // White
  27401. + case 3:
  27402. + return 29419;
  27403. + case 6: // Light Brown/Grey
  27404. + case 7:
  27405. + case 8:
  27406. + case 9:
  27407. + return 29420;
  27408. + case 10: // Completly White
  27409. + return 29421;
  27410. + default: // original - Brown
  27411. + return 2289;
  27412. + }
  27413. + }
  27414. + else if (Player::TeamForRace(player->getRace()) == ALLIANCE)
  27415. + return 2281;
  27416. + else
  27417. + return 2289;
  27418. + case FORM_FLIGHT:
  27419. + if (Player::TeamForRace(player->getRace()) == ALLIANCE)
  27420. + return 20857;
  27421. + return 20872;
  27422. + case FORM_FLIGHT_EPIC:
  27423. + if (Player::TeamForRace(player->getRace()) == ALLIANCE)
  27424. + return 21243;
  27425. + return 21244;
  27426. + default:
  27427. + break;
  27428. + }
  27429. + }
  27430. uint32 modelid = 0;
  27431. SpellShapeshiftEntry const* formEntry = sSpellShapeshiftStore.LookupEntry(form);
  27432. diff --git a/src/server/game/Groups/Group.cpp b/src/server/game/Groups/Group.cpp
  27433. index fe06c17..c097a38 100644
  27434. --- a/src/server/game/Groups/Group.cpp
  27435. +++ b/src/server/game/Groups/Group.cpp
  27436. @@ -110,6 +110,11 @@ bool Group::Create(Player* leader)
  27437. if (m_groupType & GROUPTYPE_RAID)
  27438. _initRaidSubGroupsCounter();
  27439. + if (leader->HaveBot())//npcbots so set to free-for-all on create
  27440. + m_lootMethod = FREE_FOR_ALL;
  27441. + else if (leader->HavePBot())//playerbots so set master loot
  27442. + m_lootMethod = MASTER_LOOT;
  27443. + else
  27444. if (!isLFGGroup())
  27445. m_lootMethod = GROUP_LOOT;
  27446. @@ -366,6 +371,7 @@ bool Group::AddMember(Player* player)
  27447. SubGroupCounterIncrease(subGroup);
  27448. if (player)
  27449. + if (IS_PLAYER_GUID(player->GetGUID()))
  27450. {
  27451. player->SetGroupInvite(NULL);
  27452. if (player->GetGroup())
  27453. @@ -409,6 +415,7 @@ bool Group::AddMember(Player* player)
  27454. sScriptMgr->OnGroupAddMember(this, player->GetGUID());
  27455. if (player)
  27456. + if (IS_PLAYER_GUID(player->GetGUID()))
  27457. {
  27458. if (!IsLeader(player->GetGUID()) && !isBGGroup() && !isBFGroup())
  27459. {
  27460. @@ -609,6 +616,9 @@ bool Group::RemoveMember(uint64 guid, const RemoveMethod &method /*= GROUP_REMOV
  27461. }
  27462. if (m_memberMgr.getSize() < ((isLFGGroup() || isBGGroup()) ? 1u : 2u))
  27463. + //npcbot
  27464. + if (GetMembersCount() < ((isBGGroup() || isLFGGroup()) ? 1u : 2u))
  27465. + //end npcbot
  27466. Disband();
  27467. return true;
  27468. diff --git a/src/server/game/Groups/Group.h b/src/server/game/Groups/Group.h
  27469. index 356a1a5..190a855 100644
  27470. --- a/src/server/game/Groups/Group.h
  27471. +++ b/src/server/game/Groups/Group.h
  27472. @@ -307,6 +307,9 @@ class Group
  27473. // FG: evil hacks
  27474. void BroadcastGroupUpdate(void);
  27475. + //Bot
  27476. + uint64 const *GetTargetIcons() const { return m_targetIcons; }
  27477. +
  27478. protected:
  27479. bool _setMembersGroup(uint64 guid, uint8 group);
  27480. void _homebindIfInstance(Player* player);
  27481. diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp
  27482. index f1ec017..ee5ace1 100644
  27483. --- a/src/server/game/Handlers/CharacterHandler.cpp
  27484. +++ b/src/server/game/Handlers/CharacterHandler.cpp
  27485. @@ -48,6 +48,10 @@
  27486. #include "WorldSession.h"
  27487. +//bot
  27488. +#include "Config.h"
  27489. +#include "bp_mgr.h"
  27490. +
  27491. class LoginQueryHolder : public SQLQueryHolder
  27492. {
  27493. private:
  27494. @@ -788,6 +792,78 @@ void WorldSession::HandlePlayerLoginOpcode(WorldPacket& recvData)
  27495. _charLoginCallback = CharacterDatabase.DelayQueryHolder((SQLQueryHolder*)holder);
  27496. }
  27497. +// Playerbot mod. Can't easily reuse HandlePlayerLoginOpcode for logging in bots because it assumes
  27498. +// a WorldSession exists for the bot. The WorldSession for a bot is created after the character is loaded.
  27499. +void PlayerbotMgr::AddPlayerBot(uint64 playerGuid)
  27500. +{
  27501. + if (sObjectAccessor->FindPlayer(playerGuid))
  27502. + return;
  27503. +
  27504. + uint32 accountId = sObjectMgr->GetPlayerAccountIdByGUID(playerGuid);
  27505. + if (accountId == 0)
  27506. + return;
  27507. +
  27508. + if (Group* group = m_master->GetGroup())
  27509. + {
  27510. + Player* leader = sObjectAccessor->FindPlayer(group->GetLeaderGUID());
  27511. + bool isLeader = (leader == m_master || group->IsLeader(m_master->GetGUID()));
  27512. + bool isFriend = (leader && leader->GetSocial()->HasFriend(m_master->GetGUID()));
  27513. + if (!isLeader && !isFriend)
  27514. + {
  27515. + ChatHandler ch(m_master->GetSession());
  27516. + ch.PSendSysMessage("Only group leader and his friends can summon playerbots");
  27517. + return;
  27518. + }
  27519. + }
  27520. +
  27521. + LoginQueryHolder* holder = new LoginQueryHolder(accountId, playerGuid);
  27522. + if (!holder->Initialize())
  27523. + {
  27524. + delete holder; // delete all unprocessed queries
  27525. + return;
  27526. + }
  27527. +
  27528. + WorldSession* mSession = m_master->GetSession();
  27529. + if (!mSession)
  27530. + {
  27531. + delete holder;
  27532. + return;
  27533. + }
  27534. +
  27535. + mSession->_botLoginCallbackSet.push_back(CharacterDatabase.DelayQueryHolder(holder));
  27536. +
  27537. + std::string name;
  27538. + sObjectMgr->GetPlayerNameByGUID(playerGuid, name);
  27539. + ChatHandler(m_master->GetSession()).PSendSysMessage("Bot %s added successfully.", name.c_str());
  27540. +}
  27541. +
  27542. +void WorldSession::HandlePlayerBotLogin(LoginQueryHolder* holder)
  27543. +{
  27544. + WorldSession* mSession = sWorld->FindSession(holder->GetAccountId());
  27545. + if (!mSession)
  27546. + {
  27547. + //sLog->outError(LOG_FILTER_PLAYER, "Failed to create bot. Session if not found!");
  27548. + delete holder;
  27549. + return;
  27550. + }
  27551. + Player* master = mSession->GetPlayer();
  27552. + if (!master)
  27553. + {
  27554. + //sLog->outError(LOG_FILTER_PLAYER, "Failed to create bot. Master if not found!");
  27555. + delete holder;
  27556. + return;
  27557. + }
  27558. + //sLog->outError(LOG_FILTER_PLAYER, "Creating bot for %s...", master->GetName().c_str());
  27559. + // The bot's WorldSession is owned by the bot's Player object
  27560. + // The bot's WorldSession is deleted by PlayerbotMgr::LogoutPlayerBot
  27561. + WorldSession* botSession = new WorldSession(holder->GetAccountId(), NULL, mSession->GetSecurity(), mSession->Expansion(), 0, mSession->GetSessionDbcLocale(), 0, false);
  27562. + //botSession->SetRemoteAddress("bot");
  27563. + botSession->m_master = master;
  27564. + botSession->m_Address = "bot";
  27565. + botSession->HandlePlayerLogin(holder); // will delete holder
  27566. + master->GetPlayerbotMgr()->OnBotLogin(botSession->GetPlayer());
  27567. +}
  27568. +
  27569. void WorldSession::HandlePlayerLogin(LoginQueryHolder* holder)
  27570. {
  27571. uint64 playerGuid = holder->GetGuid();
  27572. @@ -1020,6 +1096,28 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder* holder)
  27573. m_playerLoading = false;
  27574. + //the only place where we check if it has NPC bots
  27575. + if (ConfigMgr::GetBoolDefault("Bot.EnableNpcBots", true))
  27576. + {
  27577. + if (QueryResult result = CharacterDatabase.PQuery("SELECT entry,race,class,istank FROM `character_npcbot` WHERE `owner` = '%u'", pCurrChar->GetGUIDLow()))
  27578. + {
  27579. + uint32 m_bot_entry = 0;
  27580. + uint8 m_bot_race = 0;
  27581. + uint8 m_bot_class = 0;
  27582. + uint8 Tank = 0;
  27583. + do
  27584. + {
  27585. + Field* fields = result->Fetch();
  27586. + m_bot_entry = fields[0].GetUInt32();
  27587. + m_bot_race = fields[1].GetUInt8();
  27588. + m_bot_class = fields[2].GetInt8();
  27589. + Tank = fields[3].GetInt8();
  27590. + if (m_bot_entry && m_bot_race && m_bot_class)
  27591. + pCurrChar->SetBotMustBeCreated(m_bot_entry, m_bot_race, m_bot_class, bool(Tank));
  27592. + } while (result->NextRow());
  27593. + }
  27594. + }
  27595. +
  27596. sScriptMgr->OnPlayerLogin(pCurrChar);
  27597. delete holder;
  27598. }
  27599. diff --git a/src/server/game/Handlers/ChatHandler.cpp b/src/server/game/Handlers/ChatHandler.cpp
  27600. index b14e9ee..bd08e06 100644
  27601. --- a/src/server/game/Handlers/ChatHandler.cpp
  27602. +++ b/src/server/game/Handlers/ChatHandler.cpp
  27603. @@ -40,6 +40,9 @@
  27604. #include "ScriptMgr.h"
  27605. #include "AccountMgr.h"
  27606. +//Playerbot
  27607. +#include "bp_ai.h"
  27608. +
  27609. void WorldSession::HandleMessagechatOpcode(WorldPacket& recvData)
  27610. {
  27611. uint32 type;
  27612. @@ -293,6 +296,16 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket& recvData)
  27613. (HasPermission(RBAC_PERM_CAN_FILTER_WHISPERS) && !sender->isAcceptWhispers() && !sender->IsInWhisperWhiteList(receiver->GetGUID())))
  27614. sender->AddWhisperWhiteList(receiver->GetGUID());
  27615. + // Playerbot mod: handle whispered command to bot
  27616. + if (receiver->IsPlayerBot())
  27617. + {
  27618. + if (lang != LANG_ADDON)
  27619. + receiver->GetPlayerbotAI()->HandleCommand(msg, *GetPlayer());
  27620. + GetPlayer()->m_speakTime = 0;
  27621. + GetPlayer()->m_speakCount = 0;
  27622. + }
  27623. + else
  27624. + // End Playerbot mod
  27625. GetPlayer()->Whisper(msg, lang, receiver->GetGUID());
  27626. } break;
  27627. case CHAT_MSG_PARTY:
  27628. @@ -312,6 +325,23 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket& recvData)
  27629. sScriptMgr->OnPlayerChat(GetPlayer(), type, lang, msg, group);
  27630. + // Playerbot mod: broadcast message to bot members
  27631. + for (GroupReference* itr = group->GetFirstMember(); itr != NULL; itr = itr->next())
  27632. + {
  27633. + Player* player = itr->getSource();
  27634. + if (player && player->IsPlayerBot() &&
  27635. + ((msg.find("help",0) != std::string::npos)
  27636. + || (msg.find("gm",0) != std::string::npos)
  27637. + || (msg.find("complete",0) != std::string::npos)))
  27638. + {
  27639. + player->GetPlayerbotAI()->HandleCommand(msg, *GetPlayer());
  27640. + GetPlayer()->m_speakTime = 0;
  27641. + GetPlayer()->m_speakCount = 0;
  27642. + break;
  27643. + }
  27644. + }
  27645. + // END Playerbot mod
  27646. +
  27647. WorldPacket data;
  27648. ChatHandler::FillMessageData(&data, this, uint8(type), lang, NULL, 0, msg.c_str(), NULL);
  27649. group->BroadcastPacket(&data, false, group->GetMemberGroup(GetPlayer()->GetGUID()));
  27650. diff --git a/src/server/game/Handlers/QuestHandler.cpp b/src/server/game/Handlers/QuestHandler.cpp
  27651. index 97e13d5..7b97a98 100644
  27652. --- a/src/server/game/Handlers/QuestHandler.cpp
  27653. +++ b/src/server/game/Handlers/QuestHandler.cpp
  27654. @@ -33,6 +33,9 @@
  27655. #include "ScriptMgr.h"
  27656. #include "GameObjectAI.h"
  27657. +//Playerbot
  27658. +#include "bp_ai.h"
  27659. +
  27660. void WorldSession::HandleQuestgiverStatusQueryOpcode(WorldPacket& recvData)
  27661. {
  27662. uint64 guid;
  27663. @@ -580,8 +583,13 @@ void WorldSession::HandlePushQuestToParty(WorldPacket& recvPacket)
  27664. continue;
  27665. }
  27666. + if (player->IsPlayerBot())
  27667. + player->GetPlayerbotAI()->AcceptQuest(quest, _player);
  27668. + else
  27669. + {
  27670. player->PlayerTalkClass->SendQuestGiverQuestDetails(quest, player->GetGUID(), true);
  27671. player->SetDivider(_player->GetGUID());
  27672. + }
  27673. }
  27674. }
  27675. }
  27676. diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp
  27677. index 70942b8..dde88d0 100644
  27678. --- a/src/server/game/Maps/Map.cpp
  27679. +++ b/src/server/game/Maps/Map.cpp
  27680. @@ -2193,7 +2193,10 @@ uint32 Map::GetPlayersCountExceptGMs() const
  27681. uint32 count = 0;
  27682. for (MapRefManager::const_iterator itr = m_mapRefManager.begin(); itr != m_mapRefManager.end(); ++itr)
  27683. if (!itr->getSource()->isGameMaster())
  27684. + {
  27685. ++count;
  27686. + count += itr->getSource()->GetNpcBotsCount();
  27687. + }
  27688. return count;
  27689. }
  27690. diff --git a/src/server/game/Scripting/ScriptLoader.cpp b/src/server/game/Scripting/ScriptLoader.cpp
  27691. index 845c3fa..74f4037 100644
  27692. --- a/src/server/game/Scripting/ScriptLoader.cpp
  27693. +++ b/src/server/game/Scripting/ScriptLoader.cpp
  27694. @@ -1290,6 +1290,18 @@ void AddBattlegroundScripts()
  27695. #ifdef SCRIPTS
  27696. /* This is where custom scripts' loading functions should be declared. */
  27697. +//Bots
  27698. +void AddSC_druid_bot();
  27699. +void AddSC_hunter_bot();
  27700. +void AddSC_mage_bot();
  27701. +void AddSC_paladin_bot();
  27702. +void AddSC_priest_bot();
  27703. +void AddSC_rogue_bot();
  27704. +void AddSC_shaman_bot();
  27705. +void AddSC_warlock_bot();
  27706. +void AddSC_warrior_bot();
  27707. +void AddSC_script_bot_giver();
  27708. +void AddSC_script_bot_commands();
  27709. #endif
  27710. @@ -1297,6 +1309,18 @@ void AddCustomScripts()
  27711. {
  27712. #ifdef SCRIPTS
  27713. /* This is where custom scripts should be added. */
  27714. + //Bots
  27715. + AddSC_druid_bot();
  27716. + AddSC_hunter_bot();
  27717. + AddSC_mage_bot();
  27718. + AddSC_paladin_bot();
  27719. + AddSC_priest_bot();
  27720. + AddSC_rogue_bot();
  27721. + AddSC_shaman_bot();
  27722. + AddSC_warlock_bot();
  27723. + AddSC_warrior_bot();
  27724. + AddSC_script_bot_giver();
  27725. + AddSC_script_bot_commands();
  27726. #endif
  27727. }
  27728. diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp
  27729. index 5e91f0f..b465e78 100644
  27730. --- a/src/server/game/Server/WorldSession.cpp
  27731. +++ b/src/server/game/Server/WorldSession.cpp
  27732. @@ -47,6 +47,10 @@
  27733. #include "WardenWin.h"
  27734. #include "WardenMac.h"
  27735. +// Playerbot mod
  27736. +#include "bp_mgr.h"
  27737. +#include "bp_ai.h"
  27738. +
  27739. namespace {
  27740. std::string const DefaultPlayerName = "<none>";
  27741. @@ -129,6 +133,9 @@ WorldSession::WorldSession(uint32 id, WorldSocket* sock, AccountTypes sec, uint8
  27742. }
  27743. InitializeQueryCallbackParameters();
  27744. +
  27745. + //Playerbot mod
  27746. + m_master = NULL;
  27747. }
  27748. /// WorldSession destructor
  27749. @@ -182,6 +189,15 @@ uint32 WorldSession::GetGuidLow() const
  27750. /// Send a packet to the client
  27751. void WorldSession::SendPacket(WorldPacket const* packet)
  27752. {
  27753. + //Playerbot mod: send packet to ai/mgr
  27754. + if (Player* player = GetPlayer())
  27755. + {
  27756. + if (player->IsPlayerBot())
  27757. + player->GetPlayerbotAI()->OnBotOutgoingPacket(*packet);
  27758. + else if (PlayerbotMgr* mgr = player->GetPlayerbotMgr())
  27759. + mgr->HandleMasterOutgoingPacket(*packet);
  27760. + }
  27761. +
  27762. if (!m_Socket)
  27763. return;
  27764. @@ -255,8 +271,11 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater)
  27765. ///- Before we process anything:
  27766. /// If necessary, kick the player from the character select screen
  27767. + //Playerbot
  27768. + /*
  27769. if (IsConnectionIdle())
  27770. m_Socket->CloseSocket();
  27771. + */
  27772. ///- Retrieve packets from the receive queue and call the appropriate handlers
  27773. /// not process packets if socket already closed
  27774. @@ -313,6 +332,12 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater)
  27775. LogUnprocessedTail(packet);
  27776. }
  27777. // lag can cause STATUS_LOGGEDIN opcodes to arrive after the player started a transfer
  27778. +
  27779. + //Playerbot mod
  27780. + if (_player && _player->GetPlayerbotMgr())
  27781. + _player->GetPlayerbotMgr()->HandleMasterIncomingPacket(*packet);
  27782. + //End Playerbot mod
  27783. +
  27784. break;
  27785. case STATUS_LOGGEDIN_OR_RECENTLY_LOGGOUT:
  27786. if (!_player && !m_playerRecentlyLogout && !m_playerLogout) // There's a short delay between _player = null and m_playerRecentlyLogout = true during logout
  27787. @@ -382,6 +407,37 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater)
  27788. ProcessQueryCallbacks();
  27789. + // Playerbot mod - Process player bot packets
  27790. + // The PlayerbotAI class adds to the packet queue to simulate a real player
  27791. + // since Playerbots are known to the World obj only by its master's WorldSession object
  27792. + // we need to process all master's bot's packets.
  27793. + if (GetPlayer() && GetPlayer()->GetPlayerbotMgr())
  27794. + {
  27795. + for (PlayerBotMap::const_iterator itr = GetPlayer()->GetPlayerbotMgr()->GetPlayerBotsBegin(); itr != GetPlayer()->GetPlayerbotMgr()->GetPlayerBotsEnd(); ++itr)
  27796. + {
  27797. + Player* const botPlayer = itr->second;
  27798. + if (!botPlayer) continue;
  27799. + WorldSession* const pBotWorldSession = botPlayer->GetSession();
  27800. + if (!pBotWorldSession) continue;
  27801. +
  27802. + if (botPlayer->IsBeingTeleported())
  27803. + botPlayer->GetPlayerbotAI()->HandleTeleportAck();
  27804. + else if (botPlayer->IsInWorld())
  27805. + {
  27806. + WorldPacket* packet;
  27807. + while (!_recvQueue.empty() && _recvQueue.peek(true) != firstDelayedPacket &&
  27808. + pBotWorldSession->_recvQueue.next(packet, updater))
  27809. + {
  27810. + OpcodeHandler& opHandle = opcodeTable[packet->GetOpcode()];
  27811. + //sScriptMgr->OnPacketReceive(m_Socket, WorldPacket(*packet));
  27812. + (pBotWorldSession->*opHandle.handler)(*packet);
  27813. + delete packet;
  27814. + }
  27815. + }
  27816. + }
  27817. + }
  27818. +
  27819. +
  27820. //check if we are safe to proceed with logout
  27821. //logout procedure should happen only in World::UpdateSessions() method!!!
  27822. if (updater.ProcessLogout())
  27823. @@ -411,6 +467,28 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater)
  27824. /// %Log the player out
  27825. void WorldSession::LogoutPlayer(bool save)
  27826. {
  27827. + uint8 nBotCount = 0;
  27828. + if (_player)
  27829. + {
  27830. + //log out all player bots owned by this toon
  27831. + if (_player->HavePBot())
  27832. + _player->GetPlayerbotMgr()->LogoutAllBots();
  27833. + //logout playerbot properly (logout can be called from elsewhere)
  27834. + //bot will be erased from botmap before second logout call
  27835. + else if (_player->IsPlayerBot() && m_master && m_master->GetPlayerbotMgr()->GetPlayerBot(_player->GetGUID()))
  27836. + m_master->GetPlayerbotMgr()->LogoutPlayerBot(_player->GetGUID());
  27837. +
  27838. + //remove npcbots but do not delete from DB so it can be reaccured on next login
  27839. + for (uint8 i = 0; i != _player->GetMaxNpcBots(); ++i)
  27840. + {
  27841. + if (_player->GetBotMap(i)->_Guid())
  27842. + {
  27843. + _player->RemoveBot(_player->GetBotMap(i)->_Guid(), true, false);
  27844. + ++nBotCount;
  27845. + }
  27846. + }
  27847. + }
  27848. +
  27849. // finish pending transfers before starting the logout
  27850. while (_player && _player->IsBeingTeleportedFar())
  27851. HandleMoveWorldportAckOpcode();
  27852. @@ -504,6 +582,9 @@ void WorldSession::LogoutPlayer(bool save)
  27853. // remove player from the group if he is:
  27854. // a) in group; b) not in raid group; c) logging out normally (not being kicked or disconnected)
  27855. if (_player->GetGroup() && !_player->GetGroup()->isRaidGroup() && m_Socket)
  27856. + //bot d) if has no NpcBots or not in instance (trying to save instance)
  27857. + if (nBotCount == 0 || !_player->GetMap()->Instanceable())
  27858. + //end bot
  27859. _player->RemoveFromGroup();
  27860. //! Send update to group and reset stored max enchanting level
  27861. @@ -539,9 +620,14 @@ void WorldSession::LogoutPlayer(bool save)
  27862. sLog->outDebug(LOG_FILTER_NETWORKIO, "SESSION: Sent SMSG_LOGOUT_COMPLETE Message");
  27863. //! Since each account can only have one online character at any given time, ensure all characters for active account are marked as offline
  27864. + //Playerbot mod: except playerbots
  27865. + if (m_Address != "bot")
  27866. + {
  27867. PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ACCOUNT_ONLINE);
  27868. stmt->setUInt32(0, GetAccountId());
  27869. CharacterDatabase.Execute(stmt);
  27870. + }
  27871. + //end Playerbot mod
  27872. }
  27873. m_playerLogout = false;
  27874. @@ -1097,6 +1183,20 @@ void WorldSession::ProcessQueryCallbacks()
  27875. _charLoginCallback.cancel();
  27876. }
  27877. + //! HandlePlayerBotLogin
  27878. + if (!_botLoginCallbackSet.empty())
  27879. + {
  27880. + std::list<QueryResultHolderFuture>::iterator itr = _botLoginCallbackSet.begin();
  27881. + if (itr->ready())
  27882. + {
  27883. + SQLQueryHolder* param;
  27884. + itr->get(param);
  27885. + HandlePlayerBotLogin((LoginQueryHolder*)param);
  27886. + itr->cancel();
  27887. + _botLoginCallbackSet.erase(itr);
  27888. + }
  27889. + }
  27890. +
  27891. //! HandleAddFriendOpcode
  27892. if (_addFriendCallback.IsReady())
  27893. {
  27894. diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h
  27895. index b714cd7..79f09fc 100644
  27896. --- a/src/server/game/Server/WorldSession.h
  27897. +++ b/src/server/game/Server/WorldSession.h
  27898. @@ -187,6 +187,25 @@ class CharacterCreateInfo
  27899. uint8 CharCount;
  27900. };
  27901. +//npcbot
  27902. +struct NpcBotMap
  27903. +{
  27904. + friend class Player;
  27905. + protected:
  27906. + NpcBotMap() : m_guid(0), m_entry(0), m_race(0), m_class(0), m_creature(NULL), m_reviveTimer(0), tank(false) {}
  27907. + uint64 m_guid;
  27908. + uint32 m_entry;
  27909. + uint8 m_race;
  27910. + uint8 m_class;
  27911. + Creature* m_creature;
  27912. + uint32 m_reviveTimer;
  27913. + bool tank;
  27914. + public:
  27915. + uint64 _Guid() const { return m_guid; }
  27916. + Creature* _Cre() const { return m_creature; }
  27917. +};
  27918. +//end bot mods
  27919. +
  27920. /// Player session in the World
  27921. class WorldSession
  27922. {
  27923. @@ -194,6 +213,11 @@ class WorldSession
  27924. WorldSession(uint32 id, WorldSocket* sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale, uint32 recruiter, bool isARecruiter);
  27925. ~WorldSession();
  27926. + //Playerbot
  27927. + void HandlePlayerBotLogin(LoginQueryHolder* holder);
  27928. + std::list<QueryResultHolderFuture> _botLoginCallbackSet;
  27929. + Player* m_master;
  27930. +
  27931. bool PlayerLoading() const { return m_playerLoading; }
  27932. bool PlayerLogout() const { return m_playerLogout; }
  27933. bool PlayerLogoutWithSave() const { return m_playerLogout && m_playerSave; }
  27934. diff --git a/src/server/scripts/Spells/spell_priest.cpp b/src/server/scripts/Spells/spell_priest.cpp
  27935. index 4e86a89..fc24a20 100644
  27936. --- a/src/server/scripts/Spells/spell_priest.cpp
  27937. +++ b/src/server/scripts/Spells/spell_priest.cpp
  27938. @@ -377,6 +377,7 @@ class spell_pri_penance : public SpellScriptLoader
  27939. bool Load()
  27940. {
  27941. + if (GetCaster() && GetCaster()->GetTypeId() == TYPEID_UNIT && GetCaster()->ToCreature()->GetIAmABot()) return true;
  27942. return GetCaster()->GetTypeId() == TYPEID_PLAYER;
  27943. }
  27944. @@ -417,6 +418,8 @@ class spell_pri_penance : public SpellScriptLoader
  27945. SpellCastResult CheckCast()
  27946. {
  27947. Player* caster = GetCaster()->ToPlayer();
  27948. + if (!caster && GetCaster()->GetTypeId() == TYPEID_UNIT && GetCaster()->ToCreature()->GetIAmABot())
  27949. + caster = (Player*)GetCaster();
  27950. if (Unit* target = GetExplTargetUnit())
  27951. if (!caster->IsFriendlyTo(target) && !caster->IsValidAttackTarget(target))
  27952. return SPELL_FAILED_BAD_TARGETS;
  27953. @@ -559,6 +562,7 @@ class spell_pri_renew : public SpellScriptLoader
  27954. bool Load()
  27955. {
  27956. + if (GetCaster() && GetCaster()->GetTypeId() == TYPEID_UNIT && GetCaster()->ToCreature()->GetIAmABot()) return true;
  27957. return GetCaster() && GetCaster()->GetTypeId() == TYPEID_PLAYER;
  27958. }
  27959. diff --git a/src/server/shared/Database/Implementation/CharacterDatabase.cpp b/src/server/shared/Database/Implementation/CharacterDatabase.cpp
  27960. index 2b57693..e7e7a71 100644
  27961. --- a/src/server/shared/Database/Implementation/CharacterDatabase.cpp
  27962. +++ b/src/server/shared/Database/Implementation/CharacterDatabase.cpp
  27963. @@ -589,4 +589,12 @@ void CharacterDatabaseConnection::DoPrepareStatements()
  27964. PrepareStatement(CHAR_UPD_CHAR_PET_SLOT_BY_ID, "UPDATE character_pet SET slot = ? WHERE owner = ? AND id = ?", CONNECTION_ASYNC);
  27965. PrepareStatement(CHAR_DEL_CHAR_PET_BY_ID, "DELETE FROM character_pet WHERE id = ?", CONNECTION_ASYNC);
  27966. PrepareStatement(CHAR_DEL_CHAR_PET_BY_SLOT, "DELETE FROM character_pet WHERE owner = ? AND (slot = ? OR slot > ?)", CONNECTION_ASYNC);
  27967. +
  27968. + // Bot
  27969. + PrepareStatement(CHAR_SEL_PLAYERBOTS, "SELECT name FROM characters WHERE account = ? AND guid != ?", CONNECTION_SYNCH);
  27970. + PrepareStatement(CHAR_DEL_NPCBOT, "DELETE FROM character_npcbot WHERE owner = ? AND entry = ?", CONNECTION_ASYNC);
  27971. + PrepareStatement(CHAR_DEL_NPCBOTS, "DELETE FROM character_npcbot WHERE owner = ?", CONNECTION_ASYNC);
  27972. + PrepareStatement(CHAR_INS_NPCBOT, "INSERT INTO character_npcbot (owner, entry, race, class, istank) VALUES (?, ?, ?, ?, ?)", CONNECTION_ASYNC);
  27973. + PrepareStatement(CHAR_SEL_MAINTANK, "SELECT memberGuid, memberFlags FROM group_member WHERE guid = ?", CONNECTION_SYNCH);
  27974. + PrepareStatement(CHAR_UPD_NPCBOT_TANK, "UPDATE character_npcbot SET istank = ? WHERE owner = ? AND entry = ?", CONNECTION_ASYNC);
  27975. }
  27976. diff --git a/src/server/shared/Database/Implementation/CharacterDatabase.h b/src/server/shared/Database/Implementation/CharacterDatabase.h
  27977. index 3eb6a72..f847790 100644
  27978. --- a/src/server/shared/Database/Implementation/CharacterDatabase.h
  27979. +++ b/src/server/shared/Database/Implementation/CharacterDatabase.h
  27980. @@ -524,6 +524,14 @@ enum CharacterDatabaseStatements
  27981. CHAR_DEL_ITEMCONTAINER_MONEY,
  27982. CHAR_INS_ITEMCONTAINER_MONEY,
  27983. + // Bot
  27984. + CHAR_SEL_PLAYERBOTS,
  27985. + CHAR_DEL_NPCBOT,
  27986. + CHAR_DEL_NPCBOTS,
  27987. + CHAR_INS_NPCBOT,
  27988. + CHAR_SEL_MAINTANK,
  27989. + CHAR_UPD_NPCBOT_TANK,
  27990. +
  27991. MAX_CHARACTERDATABASE_STATEMENTS
  27992. };
  27993. diff --git a/src/server/shared/Database/Implementation/WorldDatabase.cpp b/src/server/shared/Database/Implementation/WorldDatabase.cpp
  27994. index 89f3cf8..f768146 100644
  27995. --- a/src/server/shared/Database/Implementation/WorldDatabase.cpp
  27996. +++ b/src/server/shared/Database/Implementation/WorldDatabase.cpp
  27997. @@ -92,4 +92,8 @@ void WorldDatabaseConnection::DoPrepareStatements()
  27998. PrepareStatement(WORLD_INS_DISABLES, "INSERT INTO disables (entry, sourceType, flags, comment) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC);
  27999. PrepareStatement(WORLD_SEL_DISABLES, "SELECT entry FROM disables WHERE entry = ? AND sourceType = ?", CONNECTION_SYNCH);
  28000. PrepareStatement(WORLD_DEL_DISABLES, "DELETE FROM disables WHERE entry = ? AND sourceType = ?", CONNECTION_ASYNC);
  28001. +
  28002. + // Bot
  28003. + PrepareStatement(WORLD_SEL_NPCBOT_TEMPLATE, "SELECT entry, trainer_race FROM creature_template WHERE scriptname = ? and trainer_class = ? and trainer_race IN (?, ?, ?, ?, ?)", CONNECTION_SYNCH);
  28004. + PrepareStatement(WORLD_SEL_NPCBOT_PET_LEVELSTATS, "SELECT hp, mana, armor, str, agi, sta, inte, spi FROM pet_levelstats WHERE creature_entry = ? AND level = ?", CONNECTION_SYNCH);
  28005. }
  28006. diff --git a/src/server/shared/Database/Implementation/WorldDatabase.h b/src/server/shared/Database/Implementation/WorldDatabase.h
  28007. index 032baf2..6464084 100644
  28008. --- a/src/server/shared/Database/Implementation/WorldDatabase.h
  28009. +++ b/src/server/shared/Database/Implementation/WorldDatabase.h
  28010. @@ -114,6 +114,10 @@ enum WorldDatabaseStatements
  28011. WORLD_INS_DISABLES,
  28012. WORLD_DEL_DISABLES,
  28013. + // Bot
  28014. + WORLD_SEL_NPCBOT_TEMPLATE,
  28015. + WORLD_SEL_NPCBOT_PET_LEVELSTATS,
  28016. +
  28017. MAX_WORLDDATABASE_STATEMENTS
  28018. };
  28019. diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist
  28020. index 0802aca..4ec064f 100644
  28021. --- a/src/server/worldserver/worldserver.conf.dist
  28022. +++ b/src/server/worldserver/worldserver.conf.dist
  28023. @@ -2759,3 +2759,196 @@ Log.Async.Enable = 0
  28024. #
  28025. ###################################################################################################
  28026. +
  28027. +
  28028. +###################################################################################################
  28029. +################################## BOTS CONFIGURATION ############################################
  28030. +###################################################################################################
  28031. +# PLAYERBOT CONFIGURATION
  28032. +#
  28033. +# Bot.EnablePlayerBots
  28034. +# Disable the bot command and bot menu
  28035. +# Default: 0 - off
  28036. +# 1 - on
  28037. +
  28038. +Bot.EnablePlayerBots = 0
  28039. +
  28040. +# Bot.DebugWhisper
  28041. +# Enable debug output by whispering master
  28042. +# Default: 0 - off
  28043. +# 1 - on
  28044. +
  28045. +Bot.DebugWhisper = 0
  28046. +
  28047. +# Bot.FollowDistanceMin
  28048. +# Bot.FollowDistanceMax
  28049. +# Min. and Max. follow distance for bots
  28050. +# Default: 0.5 / 1.0
  28051. +
  28052. +Bot.FollowDistanceMin = 0.5
  28053. +Bot.FollowDistanceMax = 1.0
  28054. +
  28055. +# Bot.MaxPlayerbots
  28056. +# Limits the number of bots per account (Max 9)
  28057. +# Default: 9
  28058. +
  28059. +Bot.MaxPlayerbots = 9
  28060. +
  28061. +# Bot.RestrictBotLevel
  28062. +# Restrict the allowed bot level (Current Max 80)
  28063. +# Default: 80
  28064. +
  28065. +Bot.RestrictBotLevel = 80
  28066. +
  28067. +# Bot.Collect.Combat
  28068. +# Bot.Collect.Quest
  28069. +# Bot.Collect.Profession
  28070. +# Bot.Collect.Loot
  28071. +# Bot.Collect.Skin
  28072. +# Bot.Collect.Objects
  28073. +# Enable collection options for after combat, quest items, profession, all loot, skin, or nearby objects
  28074. +# 0 - off
  28075. +# Default: 1 - on
  28076. +
  28077. +Bot.Collect.Combat = 1
  28078. +Bot.Collect.Quest = 1
  28079. +Bot.Collect.Profession = 1
  28080. +Bot.Collect.Loot = 1
  28081. +Bot.Collect.Skin = 1
  28082. +Bot.Collect.Objects = 1
  28083. +
  28084. +# Bot.Collect.DistanceMax
  28085. +# Maximum distance that can be configured for bots to collect objects. Allowing a higher
  28086. +# distance could increase processor usage for object searching.
  28087. +# 1-100
  28088. +# Default: 50
  28089. +
  28090. +Bot.Collect.DistanceMax = 50
  28091. +
  28092. +# Bot.Collect.Distance
  28093. +# Default distance that bots will search within for collection.
  28094. +# Default: 25 (cannot be more than DistanceMax)
  28095. +
  28096. +Bot.Collect.Distance = 25
  28097. +
  28098. +# Bot.SellGarbage
  28099. +# Allow bots to automatically sell all [GRAY|POOR] quality items as the player activates vendor
  28100. +# Default: 0 - off
  28101. +# 1 - on
  28102. +
  28103. +Bot.SellGarbage = 0
  28104. +
  28105. +# Bot.SellAll.LevelDiff
  28106. +# If 'sell all' command is given prior to selling, bots will sell all low level white items.. this number is the number
  28107. +# of levels LOWER than the bots level the Item must be before bot will sell it.
  28108. +# Default: 10 (10 levels lower than the bot) Don't set to 0 or they'll sell everything! *SellGarbage must be set to 1 to use this*
  28109. +
  28110. +Bot.SellAll.LevelDiff = 10
  28111. +
  28112. +#
  28113. +###################################################################################################
  28114. +
  28115. +###################################################################################################
  28116. +# NPCBOT CONFIGURATION
  28117. +#
  28118. +# Bot.EnableNpcBots
  28119. +# Enable NpcBot system
  28120. +# Default: 1 - enable
  28121. +# 0 - disable
  28122. +
  28123. +Bot.EnableNpcBots = 1
  28124. +
  28125. +# Bot.MaxNpcBots
  28126. +# Maximum number of Npc Bots allowed per character (disabled for GM accounts)
  28127. +# Default: 1
  28128. +# Recomended: 4
  28129. +# Max: 9
  28130. +# Absolute Max: 39
  28131. +
  28132. +Bot.MaxNpcBots = 1
  28133. +
  28134. +# Bot.MaxNpcBotsPerClass
  28135. +# Maximum Npc Bots of each class allowed per character
  28136. +# If set to 0, no restriction
  28137. +# Default: 1
  28138. +
  28139. +Bot.MaxNpcBotsPerClass = 1
  28140. +
  28141. +# Bot.BaseFollowDistance
  28142. +# Default follow distance set at login
  28143. +# Default: 30
  28144. +
  28145. +Bot.BaseFollowDistance = 30
  28146. +
  28147. +# Bot.XpReductionPercent
  28148. +# Since bot party can be pretty large, it can become an exploit to farm xp so you can reduce xp gain here
  28149. +# PERCENT of 'XP.KILL' reward reduction from each Npc Bots used (Starting with second)
  28150. +# Example:
  28151. +# You have 3 bots, xp reduction is 20 then reduction will be ((3-1)*20) = 40%; 60% exp gained only
  28152. +# Note: Minimum xp rate will be 10%
  28153. +# Min: 0
  28154. +# Max: 90
  28155. +# Default: 0
  28156. +
  28157. +Bot.XpReductionPercent = 0
  28158. +
  28159. +# Bot.HealTargetIconsMask
  28160. +# Icon number bitmask which bots are using to search for additional targets to heal (out of party)
  28161. +# 1 - Star
  28162. +# 2 - Circle
  28163. +# 4 - Diamond
  28164. +# 8 - Triangle
  28165. +# 16 - Moon
  28166. +# 32 - Square
  28167. +# 64 - Cross
  28168. +# 128 - Skull
  28169. +# Example: to check Star, Triangle and Square we need 1+8+32 = 41
  28170. +# Note that many creatures cannot accept heal
  28171. +# Min: 0 (Disable)
  28172. +# Max: 255 (Any Icon)
  28173. +# Default: 8 (Triangle)
  28174. +
  28175. +Bot.HealTargetIconsMask = 8
  28176. +
  28177. +# Bot.DamageMult
  28178. +# Myltiplier for bot's damage dealt. Allows to balance bots' compared to players' damage
  28179. +# Any damage done by bots will be modified
  28180. +# Range: 0.01 - 10.0
  28181. +# Default: 1.0
  28182. +
  28183. +Bot.DamageMult.Melee = 1.0
  28184. +Bot.DamageMult.Spell = 1.0
  28185. +
  28186. +# Bot.EnableIn... Arenas/BGs/Dungeons/Raids
  28187. +# Allows to restrict bots usage in PvE and/or PvP
  28188. +# Default: true for all
  28189. +
  28190. +Bot.EnableInArenas = 1
  28191. +Bot.EnableInBGs = 1
  28192. +Bot.EnableInDungeons = 1
  28193. +Bot.EnableInRaids = 1
  28194. +
  28195. +# Bot.InstanceLimit... Dungeons/Raids
  28196. +# If set to 1 will apply instance players limitation to bots
  28197. +# Default: false for all
  28198. +
  28199. +Bot.InstanceLimit.Dungeons = 0
  28200. +Bot.InstanceLimit.Raids = 0
  28201. +
  28202. +# Bot.Cost
  28203. +# Bot recruitment cost (in copper)
  28204. +# Note: this value is set for lvl 80 characters. Cost will be reduced for lower levels
  28205. +# Default: 0
  28206. +
  28207. +Bot.Cost = 0
  28208. +
  28209. +# Bot.AllowAllClasses
  28210. +# If set to 0 will not allow to create bots (through command) with classes which not yet implemented (in development)
  28211. +# and not shown in botgiver's dialog
  28212. +# Warning: Enabling this can cause crashes!
  28213. +# Default: 0
  28214. +
  28215. +Bot.AllowAllClasses = 0
  28216. +
  28217. +#
  28218. +###################################################################################################
  28219. \ No newline at end of file
  28220. --
  28221. 1.7.6.msysgit.0